From 52e5f37c4f4b94f595655fa071e29879f5d5ab26 Mon Sep 17 00:00:00 2001 From: Raphael Flechtner Date: Thu, 18 Jul 2024 13:15:24 +0200 Subject: [PATCH 1/8] feat: generateKeypair helper --- packages/sdk-js/src/index.ts | 3 ++- packages/utils/src/Signers.ts | 35 ++++++++++++++++++++++++++++++++--- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/packages/sdk-js/src/index.ts b/packages/sdk-js/src/index.ts index cef20b3a2..c1bd06620 100644 --- a/packages/sdk-js/src/index.ts +++ b/packages/sdk-js/src/index.ts @@ -22,7 +22,7 @@ import { resolver as DidResolver } from '@kiltprotocol/did' import * as DidHelpers from './DidHelpers/index.js' const { signAndSubmitTx } = Blockchain // TODO: maybe we don't even need that if we have the identity class -const { signerFromKeypair } = Signers +const { signerFromKeypair, generateKeypair } = Signers export { init, @@ -33,6 +33,7 @@ export { Verifier, Issuer, signerFromKeypair, + generateKeypair, signAndSubmitTx, ConfigService, DidHelpers, diff --git a/packages/utils/src/Signers.ts b/packages/utils/src/Signers.ts index e62400562..5f00a6ffa 100644 --- a/packages/utils/src/Signers.ts +++ b/packages/utils/src/Signers.ts @@ -17,6 +17,7 @@ import { import { blake2AsU8a, encodeAddress, + randomAsHex, secp256k1Sign, } from '@polkadot/util-crypto' import type { Keypair } from '@polkadot/util-crypto/types' @@ -29,13 +30,11 @@ import { createSigner as es256kSigner, cryptosuite as es256kSuite, } from '@kiltprotocol/es256k-jcs-2023' -// import { decodeMultikeyVerificationMethod } from '@kiltprotocol/jcs-data-integrity-proofs-common' import { createSigner as sr25519Signer, cryptosuite as sr25519Suite, } from '@kiltprotocol/sr25519-jcs-2023' -// import { multibaseKeyToDidKey } from '@kiltprotocol/did' import type { DidDocument, DidUrl, @@ -43,7 +42,7 @@ import type { SignerInterface, UriFragment, } from '@kiltprotocol/types' - +import { makeKeypairFromUri } from './Crypto.js' import { DidError, NoSuitableSignerError } from './SDKErrors.js' export const ALGORITHMS = Object.freeze({ @@ -469,3 +468,33 @@ export function getPolkadotSigner( }, } } + +export function generateKeypair(args?: { + seed?: string + type?: T +}): Keypair & { type: T } +export function generateKeypair({ + seed = randomAsHex(32), + type = 'ed25519', +}: { + seed?: string + type?: string +} = {}): Keypair & { type: string } { + let typeForKeyring = type as KeyringPair['type'] + switch (type.toLowerCase()) { + case 'secp256k1': + typeForKeyring = 'ecdsa' + break + case 'x25519': + typeForKeyring = 'ed25519' + break + default: + } + + const keyRingPair = makeKeypairFromUri( + seed.toLowerCase(), + typeForKeyring as any + ) + const { secretKey, publicKey } = extractPk(keyRingPair) + return { secretKey, publicKey, type } +} From 035db971fc439bdfeeedceb0dec15c72280d09d2 Mon Sep 17 00:00:00 2001 From: Raphael Flechtner Date: Thu, 18 Jul 2024 15:05:46 +0200 Subject: [PATCH 2/8] refactor!: switch to using multikey encoding --- packages/did/package.json | 4 +- packages/did/src/Did.chain.ts | 32 ++-- packages/did/src/Did.signature.spec.ts | 2 +- packages/did/src/Did.signature.ts | 11 +- packages/did/src/Did.utils.ts | 114 ++---------- .../did/src/DidDetails/LightDidDetails.ts | 26 ++- packages/sdk-js/src/DidHelpers/interfaces.ts | 22 +-- packages/sdk-js/src/index.ts | 4 +- packages/types/src/Signers.ts | 8 + packages/utils/package.json | 4 +- packages/utils/src/Multikey.ts | 162 ++++++++++++++++++ packages/utils/src/Signers.ts | 36 ++-- packages/utils/src/index.ts | 1 + yarn.lock | 4 +- 14 files changed, 257 insertions(+), 173 deletions(-) create mode 100644 packages/utils/src/Multikey.ts diff --git a/packages/did/package.json b/packages/did/package.json index bffc9bed9..2437a65e6 100644 --- a/packages/did/package.json +++ b/packages/did/package.json @@ -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": { diff --git a/packages/did/src/Did.chain.ts b/packages/did/src/Did.chain.ts index 079023915..967b58ebb 100644 --- a/packages/did/src/Did.chain.ts +++ b/packages/did/src/Did.chain.ts @@ -8,6 +8,7 @@ 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, @@ -15,6 +16,7 @@ import type { DidServiceEndpointsDidEndpoint, KiltSupportDeposit, } from '@kiltprotocol/augment-api' +import { ConfigService } from '@kiltprotocol/config' import type { BN, Deposit, @@ -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, @@ -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 && diff --git a/packages/did/src/Did.signature.spec.ts b/packages/did/src/Did.signature.spec.ts index 79fc31936..506644a5e 100644 --- a/packages/did/src/Did.signature.spec.ts +++ b/packages/did/src/Did.signature.spec.ts @@ -292,7 +292,7 @@ describe('full DID', () => { { controller: `did:kilt:${keypair.address}`, id: `did:kilt:${keypair.address}#0x12345`, - publicKeyMultibase: keypairToMultibaseKey(keypair), + publicKeyMultibase: keypairToMultibaseKey(keypair).publicKeyMultibase, type: 'Multikey', }, ], diff --git a/packages/did/src/Did.signature.ts b/packages/did/src/Did.signature.ts index ac57fa130..4339a32db 100644 --- a/packages/did/src/Did.signature.ts +++ b/packages/did/src/Did.signature.ts @@ -14,6 +14,7 @@ import type { DidSignature, DidUrl, KeyringPair, + MultibaseKeyPair, SignatureVerificationRelationship, SignerInterface, } from '@kiltprotocol/types' @@ -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 ): Promise>> { const didKeys = didDocument.verificationMethod?.map( ({ publicKeyMultibase, id }) => ({ diff --git a/packages/did/src/Did.utils.ts b/packages/did/src/Did.utils.ts index 0e46e0726..e4a11a68a 100644 --- a/packages/did/src/Did.utils.ts +++ b/packages/did/src/Did.utils.ts @@ -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' @@ -133,25 +127,6 @@ 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 = - { - [MULTICODEC_ECDSA_PREFIX]: ['ecdsa', 33], - [MULTICODEC_X25519_PREFIX]: ['x25519', 32], - [MULTICODEC_ED25519_PREFIX]: ['ed25519', 32], - [MULTICODEC_SR25519_PREFIX]: ['sr25519', 32], - } -const multicodecReversePrefixes: Record = { - ecdsa: MULTICODEC_ECDSA_PREFIX, - x25519: MULTICODEC_X25519_PREFIX, - ed25519: MULTICODEC_ED25519_PREFIX, - sr25519: MULTICODEC_SR25519_PREFIX, -} - /** * Decode a Multikey representation of a verification method into its fundamental components: the public key and the key type. * @@ -161,68 +136,19 @@ const multicodecReversePrefixes: Record = { export function multibaseKeyToDidKey( publicKeyMultibase: VerificationMethod['publicKeyMultibase'] ): DecodedVerificationMethod { - const { keyBytes, prefix } = decodeBase58BtcMultikey(publicKeyMultibase) - - const [keyType, expectedPublicKeyLength] = multicodecPrefixes[prefix] - if (keyType === undefined) { - throw new SDKErrors.DidError( - `Cannot decode key type for multibase key "${publicKeyMultibase}".` - ) - } - if (keyBytes.length !== expectedPublicKeyLength) { + const { publicKey, type } = Multikey.decodeMultibaseKeypair({ + publicKeyMultibase, + }) + const expectedPublicKeyLength = type === 'secp256k1' ? 33 : 32 + if (publicKey.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 & { - 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 + } } /** @@ -240,28 +166,16 @@ export function didKeyToVerificationMethod( id: IdType, { keyType, publicKey }: DecodedVerificationMethod ): VerificationMethod { - 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, } } diff --git a/packages/did/src/DidDetails/LightDidDetails.ts b/packages/did/src/DidDetails/LightDidDetails.ts index f92d68044..d08705569 100644 --- a/packages/did/src/DidDetails/LightDidDetails.ts +++ b/packages/did/src/DidDetails/LightDidDetails.ts @@ -5,29 +5,27 @@ * found in the LICENSE file in the root directory of this source tree. */ -import type { DidDocument, Did } from '@kiltprotocol/types' - import { base58Decode, base58Encode, decodeAddress, } from '@polkadot/util-crypto' -import { cbor, SDKErrors, ss58Format } from '@kiltprotocol/utils' -import type { - NewDidEncryptionKey, - NewDidVerificationKey, - NewService, - DidSigningMethodType, -} from './DidDetails.js' +import type { Did, DidDocument } from '@kiltprotocol/types' +import { cbor, Multikey, SDKErrors, ss58Format } from '@kiltprotocol/utils' +import { urlFragmentToChain, validateNewService } from '../Did.chain.js' import { - keypairToMultibaseKey, didKeyToVerificationMethod, getAddressFromVerificationMethod, parse, } from '../Did.utils.js' -import { urlFragmentToChain, validateNewService } from '../Did.chain.js' +import type { + DidSigningMethodType, + NewDidEncryptionKey, + NewDidVerificationKey, + NewService, +} from './DidDetails.js' import { addKeypairAsVerificationMethod, encryptionMethodTypes, @@ -231,9 +229,9 @@ export function createLightDidDocument({ // Validity is checked in validateCreateDocumentInput const authenticationKeyTypeEncoding = verificationKeyTypeToLightDidEncoding[authentication[0].type] - const address = getAddressFromVerificationMethod({ - publicKeyMultibase: keypairToMultibaseKey(authentication[0]), - }) + const address = getAddressFromVerificationMethod( + Multikey.encodeMultibaseKeypair(authentication[0]) + ) const encodedDetailsString = encodedDetails ? `:${encodedDetails}` : '' const did = diff --git a/packages/sdk-js/src/DidHelpers/interfaces.ts b/packages/sdk-js/src/DidHelpers/interfaces.ts index 4c91ad866..ced84e9b2 100644 --- a/packages/sdk-js/src/DidHelpers/interfaces.ts +++ b/packages/sdk-js/src/DidHelpers/interfaces.ts @@ -14,8 +14,11 @@ import type { DidDocument, HexString, KeyringPair, + MultibaseKeyPair, + MultibasePublicKey, SignerInterface, } from '@kiltprotocol/types' +import type { Multikey } from '@kiltprotocol/utils' export interface TransactionResult { status: 'confirmed' | 'failed' | 'rejected' | 'unknown' @@ -89,18 +92,7 @@ export interface TransactionHandlers { }> } -/** Base58 encoded bytes, using the bitcoin alphabet. */ -type Base58Btc = string -/** Multibase encoding of a public- or private key including multicodec variant flag. */ -export type KeyMultibaseEncoded = `z${Base58Btc}` - -export type AcceptedSigners = - | SignerInterface - | KeyringPair - | { - secretKeyMultibase: KeyMultibaseEncoded - publicKeyMultibase: KeyMultibaseEncoded - } +export type AcceptedSigners = SignerInterface | KeyringPair | MultibaseKeyPair export type SharedArguments = { didDocument: DidDocument @@ -111,10 +103,10 @@ export type SharedArguments = { type PublicKeyAndType = { publicKey: Uint8Array - type: KeyringPair['type'] | 'x25519' + type: Multikey.KnownTypeString } export type AcceptedPublicKeyEncodings = - | KeyMultibaseEncoded - | { publicKeyMultibase: KeyMultibaseEncoded } + | MultibasePublicKey['publicKeyMultibase'] + | MultibasePublicKey | PublicKeyAndType // interface allows KeyringPair too diff --git a/packages/sdk-js/src/index.ts b/packages/sdk-js/src/index.ts index c1bd06620..485126fb4 100644 --- a/packages/sdk-js/src/index.ts +++ b/packages/sdk-js/src/index.ts @@ -22,7 +22,7 @@ import { resolver as DidResolver } from '@kiltprotocol/did' import * as DidHelpers from './DidHelpers/index.js' const { signAndSubmitTx } = Blockchain // TODO: maybe we don't even need that if we have the identity class -const { signerFromKeypair, generateKeypair } = Signers +const { getSignersForKeypair, generateKeypair } = Signers export { init, @@ -32,7 +32,7 @@ export { Holder, Verifier, Issuer, - signerFromKeypair, + getSignersForKeypair, generateKeypair, signAndSubmitTx, ConfigService, diff --git a/packages/types/src/Signers.ts b/packages/types/src/Signers.ts index cbfe18add..66fc06ef6 100644 --- a/packages/types/src/Signers.ts +++ b/packages/types/src/Signers.ts @@ -5,6 +5,8 @@ * found in the LICENSE file in the root directory of this source tree. */ +import type { Base58BtcMultibaseString, VerificationMethod } from './Did.js' + export type SignerInterface< Alg extends string = string, Id extends string = string @@ -13,3 +15,9 @@ export type SignerInterface< id: Id sign: (input: { data: Uint8Array }) => Promise } + +export type MultibasePublicKey = Pick +export type MultibaseSecretKey = { + secretKeyMultibase: Base58BtcMultibaseString +} +export type MultibaseKeyPair = MultibasePublicKey & MultibaseSecretKey diff --git a/packages/utils/package.json b/packages/utils/package.json index fbb2cef66..c14e81f5e 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -37,6 +37,7 @@ "dependencies": { "@kiltprotocol/eddsa-jcs-2022": "0.1.0-rc.4", "@kiltprotocol/es256k-jcs-2023": "0.1.0-rc.4", + "@kiltprotocol/jcs-data-integrity-proofs-common": "0.1.0-rc.4", "@kiltprotocol/sr25519-jcs-2023": "0.1.0-rc.4", "@kiltprotocol/types": "workspace:*", "@polkadot/api": "^12.0.0", @@ -45,6 +46,7 @@ "@polkadot/util-crypto": "^13.0.0", "cbor-web": "^9.0.0", "tweetnacl": "^1.0.3", - "uuid": "^10.0.0" + "uuid": "^10.0.0", + "varint": "^6.0.0" } } diff --git a/packages/utils/src/Multikey.ts b/packages/utils/src/Multikey.ts new file mode 100644 index 000000000..d1b0d82b4 --- /dev/null +++ b/packages/utils/src/Multikey.ts @@ -0,0 +1,162 @@ +/** + * Copyright (c) 2018-2024, BOTLabs GmbH. + * + * This source code is licensed under the BSD 4-Clause "Original" license + * found in the LICENSE file in the root directory of this source tree. + */ + +import { decodeBase58BtcMultikey } from '@kiltprotocol/jcs-data-integrity-proofs-common' +// @ts-expect-error Not a typescript module +import * as varint from 'varint' + +import { + Base58BtcMultibaseString, + KeyringPair, + MultibaseKeyPair, + MultibasePublicKey, + MultibaseSecretKey, +} from '@kiltprotocol/types' +import { u8aConcat } from '@polkadot/util' +import { base58Encode } from '@polkadot/util-crypto' +import { DidError } from './SDKErrors.js' + +const MULTICODEC_SECP256K1_PREFIXES = [0xe7, 0x1301] as const +const MULTICODEC_X25519_PREFIXES = [0xec, 0x1302] as const +const MULTICODEC_ED25519_PREFIXES = [0xed, 0x1300] as const +const MULTICODEC_SR25519_PREFIXES = [0xef, 0x1303] as const + +type KeyTypeString = 'ed25519' | 'sr25519' | 'x25519' | 'secp256k1' + +export type KnownTypeString = KeyringPair['type'] | KeyTypeString + +function mapTypeStringToPrefixes(type: KnownTypeString): [number, number] { + switch (type) { + case 'ecdsa': + case 'secp256k1': + case 'ethereum': + return [...MULTICODEC_SECP256K1_PREFIXES] + case 'sr25519': + return [...MULTICODEC_SR25519_PREFIXES] + case 'x25519': + return [...MULTICODEC_X25519_PREFIXES] + case 'ed25519': + return [...MULTICODEC_ED25519_PREFIXES] + default: + throw new DidError(`The provided key type "${type}" is not supported.`) + } +} + +// 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}` +} + +export function encodeMultibaseKeypair( + args: Pick & { + type: KnownTypeString + } +): MultibasePublicKey +export function encodeMultibaseKeypair( + args: Pick & { + type: KnownTypeString + secretKey: Uint8Array + } +): MultibaseKeyPair +/** + * Calculate the Multikey representation of a keypair given its type and public/secret keys. + * + * @param keypair The input keypair to encode as Multikey. + * @param keypair.type The keypair type indicated by a type string. + * @param keypair.publicKey The keypair public key. + * @param keypair.secretKey Optionally, the keypair's secret key. + * @returns The Multikey representation (i.e., multicodec-prefixed, then multibase encoded) of the provided keypair. + */ +export function encodeMultibaseKeypair({ + type, + publicKey, + secretKey, +}: Pick & { + type: KnownTypeString + secretKey?: Uint8Array +}): MultibasePublicKey & Partial { + const [multiCodecPublicKeyPrefix, multiCodedSecretKeyPrefix] = + mapTypeStringToPrefixes(type) + + const keypair: MultibasePublicKey & Partial = { + publicKeyMultibase: multibase58BtcKeyBytesEncoding( + publicKey, + multiCodecPublicKeyPrefix + ), + } + if (secretKey) { + keypair.secretKeyMultibase = multibase58BtcKeyBytesEncoding( + secretKey, + multiCodedSecretKeyPrefix + ) + } + + return keypair +} + +const publicKeyPrefixes: Record = { + [MULTICODEC_SECP256K1_PREFIXES[0]]: 'secp256k1', + [MULTICODEC_X25519_PREFIXES[0]]: 'x25519', + [MULTICODEC_ED25519_PREFIXES[0]]: 'ed25519', + [MULTICODEC_SR25519_PREFIXES[0]]: 'sr25519', +} + +const secretKeyPrefixes: Record = { + [MULTICODEC_SECP256K1_PREFIXES[1]]: 'secp256k1', + [MULTICODEC_X25519_PREFIXES[1]]: 'x25519', + [MULTICODEC_ED25519_PREFIXES[1]]: 'ed25519', + [MULTICODEC_SR25519_PREFIXES[1]]: 'sr25519', +} + +/** + * Decode a Multikey representation of a verification method into its fundamental components: the public key and the key type. + * + * @param keyPairMultibase The verification method's public/private keys in Multikey format (i.e., multicodec-prefixed, then multibase encoded). + * @param keyPairMultibase.publicKeyMultibase The keypair's public key, encoded in Multikey format. + * @param keyPairMultibase.secretKeyMultibase Optionally, the keypair's secret key, encoded in Multikey format. + * @returns The decoded `publicKey` (and possibly `secretKey`) plus a key `type`. + */ +export function decodeMultibaseKeypair({ + publicKeyMultibase, + secretKeyMultibase, +}: MultibasePublicKey & Partial): Pick< + KeyringPair, + 'publicKey' +> & { type: KeyTypeString; secretKey?: Uint8Array } { + const { keyBytes, prefix } = decodeBase58BtcMultikey(publicKeyMultibase) + + const keyType = publicKeyPrefixes[prefix] + if (keyType === undefined) { + throw new DidError( + `Cannot decode key type for multibase key "${publicKeyMultibase}".` + ) + } + + const result: ReturnType = { + type: keyType, + publicKey: keyBytes, + } + + if (typeof secretKeyMultibase === 'string') { + const decodedKey = decodeBase58BtcMultikey(secretKeyMultibase) + const secretKeyType = secretKeyPrefixes[decodedKey.prefix] + if (secretKeyType !== keyType) { + throw new Error( + `Secret key type ${secretKeyType} (prefix ${decodedKey.prefix}) does not match public key type ${keyType} (prefix ${prefix})` + ) + } + result.secretKey = decodedKey.keyBytes + } + + return result +} diff --git a/packages/utils/src/Signers.ts b/packages/utils/src/Signers.ts index 5f00a6ffa..77f3063e1 100644 --- a/packages/utils/src/Signers.ts +++ b/packages/utils/src/Signers.ts @@ -39,10 +39,13 @@ import type { DidDocument, DidUrl, KeyringPair, + KiltKeyringPair, + MultibaseKeyPair, SignerInterface, UriFragment, } from '@kiltprotocol/types' import { makeKeypairFromUri } from './Crypto.js' +import { type KnownTypeString, encodeMultibaseKeypair } from './Multikey.js' import { DidError, NoSuitableSignerError } from './SDKErrors.js' export const ALGORITHMS = Object.freeze({ @@ -459,7 +462,10 @@ export function getPolkadotSigner( } const signature = await signer.sign({ data: signData }) // The signature is expected to be a SCALE enum, we must add a type prefix representing the signature algorithm - const prefixed = u8aConcat(TYPE_PREFIX[signer.algorithm], signature) + const prefixed = u8aConcat( + TYPE_PREFIX[signer.algorithm as keyof typeof TYPE_PREFIX], + signature + ) id += 1 return { id, @@ -469,20 +475,26 @@ export function getPolkadotSigner( } } -export function generateKeypair(args?: { - seed?: string - type?: T -}): Keypair & { type: T } +/** + * Generates a Multikey encoded keypair from a seed or mnemonic. + * + * @param args Optional generator arguments. + * @param args.seed A 32 byte hex-encoded and 0x-prefixed seed or 12-word mnemonic, optionally postfixed with a derivation path (e.g., `//authentication-key`). + * This is case-insensitive. + * @param args.type A string indicating desired key type. + * @returns A pair of `publicKeyMultibase` & `secretKeyMultibase`. + */ export function generateKeypair({ seed = randomAsHex(32), type = 'ed25519', }: { seed?: string - type?: string -} = {}): Keypair & { type: string } { - let typeForKeyring = type as KeyringPair['type'] + type?: KnownTypeString +} = {}): MultibaseKeyPair { + let typeForKeyring = type as KiltKeyringPair['type'] switch (type.toLowerCase()) { case 'secp256k1': + case 'ethereum': typeForKeyring = 'ecdsa' break case 'x25519': @@ -491,10 +503,8 @@ export function generateKeypair({ default: } - const keyRingPair = makeKeypairFromUri( - seed.toLowerCase(), - typeForKeyring as any - ) + const keyRingPair = makeKeypairFromUri(seed.toLowerCase(), typeForKeyring) + const { secretKey, publicKey } = extractPk(keyRingPair) - return { secretKey, publicKey, type } + return encodeMultibaseKeypair({ publicKey, secretKey, type }) } diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 6a0424605..ef52f484e 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -17,6 +17,7 @@ export * as DataUtils from './DataUtils.js' export * as SDKErrors from './SDKErrors.js' export * as JsonSchema from './json-schema/index.js' export * as Signers from './Signers.js' +export * as Multikey from './Multikey.js' export { Caip19, Caip2 } from './CAIP/index.js' export { ss58Format } from './ss58Format.js' export { cbor } from './cbor.js' diff --git a/yarn.lock b/yarn.lock index fd5ee1074..5a9ffb534 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2102,7 +2102,6 @@ __metadata: "@digitalbazaar/multikey-context": "npm:^2.0.1" "@digitalbazaar/security-context": "npm:^1.0.1" "@kiltprotocol/config": "workspace:*" - "@kiltprotocol/jcs-data-integrity-proofs-common": "npm:0.1.0-rc.4" "@kiltprotocol/types": "workspace:*" "@kiltprotocol/utils": "workspace:*" "@polkadot/api": "npm:^12.0.0" @@ -2112,7 +2111,6 @@ __metadata: "@polkadot/util-crypto": "npm:^13.0.0" rimraf: "npm:^3.0.2" typescript: "npm:^4.8.3" - varint: "npm:^6.0.0" peerDependencies: "@kiltprotocol/augment-api": "*" peerDependenciesMeta: @@ -2256,6 +2254,7 @@ __metadata: dependencies: "@kiltprotocol/eddsa-jcs-2022": "npm:0.1.0-rc.4" "@kiltprotocol/es256k-jcs-2023": "npm:0.1.0-rc.4" + "@kiltprotocol/jcs-data-integrity-proofs-common": "npm:0.1.0-rc.4" "@kiltprotocol/sr25519-jcs-2023": "npm:0.1.0-rc.4" "@kiltprotocol/types": "workspace:*" "@polkadot/api": "npm:^12.0.0" @@ -2268,6 +2267,7 @@ __metadata: tweetnacl: "npm:^1.0.3" typescript: "npm:^4.8.3" uuid: "npm:^10.0.0" + varint: "npm:^6.0.0" languageName: unknown linkType: soft From 2c26b0254db2a96bbe7cc99078a85722cd9ce430 Mon Sep 17 00:00:00 2001 From: Raphael Flechtner Date: Thu, 18 Jul 2024 16:27:07 +0200 Subject: [PATCH 3/8] test: fix tests --- packages/did/src/Did.signature.spec.ts | 7 +- .../src/DidDetails/LightDidDetails.spec.ts | 29 +++-- .../did/src/DidResolver/DidResolver.spec.ts | 99 ++++++++--------- tests/integration/Did.spec.ts | 102 ++++++++++-------- tests/testUtils/TestUtils.ts | 11 +- 5 files changed, 129 insertions(+), 119 deletions(-) diff --git a/packages/did/src/Did.signature.spec.ts b/packages/did/src/Did.signature.spec.ts index 506644a5e..cce8f3423 100644 --- a/packages/did/src/Did.signature.spec.ts +++ b/packages/did/src/Did.signature.spec.ts @@ -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' @@ -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') @@ -292,7 +292,8 @@ describe('full DID', () => { { controller: `did:kilt:${keypair.address}`, id: `did:kilt:${keypair.address}#0x12345`, - publicKeyMultibase: keypairToMultibaseKey(keypair).publicKeyMultibase, + publicKeyMultibase: + Multikey.encodeMultibaseKeypair(keypair).publicKeyMultibase, type: 'Multikey', }, ], diff --git a/packages/did/src/DidDetails/LightDidDetails.spec.ts b/packages/did/src/DidDetails/LightDidDetails.spec.ts index 66b704dd3..61332a0f4 100644 --- a/packages/did/src/DidDetails/LightDidDetails.spec.ts +++ b/packages/did/src/DidDetails/LightDidDetails.spec.ts @@ -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, @@ -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', }, ], @@ -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', }, ], @@ -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', }, ], diff --git a/packages/did/src/DidResolver/DidResolver.spec.ts b/packages/did/src/DidResolver/DidResolver.spec.ts index a907c0cf6..2548e3947 100644 --- a/packages/did/src/DidResolver/DidResolver.spec.ts +++ b/packages/did/src/DidResolver/DidResolver.spec.ts @@ -17,7 +17,7 @@ import { VerificationMethod, UriFragment, } from '@kiltprotocol/types' -import { Crypto, cbor } from '@kiltprotocol/utils' +import { Crypto, Multikey, cbor } from '@kiltprotocol/utils' import { stringToU8a } from '@polkadot/util' import { ApiMocks, makeSigningKeyTool } from '../../../../tests/testUtils' @@ -87,10 +87,10 @@ function generateAuthenticationVerificationMethod( id: `${controller}#auth`, controller, type: 'Multikey', - publicKeyMultibase: Did.keypairToMultibaseKey({ + publicKeyMultibase: Multikey.encodeMultibaseKeypair({ publicKey: new Uint8Array(32).fill(0), type: 'ed25519', - }), + }).publicKeyMultibase, } } @@ -101,10 +101,10 @@ function generateEncryptionVerificationMethod( id: `${controller}#enc`, controller, type: 'Multikey', - publicKeyMultibase: Did.keypairToMultibaseKey({ + publicKeyMultibase: Multikey.encodeMultibaseKeypair({ publicKey: new Uint8Array(32).fill(1), type: 'x25519', - }), + }).publicKeyMultibase, } } @@ -115,10 +115,10 @@ function generateAssertionVerificationMethod( id: `${controller}#att`, controller, type: 'Multikey', - publicKeyMultibase: Did.keypairToMultibaseKey({ + publicKeyMultibase: Multikey.encodeMultibaseKeypair({ publicKey: new Uint8Array(32).fill(2), type: 'sr25519', - }), + }).publicKeyMultibase, } } @@ -129,10 +129,10 @@ function generateCapabilityDelegationVerificationMethod( id: `${controller}#del`, controller, type: 'Multikey', - publicKeyMultibase: Did.keypairToMultibaseKey({ + publicKeyMultibase: Multikey.encodeMultibaseKeypair({ publicKey: new Uint8Array(33).fill(3), type: 'ecdsa', - }), + }).publicKeyMultibase, } } @@ -313,10 +313,10 @@ describe('When resolving a full DID', () => { controller: fullDidWithAuthenticationKey, id: `${fullDidWithAuthenticationKey}#auth`, type: 'Multikey', - publicKeyMultibase: Did.keypairToMultibaseKey({ + publicKeyMultibase: Multikey.encodeMultibaseKeypair({ type: 'ed25519', publicKey: new Uint8Array(32).fill(0), - }), + }).publicKeyMultibase, }, ], }, @@ -363,37 +363,37 @@ describe('When resolving a full DID', () => { controller: fullDidWithAllKeys, id: `${fullDidWithAllKeys}#auth`, type: 'Multikey', - publicKeyMultibase: Did.keypairToMultibaseKey({ + publicKeyMultibase: Multikey.encodeMultibaseKeypair({ type: 'ed25519', publicKey: new Uint8Array(32).fill(0), - }), + }).publicKeyMultibase, }, { controller: fullDidWithAllKeys, id: `${fullDidWithAllKeys}#enc`, type: 'Multikey', - publicKeyMultibase: Did.keypairToMultibaseKey({ + publicKeyMultibase: Multikey.encodeMultibaseKeypair({ type: 'x25519', publicKey: new Uint8Array(32).fill(1), - }), + }).publicKeyMultibase, }, { controller: fullDidWithAllKeys, id: `${fullDidWithAllKeys}#att`, type: 'Multikey', - publicKeyMultibase: Did.keypairToMultibaseKey({ + publicKeyMultibase: Multikey.encodeMultibaseKeypair({ type: 'sr25519', publicKey: new Uint8Array(32).fill(2), - }), + }).publicKeyMultibase, }, { controller: fullDidWithAllKeys, id: `${fullDidWithAllKeys}#del`, type: 'Multikey', - publicKeyMultibase: Did.keypairToMultibaseKey({ + publicKeyMultibase: Multikey.encodeMultibaseKeypair({ type: 'ecdsa', publicKey: new Uint8Array(33).fill(3), - }), + }).publicKeyMultibase, }, ], }, @@ -435,10 +435,10 @@ describe('When resolving a full DID', () => { controller: fullDidWithServiceEndpoints, id: `${fullDidWithServiceEndpoints}#auth`, type: 'Multikey', - publicKeyMultibase: Did.keypairToMultibaseKey({ + publicKeyMultibase: Multikey.encodeMultibaseKeypair({ type: 'ed25519', publicKey: new Uint8Array(32).fill(0), - }), + }).publicKeyMultibase, }, ], service: [ @@ -488,10 +488,10 @@ describe('When resolving a full DID', () => { controller: didWithAuthenticationKey, id: `${didWithAuthenticationKey}#auth`, type: 'Multikey', - publicKeyMultibase: Did.keypairToMultibaseKey({ + publicKeyMultibase: Multikey.encodeMultibaseKeypair({ type: 'ed25519', publicKey: new Uint8Array(32).fill(0), - }), + }).publicKeyMultibase, }, ], alsoKnownAs: ['w3n:w3nick'], @@ -507,9 +507,9 @@ describe('When resolving a full DID', () => { augmentedApi.createType('Option', null) ) const randomKeypair = (await makeSigningKeyTool()).authentication[0] - const randomDid = Did.getFullDidFromVerificationMethod({ - publicKeyMultibase: Did.keypairToMultibaseKey(randomKeypair), - }) + const randomDid = Did.getFullDidFromVerificationMethod( + Multikey.encodeMultibaseKeypair(randomKeypair) + ) expect(await Did.resolve(randomDid)).toStrictEqual({ didDocumentMetadata: {}, didResolutionMetadata: { error: 'notFound' }, @@ -563,10 +563,10 @@ describe('When resolving a light DID', () => { controller: lightDidWithAuthenticationKey.id, id: `${lightDidWithAuthenticationKey.id}#authentication`, type: 'Multikey', - publicKeyMultibase: Did.keypairToMultibaseKey({ + publicKeyMultibase: Multikey.encodeMultibaseKeypair({ ...authKey, type: 'sr25519', - }), + }).publicKeyMultibase, }, ], }, @@ -594,16 +594,17 @@ describe('When resolving a light DID', () => { controller: lightDid.id, id: `${lightDid.id}#authentication`, type: 'Multikey', - publicKeyMultibase: Did.keypairToMultibaseKey({ + publicKeyMultibase: Multikey.encodeMultibaseKeypair({ ...authKey, type: 'sr25519', - }), + }).publicKeyMultibase, }, { controller: lightDid.id, id: `${lightDid.id}#encryption`, type: 'Multikey', - publicKeyMultibase: Did.keypairToMultibaseKey(encryptionKey), + publicKeyMultibase: + Multikey.encodeMultibaseKeypair(encryptionKey).publicKeyMultibase, }, ], service: [ @@ -737,10 +738,10 @@ describe('DID Resolution compliance', () => { id: `${did}#auth`, controller: did, type: 'Multikey', - publicKeyMultibase: Did.keypairToMultibaseKey({ + publicKeyMultibase: Multikey.encodeMultibaseKeypair({ publicKey: new Uint8Array(32).fill(0), type: 'ed25519', - }), + }).publicKeyMultibase, }, ], }, @@ -786,10 +787,10 @@ describe('DID Resolution compliance', () => { id: `${did}#auth`, controller: did, type: 'Multikey', - publicKeyMultibase: Did.keypairToMultibaseKey({ + publicKeyMultibase: Multikey.encodeMultibaseKeypair({ publicKey: new Uint8Array(32).fill(0), type: 'ed25519', - }), + }).publicKeyMultibase, }, ], }) @@ -817,10 +818,10 @@ describe('DID Resolution compliance', () => { id: `${did}#auth`, controller: did, type: 'Multikey', - publicKeyMultibase: Did.keypairToMultibaseKey({ + publicKeyMultibase: Multikey.encodeMultibaseKeypair({ publicKey: new Uint8Array(32).fill(0), type: 'ed25519', - }), + }).publicKeyMultibase, }, ], '@context': [Did.W3C_DID_CONTEXT_URL, Did.KILT_DID_CONTEXT_URL], @@ -847,10 +848,10 @@ describe('DID Resolution compliance', () => { id: `${did}#auth`, controller: did, type: 'Multikey', - publicKeyMultibase: Did.keypairToMultibaseKey({ + publicKeyMultibase: Multikey.encodeMultibaseKeypair({ publicKey: new Uint8Array(32).fill(0), type: 'ed25519', - }), + }).publicKeyMultibase, }, ], }) @@ -911,10 +912,10 @@ describe('DID Resolution compliance', () => { id: `${did}#auth`, controller: did, type: 'Multikey', - publicKeyMultibase: Did.keypairToMultibaseKey({ + publicKeyMultibase: Multikey.encodeMultibaseKeypair({ publicKey: new Uint8Array(32).fill(0), type: 'ed25519', - }), + }).publicKeyMultibase, }, ], }, @@ -938,10 +939,10 @@ describe('DID Resolution compliance', () => { id: `${did}#auth`, controller: did, type: 'Multikey', - publicKeyMultibase: Did.keypairToMultibaseKey({ + publicKeyMultibase: Multikey.encodeMultibaseKeypair({ publicKey: new Uint8Array(32).fill(0), type: 'ed25519', - }), + }).publicKeyMultibase, }, ], '@context': [Did.W3C_DID_CONTEXT_URL, Did.KILT_DID_CONTEXT_URL], @@ -965,10 +966,10 @@ describe('DID Resolution compliance', () => { id: `${did}#auth`, controller: did, type: 'Multikey', - publicKeyMultibase: Did.keypairToMultibaseKey({ + publicKeyMultibase: Multikey.encodeMultibaseKeypair({ publicKey: new Uint8Array(32).fill(0), type: 'ed25519', - }), + }).publicKeyMultibase, }, ], }) @@ -988,10 +989,10 @@ describe('DID Resolution compliance', () => { controller: 'did:kilt:4r1WkS3t8rbCb11H8t3tJvGVCynwDXSUBiuGB6sLRHzCLCjs', type: 'Multikey', - publicKeyMultibase: Did.keypairToMultibaseKey({ + publicKeyMultibase: Multikey.encodeMultibaseKeypair({ publicKey: new Uint8Array(32).fill(0), type: 'ed25519', - }), + }).publicKeyMultibase, }, }) }) @@ -1072,10 +1073,10 @@ describe('DID Resolution compliance', () => { id: `${did}#auth`, controller: did, type: 'Multikey', - publicKeyMultibase: Did.keypairToMultibaseKey({ + publicKeyMultibase: Multikey.encodeMultibaseKeypair({ publicKey: new Uint8Array(32).fill(0), type: 'ed25519', - }), + }).publicKeyMultibase, }, ], }, diff --git a/tests/integration/Did.spec.ts b/tests/integration/Did.spec.ts index a6f6cdab4..0637d2bd8 100644 --- a/tests/integration/Did.spec.ts +++ b/tests/integration/Did.spec.ts @@ -21,7 +21,7 @@ import { CType, DelegationNode } from '@kiltprotocol/credentials' import * as Did from '@kiltprotocol/did' import { disconnect } from '@kiltprotocol/sdk-js' import { Permission } from '@kiltprotocol/types' -import { UUID } from '@kiltprotocol/utils' +import { Multikey, UUID } from '@kiltprotocol/utils' import type { KeyTool } from '../testUtils/index.js' @@ -137,10 +137,10 @@ describe('write and didDeleteTx', () => { controller: fullDid, type: 'Multikey', // We cannot match the ID of the key because it will be defined by the blockchain while saving - publicKeyMultibase: Did.keypairToMultibaseKey({ + publicKeyMultibase: Multikey.encodeMultibaseKeypair({ type: 'sr25519', publicKey: authPublicKey, - }), + }).publicKeyMultibase, }), ], }) @@ -370,13 +370,15 @@ describe('DID migration', () => { controller: migratedFullDid, type: 'Multikey', // We cannot match the ID of the key because it will be defined by the blockchain while saving - publicKeyMultibase: Did.keypairToMultibaseKey(authentication[0]), + publicKeyMultibase: Multikey.encodeMultibaseKeypair(authentication[0]) + .publicKeyMultibase, }), expect.objectContaining(>{ controller: migratedFullDid, type: 'Multikey', // We cannot match the ID of the key because it will be defined by the blockchain while saving - publicKeyMultibase: Did.keypairToMultibaseKey(keyAgreement[0]), + publicKeyMultibase: Multikey.encodeMultibaseKeypair(keyAgreement[0]) + .publicKeyMultibase, }), ]), }) @@ -427,7 +429,8 @@ describe('DID migration', () => { controller: migratedFullDid, type: 'Multikey', // We cannot match the ID of the key because it will be defined by the blockchain while saving - publicKeyMultibase: Did.keypairToMultibaseKey(authentication[0]), + publicKeyMultibase: Multikey.encodeMultibaseKeypair(authentication[0]) + .publicKeyMultibase, }), ], }) @@ -492,13 +495,15 @@ describe('DID migration', () => { controller: migratedFullDid, type: 'Multikey', // We cannot match the ID of the key because it will be defined by the blockchain while saving - publicKeyMultibase: Did.keypairToMultibaseKey(authentication[0]), + publicKeyMultibase: Multikey.encodeMultibaseKeypair(authentication[0]) + .publicKeyMultibase, }), expect.objectContaining(>{ controller: migratedFullDid, type: 'Multikey', // We cannot match the ID of the key because it will be defined by the blockchain while saving - publicKeyMultibase: Did.keypairToMultibaseKey(keyAgreement[0]), + publicKeyMultibase: Multikey.encodeMultibaseKeypair(keyAgreement[0]) + .publicKeyMultibase, }), ]), service: [ @@ -563,9 +568,9 @@ describe('DID authorization', () => { await submitTx(createTx, paymentAccount) const didLinkedInfo = await api.call.did.query( Did.toChain( - Did.getFullDidFromVerificationMethod({ - publicKeyMultibase: Did.keypairToMultibaseKey(authentication[0]), - }) + Did.getFullDidFromVerificationMethod( + Multikey.encodeMultibaseKeypair(authentication[0]) + ) ) ) did = Did.linkedInfoFromChain(didLinkedInfo).document @@ -674,9 +679,9 @@ describe('DID management batching', () => { await submitTx(extrinsic, paymentAccount) const fullDidLinkedInfo = await api.call.did.query( Did.toChain( - Did.getFullDidFromVerificationMethod({ - publicKeyMultibase: Did.keypairToMultibaseKey(authentication[0]), - }) + Did.getFullDidFromVerificationMethod( + Multikey.encodeMultibaseKeypair(authentication[0]) + ) ) ) const { document: fullDid } = Did.linkedInfoFromChain(fullDidLinkedInfo) @@ -688,52 +693,54 @@ describe('DID management batching', () => { // Authentication controller: fullDid.id, type: 'Multikey', - publicKeyMultibase: Did.keypairToMultibaseKey(authentication[0]), + publicKeyMultibase: Multikey.encodeMultibaseKeypair( + authentication[0] + ).publicKeyMultibase, }), // Assertion method expect.objectContaining({ controller: fullDid.id, type: 'Multikey', - publicKeyMultibase: Did.keypairToMultibaseKey({ + publicKeyMultibase: Multikey.encodeMultibaseKeypair({ type: 'sr25519', publicKey: new Uint8Array(32).fill(1), - }), + }).publicKeyMultibase, }), // Capability delegation expect.objectContaining({ controller: fullDid.id, type: 'Multikey', - publicKeyMultibase: Did.keypairToMultibaseKey({ + publicKeyMultibase: Multikey.encodeMultibaseKeypair({ type: 'ecdsa', publicKey: new Uint8Array(33).fill(1), - }), + }).publicKeyMultibase, }), // Key agreement 1 expect.objectContaining({ controller: fullDid.id, type: 'Multikey', - publicKeyMultibase: Did.keypairToMultibaseKey({ + publicKeyMultibase: Multikey.encodeMultibaseKeypair({ type: 'x25519', publicKey: new Uint8Array(32).fill(1), - }), + }).publicKeyMultibase, }), // Key agreement 2 expect.objectContaining({ controller: fullDid.id, type: 'Multikey', - publicKeyMultibase: Did.keypairToMultibaseKey({ + publicKeyMultibase: Multikey.encodeMultibaseKeypair({ type: 'x25519', publicKey: new Uint8Array(32).fill(2), - }), + }).publicKeyMultibase, }), // Key agreement 3 expect.objectContaining({ controller: fullDid.id, type: 'Multikey', - publicKeyMultibase: Did.keypairToMultibaseKey({ + publicKeyMultibase: Multikey.encodeMultibaseKeypair({ type: 'x25519', publicKey: new Uint8Array(32).fill(3), - }), + }).publicKeyMultibase, }), ]) ) @@ -778,9 +785,9 @@ describe('DID management batching', () => { const fullDidLinkedInfo = await api.call.did.query( Did.toChain( - Did.getFullDidFromVerificationMethod({ - publicKeyMultibase: Did.keypairToMultibaseKey(didAuthKey), - }) + Did.getFullDidFromVerificationMethod( + Multikey.encodeMultibaseKeypair(didAuthKey) + ) ) ) const { document: fullDid } = Did.linkedInfoFromChain(fullDidLinkedInfo) @@ -792,7 +799,8 @@ describe('DID management batching', () => { expect.objectContaining(>{ controller: fullDid.id, type: 'Multikey', - publicKeyMultibase: Did.keypairToMultibaseKey(didAuthKey), + publicKeyMultibase: + Multikey.encodeMultibaseKeypair(didAuthKey).publicKeyMultibase, }), ], }) @@ -853,9 +861,9 @@ describe('DID management batching', () => { const initialFullDidLinkedInfo = await api.call.did.query( Did.toChain( - Did.getFullDidFromVerificationMethod({ - publicKeyMultibase: Did.keypairToMultibaseKey(authentication[0]), - }) + Did.getFullDidFromVerificationMethod( + Multikey.encodeMultibaseKeypair(authentication[0]) + ) ) ) const { document: initialFullDid } = Did.linkedInfoFromChain( @@ -908,10 +916,10 @@ describe('DID management batching', () => { expect.objectContaining(>{ controller: finalFullDid.id, type: 'Multikey', - publicKeyMultibase: Did.keypairToMultibaseKey({ + publicKeyMultibase: Multikey.encodeMultibaseKeypair({ publicKey: keypair.publicKey, type: 'sr25519', - }), + }).publicKeyMultibase, }), ], }) @@ -937,9 +945,9 @@ describe('DID management batching', () => { const initialFullDidLinkedInfo = await api.call.did.query( Did.toChain( - Did.getFullDidFromVerificationMethod({ - publicKeyMultibase: Did.keypairToMultibaseKey(authentication[0]), - }) + Did.getFullDidFromVerificationMethod( + Multikey.encodeMultibaseKeypair(authentication[0]) + ) ) ) const { document: initialFullDid } = Did.linkedInfoFromChain( @@ -986,10 +994,10 @@ describe('DID management batching', () => { expect.objectContaining(>{ controller: finalFullDid.id, type: 'Multikey', - publicKeyMultibase: Did.keypairToMultibaseKey({ + publicKeyMultibase: Multikey.encodeMultibaseKeypair({ publicKey: newAuthKey.publicKey, type: 'ed25519', - }), + }).publicKeyMultibase, }), ], service: [ @@ -1033,9 +1041,9 @@ describe('DID management batching', () => { await submitTx(tx, paymentAccount) const fullDidLinkedInfo = await api.call.did.query( Did.toChain( - Did.getFullDidFromVerificationMethod({ - publicKeyMultibase: Did.keypairToMultibaseKey(authentication[0]), - }) + Did.getFullDidFromVerificationMethod( + Multikey.encodeMultibaseKeypair(authentication[0]) + ) ) ) const { document: fullDid } = Did.linkedInfoFromChain(fullDidLinkedInfo) @@ -1075,7 +1083,9 @@ describe('DID management batching', () => { // Authentication and assertionMethod controller: fullDid.id, type: 'Multikey', - publicKeyMultibase: Did.keypairToMultibaseKey(authentication[0]), + publicKeyMultibase: Multikey.encodeMultibaseKeypair( + authentication[0] + ).publicKeyMultibase, }), ], // Old service maintained @@ -1117,9 +1127,9 @@ describe('DID management batching', () => { await submitTx(createTx, paymentAccount) const fullDidLinkedInfo = await api.call.did.query( Did.toChain( - Did.getFullDidFromVerificationMethod({ - publicKeyMultibase: Did.keypairToMultibaseKey(authentication[0]), - }) + Did.getFullDidFromVerificationMethod( + Multikey.encodeMultibaseKeypair(authentication[0]) + ) ) ) const { document: fullDid } = Did.linkedInfoFromChain(fullDidLinkedInfo) diff --git a/tests/testUtils/TestUtils.ts b/tests/testUtils/TestUtils.ts index 0e1609695..7acae55d7 100644 --- a/tests/testUtils/TestUtils.ts +++ b/tests/testUtils/TestUtils.ts @@ -22,7 +22,7 @@ import type { } from '@kiltprotocol/types' import { ConfigService } from '@kiltprotocol/config' import { Blockchain } from '@kiltprotocol/chain-helpers' -import { Crypto, Signers, SDKErrors } from '@kiltprotocol/utils' +import { Crypto, Signers, SDKErrors, Multikey } from '@kiltprotocol/utils' import { type BaseNewDidKey, type ChainDidKey, @@ -35,7 +35,6 @@ import { getStoreTx, didKeyToVerificationMethod, createLightDidDocument, - keypairToMultibaseKey, getFullDidFromVerificationMethod, multibaseKeyToDidKey, isValidDidVerificationType, @@ -248,12 +247,12 @@ export async function createLocalDemoFullDidFromKeypair( publicKey, id: authKeyFragment, } = makeDidKeyFromKeypair(keypair) - const id = getFullDidFromVerificationMethod({ - publicKeyMultibase: keypairToMultibaseKey({ + const id = getFullDidFromVerificationMethod( + Multikey.encodeMultibaseKeypair({ type: keyType, publicKey, - }), - }) + }) + ) const authKeyId = `${id}${authKeyFragment}` as const const result: DidDocument = { From 9f0819b7262562bea2e461209b3931dabe605752 Mon Sep 17 00:00:00 2001 From: Raphael Flechtner Date: Mon, 22 Jul 2024 13:13:41 +0200 Subject: [PATCH 4/8] fix: add missing implementation on getSignersForKeypair --- packages/utils/src/Multikey.ts | 16 +++++++++++++--- packages/utils/src/Signers.ts | 23 +++++++---------------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/packages/utils/src/Multikey.ts b/packages/utils/src/Multikey.ts index d1b0d82b4..846285cd0 100644 --- a/packages/utils/src/Multikey.ts +++ b/packages/utils/src/Multikey.ts @@ -60,14 +60,14 @@ function multibase58BtcKeyBytesEncoding( export function encodeMultibaseKeypair( args: Pick & { type: KnownTypeString + secretKey: Uint8Array } -): MultibasePublicKey +): MultibaseKeyPair export function encodeMultibaseKeypair( args: Pick & { type: KnownTypeString - secretKey: Uint8Array } -): MultibaseKeyPair +): MultibasePublicKey /** * Calculate the Multikey representation of a keypair given its type and public/secret keys. * @@ -118,6 +118,16 @@ const secretKeyPrefixes: Record = { [MULTICODEC_SR25519_PREFIXES[1]]: 'sr25519', } +export function decodeMultibaseKeypair(keypair: MultibaseKeyPair): Pick< + KeyringPair, + 'publicKey' +> & { + secretKey: Uint8Array + type: KeyTypeString +} +export function decodeMultibaseKeypair( + keyPairPublicKey: MultibasePublicKey +): Pick & { type: KeyTypeString } /** * Decode a Multikey representation of a verification method into its fundamental components: the public key and the key type. * diff --git a/packages/utils/src/Signers.ts b/packages/utils/src/Signers.ts index 77f3063e1..27c95f7b0 100644 --- a/packages/utils/src/Signers.ts +++ b/packages/utils/src/Signers.ts @@ -45,7 +45,11 @@ import type { UriFragment, } from '@kiltprotocol/types' import { makeKeypairFromUri } from './Crypto.js' -import { type KnownTypeString, encodeMultibaseKeypair } from './Multikey.js' +import { + type KnownTypeString, + decodeMultibaseKeypair, + encodeMultibaseKeypair, +} from './Multikey.js' import { DidError, NoSuitableSignerError } from './SDKErrors.js' export const ALGORITHMS = Object.freeze({ @@ -229,25 +233,12 @@ export async function getSignersForKeypair({ type, }: { id?: Id - keypair: - | Keypair - | KeyringPair - | { - secretKeyMultibase: `z${string}` - publicKeyMultibase: `z${string}` - } + keypair: Keypair | KeyringPair | MultibaseKeyPair type?: string }): Promise>> { let pair: KeyringPair | (Keypair & { type: string }) if ('publicKeyMultibase' in keypair) { - throw new Error('not implemented') - // const { publicKey, keyType } = multibaseKeyToDidKey( - // keypair.publicKeyMultibase - // ) - // const { publicKey: secretKey } = decodeMultikeyVerificationMethod({ - // publicKeyMultibase: keypair.secretKeyMultibase, - // }) - // pair = { publicKey, secretKey, type: keyType } + pair = decodeMultibaseKeypair(keypair) } else if ('type' in keypair) { pair = keypair } else if (type) { From b00ada17ae882b101b2743ad619079a7cf28b7c8 Mon Sep 17 00:00:00 2001 From: Raphael Flechtner Date: Mon, 22 Jul 2024 13:15:08 +0200 Subject: [PATCH 5/8] test: use generateKeypair in did helpers integration tests --- tests/integration/didHelpers.spec.ts | 29 +++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/tests/integration/didHelpers.spec.ts b/tests/integration/didHelpers.spec.ts index 628734faa..df91f491d 100644 --- a/tests/integration/didHelpers.spec.ts +++ b/tests/integration/didHelpers.spec.ts @@ -8,11 +8,13 @@ import type { ApiPromise } from '@polkadot/api' import { CType } from '@kiltprotocol/credentials' -import { DidHelpers, disconnect } from '@kiltprotocol/sdk-js' +import { getFullDidFromVerificationMethod } from '@kiltprotocol/did' +import { DidHelpers, disconnect, generateKeypair } from '@kiltprotocol/sdk-js' import type { DidDocument, KeyringPair, KiltKeyringPair, + MultibaseKeyPair, Service, } from '@kiltprotocol/types' import { Crypto } from '@kiltprotocol/utils' @@ -31,13 +33,13 @@ beforeAll(async () => { // Create did on chain describe('create and deactivate DID', () => { - let kp: KeyringPair + let kp: MultibaseKeyPair let didDocument: DidDocument beforeAll(() => { - kp = Crypto.makeKeypairFromUri( - 'build hill second flame trigger simple rigid cabbage phrase evolve final eight', - 'sr25519' - ) + kp = generateKeypair({ + seed: 'build hill second flame trigger simple rigid cabbage phrase evolve final eight', + type: 'sr25519', + }) }) it('creates a DID', async () => { @@ -50,8 +52,9 @@ describe('create and deactivate DID', () => { expect(result.status).toBe('confirmed') didDocument = result.asConfirmed.didDocument + const did = getFullDidFromVerificationMethod(kp) expect(didDocument).toMatchObject({ - id: `did:kilt:${kp.address}`, + id: did, verificationMethod: expect.any(Array), authentication: expect.any(Array), }) @@ -77,10 +80,10 @@ describe('create and deactivate DID', () => { }) describe('w3ns', () => { - let keypair: KeyringPair + let keypair: MultibaseKeyPair let didDocument: DidDocument beforeAll(async () => { - keypair = Crypto.makeKeypairFromUri('//Blob') + keypair = generateKeypair({ seed: '//Blob' }) const result = await DidHelpers.createDid({ api, signers: [keypair], @@ -137,10 +140,10 @@ describe('w3ns', () => { }) describe('services', () => { - let keypair: KeyringPair + let keypair: MultibaseKeyPair let didDocument: DidDocument beforeAll(async () => { - keypair = Crypto.makeKeypairFromUri('//Services') + keypair = generateKeypair({ seed: '//Services' }) const result = await DidHelpers.createDid({ api, signers: [keypair], @@ -327,10 +330,10 @@ describe('verification methods', () => { }) describe('transact', () => { - let keypair: KeyringPair + let keypair: MultibaseKeyPair let didDocument: DidDocument beforeAll(async () => { - keypair = Crypto.makeKeypairFromUri('//Transact') + keypair = generateKeypair({ seed: '//Transact' }) didDocument = ( await DidHelpers.createDid({ api, From fb44a82c96317a2bd142fa8499bf3ab90bef6265 Mon Sep 17 00:00:00 2001 From: Raphael Flechtner Date: Tue, 23 Jul 2024 10:18:05 +0200 Subject: [PATCH 6/8] chore: no magic numbers --- packages/did/src/Did.utils.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/did/src/Did.utils.ts b/packages/did/src/Did.utils.ts index e4a11a68a..c2bc594f8 100644 --- a/packages/did/src/Did.utils.ts +++ b/packages/did/src/Did.utils.ts @@ -127,6 +127,9 @@ type DecodedVerificationMethod = { keyType: DidVerificationMethodType } +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. * @@ -139,7 +142,8 @@ export function multibaseKeyToDidKey( const { publicKey, type } = Multikey.decodeMultibaseKeypair({ publicKeyMultibase, }) - const expectedPublicKeyLength = type === 'secp256k1' ? 33 : 32 + const expectedPublicKeyLength = + type === 'secp256k1' ? KEY_LENGTH_ECDSA : KEY_LENGTH_OTHER if (publicKey.length !== expectedPublicKeyLength) { throw new SDKErrors.DidError( `Key of type "${type}" is expected to be ${expectedPublicKeyLength} bytes long. Provided key is ${publicKey.length} bytes long instead.` @@ -254,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) } From d69481d40bf17e19c54429c89df106f983c9919b Mon Sep 17 00:00:00 2001 From: Raphael Flechtner Date: Tue, 23 Jul 2024 10:41:27 +0200 Subject: [PATCH 7/8] docs: improve docstrings --- packages/utils/src/Multikey.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/utils/src/Multikey.ts b/packages/utils/src/Multikey.ts index 846285cd0..810eedc2e 100644 --- a/packages/utils/src/Multikey.ts +++ b/packages/utils/src/Multikey.ts @@ -69,13 +69,13 @@ export function encodeMultibaseKeypair( } ): MultibasePublicKey /** - * Calculate the Multikey representation of a keypair given its type and public/secret keys. + * Create a Multikey representation of a keypair, encoded in multibase-base58-btc, given its type and public/secret keys. * * @param keypair The input keypair to encode as Multikey. * @param keypair.type The keypair type indicated by a type string. * @param keypair.publicKey The keypair public key. * @param keypair.secretKey Optionally, the keypair's secret key. - * @returns The Multikey representation (i.e., multicodec-prefixed, then multibase encoded) of the provided keypair. + * @returns The Multikey representation (i.e., multicodec-prefixed, then base58-btc multibase encoded) of the provided keypair. */ export function encodeMultibaseKeypair({ type, @@ -130,6 +130,7 @@ export function decodeMultibaseKeypair( ): Pick & { type: KeyTypeString } /** * Decode a Multikey representation of a verification method into its fundamental components: the public key and the key type. + * Note that only base58-btc multibase encoding is currently supported. * * @param keyPairMultibase The verification method's public/private keys in Multikey format (i.e., multicodec-prefixed, then multibase encoded). * @param keyPairMultibase.publicKeyMultibase The keypair's public key, encoded in Multikey format. From 1bdf0d8ca39040dfe041f7839520cdaa52d46c76 Mon Sep 17 00:00:00 2001 From: Raphael Flechtner Date: Tue, 23 Jul 2024 11:01:22 +0200 Subject: [PATCH 8/8] docs: streamline typings and add overload docstrings for en/decodeMultibaseKeypair --- packages/utils/src/Multikey.ts | 87 ++++++++++++++++++++-------------- 1 file changed, 51 insertions(+), 36 deletions(-) diff --git a/packages/utils/src/Multikey.ts b/packages/utils/src/Multikey.ts index 810eedc2e..ce3a486ef 100644 --- a/packages/utils/src/Multikey.ts +++ b/packages/utils/src/Multikey.ts @@ -57,34 +57,46 @@ function multibase58BtcKeyBytesEncoding( return `z${base58BtcEncodedKey}` } -export function encodeMultibaseKeypair( - args: Pick & { - type: KnownTypeString - secretKey: Uint8Array - } -): MultibaseKeyPair -export function encodeMultibaseKeypair( - args: Pick & { - type: KnownTypeString - } -): MultibasePublicKey +type TypedKeypairWithOptionalSecretKey = { + publicKey: Uint8Array + secretKey?: Uint8Array + type: KeyTypes +} + +type TypedKeypair = Required< + TypedKeypairWithOptionalSecretKey +> + /** * Create a Multikey representation of a keypair, encoded in multibase-base58-btc, given its type and public/secret keys. * * @param keypair The input keypair to encode as Multikey. * @param keypair.type The keypair type indicated by a type string. - * @param keypair.publicKey The keypair public key. - * @param keypair.secretKey Optionally, the keypair's secret key. + * @param keypair.publicKey The keypair's public key bytes. + * @param keypair.secretKey The keypair's secret key bytes. * @returns The Multikey representation (i.e., multicodec-prefixed, then base58-btc multibase encoded) of the provided keypair. */ +export function encodeMultibaseKeypair( + args: TypedKeypair +): MultibaseKeyPair +/** + * Create a Multikey representation of a keypair's public key, encoded in multibase-base58-btc, given its type and public key bytes. + * + * @param keypair The input keypair to encode as Multikey. + * @param keypair.type The keypair type indicated by a type string. + * @param keypair.publicKey The keypair's public key bytes. + * @returns The Multikey representation (i.e., multicodec-prefixed, then base58-btc multibase encoded) of the public key. + */ +export function encodeMultibaseKeypair( + args: Pick, 'publicKey' | 'type'> +): MultibasePublicKey +// eslint-disable-next-line jsdoc/require-jsdoc -- Docs are provided to overloads. export function encodeMultibaseKeypair({ type, publicKey, secretKey, -}: Pick & { - type: KnownTypeString - secretKey?: Uint8Array -}): MultibasePublicKey & Partial { +}: TypedKeypairWithOptionalSecretKey): MultibasePublicKey & + Partial { const [multiCodecPublicKeyPrefix, multiCodedSecretKeyPrefix] = mapTypeStringToPrefixes(type) @@ -118,32 +130,35 @@ const secretKeyPrefixes: Record = { [MULTICODEC_SR25519_PREFIXES[1]]: 'sr25519', } -export function decodeMultibaseKeypair(keypair: MultibaseKeyPair): Pick< - KeyringPair, - 'publicKey' -> & { - secretKey: Uint8Array - type: KeyTypeString -} -export function decodeMultibaseKeypair( - keyPairPublicKey: MultibasePublicKey -): Pick & { type: KeyTypeString } /** - * Decode a Multikey representation of a verification method into its fundamental components: the public key and the key type. + * Decodes a Multikey representation of a key pair, returning the public- and secret key bytes and the key type. * Note that only base58-btc multibase encoding is currently supported. * - * @param keyPairMultibase The verification method's public/private keys in Multikey format (i.e., multicodec-prefixed, then multibase encoded). + * @param keyPairMultibase A key pair represented in Multikey format (i.e., multicodec-prefixed, then multibase encoded). * @param keyPairMultibase.publicKeyMultibase The keypair's public key, encoded in Multikey format. - * @param keyPairMultibase.secretKeyMultibase Optionally, the keypair's secret key, encoded in Multikey format. - * @returns The decoded `publicKey` (and possibly `secretKey`) plus a key `type`. + * @param keyPairMultibase.secretKeyMultibase The keypair's secret key, encoded in Multikey format. + * @returns The decoded `publicKey` and `secretKey` bytes plus a key `type`. */ +export function decodeMultibaseKeypair( + keypair: MultibaseKeyPair +): TypedKeypair +/** + * Decodes a Multikey representation of a public key/verification method, returning the publics key bytes and the key type. + * Note that only base58-btc multibase encoding is currently supported. + * + * @param keyPairMultibase A verification method or comparable public key representation. + * @param keyPairMultibase.publicKeyMultibase The keypair's public key, multicodec-prefixed, then multibase encoded. + * @returns The decoded `publicKey` bytes plus a key `type`. + */ +export function decodeMultibaseKeypair( + keyPairPublicKey: MultibasePublicKey +): Pick, 'publicKey' | 'type'> +// eslint-disable-next-line jsdoc/require-jsdoc -- Docs are provided to overloads. export function decodeMultibaseKeypair({ publicKeyMultibase, secretKeyMultibase, -}: MultibasePublicKey & Partial): Pick< - KeyringPair, - 'publicKey' -> & { type: KeyTypeString; secretKey?: Uint8Array } { +}: MultibasePublicKey & + Partial): TypedKeypairWithOptionalSecretKey { const { keyBytes, prefix } = decodeBase58BtcMultikey(publicKeyMultibase) const keyType = publicKeyPrefixes[prefix] @@ -153,7 +168,7 @@ export function decodeMultibaseKeypair({ ) } - const result: ReturnType = { + const result: TypedKeypairWithOptionalSecretKey = { type: keyType, publicKey: keyBytes, }