From be7811cd6163abc9de7cb0c03b81467018a2066e Mon Sep 17 00:00:00 2001 From: Raphael Flechtner Date: Mon, 23 Oct 2023 14:47:49 +0200 Subject: [PATCH] chore: linting --- packages/did/src/Did.chain.ts | 58 ++++---- packages/did/src/Did.signature.spec.ts | 2 - packages/legacy-credentials/src/Credential.ts | 12 +- packages/types/src/Imported.ts | 1 - packages/types/src/Signers.ts | 12 ++ packages/types/src/index.ts | 1 + packages/utils/src/Signers.ts | 138 +++++++++++------- 7 files changed, 133 insertions(+), 91 deletions(-) create mode 100644 packages/types/src/Signers.ts diff --git a/packages/did/src/Did.chain.ts b/packages/did/src/Did.chain.ts index f9d5f2dee9..7c43571066 100644 --- a/packages/did/src/Did.chain.ts +++ b/packages/did/src/Did.chain.ts @@ -535,6 +535,35 @@ export async function getStoreTxFromDidDocument( return getStoreTxFromInput(storeTxInput, submitter, sign) } +/** + * Compiles an enum-type key-value pair representation of a signature created with a signer associated with a full DID verification method. Required for creating full DID signed extrinsics. + * + * @param input Signature and algorithm. + * @param input.algorithm Descriptor of the signature algorithm used by the signer. + * @param input.signature The signature generated by the signer. + * @returns Data restructured to allow SCALE encoding by polkadot api. + */ +export function didSignatureToChain({ + algorithm, + signature, +}: { + algorithm: string + signature: Uint8Array +}): EncodedSignature { + const lower = algorithm.toLowerCase() + const keyType = + lower === Signers.ALGORITHMS.ECRECOVER_SECP256K1_BLAKE2B.toLowerCase() + ? 'ecdsa' + : lower + if (!isValidVerificationMethodType(keyType)) { + throw new SDKErrors.DidError( + `encodedDidSignature requires a verification key. A key of type "${keyType}" was used instead` + ) + } + + return { [keyType]: signature } as EncodedSignature +} + export interface SigningOptions { signer: SignerInterface } @@ -581,32 +610,3 @@ export async function generateDidAuthenticatedTx({ }) return api.tx.did.submitDidCall(signableCall, encodedSignature) } - -/** - * Compiles an enum-type key-value pair representation of a signature created with a signer associated with a full DID verification method. Required for creating full DID signed extrinsics. - * - * @param input Signature and algorithm. - * @param input.algorithm Descriptor of the signature algorithm used by the signer. - * @param input.signature The signature generated by the signer. - * @returns Data restructured to allow SCALE encoding by polkadot api. - */ -export function didSignatureToChain({ - algorithm, - signature, -}: { - algorithm: string - signature: Uint8Array -}): EncodedSignature { - const lower = algorithm.toLowerCase() - const keyType = - lower === Signers.ALGORITHMS.ECRECOVER_SECP256K1_BLAKE2B.toLowerCase() - ? 'ecdsa' - : lower - if (!isValidVerificationMethodType(keyType)) { - throw new SDKErrors.DidError( - `encodedDidSignature requires a verification key. A key of type "${keyType}" was used instead` - ) - } - - return { [keyType]: signature } as EncodedSignature -} diff --git a/packages/did/src/Did.signature.spec.ts b/packages/did/src/Did.signature.spec.ts index 453030f26d..c3d3b743d8 100644 --- a/packages/did/src/Did.signature.spec.ts +++ b/packages/did/src/Did.signature.spec.ts @@ -203,8 +203,6 @@ describe('light DID', () => { const signature = await authenticationSigner.sign({ data: Crypto.coToUInt8(SIGNED_STRING), }) - // @ts-expect-error - const keyUri = authenticationSigner.id.replace('#', '?') await expect( verifyDidSignature({ message: SIGNED_STRING, diff --git a/packages/legacy-credentials/src/Credential.ts b/packages/legacy-credentials/src/Credential.ts index 020656ff92..f6a90d1666 100644 --- a/packages/legacy-credentials/src/Credential.ts +++ b/packages/legacy-credentials/src/Credential.ts @@ -548,10 +548,10 @@ export async function createPresentation({ excludedClaimProperties ) - if (!didDocument) { - didDocument = (await resolve(credential.claim.owner)).didDocument - } - if (!didDocument) { + const didDoc = + didDocument ?? (await resolve(credential.claim.owner)).didDocument + + if (!didDoc) { throw new Error( `Unable to sign: Failed to resolve claimer DID ${credential.claim.owner}` ) @@ -559,12 +559,12 @@ export async function createPresentation({ const signer = await Signers.selectSigner( signers, verifiableOnChain(), - byDid(didDocument, { verificationRelationship: 'authentication' }) + byDid(didDoc, { verificationRelationship: 'authentication' }) ) if (!signer) { throw new SDKErrors.NoSuitableSignerError(undefined, { signerRequirements: { - did: didDocument.id, + did: didDoc.id, algorithm: Signers.DID_PALLET_SUPPORTED_ALGORITHMS, verificationRelationship: 'authentication', }, diff --git a/packages/types/src/Imported.ts b/packages/types/src/Imported.ts index 0bd426ebb3..f832284439 100644 --- a/packages/types/src/Imported.ts +++ b/packages/types/src/Imported.ts @@ -15,4 +15,3 @@ export type { HexString } from '@polkadot/util/types' export type { Prefix } from '@polkadot/util-crypto/address/types' export type { SubmittableExtrinsic } from '@polkadot/api/promise/types' export type { KeyringPair } from '@polkadot/keyring/types' -export type { SignerInterface } from '@kiltprotocol/jcs-data-integrity-proofs-common' diff --git a/packages/types/src/Signers.ts b/packages/types/src/Signers.ts new file mode 100644 index 0000000000..2987afb5c1 --- /dev/null +++ b/packages/types/src/Signers.ts @@ -0,0 +1,12 @@ +/** + * Copyright (c) 2018-2023, 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. + */ + +export type SignerInterface = { + algorithm: string + id: string + sign: (input: { data: Uint8Array }) => Promise +} diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 83fde92b10..0b119e907b 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -26,3 +26,4 @@ export * from './CryptoCallbacks.js' export * from './DidResolver.js' export * from './PublicCredential.js' export * from './Imported.js' +export * from './Signers.js' diff --git a/packages/utils/src/Signers.ts b/packages/utils/src/Signers.ts index 1979a6e617..3b238d44b4 100644 --- a/packages/utils/src/Signers.ts +++ b/packages/utils/src/Signers.ts @@ -53,18 +53,19 @@ export const DID_PALLET_SUPPORTED_ALGORITHMS = Object.freeze([ * Signer that produces an ECDSA signature over a Blake2b-256 digest of the message using the secp256k1 curve. * The signature has a recovery bit appended to the end, allowing public key recovery. * - * @param root0 - * @param root0.keyUri - * @param root0.publicKey - * @param root0.secretKey + * @param input Holds all function arguments. + * @param input.keyUri Sets the signer's id property. + * @param input.secretKey A 32 byte ECDSA secret key on the secp256k1 curve. + * @param input.publicKey The corresponding public key. May be omitted. + * @returns A signer interface capable of making ECDSA signatures with recovery bit added. */ export async function polkadotEcdsaSigner({ secretKey, - keyUri, + keyUri, // TODO: I think this should just be called id }: { - publicKey?: Uint8Array - secretKey: Uint8Array keyUri: string + secretKey: Uint8Array + publicKey?: Uint8Array }): Promise { return { id: keyUri, @@ -79,19 +80,19 @@ export async function polkadotEcdsaSigner({ * Signer that produces an ECDSA signature over a Keccak-256 digest of the message using the secp256k1 curve. * The signature has a recovery bit appended to the end, allowing public key recovery. * - * @param input - * @param input.keyUri - * @param input.publicKey - * @param input.secretKey - * @returns + * @param input Holds all function arguments. + * @param input.keyUri Sets the signer's id property. + * @param input.secretKey A 32 byte ECDSA secret key on the secp256k1 curve. + * @param input.publicKey The corresponding public key. May be omitted. + * @returns A signer interface capable of making ECDSA signatures with recovery bit added. */ export async function ethereumEcdsaSigner({ secretKey, keyUri, }: { - publicKey?: Uint8Array - secretKey: Uint8Array keyUri: string + secretKey: Uint8Array + publicKey?: Uint8Array }): Promise { return { id: keyUri, @@ -105,19 +106,19 @@ export async function ethereumEcdsaSigner({ /** * Signer that produces an ES256K signature over the message. * - * @param input - * @param input.keyUri - * @param input.publicKey - * @param input.secretKey - * @returns + * @param input Holds all function arguments. + * @param input.keyUri Sets the signer's id property. + * @param input.secretKey A 32 byte ECDSA secret key on the secp256k1 curve. + * @param input.publicKey The corresponding public key. May be omitted. + * @returns A signer interface capable of making ES256K signatures. */ export async function es256kSigner({ secretKey, keyUri, }: { - publicKey?: Uint8Array - secretKey: Uint8Array keyUri: string + secretKey: Uint8Array + publicKey?: Uint8Array }): Promise { // only exists to map secretKey to seed return es256kSignerWrapped({ seed: secretKey, keyUri }) @@ -126,19 +127,19 @@ export async function es256kSigner({ /** * Signer that produces an Ed25519 signature over the message. * - * @param input - * @param input.keyUri - * @param input.publicKey - * @param input.secretKey - * @returns + * @param input Holds all function arguments. + * @param input.keyUri Sets the signer's id property. + * @param input.secretKey A 32 byte Ed25519 secret key. Some key representations append the public key to the private key; to allow these, all bytes after the 32nd byte will be dropped. + * @param input.publicKey The corresponding public key. May be omitted. + * @returns A signer interface capable of making Ed25519 signatures. */ export async function ed25519Signer({ secretKey, keyUri, }: { - publicKey?: Uint8Array - secretKey: Uint8Array keyUri: string + secretKey: Uint8Array + publicKey?: Uint8Array }): Promise { // polkadot ed25519 private keys are a concatenation of private and public key for some reason return ed25519SignerWrapped({ seed: secretKey.slice(0, 32), keyUri }) @@ -147,11 +148,11 @@ export async function ed25519Signer({ /** * Signer that produces an Sr25519 signature over the message. * - * @param input - * @param input.keyUri - * @param input.publicKey - * @param input.secretKey - * @returns + * @param input Holds all function arguments. + * @param input.keyUri Sets the signer's id property. + * @param input.secretKey A 64 byte Sr25519 secret key. + * @param input.publicKey The corresponding 32 byte public key. + * @returns A signer interface capable of making Sr25519 signatures. */ export async function sr25519Signer({ secretKey, @@ -188,14 +189,17 @@ const signerFactory = { } /** - * @param root0 - * @param root0.keypair - * @param root0.algorithm - * @param root0.keyUri + * Creates a signer interface based on an existing keypair and an algorithm descriptor. + * + * @param input Holds all function arguments. + * @param input.keyUri Sets the signer's id property. + * @param input.keypair A polkadot {@link KeyringPair} or combination of `secretKey` & `publicKey`. + * @param input.algorithm An algorithm identifier from the {@link ALGORITHMS} map. + * @returns A signer interface. */ export async function signerFromKeypair({ - keypair, keyUri, + keypair, algorithm, }: { keypair: Keypair | KeyringPair @@ -256,19 +260,22 @@ function algsForKeyType(keyType: string): string[] { } /** - * @param root0 - * @param root0.keypair - * @param root0.type - * @param root0.keyUri + * Based on an existing keypair and its type, creates all available signers that work with this key type. + * + * @param input Holds all function arguments. + * @param input.keyUri Sets the signer's id property. + * @param input.keypair A polkadot {@link KeyringPair} or combination of `secretKey` & `publicKey`. + * @param input.type If `keypair` is not a {@link KeyringPair}, provide the key type here; otherwise, this is ignored. + * @returns An array of signer interfaces based on the keypair and type. */ export async function getSignersForKeypair({ + keyUri, keypair, type = (keypair as KeyringPair).type, - keyUri, }: { + keyUri?: string keypair: Keypair | KeyringPair type?: string - keyUri?: string }): Promise { if (!type) { throw new Error('type is required if keypair.type is not given') @@ -282,12 +289,15 @@ export async function getSignersForKeypair({ } export interface SignerSelector { - (signer: SignerInterface): boolean + (signer: SignerInterface): boolean // TODO: allow async } /** - * @param signers - * @param selectors + * Filters signer interfaces, returning only those accepted by all selectors. + * + * @param signers An array of signer interfaces. + * @param selectors One or more selector callbacks, receiving a signer as input and returning `true` in case it meets selection criteria. + * @returns An array of those signers for which all selectors returned `true`. */ export async function selectSigners( signers: readonly SignerInterface[], @@ -299,8 +309,11 @@ export async function selectSigners( } /** - * @param signers - * @param selectors + * Finds a suiteable signer interfaces in an array of signers, returning the first signer accepted by all selectors. + * + * @param signers An array of signer interfaces. + * @param selectors One or more selector callbacks, receiving a signer as input and returning `true` in case it meets selection criteria. + * @returns The first signer for which all selectors returned `true`, or `undefined` if none meet selection criteria. */ export async function selectSigner( signers: readonly SignerInterface[], @@ -311,17 +324,36 @@ export async function selectSigner( }) } -function byId(id: string): SignerSelector { - return (signer) => signer.id === id +/** + * Select signers based on (key) ids. + * + * @param ids Allowed signer/key ids to filter for. + * @returns A selector identifying signers whose id property is in `ids`. + */ +function byId(ids: readonly string[]): SignerSelector { + return ({ id }) => ids.includes(id) } - +/** + * Select signers based on algorithm identifiers. + * + * @param algorithms Allowed algorithms to filter for. + * @returns A selector identifying signers whose algorithm property is in `algorithms`. + */ function byAlgorithm(algorithms: readonly string[]): SignerSelector { return (signer) => algorithms.some( (algorithm) => algorithm.toLowerCase() === signer.algorithm.toLowerCase() ) } - +/** + * Select signers based on the association of key ids with a given DID. + * + * @param didDocument DidDocument of the DID, on which the signer id must be listed as a verification method. + * @param options Additional optional filter criteria. + * @param options.verificationRelationship If set, the signer id must be listed under this verification relationship on the DidDocument. + * @param options.controller If set, only verificationMethods with this controller are considered. + * @returns A selector identifying signers whose id is associated with the DidDocument. + */ function byDid( didDocument: DidDocument, {