From 367babb0caefdebbcb61f8e31ff33182f22e0c64 Mon Sep 17 00:00:00 2001 From: AdriGeorge Date: Wed, 20 Nov 2024 13:06:04 +0200 Subject: [PATCH] chore: add tests --- src/utils/SignDDO.ts | 40 ++++++++++++++++++++- test/unit/SignDDO.test.ts | 75 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 test/unit/SignDDO.test.ts diff --git a/src/utils/SignDDO.ts b/src/utils/SignDDO.ts index 9b857b79..f0af3070 100644 --- a/src/utils/SignDDO.ts +++ b/src/utils/SignDDO.ts @@ -1,4 +1,4 @@ -import { base64url, importJWK, JWTPayload, SignJWT } from 'jose' +import { base64url, importJWK, JWTPayload, jwtVerify, SignJWT } from 'jose' import axios from 'axios' import { ethers } from 'ethers' import { IssuerKey, SignedCredential } from '../@types/IssuerSignature' @@ -85,3 +85,41 @@ export async function signCredential( throw error } } + +/** + * Verifies a verifiable credential's JWS using the issuer's public key. + * @param {string} jws - The JSON Web Signature (JWS) to verify. + * @param {string} issuerPublicKey - The public key of the issuer in hexadecimal format. + * @returns {Promise} - The verified payload of the credential. + * @throws {Error} If the verification fails. + */ +export async function verifyCredential( + jws: string, + issuerPublicKey: string +): Promise { + const publicKeyBuffer = Buffer.from(issuerPublicKey.substring(2), 'hex') + const xBuffer = publicKeyBuffer.slice(1, 33) + const yBuffer = publicKeyBuffer.slice(33, 65) + + const x = base64url.encode(xBuffer as any as Uint8Array) + const y = base64url.encode(yBuffer as any as Uint8Array) + + const publicJwk = { + kty: 'EC', + crv: 'secp256k1', + x, + y, + alg: 'ES256K', + use: 'sig' + } + + const key = await importJWK(publicJwk, 'ES256K') + + try { + const { payload } = await jwtVerify(jws, key) + return payload + } catch (error) { + console.error('Verification failed:', error) + throw error + } +} diff --git a/test/unit/SignDDO.test.ts b/test/unit/SignDDO.test.ts new file mode 100644 index 00000000..99292ceb --- /dev/null +++ b/test/unit/SignDDO.test.ts @@ -0,0 +1,75 @@ +import { assert } from 'chai' +import { signCredential, verifyCredential } from '../../src' +import { ethers } from 'ethers' + +const mockVerifiableCredential = { + '@context': ['https://www.w3.org/2018/credentials/v1'], + type: ['VerifiableCredential'], + credentialSubject: { id: 'did:example:123' }, + issuer: 'did:example:issuer', + issuanceDate: '2023-01-01T00:00:00Z' +} + +describe('Credential Signing and Verification Functions', () => { + describe('signCredential', () => { + it('should sign the credential locally using a private key', async () => { + const privateKey = + '0xc594c6e5def4bab63ac29eed19a134c130388f74f019bc74b8f4389df2837a58' + + const result = await signCredential(mockVerifiableCredential, privateKey) + + assert.isString(result.jws, 'JWS should be a string') + assert.isObject(result.header, 'Header should be an object') + assert.equal(result.header.alg, 'ES256K', 'Algorithm should be ES256K') + assert.isString(result.issuer, 'Issuer should be a string') + }) + }) + + describe('verifyCredential', () => { + it('should verify the signed credential with the correct public key', async () => { + const privateKey = + '0xc594c6e5def4bab63ac29eed19a134c130388f74f019bc74b8f4389df2837a58' + const wallet = new ethers.Wallet(privateKey) + const { publicKey } = wallet._signingKey() + + const { jws } = await signCredential(mockVerifiableCredential, privateKey) + + const payload = await verifyCredential(jws, publicKey) + assert.deepEqual( + { + type: payload.type, + credentialSubject: payload.credentialSubject, + issuer: payload.issuer, + issuanceDate: payload.issuanceDate + }, + { + type: mockVerifiableCredential.type, + credentialSubject: mockVerifiableCredential.credentialSubject, + issuer: mockVerifiableCredential.issuer, + issuanceDate: mockVerifiableCredential.issuanceDate + }, + 'Payload should match the original credential' + ) + }) + + it('should throw an error if verification fails due to an invalid signature', async () => { + const privateKey = + '0xc594c6e5def4bab63ac29eed19a134c130388f74f019bc74b8f4389df2837a58' + const invalidPublicKey = + '0x0491d20394c7c2b191c6db3a3a9e7eac21d9c6741dcf66010e0a743530d8c1b05656fb9b555ebc4162df5d1cf3e372a4e0230205932c27fcd998bdbe26399236f9' + + const { jws } = await signCredential(mockVerifiableCredential, privateKey) + + try { + await verifyCredential(jws, invalidPublicKey) + assert.fail('Expected error to be thrown') + } catch (error) { + assert.include( + error.message, + 'Invalid JWK EC key', + 'Error should indicate failed verification' + ) + } + }) + }) +})