UNPKG

@microsoft/useragent-sdk

Version:

SDK for building decentralized identity wallets and enterprise agents.

244 lines (210 loc) 12.4 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ // tslint:disable-next-line: import-name import CryptoFactory from '../../../src/crypto/plugin/CryptoFactory'; import KeyStoreInMemory from '../../../src/crypto/keyStore/KeyStoreInMemory'; import SubtleCryptoExtension from '../../../src/crypto/plugin/SubtleCryptoExtension'; import PrivateKey from '../../../src/crypto/keys/PrivateKey'; import EcPrivateKey from '../../../src/crypto/keys/ec/EcPrivateKey'; import PairwiseKey from '../../../src/crypto/keys/PairwiseKey'; import base64url from 'base64url'; import OctKey from '../../../src/crypto/keys/Oct/OctKey'; import SubtleCryptoNode from '../../../src/crypto/plugin/SubtleCryptoNode'; import KeyContainer from '../../../src/crypto/keys/KeyContainer'; class Helpers { // Make sure we generate the same pairwise key public static async generateSamePairwise (subtleCryptoExtensions: SubtleCryptoExtension, seedReference: string, alg: any, persona: string, peer: string) { // Generate key const pairwiseKey1: PrivateKey = await subtleCryptoExtensions.generatePairwiseKey(<any>alg, seedReference, persona, peer); // return the same const pairwiseKey2: PrivateKey = await subtleCryptoExtensions.generatePairwiseKey(<any>alg, seedReference, persona, peer); expect(pairwiseKey1.getPublicKey()).toEqual(pairwiseKey2.getPublicKey()); } // Make sure the pairwise key is unique public static async generateUniquePairwise (subtleCryptoExtensions: SubtleCryptoExtension, seedReference: string, alg: any, persona: string, peer: string) { const results: string[] = []; for (let index = 0 ; index < 10; index++) { const pairwiseKey: EcPrivateKey = <EcPrivateKey> await subtleCryptoExtensions.generatePairwiseKey(<any>alg, seedReference, `${persona}-${index}`, peer); results.push(<string>pairwiseKey.d); expect(1).toBe(results.filter(element => element === pairwiseKey.d).length); } } } describe('PairwiseKey', () => { const KeyGenerationAlgorithm_RSA256 = 0; const KeyGenerationAlgorithm_ECDSA = 1; // tslint:disable-next-line:mocha-no-side-effect-code const supportedKeyGenerationAlgorithms = [ { name: 'RSASSA-PKCS1-v1_5', modulusLength: 1024, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: { name: 'SHA-256' } }, { name: 'ECDSA', namedCurve: 'P-256K', hash: { name: 'SHA-256' } }, { name: 'EDDSA', namedCurve: 'ed25519', hash: { name: 'SHA-256' } } ]; const unsupportedKeyGenerationAlgorithms = [ { name: 'HMAC', hash: 'SHA-256' } ]; // Default Crypto factory let keyStore: KeyStoreInMemory; let defaultCryptoFactory: CryptoFactory; let subtleCryptoExtensions:SubtleCryptoExtension; const subtle = SubtleCryptoNode.getSubtleCrypto(); const seedReference = 'masterSeed'; let originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL; beforeEach(async() => { // Default Crypto factory keyStore = new KeyStoreInMemory(); const seed = new OctKey('ABDE'); await keyStore.save(seedReference, new KeyContainer(seed)); defaultCryptoFactory = new CryptoFactory(keyStore, subtle); subtleCryptoExtensions = new SubtleCryptoExtension(defaultCryptoFactory); jasmine.DEFAULT_TIMEOUT_INTERVAL = 60000; }); afterEach(() => { jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout; }); it(`should throw because the algorithm is not supported for pairwise key generation`, async () => { const alg = unsupportedKeyGenerationAlgorithms[0]; let throwed = false; await subtleCryptoExtensions.generatePairwiseKey(<any>alg, seedReference, 'did:persona', 'did:peer') .catch((err: any) => { throwed = true; expect(`Pairwise key for type 'oct' is not supported.`).toBe(err.message); }); expect(throwed).toBeTruthy(); }); it(`should throw because the EC curve is not supported for pairwise key generation`, async () => { const alg = { name: 'ECDSA', namedCurve: 'P-256', hash: { name: 'SHA-256' } }; let throwed = false; await subtleCryptoExtensions.generatePairwiseKey(<any>alg, seedReference, 'did:persona', 'did:peer') .catch((err: any) => { throwed = true; expect(err.message).toBe('Curve P-256 is not supported'); }); expect(throwed).toBeTruthy(); }); // tslint:disable-next-line: mocha-unneeded-done it('should generate a masterkey', async (done) => { const keyStore = new KeyStoreInMemory(); const seed = new OctKey(base64url.encode(Buffer.from('abcdefg'))); await keyStore.save(seedReference, new KeyContainer(seed)); const pairwise = new PairwiseKey(new CryptoFactory(keyStore, subtle)); let masterkey = await (<any>pairwise).generatePersonaMasterKey(seedReference, 'persona'); let encoded = base64url.encode(masterkey); expect(encoded).toEqual('h-Z5gO1eBjY1EYXh64-f8qQF5ojeh1KVMKxmd0JI3YKScTOYjVm-h1j2pUNV8q6s8yphAR4lk5yXYiQhAOVlUw'); masterkey = await (<any>pairwise).generatePersonaMasterKey(seedReference, 'persona'); encoded = base64url.encode(masterkey); expect(encoded).toEqual('h-Z5gO1eBjY1EYXh64-f8qQF5ojeh1KVMKxmd0JI3YKScTOYjVm-h1j2pUNV8q6s8yphAR4lk5yXYiQhAOVlUw'); masterkey = await (<any>pairwise).generatePersonaMasterKey(seedReference, 'persona1'); encoded = base64url.encode(masterkey); expect(encoded).not.toEqual('h-Z5gO1eBjY1EYXh64-f8qQF5ojeh1KVMKxmd0JI3YKScTOYjVm-h1j2pUNV8q6s8yphAR4lk5yXYiQhAOVlUw'); done(); }); // tslint:disable-next-line:mocha-unneeded-done it('should generate the same keys as in the EC reference file', async (done) => { let inx: number = 0; let nrIds: number = 10; const alg = supportedKeyGenerationAlgorithms[KeyGenerationAlgorithm_ECDSA]; const pairwiseKeys = require('./Pairwise.EC.json'); const seed = new OctKey(base64url.encode('xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi')); await keyStore.save('masterSeed', new KeyContainer(seed)); const seedReference = 'masterkey'; await keyStore.save(seedReference, new KeyContainer(seed)); for (inx = 0; inx < nrIds; inx++) { const persona: string = 'abcdef'; let id = `${inx}`; // Generate key const pairwiseKey: EcPrivateKey = <EcPrivateKey> await subtleCryptoExtensions.generatePairwiseKey(<any>alg, seedReference, persona, id); expect(pairwiseKey.kid).toBeDefined(); // console.log(`{ "pwid": "${id}", "key": "${jwk.d}"},`); expect(pairwiseKeys[inx].key).toBe(pairwiseKey.d); expect(1).toBe(pairwiseKeys.filter((element: any) => element.key === pairwiseKey.d).length); } done(); }); // tslint:disable-next-line:mocha-unneeded-done it('should generate the same keys as in the RSA reference file', async (done) => { const alg = supportedKeyGenerationAlgorithms[KeyGenerationAlgorithm_RSA256]; const pairwiseKeys = require('./Pairwise.RSA.json'); const seed = new OctKey(base64url.encode(Buffer.from('xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi'))); const seedReference = 'masterkey'; await keyStore.save(seedReference, new KeyContainer(seed)); for (let inx = 0; inx < 30; inx++) { const persona: string = 'abcdef'; let id = `${inx}`; // Generate key const pairwiseKey: EcPrivateKey = <EcPrivateKey>await subtleCryptoExtensions.generatePairwiseKey(<any>alg, seedReference, persona, id); expect(pairwiseKey.kid).toBeDefined(); // console.log(`{ "pwid": "${id}", "key": "${pairwiseKey.d}"},`); expect(pairwiseKeys[inx].key).toBe(pairwiseKey.d); expect(1).toBe(pairwiseKeys.filter((element: any) => element.key === pairwiseKey.d).length); } done(); }); // tslint:disable-next-line:mocha-unneeded-done it('should generate a 2048 bit pairwise RSA key', async (done) => { const alg = supportedKeyGenerationAlgorithms[KeyGenerationAlgorithm_RSA256]; alg.modulusLength = 2048; const seed = new OctKey(base64url.encode(Buffer.from('xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi'))); const seedReference = 'masterkey'; await keyStore.save(seedReference, new KeyContainer(seed)); const persona: string = 'did:persona'; const peer = 'did:peer'; // Generate key const pairwiseKey: EcPrivateKey = <EcPrivateKey>await subtleCryptoExtensions.generatePairwiseKey(<any>alg, seedReference, persona, peer); expect(pairwiseKey.kid).toBeDefined(); expect(pairwiseKey.d).toBe('LrJvTmCSQsIZajmSHGw98j7pOE1-umx7gemMxXS29cWPmaw8XZGnrpp6PG3zvIC5mF0zu8BtbRl8Bws-fZGqJXiqJNBFcA03erU22yNEsbA-IeylCIZ34Qk34hcRCzP9Q7AkOn5EaS9I2k22Bk0qgOjJS8WVByj-B-ll9GHzahI1Vq76BVTh8lahHI6TEf5kdg5byndw-pFwab_zZ5ftdAPtmIu61mPr5wA0ykxn-MzSTZVvggpvNef-Obdj32sCp1Rz2x8KCrkrWUD7W4hhVF2QLHve4Cm9IpfzPZttSp-OlIdTYEAJhd3nj2BhStKK-K6FGgi65VrbPYSbh21cAQ'); done(); }); it('should generate a deterministic pairwise key capable of signing', async () => { const alg = supportedKeyGenerationAlgorithms[KeyGenerationAlgorithm_ECDSA]; // Generate key const pairwiseKey1: PrivateKey = await subtleCryptoExtensions.generatePairwiseKey(<any>alg, seedReference, 'did:persona', 'did:peer'); keyStore.save('key', new KeyContainer(pairwiseKey1)); const data = Buffer.from('1234567890'); const signature = await subtleCryptoExtensions.signByKeyStore(alg, 'key', Buffer.from('1234567890')); const verify = await subtleCryptoExtensions.verifyByJwk(alg, pairwiseKey1.getPublicKey(), signature, data); expect(verify).toBeTruthy(); }); it('should generate a deterministic pairwise key', async () => { supportedKeyGenerationAlgorithms.forEach(async (alg) => { const persona = 'did:persona:1'; const peer = 'did:peer:1'; await Helpers.generateSamePairwise(subtleCryptoExtensions, seedReference, alg, persona, peer); }); }); it('should generate unique pairwise keys for different personas', async () => { supportedKeyGenerationAlgorithms.forEach(async (alg) => { const persona = 'did:persona:1'; const peer = 'did:peer:1'; console.log('Generate unique pairwise key'); await Helpers.generateUniquePairwise(subtleCryptoExtensions, seedReference, alg, persona, peer); }); }); it('should generate unique pairwise identifiers using a different seed', async () => { const results: string[] = []; const alg = supportedKeyGenerationAlgorithms[KeyGenerationAlgorithm_ECDSA]; const persona = 'did:persona:1'; const peer = 'did:peer:1'; for (let index = 0 ; index < 100; index++) { const keyReference = `key-${index}`; const keyValue = new OctKey(base64url.encode(`1234567890-${index}`)); await keyStore.save(seedReference, new KeyContainer(keyValue)); keyStore.save(keyReference, new KeyContainer(keyValue)); const pairwiseKey: EcPrivateKey = <EcPrivateKey> await subtleCryptoExtensions.generatePairwiseKey(<any>alg, keyReference, persona, peer); results.push(<string>pairwiseKey.d); expect(1).toBe(results.filter(element => element === pairwiseKey.d).length); } }); it('should generate unique pairwise identifiers for different peers', async () => { const results: string[] = []; const alg = supportedKeyGenerationAlgorithms[KeyGenerationAlgorithm_ECDSA]; const persona = 'did:persona:1'; const peer = 'did:peer:1'; for (let index = 0 ; index < 100; index++) { const pairwiseKey: EcPrivateKey = <EcPrivateKey> await subtleCryptoExtensions.generatePairwiseKey(<any>alg, seedReference, persona,`${peer}-${index}`); results.push(<string>pairwiseKey.d); expect(1).toBe(results.filter(element => element === pairwiseKey.d).length); } }); });