UNPKG

@microsoft/useragent-sdk

Version:

SDK for building decentralized identity wallets and enterprise agents.

225 lines 10.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const CryptoHelpers_1 = require("../utilities/CryptoHelpers"); const KeyTypeFactory_1 = require("../keys/KeyTypeFactory"); const PairwiseKey_1 = require("../keys/PairwiseKey"); const webcrypto_core_1 = require("webcrypto-core"); const CryptoError_1 = require("../CryptoError"); const clone = require('clone'); // Named curves const CURVE_P256K = 'P-256K'; const CURVE_K256 = 'K-256'; /** * The class extends the @class SubtleCrypto with addtional methods. * Adds methods to work with key references. * Extends SubtleCrypto to work with JWK keys. */ class SubtleCryptoExtension extends webcrypto_core_1.SubtleCrypto { constructor(cryptoFactory) { super(); this.keyStore = cryptoFactory.keyStore; this.cryptoFactory = cryptoFactory; } /** * Generate a pairwise key for the algorithm * @param algorithm for the key * @param seedReference Reference to the seed * @param personaId Id for the persona * @param peerId Id for the peer * @param extractable True if key is exportable * @param keyops Key operations */ async generatePairwiseKey(algorithm, seedReference, personaId, peerId) { const pairwiseKey = new PairwiseKey_1.default(this.cryptoFactory); return pairwiseKey.generatePairwiseKey(algorithm, seedReference, personaId, peerId); } /** * Sign with a key referenced in the key store * @param algorithm used for signature * @param keyReference points to key in the key store * @param data to sign * @returns The signature in the requested algorithm */ async signByKeyStore(algorithm, keyReference, data) { const jwk = (await this.keyStore.get(keyReference, false)).getKey(); const crypto = CryptoHelpers_1.default.getSubtleCryptoForAlgorithm(this.cryptoFactory, algorithm); const keyImportAlgorithm = SubtleCryptoExtension.normalizeAlgorithm(CryptoHelpers_1.default.getKeyImportAlgorithm(algorithm, jwk)); const key = await crypto.importKey('jwk', SubtleCryptoExtension.normalizeJwk(jwk), keyImportAlgorithm, true, ['sign']); const signature = await crypto.sign(jwk.kty === KeyTypeFactory_1.KeyType.EC || jwk.kty === KeyTypeFactory_1.KeyType.OKP ? SubtleCryptoExtension.normalizeAlgorithm(algorithm) : algorithm, key, data); // only applicable for EC algorithms and when no encoding is applied const isElliptic = algorithm.name === 'ECDSA' || algorithm.name === 'EDDSA'; // EDDSA/ECDSA returns two 32 bit values R & S. Some API's will encode these values in DER const format = algorithm.format; if (isElliptic && signature.byteLength <= 64 && format) { if (format.toUpperCase() !== 'DER') { throw new CryptoError_1.default(algorithm, 'Only DER format supported for signature'); } // DER format needed for signature, specied in algorithm const r = signature.slice(0, signature.byteLength / 2); const s = signature.slice(signature.byteLength / 2, signature.byteLength); return SubtleCryptoExtension.toDer([r, s]); } if (isElliptic && signature.byteLength > 64 && format) { // DER encoded is not requested and signature is DER encoded // In this case the encoding is removed and returned as 64 bytes const decodedSignature = SubtleCryptoExtension.fromDer(new Uint8Array(signature)); const signed = new Uint8Array(decodedSignature[0].length + decodedSignature[1].length); signed.set(decodedSignature[0]); signed.set(decodedSignature[1], decodedSignature[1].length); return signed; } return signature; } /** * format the signature output to DER format * @param elements Array of elements to encode in DER */ static toDer(elements) { let index = 0; // calculate total size. let lengthOfRemaining = 0; for (let element = 0; element < elements.length; element++) { // Add element format bytes lengthOfRemaining += 2; const buffer = new Uint8Array(elements[element]); const size = (buffer[0] & 0x80) === 0x80 ? buffer.length + 1 : buffer.length; lengthOfRemaining += size; } // Prepare output index = 0; const result = new Uint8Array(lengthOfRemaining + 2); result.set([0x30, lengthOfRemaining], index); index += 2; for (let element = 0; element < elements.length; element++) { // Add element format bytes const buffer = new Uint8Array(elements[element]); const size = (buffer[0] & 0x80) === 0x80 ? buffer.length + 1 : buffer.length; result.set([0x02, size], index); index += 2; if (size > buffer.length) { result.set([0x0], index++); } result.set(buffer, index); index += buffer.length; } return result; } /** * Verify with JWK. * @param algorithm used for verification * @param jwk Json web key used to verify * @param signature to verify * @param payload which was signed */ async verifyByJwk(algorithm, jwk, signature, payload) { const crypto = CryptoHelpers_1.default.getSubtleCryptoForAlgorithm(this.cryptoFactory, algorithm); const keyImportAlgorithm = SubtleCryptoExtension.normalizeAlgorithm(CryptoHelpers_1.default.getKeyImportAlgorithm(algorithm, jwk)); const key = await crypto.importKey('jwk', SubtleCryptoExtension.normalizeJwk(jwk), keyImportAlgorithm, true, ['verify']); const isElliptic = algorithm.name === 'ECDSA' || algorithm.name === 'EDDSA'; // The underlying signature validation does not support DER encoding so needs to be removed if (isElliptic && signature.byteLength > 64) { const elements = SubtleCryptoExtension.fromDer(signature); signature = new Uint8Array(elements[0].length + elements[1].length); signature.set(elements[0]); signature.set(elements[1], elements[1].length); } return crypto.verify(isElliptic ? SubtleCryptoExtension.normalizeAlgorithm(algorithm) : algorithm, key, signature, payload); } /** * format the signature output from DER format * @param signature to decode from DER */ static fromDer(signature) { if (signature[0] !== 0x30) { throw new Error('No DER format to decode'); } const lengthOfRemaining = signature[1]; const results = []; let index = 2; while (index < lengthOfRemaining) { const marker = signature[index++]; if (marker !== 0x02) { throw new Error(`Marker on index ${index - 1} must be 0x02`); } let length = signature[index++]; while (signature[index] === 0) { index++; length--; } const data = signature.slice(index, index + length); results.push(data); index = index + length; } return results; } /** * Decrypt with a key referenced in the key store. * The referenced key must be a jwk key. * @param algorithm used for signature * @param keyReference points to key in the key store * @param cipher to decrypt */ async decryptByKeyStore(algorithm, keyReference, cipher) { const jwk = (await this.keyStore.get(keyReference, false)).getKey(); const crypto = CryptoHelpers_1.default.getSubtleCryptoForAlgorithm(this.cryptoFactory, algorithm); const keyImportAlgorithm = SubtleCryptoExtension.normalizeAlgorithm(CryptoHelpers_1.default.getKeyImportAlgorithm(algorithm, jwk)); const key = await crypto.importKey('jwk', SubtleCryptoExtension.normalizeJwk(jwk), SubtleCryptoExtension.normalizeAlgorithm(keyImportAlgorithm), true, ['decrypt']); return crypto.decrypt(algorithm, key, cipher); } /** * Decrypt with JWK. * @param algorithm used for decryption * @param jwk Json web key to decrypt * @param cipher to decrypt */ async decryptByJwk(algorithm, jwk, cipher) { const crypto = CryptoHelpers_1.default.getSubtleCryptoForAlgorithm(this.cryptoFactory, algorithm); const keyImportAlgorithm = SubtleCryptoExtension.normalizeAlgorithm(CryptoHelpers_1.default.getKeyImportAlgorithm(algorithm, jwk)); const key = await crypto.importKey('jwk', SubtleCryptoExtension.normalizeJwk(jwk), SubtleCryptoExtension.normalizeAlgorithm(keyImportAlgorithm), true, ['decrypt']); return crypto.decrypt(algorithm, key, cipher); } /** * Encrypt with a jwk key referenced in the key store * @param algorithm used for encryption * @param jwk Json web key public key * @param data to encrypt */ async encryptByJwk(algorithm, jwk, data) { const keyImportAlgorithm = CryptoHelpers_1.default.getKeyImportAlgorithm(algorithm, jwk); const crypto = CryptoHelpers_1.default.getSubtleCryptoForAlgorithm(this.cryptoFactory, algorithm); const key = await crypto.importKey('jwk', SubtleCryptoExtension.normalizeJwk(jwk), SubtleCryptoExtension.normalizeAlgorithm(keyImportAlgorithm), true, ['encrypt']); return crypto.encrypt(algorithm, key, data); } /** * Normalize the algorithm so it can be used by underlying crypto. * @param algorithm Algorithm to be normalized */ static normalizeAlgorithm(algorithm) { if (algorithm.namedCurve) { if (algorithm.namedCurve === CURVE_P256K) { const alg = clone(algorithm); alg.namedCurve = CURVE_K256; return alg; } } return algorithm; } /** * Normalize the JWK parameters so it can be used by underlying crypto. * @param jwk Json web key to be normalized */ static normalizeJwk(jwk) { if (jwk.crv) { if (jwk.crv === CURVE_P256K) { const clonedKey = clone(jwk); clonedKey.crv = CURVE_K256; return clonedKey; } } return jwk; } } exports.default = SubtleCryptoExtension; //# sourceMappingURL=SubtleCryptoExtension.js.map