UNPKG

did-jwt

Version:

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

154 lines • 6.59 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.toSignatureObject = toSignatureObject; const Digest_js_1 = require("./Digest.js"); const util_js_1 = require("./util.js"); const index_js_1 = require("./blockchains/index.js"); const secp256k1_js_1 = require("@noble/curves/secp256k1.js"); const nist_js_1 = require("@noble/curves/nist.js"); const ed25519_js_1 = require("@noble/curves/ed25519.js"); // converts a JOSE signature to it's components function toSignatureObject(signature, recoverable = false) { const rawSig = (0, util_js_1.base64ToBytes)(signature); if (rawSig.length !== (recoverable ? 65 : 64)) { throw new Error('wrong signature length'); } const r = (0, util_js_1.bytesToHex)(rawSig.slice(0, 32)); const s = (0, util_js_1.bytesToHex)(rawSig.slice(32, 64)); const sigObj = { r, s }; if (recoverable) { sigObj.recoveryParam = rawSig[64]; } return sigObj; } function toSignatureObject2(signature, recoverable = false) { const bytes = (0, util_js_1.base64ToBytes)(signature); if (bytes.length !== (recoverable ? 65 : 64)) { throw new Error('wrong signature length'); } return { compact: bytes.slice(0, 64), recovery: bytes[64], }; } function verifyES256(data, signature, authenticators) { const hash = (0, Digest_js_1.sha256)(data); const sig = (0, util_js_1.base64ToBytes)(signature); if (sig.length !== 64) { throw new Error(`"compact signature" expected Uint8Array of length 64, got length=${sig.length}`); } const fullPublicKeys = authenticators.filter((a) => !a.ethereumAddress && !a.blockchainAccountId); const signer = fullPublicKeys.find((pk) => { try { const { keyBytes } = (0, util_js_1.extractPublicKeyBytes)(pk); return nist_js_1.p256.verify(sig, hash, keyBytes, { prehash: false, lowS: false }); } catch { return false; } }); if (!signer) throw new Error('invalid_signature: Signature invalid for JWT'); return signer; } function verifyES256K(data, signature, authenticators) { const hash = (0, Digest_js_1.sha256)(data); const signatureBytes = (0, util_js_1.base64ToBytes)(signature); if (signatureBytes.length !== 64) { throw new Error(`"compact signature" expected Uint8Array of length 64, got length=${signatureBytes.length}`); } const fullPublicKeys = authenticators.filter((a) => { return !a.ethereumAddress && !a.blockchainAccountId; }); const blockchainAddressKeys = authenticators.filter((a) => { return a.ethereumAddress || a.blockchainAccountId; }); let signer = fullPublicKeys.find((pk) => { try { const { keyBytes } = (0, util_js_1.extractPublicKeyBytes)(pk); return secp256k1_js_1.secp256k1.verify(signatureBytes, hash, keyBytes, { prehash: false, lowS: false, format: 'compact' }); } catch { return false; } }); if (!signer && blockchainAddressKeys.length > 0) { signer = verifyRecoverableES256K(data, signature, blockchainAddressKeys); } if (!signer) throw new Error('invalid_signature: Signature invalid for JWT'); return signer; } function verifyRecoverableES256K(data, signature, authenticators) { const signatures = []; if (signature.length > 86) { signatures.push(toSignatureObject2(signature, true)); } else { const so = toSignatureObject2(signature, false); signatures.push({ ...so, recovery: 0 }); signatures.push({ ...so, recovery: 1 }); } const hash = (0, Digest_js_1.sha256)(data); const checkSignatureAgainstSigner = (sigObj) => { const signature = secp256k1_js_1.secp256k1.Signature.fromBytes(sigObj.compact, 'compact').addRecoveryBit(sigObj.recovery || 0); const recoveredPublicKey = signature.recoverPublicKey(hash); const recoveredAddress = (0, Digest_js_1.toEthereumAddress)(recoveredPublicKey.toHex(false)).toLowerCase(); const recoveredPublicKeyHex = recoveredPublicKey.toHex(false); const recoveredCompressedPublicKeyHex = recoveredPublicKey.toHex(true); return authenticators.find((a) => { const { keyBytes } = (0, util_js_1.extractPublicKeyBytes)(a); const keyHex = (0, util_js_1.bytesToHex)(keyBytes); return (keyHex === recoveredPublicKeyHex || keyHex === recoveredCompressedPublicKeyHex || a.ethereumAddress?.toLowerCase() === recoveredAddress || a.blockchainAccountId?.split('@eip155')?.[0].toLowerCase() === recoveredAddress || // CAIP-2 (0, index_js_1.verifyBlockchainAccountId)(recoveredPublicKeyHex, a.blockchainAccountId) // CAIP-10 ); }); }; // Find first verification method for (const signature of signatures) { const verificationMethod = checkSignatureAgainstSigner(signature); if (verificationMethod) return verificationMethod; } // If no one found matching throw new Error('invalid_signature: Signature invalid for JWT'); } function verifyEd25519(data, signature, authenticators) { const clear = (0, util_js_1.stringToBytes)(data); const signatureBytes = (0, util_js_1.base64ToBytes)(signature); const signer = authenticators.find((a) => { const { keyBytes, keyType } = (0, util_js_1.extractPublicKeyBytes)(a); if (keyType === 'Ed25519') { return ed25519_js_1.ed25519.verify(signatureBytes, clear, keyBytes); } else { return false; } }); if (!signer) throw new Error('invalid_signature: Signature invalid for JWT'); return signer; } const algorithms = { ES256: verifyES256, ES256K: verifyES256K, // This is a non-standard algorithm but retained for backwards compatibility // see https://github.com/decentralized-identity/did-jwt/issues/146 'ES256K-R': verifyRecoverableES256K, // This is actually incorrect but retained for backwards compatibility // see https://github.com/decentralized-identity/did-jwt/issues/130 Ed25519: verifyEd25519, EdDSA: verifyEd25519, }; function VerifierAlgorithm(alg) { const impl = algorithms[alg]; if (!impl) throw new Error(`not_supported: Unsupported algorithm ${alg}`); return impl; } VerifierAlgorithm.toSignatureObject = toSignatureObject; exports.default = VerifierAlgorithm; //# sourceMappingURL=VerifierAlgorithm.js.map