Skip to content

feat!: add generate keypair helper #887

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions packages/did/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,13 @@
"@digitalbazaar/multikey-context": "^2.0.1",
"@digitalbazaar/security-context": "^1.0.1",
"@kiltprotocol/config": "workspace:*",
"@kiltprotocol/jcs-data-integrity-proofs-common": "0.1.0-rc.4",
"@kiltprotocol/types": "workspace:*",
"@kiltprotocol/utils": "workspace:*",
"@polkadot/api": "^12.0.0",
"@polkadot/keyring": "^13.0.0",
"@polkadot/types": "^12.0.0",
"@polkadot/util": "^13.0.0",
"@polkadot/util-crypto": "^13.0.0",
"varint": "^6.0.0"
"@polkadot/util-crypto": "^13.0.0"
},
"peerDependenciesMeta": {
"@kiltprotocol/augment-api": {
Expand Down
32 changes: 19 additions & 13 deletions packages/did/src/Did.chain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@
import type { Option } from '@polkadot/types'
import type { AccountId32, Extrinsic, Hash } from '@polkadot/types/interfaces'
import type { AnyNumber } from '@polkadot/types/types'

import type {
DidDidDetails,
DidDidDetailsDidAuthorizedCallOperation,
DidDidDetailsDidPublicKeyDetails,
DidServiceEndpointsDidEndpoint,
KiltSupportDeposit,
} from '@kiltprotocol/augment-api'
import { ConfigService } from '@kiltprotocol/config'
import type {
BN,
Deposit,
Expand All @@ -26,23 +28,27 @@ import type {
SubmittableExtrinsic,
UriFragment,
} from '@kiltprotocol/types'
import { ConfigService } from '@kiltprotocol/config'
import { Crypto, SDKErrors, Signers, ss58Format } from '@kiltprotocol/utils'
import {
Crypto,
Multikey,
SDKErrors,
Signers,
ss58Format,
} from '@kiltprotocol/utils'

import {
getAddressFromVerificationMethod,
getFullDid,
parse,
} from './Did.utils.js'
import type {
DidEncryptionMethodType,
NewService,
DidSigningMethodType,
NewDidVerificationKey,
NewDidEncryptionKey,
NewDidVerificationKey,
NewService,
} from './DidDetails/DidDetails.js'
import { isValidVerificationMethodType } from './DidDetails/DidDetails.js'
import {
keypairToMultibaseKey,
getAddressFromVerificationMethod,
getFullDid,
parse,
} from './Did.utils.js'
import {
type NewLightDidVerificationKey,
createLightDidDocument,
Expand Down Expand Up @@ -395,9 +401,9 @@ export async function getStoreTx(
}

const [authenticationKey] = authentication
const did = getAddressFromVerificationMethod({
publicKeyMultibase: keypairToMultibaseKey(authenticationKey),
})
const did = getAddressFromVerificationMethod(
Multikey.encodeMultibaseKeypair(authenticationKey)
)

const newAttestationKey =
assertionMethod &&
Expand Down
7 changes: 4 additions & 3 deletions packages/did/src/Did.signature.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import type {
SignerInterface,
} from '@kiltprotocol/types'

import { Crypto, SDKErrors, Signers } from '@kiltprotocol/utils'
import { Crypto, Multikey, SDKErrors, Signers } from '@kiltprotocol/utils'
import { randomAsHex, randomAsU8a } from '@polkadot/util-crypto'

import type { NewLightDidVerificationKey } from './DidDetails'
Expand All @@ -26,7 +26,7 @@ import {
verifyDidSignature,
} from './Did.signature'
import { resolve } from './DidResolver/DidResolver'
import { keypairToMultibaseKey, multibaseKeyToDidKey, parse } from './Did.utils'
import { multibaseKeyToDidKey, parse } from './Did.utils'
import { createLightDidDocument } from './DidDetails'

jest.mock('./DidResolver/DidResolver')
Expand Down Expand Up @@ -292,7 +292,8 @@ describe('full DID', () => {
{
controller: `did:kilt:${keypair.address}`,
id: `did:kilt:${keypair.address}#0x12345`,
publicKeyMultibase: keypairToMultibaseKey(keypair),
publicKeyMultibase:
Multikey.encodeMultibaseKeypair(keypair).publicKeyMultibase,
type: 'Multikey',
},
],
Expand Down
11 changes: 2 additions & 9 deletions packages/did/src/Did.signature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type {
DidSignature,
DidUrl,
KeyringPair,
MultibaseKeyPair,
SignatureVerificationRelationship,
SignerInterface,
} from '@kiltprotocol/types'
Expand Down Expand Up @@ -190,15 +191,7 @@ export function signatureFromJson(input: DidSignature | LegacyDidSignature): {
*/
export async function signersForDid(
didDocument: DidDocument,
...keypairs: Array<
| SignerInterface
| KeyringPair
| Keypair
| {
secretKeyMultibase: `z${string}`
publicKeyMultibase: `z${string}`
}
>
...keypairs: Array<SignerInterface | KeyringPair | Keypair | MultibaseKeyPair>
): Promise<Array<SignerInterface<string, DidUrl>>> {
const didKeys = didDocument.verificationMethod?.map(
({ publicKeyMultibase, id }) => ({
Expand Down
119 changes: 19 additions & 100 deletions packages/did/src/Did.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,15 @@
* found in the LICENSE file in the root directory of this source tree.
*/

import { blake2AsU8a, encodeAddress, base58Encode } from '@polkadot/util-crypto'
import { u8aConcat } from '@polkadot/util'
import type {
Base58BtcMultibaseString,
Did,
DidUrl,
KeyringPair,
KiltAddress,
UriFragment,
VerificationMethod,
} from '@kiltprotocol/types'
import { DataUtils, SDKErrors, ss58Format } from '@kiltprotocol/utils'
// @ts-expect-error Not a typescript module
import * as varint from 'varint'
import { decodeBase58BtcMultikey } from '@kiltprotocol/jcs-data-integrity-proofs-common'
import { DataUtils, Multikey, SDKErrors, ss58Format } from '@kiltprotocol/utils'
import { blake2AsU8a, encodeAddress } from '@polkadot/util-crypto'

import type { DidVerificationMethodType } from './DidDetails/DidDetails.js'
import { parseDocumentFromLightDid } from './DidDetails/LightDidDetails.js'
Expand Down Expand Up @@ -133,24 +127,8 @@ type DecodedVerificationMethod = {
keyType: DidVerificationMethodType
}

const MULTICODEC_ECDSA_PREFIX = 0xe7
const MULTICODEC_X25519_PREFIX = 0xec
const MULTICODEC_ED25519_PREFIX = 0xed
const MULTICODEC_SR25519_PREFIX = 0xef

const multicodecPrefixes: Record<number, [DidVerificationMethodType, number]> =
{
[MULTICODEC_ECDSA_PREFIX]: ['ecdsa', 33],
[MULTICODEC_X25519_PREFIX]: ['x25519', 32],
[MULTICODEC_ED25519_PREFIX]: ['ed25519', 32],
[MULTICODEC_SR25519_PREFIX]: ['sr25519', 32],
}
const multicodecReversePrefixes: Record<DidVerificationMethodType, number> = {
ecdsa: MULTICODEC_ECDSA_PREFIX,
x25519: MULTICODEC_X25519_PREFIX,
ed25519: MULTICODEC_ED25519_PREFIX,
sr25519: MULTICODEC_SR25519_PREFIX,
}
const KEY_LENGTH_ECDSA = 33
const KEY_LENGTH_OTHER = 32

/**
* Decode a Multikey representation of a verification method into its fundamental components: the public key and the key type.
Expand All @@ -161,68 +139,20 @@ const multicodecReversePrefixes: Record<DidVerificationMethodType, number> = {
export function multibaseKeyToDidKey(
publicKeyMultibase: VerificationMethod['publicKeyMultibase']
): DecodedVerificationMethod {
const { keyBytes, prefix } = decodeBase58BtcMultikey(publicKeyMultibase)

const [keyType, expectedPublicKeyLength] = multicodecPrefixes[prefix]
if (keyType === undefined) {
const { publicKey, type } = Multikey.decodeMultibaseKeypair({
publicKeyMultibase,
})
const expectedPublicKeyLength =
type === 'secp256k1' ? KEY_LENGTH_ECDSA : KEY_LENGTH_OTHER
if (publicKey.length !== expectedPublicKeyLength) {
throw new SDKErrors.DidError(
`Cannot decode key type for multibase key "${publicKeyMultibase}".`
)
}
if (keyBytes.length !== expectedPublicKeyLength) {
throw new SDKErrors.DidError(
`Key of type "${keyType}" is expected to be ${expectedPublicKeyLength} bytes long. Provided key is ${keyBytes.length} bytes long instead.`
`Key of type "${type}" is expected to be ${expectedPublicKeyLength} bytes long. Provided key is ${publicKey.length} bytes long instead.`
)
}
return {
keyType,
publicKey: keyBytes,
}
}

// TODO: This could also be exposed in a new release candidate of the `@kiltprotocol/jcs-data-integrity-proofs-common` package.
function multibase58BtcKeyBytesEncoding(
key: Uint8Array,
keyPrefix: number
): Base58BtcMultibaseString {
const varintEncodedPrefix = varint.encode(keyPrefix)
const prefixedKey = u8aConcat(varintEncodedPrefix, key)
const base58BtcEncodedKey = base58Encode(prefixedKey)
return `z${base58BtcEncodedKey}`
}

/**
* Calculate the Multikey representation of a keypair given its type and public key.
*
* @param keypair The input keypair to encode as Multikey.
* @param keypair.type The keypair {@link DidVerificationMethodType}.
* @param keypair.publicKey The keypair public key.
* @returns The Multikey representation (i.e., multicodec-prefixed, then multibase encoded) of the provided keypair.
*/
export function keypairToMultibaseKey({
type,
publicKey,
}: Pick<KeyringPair, 'publicKey'> & {
type: DidVerificationMethodType
}): VerificationMethod['publicKeyMultibase'] {
const multiCodecPublicKeyPrefix = multicodecReversePrefixes[type]
if (multiCodecPublicKeyPrefix === undefined) {
throw new SDKErrors.DidError(
`The provided key type "${type}" is not supported.`
)
}
const expectedPublicKeySize = multicodecPrefixes[multiCodecPublicKeyPrefix][1]
if (publicKey.length !== expectedPublicKeySize) {
throw new SDKErrors.DidError(
`Key of type "${type}" is expected to be ${expectedPublicKeySize} bytes long. Provided key is ${publicKey.length} bytes long instead.`
)
}
const prefixedEncodedPublicKey = multibase58BtcKeyBytesEncoding(
keyType: type === 'secp256k1' ? 'ecdsa' : type,
publicKey,
multiCodecPublicKeyPrefix
)

return prefixedEncodedPublicKey
}
}

/**
Expand All @@ -240,28 +170,16 @@ export function didKeyToVerificationMethod<IdType extends DidUrl | UriFragment>(
id: IdType,
{ keyType, publicKey }: DecodedVerificationMethod
): VerificationMethod<IdType> {
const multiCodecPublicKeyPrefix = multicodecReversePrefixes[keyType]
if (multiCodecPublicKeyPrefix === undefined) {
throw new SDKErrors.DidError(
`Provided key type "${keyType}" not supported.`
)
}
const expectedPublicKeySize = multicodecPrefixes[multiCodecPublicKeyPrefix][1]
if (publicKey.length !== expectedPublicKeySize) {
throw new SDKErrors.DidError(
`Key of type "${keyType}" is expected to be ${expectedPublicKeySize} bytes long. Provided key is ${publicKey.length} bytes long instead.`
)
}
const prefixedEncodedPublicKey = multibase58BtcKeyBytesEncoding(
const { publicKeyMultibase } = Multikey.encodeMultibaseKeypair({
publicKey,
multiCodecPublicKeyPrefix
)
type: keyType,
})

return {
controller,
id,
type: 'Multikey',
publicKeyMultibase: prefixedEncodedPublicKey,
publicKeyMultibase,
}
}

Expand Down Expand Up @@ -340,7 +258,8 @@ export function getAddressFromVerificationMethod({

// Otherwise it’s ecdsa.
// Taken from https://github.com/polkadot-js/common/blob/master/packages/keyring/src/pair/index.ts#L44
const address = publicKey.length > 32 ? blake2AsU8a(publicKey) : publicKey
const address =
publicKey.length > KEY_LENGTH_OTHER ? blake2AsU8a(publicKey) : publicKey
return encodeAddress(address, ss58Format)
}

Expand Down
29 changes: 14 additions & 15 deletions packages/did/src/DidDetails/LightDidDetails.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,12 @@
*/

import type { DidDocument, Did, DidUrl } from '@kiltprotocol/types'

import { Crypto } from '@kiltprotocol/utils'
import { Crypto, Multikey } from '@kiltprotocol/utils'

import type { NewService } from './DidDetails.js'
import type { CreateDocumentInput } from './LightDidDetails.js'

import { keypairToMultibaseKey, parse } from '../Did.utils.js'
import { parse } from '../Did.utils.js'
import {
createLightDidDocument,
parseDocumentFromLightDid,
Expand Down Expand Up @@ -62,19 +61,19 @@ describe('When creating an instance from the details', () => {
{
controller: did,
id: `${did}#authentication`,
publicKeyMultibase: keypairToMultibaseKey({
publicKeyMultibase: Multikey.encodeMultibaseKeypair({
publicKey: authKey.publicKey,
type: 'sr25519',
}),
}).publicKeyMultibase,
type: 'Multikey',
},
{
controller: did,
id: `${did}#encryption`,
publicKeyMultibase: keypairToMultibaseKey({
publicKeyMultibase: Multikey.encodeMultibaseKeypair({
publicKey: encKey.publicKey,
type: 'x25519',
}),
}).publicKeyMultibase,
type: 'Multikey',
},
],
Expand Down Expand Up @@ -115,19 +114,19 @@ describe('When creating an instance from the details', () => {
{
controller: did,
id: `${did}#authentication`,
publicKeyMultibase: keypairToMultibaseKey({
publicKeyMultibase: Multikey.encodeMultibaseKeypair({
publicKey: authKey.publicKey,
type: 'ed25519',
}),
}).publicKeyMultibase,
type: 'Multikey',
},
{
controller: did,
id: `${did}#encryption`,
publicKeyMultibase: keypairToMultibaseKey({
publicKeyMultibase: Multikey.encodeMultibaseKeypair({
publicKey: encKey.publicKey,
type: 'x25519',
}),
}).publicKeyMultibase,
type: 'Multikey',
},
],
Expand Down Expand Up @@ -197,19 +196,19 @@ describe('When creating an instance from a light DID', () => {
{
controller: did,
id: `${did}#authentication`,
publicKeyMultibase: keypairToMultibaseKey({
publicKeyMultibase: Multikey.encodeMultibaseKeypair({
publicKey: authKey.publicKey,
type: 'sr25519',
}),
}).publicKeyMultibase,
type: 'Multikey',
},
{
controller: did,
id: `${did}#encryption`,
publicKeyMultibase: keypairToMultibaseKey({
publicKeyMultibase: Multikey.encodeMultibaseKeypair({
publicKey: encKey.publicKey,
type: 'x25519',
}),
}).publicKeyMultibase,
type: 'Multikey',
},
],
Expand Down
Loading