UNPKG

@microsoft/useragent-sdk

Version:

SDK for building decentralized identity wallets and enterprise agents.

161 lines 6.95 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const KeyTypeFactory_1 = require("../KeyTypeFactory"); const W3cCryptoApiConstants_1 = require("../../utilities/W3cCryptoApiConstants"); const base64url_1 = require("base64url"); const KeyUseFactory_1 = require("../KeyUseFactory"); const RsaPrivateKey_1 = require("./RsaPrivateKey"); const bigInt = require('big-integer'); const JoseConstants_1 = require("../../protocols/jose/JoseConstants"); /** * Class to model RSA pairwise keys */ class RsaPairwiseKey { /** * Generate a pairwise key for the specified algorithms * @param cryptoFactory defining the key store and the used crypto api * @param personaMasterKey Master key for the current selected persona * @param algorithm for the key * @param peerId Id for the peer */ static async generate(cryptoFactory, personaMasterKey, algorithm, peerId) { // This method is currently breaking the subtle crypto pattern and needs to be fixed to be platform independent // Set the key size const keySize = algorithm.modulusLength || 1024; // Get deterministic base number for p const peerIdBuffer = Buffer.from(peerId); const pBase = await RsaPairwiseKey.generateDeterministicNumberForPrime(cryptoFactory, keySize / 2, personaMasterKey, peerIdBuffer); // Get deterministic base number for q const qBase = await this.generateDeterministicNumberForPrime(cryptoFactory, keySize / 2, pBase, peerIdBuffer); const p = RsaPairwiseKey.getPrime(pBase); const q = RsaPairwiseKey.getPrime(qBase); // compute key components const modulus = p.multiply(q); const pMinus = p.subtract(bigInt.one); const qMinus = q.subtract(bigInt.one); const phi = pMinus.multiply(qMinus); const e = bigInt(65537); const d = e.modInv(phi); const dp = d.mod(pMinus); const dq = d.mod(qMinus); const qi = q.modInv(p); const pairwise = { kty: KeyTypeFactory_1.KeyType.RSA, use: KeyUseFactory_1.default.createViaWebCrypto(algorithm), e: RsaPairwiseKey.toBase(e), n: RsaPairwiseKey.toBase(modulus), d: RsaPairwiseKey.toBase(d), p: RsaPairwiseKey.toBase(p), q: RsaPairwiseKey.toBase(q), dp: RsaPairwiseKey.toBase(dp), dq: RsaPairwiseKey.toBase(dq), qi: RsaPairwiseKey.toBase(qi), // Need an algorithm for kid generation - todo kid: '#key1' }; return new RsaPrivateKey_1.default(pairwise); } /** * Uses primeBase as reference and generate the closest prime number */ static getPrime(primeBase) { const qArray = Array.from(primeBase); const prime = RsaPairwiseKey.generatePrime(qArray); return new bigInt(prime); } /** * Generate a deterministic number that can be used as prime * @param cryptoFactory The crypto factory. * @param keySize Desired key size * @param personaMasterKey The persona master key * @param peerId The peer id */ static async generateDeterministicNumberForPrime(cryptoFactory, primeSize, personaMasterKey, peerId) { const numberOfRounds = primeSize / (8 * 64); let deterministicKey = Buffer.from(''); const rounds = []; for (let inx = 0; inx < numberOfRounds; inx++) { rounds.push(async (cryptoFactory, inx, key, data, deterministicKey) => { deterministicKey = await this.generateHashForPrime(cryptoFactory, inx, key, data, deterministicKey); return deterministicKey; }); } return this.executeRounds(cryptoFactory, rounds, 0, personaMasterKey, peerId, deterministicKey); } /** * Generate a hash used as component for prime number * @param crypto The crypto object. * @param inx Round number * @param key Signature key * @param data Data to sign */ static async generateHashForPrime(cryptoFactory, _inx, key, data, deterministicKey) { // Get the subtle crypto const crypto = cryptoFactory.getMessageAuthenticationCodeSigners(W3cCryptoApiConstants_1.default.Hmac); // Generate the master key const alg = { name: W3cCryptoApiConstants_1.default.Hmac, hash: W3cCryptoApiConstants_1.default.Sha512 }; const signingKey = { kty: 'oct', alg: JoseConstants_1.default.Hs512, k: base64url_1.default.encode(key) }; const importedKey = await crypto.importKey('jwk', signingKey, alg, false, ['sign']); const signature = await crypto.sign(alg, importedKey, data); return Buffer.concat([deterministicKey, Buffer.from(signature)]); } /** * Execute all rounds * @param rounds Array of functions to execute * @param inx Current step * @param key Key to sign * @param data Data to sign */ static async executeRounds(cryptoFactory, rounds, inx, key, data, deterministicKey) { deterministicKey = await rounds[inx](cryptoFactory, inx, key, data, deterministicKey); if (inx + 1 === rounds.length) { return deterministicKey; } else { deterministicKey = await this.executeRounds(cryptoFactory, rounds, inx + 1, key, Buffer.from(deterministicKey), deterministicKey); return deterministicKey; } } /** * Generate a prime number from the seed. * isProbablyPrime is based on the Miller-Rabin prime test. * @param primeSeed seed for prime generator */ // tslint:disable-next-line:prefer-array-literal static generatePrime(primeSeed) { // make sure candidate is uneven, set high order bit primeSeed[primeSeed.length - 1] |= 0x1; primeSeed[0] |= 0x80; const two = bigInt(2); let prime = bigInt.fromArray(primeSeed, 256, false); RsaPairwiseKey.numberOfPrimeTests = 1; // tslint:disable-next-line:no-constant-condition while (true) { // 64 tests give 128 bit security if (prime.isProbablePrime(64)) { break; } prime = prime.add(two); RsaPairwiseKey.numberOfPrimeTests++; } return prime; } /** * Convert big number to base64 url. * @param bigNumber Number to convert */ static toBase(bigNumber) { let buf = Buffer.from(bigNumber.toArray(256).value); return base64url_1.default(buf); } } /** * Statistics on the number of prime tests */ RsaPairwiseKey.numberOfPrimeTests = 0; exports.default = RsaPairwiseKey; //# sourceMappingURL=RsaPairwiseKey.js.map