From 3e267376afe7f752a09b774553d489aed6e5a3aa Mon Sep 17 00:00:00 2001 From: Raphael Flechtner Date: Tue, 18 Oct 2022 12:23:35 +0200 Subject: [PATCH 1/6] refactor!: make verifyDidSignature accept Uint8Array --- packages/core/src/credential/Credential.ts | 7 +- packages/core/src/quote/Quote.spec.ts | 14 ++- packages/core/src/quote/Quote.ts | 13 ++- packages/did/src/Did.signature.spec.ts | 116 +++++++++++++-------- packages/did/src/Did.signature.ts | 38 ++++--- packages/messaging/src/Message.spec.ts | 31 +++--- packages/testing/src/TestUtils.ts | 18 ---- 7 files changed, 141 insertions(+), 96 deletions(-) diff --git a/packages/core/src/credential/Credential.ts b/packages/core/src/credential/Credential.ts index bf06ba147..5116a84dd 100644 --- a/packages/core/src/credential/Credential.ts +++ b/packages/core/src/credential/Credential.ts @@ -24,6 +24,7 @@ import { } from '@kiltprotocol/did' import type { DidResolveKey, + DidResourceUri, Hash, IAttestation, IClaim, @@ -233,7 +234,11 @@ export async function verifySignature( ) const signingData = makeSigningData(input, claimerSignature.challenge) await verifyDidSignature({ - signature: claimerSignature, + signature: Crypto.coToUInt8(claimerSignature.signature), + keyUri: + claimerSignature.keyUri ?? + // accept the old did signature format where keyUri was keyId + (claimerSignature as unknown as { keyId: DidResourceUri }).keyId, message: signingData, expectedVerificationMethod: 'authentication', didResolveKey, diff --git a/packages/core/src/quote/Quote.spec.ts b/packages/core/src/quote/Quote.spec.ts index 592225b60..dfe7be416 100644 --- a/packages/core/src/quote/Quote.spec.ts +++ b/packages/core/src/quote/Quote.spec.ts @@ -27,7 +27,6 @@ import { Crypto } from '@kiltprotocol/utils' import * as Did from '@kiltprotocol/did' import { createLocalDemoFullDidFromKeypair, - makeDidSignature, makeSigningKeyTool, } from '@kiltprotocol/testing' import * as CType from '../ctype' @@ -142,10 +141,15 @@ describe('Quote', () => { it('tests created quote data against given data', async () => { expect(validQuoteData.attesterDid).toEqual(attesterIdentity.uri) - const signature = await makeDidSignature( - Crypto.hashStr(Crypto.encodeObjectAsStr(validAttesterSignedQuote)), - claimerIdentity.uri, - claimer.getSignCallback(claimerIdentity) + const sign = claimer.getSignCallback(claimerIdentity) + const signature = Did.signatureToJson( + await sign({ + data: Crypto.coToUInt8( + Crypto.hashStr(Crypto.encodeObjectAsStr(validAttesterSignedQuote)) + ), + did: claimerIdentity.uri, + keyRelationship: 'authentication', + }) ) expect(signature).toEqual(quoteBothAgreed.claimerSignature) diff --git a/packages/core/src/quote/Quote.ts b/packages/core/src/quote/Quote.ts index 687e3471f..d56b77b9d 100644 --- a/packages/core/src/quote/Quote.ts +++ b/packages/core/src/quote/Quote.ts @@ -23,6 +23,7 @@ import type { SignCallback, DidResolveKey, DidUri, + DidResourceUri, } from '@kiltprotocol/types' import { Crypto, JsonSchema, SDKErrors } from '@kiltprotocol/utils' import { resolveKey, verifyDidSignature } from '@kiltprotocol/did' @@ -103,7 +104,11 @@ export async function verifyAttesterSignedQuote( ): Promise { const { attesterSignature, ...basicQuote } = quote await verifyDidSignature({ - signature: attesterSignature, + signature: Crypto.coToUInt8(attesterSignature.signature), + keyUri: + attesterSignature.keyUri ?? + // accept the old did signature format where keyUri was keyId + (attesterSignature as unknown as { keyId: DidResourceUri }).keyId, message: Crypto.hashStr(Crypto.encodeObjectAsStr(basicQuote)), expectedVerificationMethod: 'authentication', didResolveKey, @@ -140,7 +145,11 @@ export async function createQuoteAgreement( const { attesterSignature, ...basicQuote } = attesterSignedQuote await verifyDidSignature({ - signature: attesterSignature, + signature: Crypto.coToUInt8(attesterSignature.signature), + keyUri: + attesterSignature.keyUri ?? + // accept the old did signature format where keyUri was keyId + (attesterSignature as unknown as { keyId: DidResourceUri }).keyId, message: Crypto.hashStr(Crypto.encodeObjectAsStr(basicQuote)), expectedVerificationMethod: 'authentication', didResolveKey, diff --git a/packages/did/src/Did.signature.spec.ts b/packages/did/src/Did.signature.spec.ts index f190a997b..3b9efbfca 100644 --- a/packages/did/src/Did.signature.spec.ts +++ b/packages/did/src/Did.signature.spec.ts @@ -19,7 +19,7 @@ import { } from '@kiltprotocol/types' import { randomAsHex, randomAsU8a } from '@polkadot/util-crypto' import { Crypto } from '@kiltprotocol/utils' -import { makeSigningKeyTool, makeDidSignature } from '@kiltprotocol/testing' +import { makeSigningKeyTool } from '@kiltprotocol/testing' import * as Did from './index.js' import { verifyDidSignature, isDidSignature } from './Did.signature' import { resolveKey, keyToResolvedKey } from './DidResolver' @@ -55,34 +55,16 @@ describe('light DID', () => { it('verifies did signature over string', async () => { const SIGNED_STRING = 'signed string' - const signature = await makeDidSignature(SIGNED_STRING, did.uri, sign) - await expect( - verifyDidSignature({ - message: SIGNED_STRING, - signature, - expectedVerificationMethod: 'authentication', - }) - ).resolves.not.toThrow() - }) - - it('verifies old did signature (with `keyId` property) over string', async () => { - const SIGNED_STRING = 'signed string' - const signature = await makeDidSignature(SIGNED_STRING, did.uri, sign) - const oldSignature: any = { - ...signature, - keyId: signature.keyUri, - } - delete oldSignature.keyUri - - // Test the old signature is correctly crafted - expect(oldSignature.signature).toBeDefined() - expect(oldSignature.keyId).toBeDefined() - expect(oldSignature.keyUri).toBeUndefined() - + const { signature, keyUri } = await sign({ + data: Crypto.coToUInt8(SIGNED_STRING), + did: did.uri, + keyRelationship: 'authentication', + }) await expect( verifyDidSignature({ message: SIGNED_STRING, signature, + keyUri, expectedVerificationMethod: 'authentication', }) ).resolves.not.toThrow() @@ -90,15 +72,16 @@ describe('light DID', () => { it('verifies did signature over bytes', async () => { const SIGNED_BYTES = Uint8Array.from([1, 2, 3, 4, 5]) - const signature = await makeDidSignature( - Crypto.u8aToHex(SIGNED_BYTES), - did.uri, - sign - ) + const { signature, keyUri } = await sign({ + data: Crypto.coToUInt8(Crypto.u8aToHex(SIGNED_BYTES)), + did: did.uri, + keyRelationship: 'authentication', + }) await expect( verifyDidSignature({ message: SIGNED_BYTES, signature, + keyUri, expectedVerificationMethod: 'authentication', }) ).resolves.not.toThrow() @@ -106,11 +89,16 @@ describe('light DID', () => { it('fails if relationship does not match', async () => { const SIGNED_STRING = 'signed string' - const signature = await makeDidSignature(SIGNED_STRING, did.uri, sign) + const { signature, keyUri } = await sign({ + data: Crypto.coToUInt8(SIGNED_STRING), + did: did.uri, + keyRelationship: 'authentication', + }) await expect(() => verifyDidSignature({ message: SIGNED_STRING, signature, + keyUri, expectedVerificationMethod: 'assertionMethod', }) ).rejects.toThrow() @@ -118,13 +106,19 @@ describe('light DID', () => { it('fails if key id does not match', async () => { const SIGNED_STRING = 'signed string' - const signature = await makeDidSignature(SIGNED_STRING, did.uri, sign) - signature.keyUri += '1a' + // eslint-disable-next-line prefer-const + let { signature, keyUri } = await sign({ + data: Crypto.coToUInt8(SIGNED_STRING), + did: did.uri, + keyRelationship: 'authentication', + }) + keyUri += '1a' jest.mocked(resolveKey).mockRejectedValue(new Error('Key not found')) await expect(() => verifyDidSignature({ message: SIGNED_STRING, signature, + keyUri, expectedVerificationMethod: 'authentication', }) ).rejects.toThrow() @@ -132,11 +126,16 @@ describe('light DID', () => { it('fails if signature does not match', async () => { const SIGNED_STRING = 'signed string' - const signature = await makeDidSignature(SIGNED_STRING, did.uri, sign) + const { signature, keyUri } = await sign({ + data: Crypto.coToUInt8(SIGNED_STRING), + did: did.uri, + keyRelationship: 'authentication', + }) await expect(() => verifyDidSignature({ message: SIGNED_STRING.substring(1), signature, + keyUri, expectedVerificationMethod: 'authentication', }) ).rejects.toThrow() @@ -144,13 +143,19 @@ describe('light DID', () => { it('fails if key id malformed', async () => { const SIGNED_STRING = 'signed string' - const signature = await makeDidSignature(SIGNED_STRING, did.uri, sign) + // eslint-disable-next-line prefer-const + let { signature, keyUri } = await sign({ + data: Crypto.coToUInt8(SIGNED_STRING), + did: did.uri, + keyRelationship: 'authentication', + }) // @ts-expect-error - signature.keyUri = signature.keyUri.replace('#', '?') + keyUri = keyUri.replace('#', '?') await expect(() => verifyDidSignature({ message: SIGNED_STRING, signature, + keyUri, expectedVerificationMethod: 'authentication', }) ).rejects.toThrow() @@ -159,11 +164,16 @@ describe('light DID', () => { it('does not verify if migrated to Full DID', async () => { jest.mocked(resolveKey).mockRejectedValue(new Error('Migrated')) const SIGNED_STRING = 'signed string' - const signature = await makeDidSignature(SIGNED_STRING, did.uri, sign) + const { signature, keyUri } = await sign({ + data: Crypto.coToUInt8(SIGNED_STRING), + did: did.uri, + keyRelationship: 'authentication', + }) await expect(() => verifyDidSignature({ message: SIGNED_STRING, signature, + keyUri, expectedVerificationMethod: 'authentication', }) ).rejects.toThrow() @@ -214,11 +224,16 @@ describe('full DID', () => { it('verifies did signature over string', async () => { const SIGNED_STRING = 'signed string' - const signature = await makeDidSignature(SIGNED_STRING, did.uri, sign) + const { signature, keyUri } = await sign({ + data: Crypto.coToUInt8(SIGNED_STRING), + did: did.uri, + keyRelationship: 'authentication', + }) await expect( verifyDidSignature({ message: SIGNED_STRING, signature, + keyUri, expectedVerificationMethod: 'authentication', }) ).resolves.not.toThrow() @@ -226,15 +241,16 @@ describe('full DID', () => { it('verifies did signature over bytes', async () => { const SIGNED_BYTES = Uint8Array.from([1, 2, 3, 4, 5]) - const signature = await makeDidSignature( - Crypto.u8aToHex(SIGNED_BYTES), - did.uri, - sign - ) + const { signature, keyUri } = await sign({ + data: Crypto.coToUInt8(Crypto.u8aToHex(SIGNED_BYTES)), + did: did.uri, + keyRelationship: 'authentication', + }) await expect( verifyDidSignature({ message: SIGNED_BYTES, signature, + keyUri, expectedVerificationMethod: 'authentication', }) ).resolves.not.toThrow() @@ -243,11 +259,16 @@ describe('full DID', () => { it('does not verify if deactivated', async () => { jest.mocked(resolveKey).mockRejectedValue(new Error('Deactivated')) const SIGNED_STRING = 'signed string' - const signature = await makeDidSignature(SIGNED_STRING, did.uri, sign) + const { signature, keyUri } = await sign({ + data: Crypto.coToUInt8(SIGNED_STRING), + did: did.uri, + keyRelationship: 'authentication', + }) await expect(() => verifyDidSignature({ message: SIGNED_STRING, signature, + keyUri, expectedVerificationMethod: 'authentication', }) ).rejects.toThrow() @@ -256,11 +277,16 @@ describe('full DID', () => { it('does not verify if not on chain', async () => { jest.mocked(resolveKey).mockRejectedValue(new Error('Not on chain')) const SIGNED_STRING = 'signed string' - const signature = await makeDidSignature(SIGNED_STRING, did.uri, sign) + const { signature, keyUri } = await sign({ + data: Crypto.coToUInt8(SIGNED_STRING), + did: did.uri, + keyRelationship: 'authentication', + }) await expect(() => verifyDidSignature({ message: SIGNED_STRING, signature, + keyUri, expectedVerificationMethod: 'authentication', }) ).rejects.toThrow() diff --git a/packages/did/src/Did.signature.ts b/packages/did/src/Did.signature.ts index dce02c54e..7aa609663 100644 --- a/packages/did/src/Did.signature.ts +++ b/packages/did/src/Did.signature.ts @@ -9,7 +9,9 @@ import { u8aToHex, isHex } from '@polkadot/util' import { DidResolveKey, + DidResourceUri, DidSignature, + SignResponseData, VerificationKeyRelationship, } from '@kiltprotocol/types' import { Crypto, SDKErrors } from '@kiltprotocol/utils' @@ -19,7 +21,8 @@ import { parse, validateUri } from './Did.utils.js' export type DidSignatureVerificationInput = { message: string | Uint8Array - signature: DidSignature + signature: Uint8Array + keyUri: DidResourceUri expectedVerificationMethod?: VerificationKeyRelationship didResolveKey?: DidResolveKey } @@ -54,32 +57,28 @@ function verifyDidSignatureDataStructure( * * @param input Object wrapping all input. * @param input.message The message that was signed. - * @param input.signature An object containing signature and signer key. + * @param input.signature Signature bytes. + * @param input.keyUri DID URI of the key used for signing. * @param input.expectedVerificationMethod Which relationship to the signer DID the key must have. * @param input.didResolveKey Allows specifying a custom DID key resolve. Defaults to the built-in [[resolveKey]]. */ export async function verifyDidSignature({ message, signature, + keyUri, expectedVerificationMethod, didResolveKey = resolveKey, }: DidSignatureVerificationInput): Promise { - verifyDidSignatureDataStructure(signature) - // Add support for old signatures that had the `keyId` instead of the `keyUri` - const inputUri = signature.keyUri || (signature as any).keyId // Verification fails if the signature key URI is not valid - const { fragment } = parse(inputUri) + const { fragment } = parse(keyUri) if (!fragment) throw new SDKErrors.SignatureMalformedError( - `Signature key URI "${inputUri}" invalid` + `Signature key URI "${keyUri}" invalid` ) - const { publicKey } = await didResolveKey( - inputUri, - expectedVerificationMethod - ) + const { publicKey } = await didResolveKey(keyUri, expectedVerificationMethod) - Crypto.verify(message, signature.signature, u8aToHex(publicKey)) + Crypto.verify(message, signature, u8aToHex(publicKey)) } /** @@ -99,3 +98,18 @@ export function isDidSignature( return false } } + +/** + * Transforms the output of a [[SignCallback]] into the [[DidSignature]] format suitable for json-based data exchange. + * + * @param input Signature data returned from the [[SignCallback]]. + * @param input.signature Signature bytes. + * @param input.keyUri DID URI of the key used for signing. + * @returns A [[DidSignature]] object where signature is hex-encoded. + */ +export function signatureToJson({ + signature, + keyUri, +}: SignResponseData): DidSignature { + return { signature: Crypto.u8aToHex(signature), keyUri } +} diff --git a/packages/messaging/src/Message.spec.ts b/packages/messaging/src/Message.spec.ts index 50418c703..6730698f2 100644 --- a/packages/messaging/src/Message.spec.ts +++ b/packages/messaging/src/Message.spec.ts @@ -64,7 +64,6 @@ import { createLocalDemoFullDidFromKeypair, KeyTool, KeyToolSignCallback, - makeDidSignature, } from '@kiltprotocol/testing' import { u8aToHex } from '@polkadot/util' import { Crypto, SDKErrors } from '@kiltprotocol/utils' @@ -890,10 +889,12 @@ describe('Error checking / Verification', () => { }, metaData: {}, signatures: { - inviter: await makeDidSignature( - 'signature', - identityAlice.uri, - keyAlice.getSignCallback(identityAlice) + inviter: Did.signatureToJson( + await keyAlice.getSignCallback(identityAlice)({ + data: Crypto.coToUInt8('signature'), + did: identityAlice.uri, + keyRelationship: 'authentication', + }) ), }, } @@ -907,15 +908,19 @@ describe('Error checking / Verification', () => { isPCR: false, }, signatures: { - inviter: await makeDidSignature( - 'signature', - identityAlice.uri, - keyAlice.getSignCallback(identityAlice) + inviter: Did.signatureToJson( + await keyAlice.getSignCallback(identityAlice)({ + data: Crypto.coToUInt8('signature'), + did: identityAlice.uri, + keyRelationship: 'authentication', + }) ), - invitee: await makeDidSignature( - 'signature', - identityBob.uri, - keyBob.getSignCallback(identityBob) + invitee: Did.signatureToJson( + await keyBob.getSignCallback(identityBob)({ + data: Crypto.coToUInt8('signature'), + did: identityBob.uri, + keyRelationship: 'authentication', + }) ), }, } diff --git a/packages/testing/src/TestUtils.ts b/packages/testing/src/TestUtils.ts index 2fa692391..0e5e0648b 100644 --- a/packages/testing/src/TestUtils.ts +++ b/packages/testing/src/TestUtils.ts @@ -12,8 +12,6 @@ import type { DidDocument, DidKey, DidServiceEndpoint, - DidSignature, - DidUri, DidVerificationKey, EncryptCallback, KeyRelationship, @@ -333,19 +331,3 @@ export async function createFullDidFromSeed( const sign = makeStoreDidCallback(keypair) return createFullDidFromLightDid(payer, lightDid, sign) } - -export async function makeDidSignature( - data: string, - didUri: DidUri, - signCallback: SignCallback -): Promise { - const { signature, keyUri } = await signCallback({ - data: Crypto.coToUInt8(data), - did: didUri, - keyRelationship: 'authentication', - }) - return { - signature: Crypto.u8aToHex(signature), - keyUri, - } -} From 7e16e5a81ff3198b59992a7e05958afdea8dab40 Mon Sep 17 00:00:00 2001 From: Raphael Flechtner Date: Tue, 18 Oct 2022 16:46:30 +0200 Subject: [PATCH 2/6] chore: let Crypto.verify accept public keys in Uint8 --- packages/core/src/quote/Quote.spec.ts | 8 ++------ packages/did/src/Did.signature.ts | 4 ++-- packages/utils/src/Crypto.ts | 6 +++--- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/packages/core/src/quote/Quote.spec.ts b/packages/core/src/quote/Quote.spec.ts index dfe7be416..d1c08d319 100644 --- a/packages/core/src/quote/Quote.spec.ts +++ b/packages/core/src/quote/Quote.spec.ts @@ -9,8 +9,6 @@ * @group unit/quote */ -import { u8aToHex } from '@polkadot/util' - import type { DidDocument, IClaim, @@ -170,10 +168,8 @@ describe('Quote', () => { }) ), validAttesterSignedQuote.attesterSignature.signature, - u8aToHex( - Did.getKey(attesterIdentity, attesterKeyId!)?.publicKey || - new Uint8Array() - ) + Did.getKey(attesterIdentity, attesterKeyId!)?.publicKey || + new Uint8Array() ) ).not.toThrow() await Quote.verifyAttesterSignedQuote(validAttesterSignedQuote, { diff --git a/packages/did/src/Did.signature.ts b/packages/did/src/Did.signature.ts index 7aa609663..4c4d7f19f 100644 --- a/packages/did/src/Did.signature.ts +++ b/packages/did/src/Did.signature.ts @@ -5,7 +5,7 @@ * found in the LICENSE file in the root directory of this source tree. */ -import { u8aToHex, isHex } from '@polkadot/util' +import { isHex } from '@polkadot/util' import { DidResolveKey, @@ -78,7 +78,7 @@ export async function verifyDidSignature({ const { publicKey } = await didResolveKey(keyUri, expectedVerificationMethod) - Crypto.verify(message, signature, u8aToHex(publicKey)) + Crypto.verify(message, signature, publicKey) } /** diff --git a/packages/utils/src/Crypto.ts b/packages/utils/src/Crypto.ts index a841fb1a6..197c6575e 100644 --- a/packages/utils/src/Crypto.ts +++ b/packages/utils/src/Crypto.ts @@ -117,14 +117,14 @@ export function signStr( * * @param message Original signed message to be verified. * @param signature Signature as hex string or byte array. - * @param address Substrate address or public key of the signer. + * @param addressOrPublicKey Substrate address or public key of the signer. */ export function verify( message: CryptoInput, signature: CryptoInput, - address: Address + addressOrPublicKey: Address | HexString | Uint8Array ): void { - if (signatureVerify(message, signature, address).isValid !== true) + if (signatureVerify(message, signature, addressOrPublicKey).isValid !== true) throw new SDKErrors.SignatureUnverifiableError() } From 148c83ce1d2be6f0f59a9d4d35e8a1e142d0087d Mon Sep 17 00:00:00 2001 From: Raphael Flechtner Date: Tue, 18 Oct 2022 17:04:58 +0200 Subject: [PATCH 3/6] chore: use signatureToJson everywhere --- packages/core/src/credential/Credential.ts | 6 +++--- packages/core/src/quote/Quote.ts | 18 ++++++++---------- packages/did/src/Did.signature.spec.ts | 4 ++-- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/packages/core/src/credential/Credential.ts b/packages/core/src/credential/Credential.ts index 5116a84dd..82228d8df 100644 --- a/packages/core/src/credential/Credential.ts +++ b/packages/core/src/credential/Credential.ts @@ -21,6 +21,7 @@ import { isDidSignature, verifyDidSignature, resolveKey, + signatureToJson, } from '@kiltprotocol/did' import type { DidResolveKey, @@ -35,7 +36,6 @@ import type { SignCallback, } from '@kiltprotocol/types' import { Crypto, DataUtils, SDKErrors } from '@kiltprotocol/utils' -import { u8aToHex } from '@polkadot/util' import * as Claim from '../claim/index.js' import { hashClaimContents } from '../claim/index.js' import { verifyClaimAgainstSchema } from '../ctype/index.js' @@ -420,7 +420,7 @@ export async function createPresentation({ excludedClaimProperties ) - const { signature, keyUri } = await signCallback({ + const signature = await signCallback({ data: makeSigningData(presentation, challenge), did: credential.claim.owner, keyRelationship: 'authentication', @@ -428,6 +428,6 @@ export async function createPresentation({ return { ...presentation, - claimerSignature: { signature: u8aToHex(signature), keyUri, challenge }, + claimerSignature: { ...signatureToJson(signature), challenge }, } } diff --git a/packages/core/src/quote/Quote.ts b/packages/core/src/quote/Quote.ts index d56b77b9d..3540462c0 100644 --- a/packages/core/src/quote/Quote.ts +++ b/packages/core/src/quote/Quote.ts @@ -26,7 +26,11 @@ import type { DidResourceUri, } from '@kiltprotocol/types' import { Crypto, JsonSchema, SDKErrors } from '@kiltprotocol/utils' -import { resolveKey, verifyDidSignature } from '@kiltprotocol/did' +import { + resolveKey, + verifyDidSignature, + signatureToJson, +} from '@kiltprotocol/did' import { QuoteSchema } from './QuoteSchema.js' /** @@ -80,10 +84,7 @@ export async function createAttesterSignedQuote( }) return { ...quoteInput, - attesterSignature: { - keyUri: signature.keyUri, - signature: Crypto.u8aToHex(signature.signature), - }, + attesterSignature: signatureToJson(signature), } } @@ -155,7 +156,7 @@ export async function createQuoteAgreement( didResolveKey, }) - const { signature, keyUri } = await sign({ + const signature = await sign({ data: Crypto.hash(Crypto.encodeObjectAsStr(attesterSignedQuote)), did: claimerDid, keyRelationship: 'authentication', @@ -164,10 +165,7 @@ export async function createQuoteAgreement( return { ...attesterSignedQuote, rootHash: credentialRootHash, - claimerSignature: { - signature: Crypto.u8aToHex(signature), - keyUri, - }, + claimerSignature: signatureToJson(signature), } } diff --git a/packages/did/src/Did.signature.spec.ts b/packages/did/src/Did.signature.spec.ts index 3b9efbfca..ca244ca98 100644 --- a/packages/did/src/Did.signature.spec.ts +++ b/packages/did/src/Did.signature.spec.ts @@ -73,7 +73,7 @@ describe('light DID', () => { it('verifies did signature over bytes', async () => { const SIGNED_BYTES = Uint8Array.from([1, 2, 3, 4, 5]) const { signature, keyUri } = await sign({ - data: Crypto.coToUInt8(Crypto.u8aToHex(SIGNED_BYTES)), + data: SIGNED_BYTES, did: did.uri, keyRelationship: 'authentication', }) @@ -242,7 +242,7 @@ describe('full DID', () => { it('verifies did signature over bytes', async () => { const SIGNED_BYTES = Uint8Array.from([1, 2, 3, 4, 5]) const { signature, keyUri } = await sign({ - data: Crypto.coToUInt8(Crypto.u8aToHex(SIGNED_BYTES)), + data: SIGNED_BYTES, did: did.uri, keyRelationship: 'authentication', }) From 55a5f8cf8d2b8157409bfe1fa9d91c9d678a8198 Mon Sep 17 00:00:00 2001 From: Raphael Flechtner Date: Tue, 18 Oct 2022 17:26:09 +0200 Subject: [PATCH 4/6] chore: remove unecessary conversion --- packages/core/src/quote/Quote.spec.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/core/src/quote/Quote.spec.ts b/packages/core/src/quote/Quote.spec.ts index d1c08d319..724fb2d54 100644 --- a/packages/core/src/quote/Quote.spec.ts +++ b/packages/core/src/quote/Quote.spec.ts @@ -142,9 +142,7 @@ describe('Quote', () => { const sign = claimer.getSignCallback(claimerIdentity) const signature = Did.signatureToJson( await sign({ - data: Crypto.coToUInt8( - Crypto.hashStr(Crypto.encodeObjectAsStr(validAttesterSignedQuote)) - ), + data: Crypto.hash(Crypto.encodeObjectAsStr(validAttesterSignedQuote)), did: claimerIdentity.uri, keyRelationship: 'authentication', }) From c9074428a0df0157c2e02cf95b247164cd0f70b1 Mon Sep 17 00:00:00 2001 From: Raphael Flechtner Date: Tue, 18 Oct 2022 17:34:24 +0200 Subject: [PATCH 5/6] feat: did signatureFromJson --- packages/core/src/credential/Credential.ts | 8 ++------ packages/core/src/quote/Quote.ts | 14 +++----------- packages/did/src/Did.signature.ts | 15 +++++++++++++++ 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/packages/core/src/credential/Credential.ts b/packages/core/src/credential/Credential.ts index 82228d8df..bd3d627a4 100644 --- a/packages/core/src/credential/Credential.ts +++ b/packages/core/src/credential/Credential.ts @@ -22,10 +22,10 @@ import { verifyDidSignature, resolveKey, signatureToJson, + signatureFromJson, } from '@kiltprotocol/did' import type { DidResolveKey, - DidResourceUri, Hash, IAttestation, IClaim, @@ -234,11 +234,7 @@ export async function verifySignature( ) const signingData = makeSigningData(input, claimerSignature.challenge) await verifyDidSignature({ - signature: Crypto.coToUInt8(claimerSignature.signature), - keyUri: - claimerSignature.keyUri ?? - // accept the old did signature format where keyUri was keyId - (claimerSignature as unknown as { keyId: DidResourceUri }).keyId, + ...signatureFromJson(claimerSignature), message: signingData, expectedVerificationMethod: 'authentication', didResolveKey, diff --git a/packages/core/src/quote/Quote.ts b/packages/core/src/quote/Quote.ts index 3540462c0..60e0b134c 100644 --- a/packages/core/src/quote/Quote.ts +++ b/packages/core/src/quote/Quote.ts @@ -23,13 +23,13 @@ import type { SignCallback, DidResolveKey, DidUri, - DidResourceUri, } from '@kiltprotocol/types' import { Crypto, JsonSchema, SDKErrors } from '@kiltprotocol/utils' import { resolveKey, verifyDidSignature, signatureToJson, + signatureFromJson, } from '@kiltprotocol/did' import { QuoteSchema } from './QuoteSchema.js' @@ -105,11 +105,7 @@ export async function verifyAttesterSignedQuote( ): Promise { const { attesterSignature, ...basicQuote } = quote await verifyDidSignature({ - signature: Crypto.coToUInt8(attesterSignature.signature), - keyUri: - attesterSignature.keyUri ?? - // accept the old did signature format where keyUri was keyId - (attesterSignature as unknown as { keyId: DidResourceUri }).keyId, + ...signatureFromJson(attesterSignature), message: Crypto.hashStr(Crypto.encodeObjectAsStr(basicQuote)), expectedVerificationMethod: 'authentication', didResolveKey, @@ -146,11 +142,7 @@ export async function createQuoteAgreement( const { attesterSignature, ...basicQuote } = attesterSignedQuote await verifyDidSignature({ - signature: Crypto.coToUInt8(attesterSignature.signature), - keyUri: - attesterSignature.keyUri ?? - // accept the old did signature format where keyUri was keyId - (attesterSignature as unknown as { keyId: DidResourceUri }).keyId, + ...signatureFromJson(attesterSignature), message: Crypto.hashStr(Crypto.encodeObjectAsStr(basicQuote)), expectedVerificationMethod: 'authentication', didResolveKey, diff --git a/packages/did/src/Did.signature.ts b/packages/did/src/Did.signature.ts index 4c4d7f19f..df9ed6015 100644 --- a/packages/did/src/Did.signature.ts +++ b/packages/did/src/Did.signature.ts @@ -113,3 +113,18 @@ export function signatureToJson({ }: SignResponseData): DidSignature { return { signature: Crypto.u8aToHex(signature), keyUri } } + +/** + * Deserializes a [[DidSignature]] for signature verification. + * Handles backwards compatibility to an older version of the interface where the `keyUri` property was called `keyId`. + * + * @param input A [[DidSignature]] object. + * @returns The deserialized DidSignature where the signature is represented as a Uint8Array. + */ +export function signatureFromJson( + input: DidSignature | OldDidSignature +): Pick { + const keyUri = 'keyUri' in input ? input.keyUri : input.keyId + const signature = Crypto.coToUInt8(input.signature) + return { signature, keyUri } +} From 7a2a074e7dee786e4e0df62bc01a4f9b75ba4dd0 Mon Sep 17 00:00:00 2001 From: Raphael Flechtner Date: Tue, 18 Oct 2022 17:52:28 +0200 Subject: [PATCH 6/6] test: re-add tests for signature backwards compatibility --- packages/did/src/Did.signature.spec.ts | 27 +++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/packages/did/src/Did.signature.spec.ts b/packages/did/src/Did.signature.spec.ts index ca244ca98..f5bb3a339 100644 --- a/packages/did/src/Did.signature.spec.ts +++ b/packages/did/src/Did.signature.spec.ts @@ -21,7 +21,12 @@ import { randomAsHex, randomAsU8a } from '@polkadot/util-crypto' import { Crypto } from '@kiltprotocol/utils' import { makeSigningKeyTool } from '@kiltprotocol/testing' import * as Did from './index.js' -import { verifyDidSignature, isDidSignature } from './Did.signature' +import { + verifyDidSignature, + isDidSignature, + signatureFromJson, + signatureToJson, +} from './Did.signature' import { resolveKey, keyToResolvedKey } from './DidResolver' jest.mock('./DidResolver') @@ -70,6 +75,26 @@ describe('light DID', () => { ).resolves.not.toThrow() }) + it('deserializes old did signature (with `keyId` property) to new format', async () => { + const SIGNED_STRING = 'signed string' + const { signature, keyUri } = signatureToJson( + await sign({ + data: Crypto.coToUInt8(SIGNED_STRING), + did: did.uri, + keyRelationship: 'authentication', + }) + ) + const oldSignature = { + signature, + keyId: keyUri, + } + + const deserialized = signatureFromJson(oldSignature) + expect(deserialized.signature).toBeInstanceOf(Uint8Array) + expect(deserialized.keyUri).toStrictEqual(keyUri) + expect(deserialized).not.toHaveProperty('keyId') + }) + it('verifies did signature over bytes', async () => { const SIGNED_BYTES = Uint8Array.from([1, 2, 3, 4, 5]) const { signature, keyUri } = await sign({