UNPKG

@microsoft/useragent-sdk

Version:

SDK for building decentralized identity wallets and enterprise agents.

355 lines (319 loc) 13.1 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License in the project root for license information. *--------------------------------------------------------------------------------------------*/ import IdentifierDocument from '../src/IdentifierDocument'; import Identifier from '../src/Identifier'; import UserAgentOptions from '../src/UserAgentOptions'; import TestResolver from './resolvers/TestResolver'; import UserAgentError from '../src/UserAgentError'; import CryptoOptions from '../src/CryptoOptions'; import TestRegistrar from './registrars/TestRegistrar'; import { KeyType } from '../src/crypto/keys/KeyTypeFactory'; import { KeyUse } from '../src/crypto/keys/KeyUseFactory'; import KeyStoreInMemory from '../src/crypto/keyStore/KeyStoreInMemory'; import JwsToken from '../src/crypto/protocols/jose/jws/JwsToken'; import OctKey from '../src/crypto/keys/Oct/OctKey'; import CryptoFactory from '../src/crypto/plugin/CryptoFactory'; import SubtleCryptoNode from '../src/crypto/plugin/SubtleCryptoNode'; import KeyContainer from '../src/crypto/keys/KeyContainer'; describe('Identifier', () => { let testResolver: TestResolver; let options: UserAgentOptions; beforeAll(() => { testResolver = new TestResolver(); // Configure the agent options for the tests options = new UserAgentOptions(); options.resolver = testResolver; options.timeoutInSeconds = 30; }); it('should construct a storage identfier for the key', () => { let identifier = Identifier.keyStorageIdentifier('did:ion:identifier', 'peer', KeyUse.Encryption, KeyType.EC); expect(identifier).toEqual('did:ion:identifier-peer-enc-EC'); identifier = Identifier.keyStorageIdentifier('did:ion:identifier', 'peer', KeyUse.Encryption, KeyType.RSA); expect(identifier).toEqual('did:ion:identifier-peer-enc-RSA'); identifier = Identifier.keyStorageIdentifier('did:ion:identifier', 'peer', KeyUse.Signature, KeyType.EC); expect(identifier).toEqual('did:ion:identifier-peer-sig-EC'); identifier = Identifier.keyStorageIdentifier('did:ion:identifier', 'peer', KeyUse.Signature, KeyType.RSA); expect(identifier).toEqual('did:ion:identifier-peer-sig-RSA'); }); it('should construct new instance when provided an identifier string', () => { const identifier = new Identifier('did:ion:identifier', options); expect(identifier).toBeDefined(); expect(identifier.id).toEqual('did:ion:identifier'); }); it('should construct new instance when provided an identifier document', () => { const identifierDocument = new IdentifierDocument( { id: 'did:ion:identifier', created: '2019-01-25T01:08:44.732Z' } ); const identifier = new Identifier(identifierDocument, options); expect(identifier).toBeDefined(); expect(identifier.id).toEqual('did:ion:identifier'); }); it('should return document from local', () => { const identifierDocument = new IdentifierDocument( { id: 'did:ion:identifier', created: '2019-01-25T01:08:44.732Z' } ); const identifier = new Identifier(identifierDocument, options); const resolver = spyOn<any>(testResolver, 'resolve').and.callThrough(); const localDocument = identifier.getDocument(); expect(localDocument).toBeDefined(); expect(resolver).not.toHaveBeenCalled(); }); it('should throw when no resolver specified and document not cached', async done => { const options: UserAgentOptions = <UserAgentOptions> { timeoutInSeconds: 30 }; const identifier = new Identifier('did:ion:identifier', options); await identifier .getDocument() .catch(error => { expect(error instanceof UserAgentError).toBeTruthy(); expect(error.message).toEqual('Resolver not specified in user agent options.'); }) .finally(done); }); it('should resolve identifier and return document', () => { const identifier = new Identifier('did:ion:identifier', options); const identifierDocument = new IdentifierDocument( { id: 'did:ion:identifier', created: '2019-01-25T01:08:44.732Z' } ); (<TestResolver> options.resolver).prepareTest(identifier, identifierDocument); const resolver = spyOn(testResolver, 'resolve').and.callThrough(); const result = identifier.getDocument(); expect(result).toBeDefined(); expect(resolver).toHaveBeenCalled(); }); it('should call getDocument() when local document undefined', async () => { const identifier = new Identifier('did:ion:identifier', options); const identifierDocument = new IdentifierDocument( { id: 'did:ion:identifier', created: '2019-01-25T01:08:44.732Z', publicKeys: [ { id: '#master', type: 'RsaVerificationKey2018', publicKeyJwk: { kty: 'RSA', kid: '#master', keyOps: [ 'sign', 'verify' ], n: 'vdpHn7kNq42UMC1W8bwxgE7K...', e: 'AQAB' } } ] } ); (<TestResolver> options.resolver).prepareTest(identifier, identifierDocument); const resolver = spyOn(testResolver, 'resolve').and.callThrough(); const result = await identifier.getPublicKey('#master'); expect(result).toBeDefined(); expect(resolver).toHaveBeenCalled(); }); it('should throw when document has no keys', async done => { const identifier = new Identifier('did:ion:identifier', options); const identifierDocument = new IdentifierDocument( { id: 'did:ion:identifier', created: '2019-01-25T01:08:44.732Z' } ); (<TestResolver> options.resolver).prepareTest(identifier, identifierDocument); await identifier .getPublicKey('#master') .catch(error => { expect(error instanceof UserAgentError).toBeTruthy(); expect(error.message).toEqual('Document does not contain any public keys'); }) .finally(done); }); it('should throw when document does not contain specified key', async done => { const identifier = new Identifier('did:ion:identifier', options); const identifierDocument = new IdentifierDocument( { id: 'did:ion:identifier', created: '2019-01-25T01:08:44.732Z', publicKeys: [ { id: '#master', type: 'RsaVerificationKey2018', publicKeyJwk: { kty: 'RSA', kid: '#master', keyOps: [ 'sign', 'verify' ], n: 'vdpHn7kNq42UMC1W8bwxgE7K...', e: 'AQAB' } } ] } ); (<TestResolver> options.resolver).prepareTest(identifier, identifierDocument); await identifier .getPublicKey('#notInDocument') .catch(error => { expect(error instanceof UserAgentError).toBeTruthy(); expect(error.message).toEqual(`No matching public key found for #notInDocument`); }) .finally(done); }); it('should return public key for specified key identifier', async () => { const identifier = new Identifier('did:ion:identifier', options); const identifierDocument = new IdentifierDocument( { id: 'did:ion:identifier', created: '2019-01-25T01:08:44.732Z', publicKeys: [ { id: '#master', type: 'RsaVerificationKey2018', publicKeyJwk: { kty: 'RSA', kid: '#master', keyOps: [ 'sign', 'verify' ], n: 'vdpHn7kNq42UMC1W8bwxgE7K...', e: 'AQAB' } } ] } ); (<TestResolver> options.resolver).prepareTest(identifier, identifierDocument); const publicKey = await identifier.getPublicKey('#master'); expect(publicKey).toBeDefined(); expect(publicKey.id).toEqual('#master'); }); it('should return first public key when no key identifier specified', async () => { const identifier = new Identifier('did:ion:identifier', options); const identifierDocument = new IdentifierDocument( { id: 'did:ion:identifier', created: '2019-01-25T01:08:44.732Z', publicKeys: [ { id: '#first', type: 'RsaVerificationKey2018', publicKeyJwk: { kty: 'RSA', kid: '#master', keyOps: [ 'sign', 'verify' ], n: 'vdpHn7kNq42UMC1W8bwxgE7K...', e: 'AQAB' } }, { id: '#second', type: 'RsaVerificationKey2018', publicKeyJwk: { kty: 'RSA', kid: '#master', keyOps: [ 'sign', 'verify' ], n: 'vdpHn7kNq42UMC1W8bwxgE7K...', e: 'AQAB' } } ] } ); (<TestResolver> options.resolver).prepareTest(identifier, identifierDocument); const publicKey = await identifier.getPublicKey(); expect(publicKey).toBeDefined(); expect(publicKey.id).toEqual('#first'); }); describe('sign', () => { let options: UserAgentOptions; beforeEach(() => { options = new UserAgentOptions(); options.resolver = testResolver; options.timeoutInSeconds = 30; options.didPrefix = 'did:ion'; options.registrar = new TestRegistrar(); }); it('should sign a payload that is a string', async () => { options.cryptoOptions = new CryptoOptions(new CryptoFactory(new KeyStoreInMemory(), SubtleCryptoNode.getSubtleCrypto())); options.cryptoOptions.signingAlgorithm = 'ES256K'; const seed = new OctKey('ABDE'); await options.keyStore.save('masterSeed', new KeyContainer(seed)); const identifier = await Identifier.create(options); const keystore = await options.keyStore.list(); console.log(keystore); const signedPayload = await identifier.sign('examplePayload', 'did:ion-did:ion-ES256K-sig'); expect(signedPayload).toBeDefined(); expect(JwsToken.deserialize(signedPayload).getPayload()).toEqual('examplePayload'); }); it('should sign a payload that is an object', async () => { options.cryptoOptions = new CryptoOptions(new CryptoFactory(new KeyStoreInMemory(), SubtleCryptoNode.getSubtleCrypto())); options.cryptoOptions.signingAlgorithm = 'ES256K'; options.keyStore = new KeyStoreInMemory(); const seed = new OctKey('ABDE'); await options.keyStore.save('masterSeed', new KeyContainer(seed)); const identifier = await Identifier.create(options); const signedPayload = await identifier.sign({ payload: 'examplePayload' }, 'did:ion-did:ion-ES256K-sig'); expect(signedPayload).toBeDefined(); expect(JwsToken.deserialize(signedPayload).getPayload()).toEqual(`{"payload":"examplePayload"}`); }); }); describe('verify', () => { let identifier: Identifier; let identifierDocument: IdentifierDocument; beforeAll(() => { identifier = new Identifier('did:ion:identifier', options); identifierDocument = new IdentifierDocument( { id: 'did:ion:identifier', created: '2019-01-25T01:08:44.732Z', publicKeys: [ { id: '#master', type: 'RsaVerificationKey2018', publicKeyJwk: { kty: 'RSA', kid: '#master', keyOps: [ 'sign', 'verify' ], n: 'vdpHn7kNq42UMC1W8bwxgE7K...', e: 'AQAB' } } ] } ); console.log(identifier); console.log(identifierDocument); }); /* const testJws = 'testJWS'; fit('should resolve identifier document and verify jws', async () => { (<TestResolver> options.resolver).prepareTest(identifier, identifierDocument); const verifiedPayload = await identifier.verify(testJws); expect(verifiedPayload).toBeDefined(); expect(verifiedPayload).toBe('verifiedPayload'); }); fit('should verify jws', async () => { console.log(identifier); const testIdentifier = new Identifier(identifierDocument, options); const verifiedPayload = await testIdentifier.verify(testJws); expect(verifiedPayload).toBeDefined(); expect(verifiedPayload).toBe('verifiedPayload'); }); */ }); });