UNPKG

@microsoft/useragent-sdk

Version:

SDK for building decentralized identity wallets and enterprise agents.

219 lines (191 loc) 11.8 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import base64url from "base64url"; import JwsToken from "../../../../../src/crypto/protocols/jose/jws/JwsToken"; import OctKey from "../../../../../src/crypto/keys/Oct/OctKey"; import KeyStoreInMemory from "../../../../../src/crypto/keyStore/KeyStoreInMemory"; import CryptoFactory from "../../../../../src/crypto/plugin/CryptoFactory"; import SubtleCryptoNode from "../../../../../src/crypto/plugin/SubtleCryptoNode"; import { ProtectionFormat } from "../../../../../src/crypto/keyStore/ProtectionFormat"; import SubtleCryptoExtension from '../../../../../src/crypto/plugin/SubtleCryptoExtension'; import { IJwsSigningOptions } from "../../../../../src/crypto/protocols/jose/IJoseOptions"; import IPayloadProtectionOptions from "../../../../../src/crypto/protocols/IPayloadProtectionOptions"; import JoseProtocol from "../../../../../src/crypto/protocols/jose/JoseProtocol"; import { TSMap } from "typescript-map"; import JoseConstants from "../../../../../src/crypto/protocols/jose/JoseConstants"; import KeyContainer from "../../../../../src/crypto/keys/KeyContainer"; import { PrivateKey, PublicKey } from "../../../../../src"; describe('JwsToken', () => { it('should create a jws token', async () => { const payload = 'test payload'; const keyStore = new KeyStoreInMemory(); const seedReference = 'seed'; await keyStore.save(seedReference, new KeyContainer(new OctKey('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'))); const subtle = SubtleCryptoNode.getSubtleCrypto(); const options: IJwsSigningOptions = { algorithm: <Algorithm> { name: 'ECDSA', namedCurve: 'P-256K', hash: { name: 'SHA-256' } }, cryptoFactory: new CryptoFactory(keyStore, subtle) }; const generate = new SubtleCryptoExtension(options.cryptoFactory); const privateKey = await generate.generatePairwiseKey(options.algorithm, seedReference, 'did:personaId', 'did:peerId'); (<any>privateKey).alg = 'ES256K'; (<any>privateKey).defaultSignAlgorithm = 'ES256K'; await keyStore.save('key', new KeyContainer(privateKey)); const jwsToken = new JwsToken(options); const signature = await jwsToken.sign('key', Buffer.from(payload), ProtectionFormat.JwsGeneralJson); expect(signature).toBeDefined(); }); it('should create, validate and serialize a JwsToken', async () => { const payload = 'The true sign of intelligence is not knowledge but imagination.'; const keyStore = new KeyStoreInMemory(); await keyStore.save('seed', new KeyContainer(new OctKey('ABEE'))); const cryptoFactory = new CryptoFactory(keyStore, SubtleCryptoNode.getSubtleCrypto()) const options: IPayloadProtectionOptions = { cryptoFactory: cryptoFactory, options: new TSMap<string, any>(), payloadProtection: new JoseProtocol() }; const alg = { name: 'RSASSA-PKCS1-V1_5', hash: 'SHA-256', modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]) }; const generator = new SubtleCryptoExtension(cryptoFactory); const privateKey = await generator.generatePairwiseKey(alg, 'seed', 'persona','peer'); expect((<any>privateKey).alg).toBeUndefined(); (<any>privateKey).alg = 'RS256'; await keyStore.save('key', new KeyContainer(privateKey)); // sign const signature = await options.payloadProtection.sign('key', Buffer.from(payload), 'JwsGeneralJson', options); const signatures = signature.get(JoseConstants.tokenSignatures); expect(signatures[0].protected).toBeDefined(); expect(signatures[0].signature).toBeDefined(); expect(signature.get(JoseConstants.tokenPayload)).toEqual(Buffer.from(payload)); // serialize let serialized = options.payloadProtection.serialize(signature, 'JwsGeneralJson', options); let parsed = JSON.parse(serialized); expect(parsed['payload']).toBeDefined(); expect(parsed['signatures']).toBeDefined(); // deserialize let deserialized = options.payloadProtection.deserialize(serialized, 'JwsGeneralJson', options); let deSignatures = deserialized.get(JoseConstants.tokenSignatures); expect(deSignatures[0].protected).toEqual(signatures[0].protected); expect(deSignatures[0].signature).toEqual(signatures[0].signature); expect(deserialized.get(JoseConstants.tokenPayload)).toEqual(signature.get(JoseConstants.tokenPayload)); // validate const publicKeyContainer = (await keyStore.get('key', true)).getKey<PublicKey>(); const result = await options.payloadProtection.verify([publicKeyContainer], Buffer.from(payload), signature, options); expect(result.result).toBeTruthy(); // Flat serialization serialized = options.payloadProtection.serialize(signature, 'JwsFlatJson', options); parsed = JSON.parse(serialized); expect(parsed['payload']).toBeDefined(); expect(parsed['protected']).toBeDefined(); expect(parsed['signature']).toBeDefined(); deserialized = options.payloadProtection.deserialize(serialized, 'JwsFlatJson', options); deSignatures = deserialized.get(JoseConstants.tokenSignatures); expect(deSignatures[0].protected).toEqual(signatures[0].protected); expect(deSignatures[0].signature).toEqual(signatures[0].signature); expect(deserialized.get(JoseConstants.tokenPayload)).toEqual(signature.get(JoseConstants.tokenPayload)); // Compact serialization serialized = options.payloadProtection.serialize(signature, 'JwsCompactJson', options); parsed = serialized.split('.'); expect(parsed.length).toEqual(3); deserialized = options.payloadProtection.deserialize(serialized, 'JwsCompactJson', options); deSignatures = deserialized.get(JoseConstants.tokenSignatures); expect(deSignatures[0].protected).toEqual(signatures[0].protected); expect(deSignatures[0].signature).toEqual(signatures[0].signature); expect(deserialized.get(JoseConstants.tokenPayload)).toEqual(signature.get(JoseConstants.tokenPayload)); }); // tslint:disable-next-line: max-func-body-length it('should set headers in JwsToken', async () => { const payload = 'The true sign of intelligence is not knowledge but imagination.'; const keyStore = new KeyStoreInMemory(); await keyStore.save('seed', new KeyContainer(new OctKey('ABEE'))); const cryptoFactory = new CryptoFactory(keyStore, SubtleCryptoNode.getSubtleCrypto()) const options: IPayloadProtectionOptions = { cryptoFactory: cryptoFactory, options: new TSMap<string, any>([ [JoseConstants.optionHeader, new TSMap([['test', 'ES256K']]) ], [JoseConstants.optionProtectedHeader, new TSMap([['test', 'elo']]) ] ]), payloadProtection: new JoseProtocol() }; const alg = { name: 'ECDSA', namedCurve: 'P-256K', hash: { name: 'SHA-256' }, format: 'DER' }; const generator = new SubtleCryptoExtension(cryptoFactory); const privateKey = await generator.generatePairwiseKey(alg, 'seed', 'persona','peer'); (<any>privateKey).alg = 'ES256K'; await keyStore.save('key', new KeyContainer(privateKey)); let publicKey = await keyStore.get('key', true); // sign const signature = await options.payloadProtection.sign('key', Buffer.from(payload), 'JwsGeneralJson', options); const signatures = signature.get(JoseConstants.tokenSignatures); expect(signatures[0].protected.get('test')).toEqual('elo'); expect(signatures[0].header.get('test')).toEqual('ES256K'); expect(signatures[0].signature).toBeDefined(); expect(signature.get(JoseConstants.tokenPayload)).toEqual(Buffer.from(payload)); // serialize let serialized = options.payloadProtection.serialize(signature, 'JwsGeneralJson', options); let parsed = JSON.parse(serialized); expect(parsed['payload']).toBeDefined(); expect(parsed['signatures']).toBeDefined(); // deserialize let deserialized = options.payloadProtection.deserialize(serialized, 'JwsGeneralJson', options); let deSignatures = deserialized.get(JoseConstants.tokenSignatures); expect(deSignatures[0].protected).toEqual(signatures[0].protected); expect(deSignatures[0].header).toEqual(signatures[0].header); expect(deSignatures[0].signature).toEqual(signatures[0].signature); expect(deserialized.get(JoseConstants.tokenPayload)).toEqual(signature.get(JoseConstants.tokenPayload)); // validate const publicKeyContainer = (await keyStore.get('key', true)).getKey<PublicKey>(); const result = await options.payloadProtection.verify([publicKeyContainer], Buffer.from(payload), signature, options); expect(result.result).toBeTruthy(); // Flat serialization serialized = options.payloadProtection.serialize(signature, 'JwsFlatJson', options); parsed = JSON.parse(serialized); expect(parsed['payload']).toBeDefined(); expect(parsed['protected']).toBeDefined(); expect(parsed['header']).toBeDefined(); expect(parsed['signature']).toBeDefined(); deserialized = options.payloadProtection.deserialize(serialized, 'JwsFlatJson', options); deSignatures = deserialized.get(JoseConstants.tokenSignatures); expect(deSignatures[0].protected).toEqual(signatures[0].protected); expect(deSignatures[0].header).toEqual(signatures[0].header); expect(deSignatures[0].signature).toEqual(signatures[0].signature); expect(deserialized.get(JoseConstants.tokenPayload)).toEqual(signature.get(JoseConstants.tokenPayload)); // Compact serialization serialized = options.payloadProtection.serialize(signature, 'JwsCompactJson', options); parsed = serialized.split('.'); expect(parsed.length).toEqual(3); deserialized = options.payloadProtection.deserialize(serialized, 'JwsCompactJson', options); deSignatures = deserialized.get(JoseConstants.tokenSignatures); expect(deSignatures[0].protected).toEqual(signatures[0].protected); expect(deSignatures[0].signature).toEqual(signatures[0].signature); expect(deserialized.get(JoseConstants.tokenPayload)).toEqual(signature.get(JoseConstants.tokenPayload)); // negative cases let throwed = false; try { options.payloadProtection.serialize(signature, 'bluesky', options); } catch (err) { throwed = true; expect(err.message).toEqual(`Format 'bluesky' is not supported`); } expect(throwed).toBeTruthy(); throwed = false; try { options.payloadProtection.deserialize(serialized, 'bluesky', options); } catch (err) { throwed = true; expect(err.message).toEqual(`Format 'bluesky' is not supported`); } expect(throwed).toBeTruthy(); const sigs = signature.get(JoseConstants.tokenSignatures); sigs[0].protected.set('alg', ''); throwed = false; try { await options.payloadProtection.verify([publicKey], Buffer.from(payload), signature, options); } catch (err) { expect(err.message).toEqual('Unable to validate signature as no signature algorithm has been specified in the header.'); throwed = true; } expect(throwed).toBeTruthy(); }); });