UNPKG

did-jwt

Version:

Library for Signing and Verifying JWTs that use DIDs as issuers and JWEs that use DIDs as recipients

689 lines (611 loc) 27.6 kB
// @ts-ignore import nacl from 'tweetnacl' import { base64ToBytes, bigintToBytes, bytesToBase58, bytesToBase64, bytesToBase64url, bytesToHex, bytesToMultibase, hexToBytes, } from '../util.js' import { EdDSASigner } from '../signers/EdDSASigner.js' import { ES256KSigner } from '../signers/ES256KSigner.js' import { toEthereumAddress } from '../Digest.js' import { publicKeyToAddress as toBip122Address } from '../blockchains/bip122.js' import { publicKeyToAddress as toCosmosAddressWithoutPrefix } from '../blockchains/cosmos.js' import { p256 } from '@noble/curves/p256' import { secp256k1 } from '@noble/curves/secp256k1' import { ES256Signer } from '../signers/ES256Signer.js' import VerifierAlgorithm from '../VerifierAlgorithm.js' import { createJWT } from '../JWT.js' describe('VerifierAlgorithm', () => { it('supports ES256', () => { expect(typeof VerifierAlgorithm('ES256')).toEqual('function') }) it('supports ES256K', () => { expect(typeof VerifierAlgorithm('ES256K')).toEqual('function') }) it('supports ES256K-R', () => { expect(typeof VerifierAlgorithm('ES256K-R')).toEqual('function') }) it('fails on unsupported algorithm', () => { expect(() => VerifierAlgorithm('BADALGO')).toThrowError('Unsupported algorithm BADALGO') }) it('supports EdDSA', () => { expect(typeof VerifierAlgorithm('EdDSA')).toEqual('function') }) }) describe('ES256', () => { const mnid = '2nQtiQG6Cgm1GYTBaaKAgr76uY7iSexUkqX' const did = `did:uport:${mnid}` const privateKey = hexToBytes('736f625c9dda78a94bb16840c82779bb7bc18014b8ede52f0f03429902fc4ba8') const kp = p256.ProjectivePoint.fromPrivateKey(privateKey) const publicKeyBytes = kp.toRawBytes(false) const publicKey = kp.toHex(false) const compressedPublicKey = kp.toHex(true) const publicKeyBase64 = bytesToBase64(publicKeyBytes) const publicKeyBase58 = bytesToBase58(publicKeyBytes) const publicKeyJwk = { crv: 'P-256', kty: 'EC', x: bytesToBase64url(hexToBytes(kp.x.toString(16))), y: bytesToBase64url(hexToBytes(kp.y.toString(16))), } const signer = ES256Signer(privateKey) const publicKeyMultibase = bytesToMultibase(hexToBytes(compressedPublicKey), 'base58btc', 'p256-pub') const publicKeyMultibaseNoCodec = bytesToMultibase(hexToBytes(compressedPublicKey), 'base58btc') const ecKey1 = { id: `${did}#keys-1`, type: 'JsonWebKey2020', controller: did, publicKeyHex: '04f9c36f8964623378bdc068d4bce07ed17c8fa486f9ac0c2613ca3c8c306d7bb61cd36717b8ac5e4fea8ad23dc8d0783c2318ee4ad7a80db6e0026ad0b072a24f', } const ecKey2 = { id: `${did}#keys-2`, type: 'JsonWebKey2020', controller: did, publicKeyHex: publicKey, } const ecKey3 = { id: `${did}#keys-3`, type: 'Multikey', controller: did, publicKeyMultibase, } const ecKey4 = { id: `${did}#keys-4`, type: 'Multikey', controller: did, publicKeyMultibase: publicKeyMultibaseNoCodec, } const compressedKey = { id: `${did}#keys-4`, type: 'JsonWebKey2020', controller: did, publicKeyHex: compressedPublicKey, } const malformedKey1 = { id: `${did}#keys-7`, type: 'JsonWebKey2020', controller: did, publicKeyHex: '05f9c36f8964623378bdc068d4bce07ed17c8fa486f9ac0c2613ca3c8c306d7bb6', } const malformedKey2 = { id: `${did}#keys-8`, type: 'JsonWebKey2020', controller: did, publicKeyHex: '04f9c36f8964623378bdc068d4bce07ed17c8fa486f9ac0c2613ca3c8c306d7bb61cd36717b8ac5e4fea8ad23dc8d0783c2318ee4ad7a80db6e0026ad0b072a24f07', } const malformedKey3 = { id: `${did}#keys-8`, type: 'JsonWebKey2020', controller: did, publicKeyHex: '0404f9c36f8964623378bdc068d4bce07ed17c8fa486f9ac0c2613ca3c8c306d7bb61cd36717b8ac5e4fea8ad23dc8d0783c2318ee4ad7a80db6e0026ad0b072a24f', } const verifier = VerifierAlgorithm('ES256') it('validates signature and picks correct public key', async () => { expect.assertions(1) const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer }, { alg: 'ES256' }) const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) // @ts-ignore return expect(verifier(parts[1], parts[2], [ecKey1, ecKey2])).toEqual(ecKey2) }) it('validates with publicKeyBase58', async () => { expect.assertions(1) const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer }, { alg: 'ES256' }) const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) const pubkey = Object.assign({ publicKeyBase58 }, ecKey2) // @ts-ignore delete pubkey.publicKeyHex // @ts-ignore return expect(verifier(parts[1], parts[2], [pubkey])).toEqual(pubkey) }) it('validates with publicKeyBase64', async () => { expect.assertions(1) const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer }, { alg: 'ES256' }) const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) const pubkey = Object.assign({ publicKeyBase64 }, ecKey2) // @ts-ignore delete pubkey.publicKeyHex // @ts-ignore return expect(verifier(parts[1], parts[2], [pubkey])).toEqual(pubkey) }) it('validates with publicKeyJwk', async () => { expect.assertions(1) const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer }, { alg: 'ES256' }) const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) const pubkey = Object.assign({ publicKeyJwk }, ecKey2) // @ts-ignore delete pubkey.publicKeyHex // @ts-ignore return expect(verifier(parts[1], parts[2], [pubkey])).toEqual(pubkey) }) it('validates with publicKeyMultibase', async () => { expect.assertions(1) const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer }, { alg: 'ES256' }) const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) const pubkey = ecKey3 // @ts-ignore return expect(verifier(parts[1], parts[2], [pubkey])).toEqual(pubkey) }) it('validates with publicKeyMultibase Multikey', async () => { expect.assertions(1) const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer }, { alg: 'ES256' }) const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) const pubkey = ecKey3 // @ts-ignore delete pubkey.publicKeyHex // @ts-ignore return expect(verifier(parts[1], parts[2], [pubkey])).toEqual(pubkey) }) it('validates with publicKeyMultibase Multikey without codec', async () => { expect.assertions(1) const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer }, { alg: 'ES256' }) const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) const pubkey = ecKey4 // @ts-ignore delete pubkey.publicKeyHex // @ts-ignore return expect(verifier(parts[1], parts[2], [pubkey])).toEqual(pubkey) }) it('validates signature with compressed public key and picks correct public key', async () => { expect.assertions(1) const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer }, { alg: 'ES256' }) const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) // @ts-ignore return expect(verifier(parts[1], parts[2], [ecKey1, compressedKey])).toEqual(compressedKey) }) it('throws error if invalid signature', async () => { expect.assertions(1) const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer }, { alg: 'ES256' }) const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) // @ts-ignore return expect(() => verifier(parts[1], parts[2], [ecKey1])).toThrowError( new Error('invalid_signature: Signature invalid for JWT') ) }) it('throws error if invalid signature length', async () => { expect.assertions(1) const jwt = (await createJWT({ bla: 'bla' }, { issuer: did, signer }, { alg: 'ES256' })) + 'aa' const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) // @ts-ignore return expect(() => verifier(parts[1], parts[2], [ecKey1])).toThrowError(new Error('wrong signature length')) }) it('validates signature with compressed public key and picks correct public key when malformed keys are encountered first', async () => { const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer }, { alg: 'ES256' }) const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) // @ts-ignore return expect(verifier(parts[1], parts[2], [malformedKey1, malformedKey2, malformedKey3, compressedKey])).toEqual( compressedKey ) }) }) const mnid = '2nQtiQG6Cgm1GYTBaaKAgr76uY7iSexUkqX' const did = `did:uport:${mnid}` const privateKey = '278a5de700e29faae8e40e366ec5012b5ec63d36ec77e8a2417154cc1d25383f' const publicKeyBytes = secp256k1.getPublicKey(privateKey, false) const publicKeyPoint = secp256k1.ProjectivePoint.fromHex(publicKeyBytes) const publicKeyHex = bytesToHex(publicKeyBytes) const compressedPublicKeyBytes = secp256k1.getPublicKey(privateKey, true) const compressedPublicKey = bytesToHex(compressedPublicKeyBytes) const publicKeyBase64 = bytesToBase64(publicKeyBytes) const publicKeyBase58 = bytesToBase58(publicKeyBytes) const publicKeyJwk = { crv: 'secp256k1', kty: 'EC', x: bytesToBase64url(bigintToBytes(publicKeyPoint.x, 32)), y: bytesToBase64url(bigintToBytes(publicKeyPoint.y, 32)), } const publicKeyMultibase = bytesToMultibase(publicKeyBytes, 'base58btc') const publicKeyMultibaseMulticodec = bytesToMultibase(publicKeyBytes, 'base58btc', 'secp256k1-pub') const eip155 = toEthereumAddress(publicKeyHex) const bip122 = toBip122Address(publicKeyHex, 'undefined') const cosmosPrefix = 'example' const cosmos = toCosmosAddressWithoutPrefix(publicKeyHex, cosmosPrefix) const signer = ES256KSigner(hexToBytes(privateKey)) const recoverySigner = ES256KSigner(hexToBytes(privateKey), true) const ed25519PrivateKey = 'nlXR4aofRVuLqtn9+XVQNlX4s1nVQvp+TOhBBtYls1IG+sHyIkDP/WN+rWZHGIQp+v2pyct+rkM4asF/YRFQdQ==' const edSigner = EdDSASigner(base64ToBytes(ed25519PrivateKey)) const edKp = nacl.sign.keyPair.fromSecretKey(base64ToBytes(ed25519PrivateKey)) const edPublicKey = bytesToBase64(edKp.publicKey) const edPublicKey2 = bytesToBase64(nacl.sign.keyPair().publicKey) const ecKey1 = { id: `${did}#keys-1`, type: 'Secp256k1VerificationKey2018', controller: did, publicKeyHex: '04613bb3a4874d27032618f020614c21cbe4c4e4781687525f6674089f9bd3d6c7f6eb13569053d31715a3ba32e0b791b97922af6387f087d6b5548c06944ab062', } const ecKey2 = { id: `${did}#keys-2`, type: 'Secp256k1VerificationKey2018', controller: did, publicKeyHex: publicKeyHex, } const ethAddress = { id: `${did}#keys-3`, type: 'Secp256k1VerificationKey2018', controller: did, ethereumAddress: eip155, } const blockchainAddress = { id: `${did}#keys-blockchain`, type: 'EcdsaSecp256k1RecoveryMethod2020', controller: did, blockchainAccountId: `${eip155}@eip155:1`, } const blockchainAddressCaip10 = { id: `${did}#keys-blockchain`, type: 'EcdsaSecp256k1RecoveryMethod2020', controller: did, blockchainAccountId: `eip155:1:${eip155}`, } const blockchainAddressBip122 = { id: `${did}#keys-blockchain`, type: 'EcdsaSecp256k1RecoveryMethod2020', controller: did, blockchainAccountId: `bip122:000000000019d6689c085ae165831e93:${bip122}`, } const blockchainAddressCosmos = { id: `${did}#keys-blockchain`, type: 'EcdsaSecp256k1RecoveryMethod2020', controller: did, blockchainAccountId: `cosmos:${cosmosPrefix}:${cosmos}`, } const compressedKey = { id: `${did}#keys-4`, type: 'Secp256k1VerificationKey2018', controller: did, publicKeyHex: compressedPublicKey, } const recoveryMethod2020Key = { id: `${did}#keys-recovery`, type: 'EcdsaSecp256k1RecoveryMethod2020', controller: did, ethereumAddress: eip155, } const edKey = { id: `${did}#keys-5`, type: 'ED25519SignatureVerification', controller: did, publicKeyBase64: edPublicKey, } const edKey2 = { id: `${did}#keys-6`, type: 'ED25519SignatureVerification', controller: did, publicKeyBase64: edPublicKey2, } const malformedKey1 = { id: `${did}#keys-7`, type: 'Secp256k1VerificationKey2018', controller: did, publicKeyHex: '05613bb3a4874d27032618f020614c21cbe4c4e4781687525f6674089f9bd3d6c7f6eb13569053d31715a3ba32e0b791b97922af6387f087d6b5548c06944ab062', } const malformedKey2 = { id: `${did}#keys-8`, type: 'Secp256k1VerificationKey2018', controller: did, publicKeyHex: '04613bb3a4874d27032618f020614c21cbe4c4e4781687525f6674089f9bd3d6c7f6eb13569053d31715a3ba32e0b791b97922af6387f087d6b5548c06944ab062aabbccdd', } const malformedKey3 = { id: `${did}#keys-8`, type: 'Secp256k1VerificationKey2018', controller: did, publicKeyHex: '04613bb3a4874d27032618f020614c21cbe4c4e4781687525f6674089f9bd3d6c7f6eb13569053d31715a3ba32e0b791b97922af6387f087d6b5548c06', } describe('ES256K', () => { const verifier = VerifierAlgorithm('ES256K') it('validates signature and picks correct public key', async () => { expect.assertions(1) const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer }) const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) // @ts-ignore return expect(verifier(parts[1], parts[2], [ecKey1, ecKey2])).toEqual(ecKey2) }) it('validates with publicKeyBase58', async () => { expect.assertions(1) const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer }) const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) const pubkey = Object.assign({ publicKeyBase58 }, ecKey2) // @ts-ignore delete pubkey.publicKeyHex // @ts-ignore return expect(verifier(parts[1], parts[2], [pubkey])).toEqual(pubkey) }) it('validates with publicKeyBase64', async () => { expect.assertions(1) const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer }) const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) const pubkey = Object.assign({ publicKeyBase64 }, ecKey2) // @ts-ignore delete pubkey.publicKeyHex // @ts-ignore return expect(verifier(parts[1], parts[2], [pubkey])).toEqual(pubkey) }) it('validates with publicKeyJwk', async () => { expect.assertions(1) const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer }) const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) const pubkey = Object.assign({ publicKeyJwk }, ecKey2) // @ts-ignore delete pubkey.publicKeyHex // @ts-ignore return expect(verifier(parts[1], parts[2], [pubkey])).toEqual(pubkey) }) it('validates with publicKeyMultibase', async () => { expect.assertions(1) const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer }) const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) const pubkey = Object.assign({ publicKeyMultibase }, ecKey2) // @ts-ignore delete pubkey.publicKeyHex // @ts-ignore return expect(verifier(parts[1], parts[2], [pubkey])).toEqual(pubkey) }) it('validates with publicKeyMultibase multicodec', async () => { expect.assertions(1) const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer }) const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) const pubkey = Object.assign({ publicKeyMultibase: publicKeyMultibaseMulticodec }, ecKey2) // @ts-ignore delete pubkey.publicKeyHex // @ts-ignore return expect(verifier(parts[1], parts[2], [pubkey])).toEqual(pubkey) }) it('validates signature with compressed public key and picks correct public key', async () => { expect.assertions(1) const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer }) const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) // @ts-ignore return expect(verifier(parts[1], parts[2], [ecKey1, compressedKey])).toEqual(compressedKey) }) it('throws error if invalid signature', async () => { expect.assertions(1) const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer }) const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) // @ts-ignore return expect(() => verifier(parts[1], parts[2], [ecKey1])).toThrowError( new Error('invalid_signature: Signature invalid for JWT') ) }) it('throws error if invalid signature length', async () => { expect.assertions(1) const jwt = (await createJWT({ bla: 'bla' }, { issuer: did, signer })) + 'aa' const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) return expect(() => verifier(parts!![1], parts!![2], [ecKey1])).toThrowError( new Error('compactSignature of length 64 expected, got 66') ) }) it('validates signature with compressed public key and picks correct public key when malformed keys are encountered first', async () => { const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer }) const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) // @ts-ignore return expect(verifier(parts[1], parts[2], [malformedKey1, malformedKey2, malformedKey3, compressedKey])).toEqual( compressedKey ) }) it('validates signature produced by ethAddress - github #14', async () => { expect.assertions(1) const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer }) const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) // @ts-ignore return expect(verifier(parts[1], parts[2], [ethAddress])).toEqual(ethAddress) }) it('validates signature produced by blockchainAccountId - github #14, #155', async () => { expect.assertions(1) const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer }) const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) // @ts-ignore return expect(verifier(parts[1], parts[2], [blockchainAddress])).toEqual(blockchainAddress) }) it('validates signature produced by blockchainAccountId - CAIP 10 (EIP 155)', async () => { expect.assertions(1) const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer }) const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) // @ts-ignore return expect(verifier(parts[1], parts[2], [blockchainAddressCaip10])).toEqual(blockchainAddressCaip10) }) it('validates signature produced by blockchainAccountId - CAIP 10 (BIP 122)', async () => { expect.assertions(1) const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer }) const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) // @ts-ignore return expect(verifier(parts[1], parts[2], [blockchainAddressBip122])).toEqual(blockchainAddressBip122) }) it('validates signature produced by blockchainAccountId - CAIP 10 (Cosmos)', async () => { expect.assertions(1) const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer }) const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) // @ts-ignore return expect(verifier(parts[1], parts[2], [blockchainAddressCosmos])).toEqual(blockchainAddressCosmos) }) it('validates signature produced by EcdsaSecp256k1RecoveryMethod2020 - github #152', async () => { expect.assertions(1) const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer }) const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) // @ts-ignore return expect(verifier(parts[1], parts[2], [recoveryMethod2020Key])).toEqual(recoveryMethod2020Key) }) }) describe('ES256K-R', () => { const verifier = VerifierAlgorithm('ES256K-R') it('validates signature and picks correct public key', async () => { expect.assertions(1) const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer: recoverySigner, alg: 'ES256K-R' }) const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) // @ts-ignore return expect(verifier(parts[1], parts[2], [ecKey1, ecKey2])).toEqual(ecKey2) }) it('validates signature and picks correct compressed public key', async () => { expect.assertions(1) const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer: recoverySigner, alg: 'ES256K-R' }) const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) // @ts-ignore return expect(verifier(parts[1], parts[2], [ecKey1, compressedKey])).toEqual(compressedKey) }) it('validates signature with ethereum address', async () => { expect.assertions(1) const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer: recoverySigner, alg: 'ES256K-R' }) const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) // @ts-ignore return expect(verifier(parts[1], parts[2], [ecKey1, ethAddress])).toEqual(ethAddress) }) it('validates signature with blockchainAccountId - github #155', async () => { expect.assertions(1) const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer: recoverySigner, alg: 'ES256K-R' }) const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) // @ts-ignore return expect(verifier(parts[1], parts[2], [ecKey1, blockchainAddress])).toEqual(blockchainAddress) }) it('validates signature with blockchainAccountId - CAIP 10 (EIP 155)', async () => { expect.assertions(1) const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer: recoverySigner, alg: 'ES256K-R' }) const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) // @ts-ignore return expect(verifier(parts[1], parts[2], [ecKey1, blockchainAddressCaip10])).toEqual(blockchainAddressCaip10) }) it('validates signature with blockchainAccountId - CAIP 10 (BIP 122)', async () => { expect.assertions(1) const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer: recoverySigner, alg: 'ES256K-R' }) const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) // @ts-ignore return expect(verifier(parts[1], parts[2], [ecKey1, blockchainAddressBip122])).toEqual(blockchainAddressBip122) }) it('validates signature with blockchainAccountId - CAIP 10 (COSMOS)', async () => { expect.assertions(1) const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer: recoverySigner, alg: 'ES256K-R' }) const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) // @ts-ignore return expect(verifier(parts[1], parts[2], [ecKey1, blockchainAddressCosmos])).toEqual(blockchainAddressCosmos) }) it('validates signature with EcdsaSecp256k1RecoveryMethod2020 - github #152', async () => { expect.assertions(1) const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer: recoverySigner, alg: 'ES256K-R' }) const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) // @ts-ignore return expect(verifier(parts[1], parts[2], [ecKey1, recoveryMethod2020Key])).toEqual(recoveryMethod2020Key) }) it('validates with publicKeyBase58', async () => { expect.assertions(1) const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer: recoverySigner, alg: 'ES256K-R' }) const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) const pubkey = Object.assign({ publicKeyBase58 }, ecKey2) // @ts-ignore delete pubkey.publicKeyHex // @ts-ignore return expect(verifier(parts[1], parts[2], [pubkey])).toEqual(pubkey) }) it('validates with publicKeyBase64', async () => { expect.assertions(1) const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer: recoverySigner, alg: 'ES256K-R' }) const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) const pubkey = Object.assign({ publicKeyBase64 }, ecKey2) // @ts-ignore delete pubkey.publicKeyHex // @ts-ignore return expect(verifier(parts[1], parts[2], [pubkey])).toEqual(pubkey) }) it('validates with publicKeyJwk', async () => { expect.assertions(1) const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer: recoverySigner, alg: 'ES256K-R' }) const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) const pubkey = Object.assign({ publicKeyJwk }, ecKey2) // @ts-ignore delete pubkey.publicKeyHex // @ts-ignore return expect(verifier(parts[1], parts[2], [pubkey])).toEqual(pubkey) }) it('validates with publicKeyMultibase', async () => { expect.assertions(1) const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer: recoverySigner, alg: 'ES256K-R' }) const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) const pubkey = Object.assign({ publicKeyMultibase }, ecKey2) // @ts-ignore delete pubkey.publicKeyHex // @ts-ignore return expect(verifier(parts[1], parts[2], [pubkey])).toEqual(pubkey) }) it('throws error if invalid signature', async () => { expect.assertions(1) const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer: recoverySigner, alg: 'ES256K-R' }) const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) // @ts-ignore return expect(() => verifier(parts[1], parts[2], [ecKey1])).toThrowError( new Error('invalid_signature: Signature invalid for JWT') ) }) }) describe('Ed25519', () => { const verifier = VerifierAlgorithm('Ed25519') it('validates signature and picks correct public key', async () => { expect.assertions(1) const jwt = await createJWT({ bla: 'bla' }, { alg: 'Ed25519', issuer: did, signer: edSigner }) const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) // @ts-ignore return expect(verifier(parts[1], parts[2], [edKey, edKey2])).toEqual(edKey) }) it('validates with publicKeyBase58', async () => { expect.assertions(1) const jwt = await createJWT({ bla: 'bla' }, { alg: 'Ed25519', issuer: did, signer: edSigner }) const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) const publicKeyBase58 = bytesToBase58(base64ToBytes(edKey.publicKeyBase64)) const pubkey = Object.assign({ publicKeyBase58 }, edKey) // @ts-ignore delete pubkey.publicKeyBase64 // @ts-ignore return expect(verifier(parts[1], parts[2], [pubkey])).toEqual(pubkey) }) it('validates with publicKeyJwk', async () => { expect.assertions(1) const jwt = await createJWT({ bla: 'bla' }, { alg: 'Ed25519', issuer: did, signer: edSigner }) const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) const publicKeyJwk = { crv: 'Ed25519', kty: 'OKP', x: bytesToBase64url(base64ToBytes(edKey.publicKeyBase64)), } const pubkey = Object.assign({ publicKeyJwk }, edKey) // @ts-ignore delete pubkey.publicKeyBase64 // @ts-ignore return expect(verifier(parts[1], parts[2], [pubkey])).toEqual(pubkey) }) it('throws error if invalid signature', async () => { expect.assertions(1) const jwt = await createJWT({ bla: 'bla' }, { alg: 'Ed25519', issuer: did, signer: edSigner }) const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) // @ts-ignore return expect(() => verifier(parts[1], parts[2], [edKey2])).toThrowError( new Error('invalid_signature: Signature invalid for JWT') ) }) })