@microsoft/useragent-sdk
Version:
SDK for building decentralized identity wallets and enterprise agents.
161 lines • 6.95 kB
JavaScript
"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