Merge pull request #38 from microsoft/beejones/add-unit-tests

Beejones/add unit tests
This commit is contained in:
beejones 2021-01-15 14:30:45 +01:00 коммит произвёл GitHub
Родитель c97979dc06 5b6e2dfbeb
Коммит f21da34940
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
46 изменённых файлов: 1207 добавлений и 309 удалений

Просмотреть файл

@ -1,3 +1,16 @@
# version 1.1.12-preview.1
## Keyvault exportKey did not add the kid in the jwk.
**Type of change:** engineering
**Customer impact:** low
## async functions no longer throw but reject an Error
**Type of change:** engineering
**Customer impact:** low
## Round one to come to 100% line coverage.
**Type of change:** engineering
**Customer impact:** low
# version 1.1.12-preview.0
## Remove all console.log calls from the SDK
**Type of change:** engineering

Просмотреть файл

@ -1,6 +1,6 @@
{
"name": "verifiablecredentials-crypto-sdk-typescript-keystore",
"version": "1.1.12-preview.0",
"version": "1.1.12-preview.1",
"description": "Package for managing keys in a key store.",
"repository": {
"type": "git",
@ -35,7 +35,7 @@
"typescript": "4.0.3"
},
"dependencies": {
"verifiablecredentials-crypto-sdk-typescript-keys": "1.1.12-preview.0",
"verifiablecredentials-crypto-sdk-typescript-keys": "1.1.12-preview.1",
"@types/node": "14.6.2",
"base64url": "^3.0.1",
"clone": "^2.1.2",

Просмотреть файл

@ -40,11 +40,19 @@ describe('KeyStoreInMemory', () => {
y: 'AAEE',
alg: 'ECDSA'
};
let list = await keyStore.list();
expect(list).toEqual({});
(<any>keyStore).store = new Map<string, any>();
(<any>keyStore).store.set('1', undefined);
expect(await keyStore.list()).toEqual({});
await keyStore.save(new KeyReference('1'), key1);
await keyStore.save(new KeyReference('1'), key2);
await keyStore.save(new KeyReference('2'), <PublicKey>key3);
await keyStore.save(new KeyReference('3'), <PublicKey>key4);
let list = await keyStore.list();
list = await keyStore.list();
// tslint:disable-next-line: no-backbone-get-set-outside-model
expect(list['1'].kids.length).toEqual(2);

Просмотреть файл

@ -1,6 +1,6 @@
{
"name": "verifiablecredentials-crypto-sdk-typescript-keys",
"version": "1.1.12-preview.0",
"version": "1.1.12-preview.1",
"description": "Package for managing keys in the DID space.",
"repository": {
"type": "git",

Просмотреть файл

@ -1,6 +1,6 @@
{
"name": "verifiablecredentials-crypto-sdk-typescript-plugin-cryptofactory-suites",
"version": "1.1.12-preview.0",
"version": "1.1.12-preview.1",
"description": "Package crypto factory suites.",
"repository": {
"type": "git",
@ -36,9 +36,9 @@
"typescript": "4.0.3"
},
"dependencies": {
"verifiablecredentials-crypto-sdk-typescript-keystore": "1.1.12-preview.0",
"verifiablecredentials-crypto-sdk-typescript-plugin": "1.1.12-preview.0",
"verifiablecredentials-crypto-sdk-typescript-plugin-elliptic": "1.1.12-preview.0",
"verifiablecredentials-crypto-sdk-typescript-keystore": "1.1.12-preview.1",
"verifiablecredentials-crypto-sdk-typescript-plugin": "1.1.12-preview.1",
"verifiablecredentials-crypto-sdk-typescript-plugin-elliptic": "1.1.12-preview.1",
"base64url": "^3.0.1",
"clone": "2.1.2",
"webcrypto-core": "1.1.8"

Просмотреть файл

@ -1,6 +1,6 @@
{
"name": "verifiablecredentials-crypto-sdk-typescript-plugin-elliptic",
"version": "1.1.12-preview.0",
"version": "1.1.12-preview.1",
"repository": {
"type": "git",
"url": "https://github.com/microsoft/VerifiableCredentials-Crypto-SDK-Typescript.git"
@ -29,7 +29,7 @@
"elliptic": "6.5.3",
"minimalistic-crypto-utils": "1.0.1",
"sha.js": "^2.4.11",
"verifiablecredentials-crypto-sdk-typescript-plugin": "1.1.12-preview.0",
"verifiablecredentials-crypto-sdk-typescript-plugin": "1.1.12-preview.1",
"webcrypto-core": "1.1.8"
},
"devDependencies": {

Просмотреть файл

@ -115,7 +115,7 @@ export default class EllipticEcDsaProvider extends EllipticDsaProvider {
async onExportKey (format: KeyFormat, key: CryptoKey): Promise<JsonWebKey | ArrayBuffer> {
//const ec = this.getCurve((<any>key.algorithm).namedCurve);
if (format !== 'jwk') {
throw new Error(`Export key only supports jwk`);
return Promise.reject(new Error(`Export key only supports jwk`));
}
const cryptoKey: any = (<EllipticCurveKey> key).key;
@ -151,7 +151,7 @@ export default class EllipticEcDsaProvider extends EllipticDsaProvider {
async onImportKey (format: KeyFormat,
keyData: JsonWebKey | ArrayBuffer, algorithm: EcKeyImportParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKey> {
if (format !== 'jwk') {
throw new Error(`Import key only supports jwk`);
return Promise.reject(new Error(`Import key only supports jwk`));
}
const ec = this.getCurve(algorithm.namedCurve);
const jwkKey: JsonWebKey = <JsonWebKey> keyData;

Просмотреть файл

@ -116,7 +116,7 @@ export default class EllipticEdDsaProvider extends EllipticDsaProvider {
async onImportKey(format: KeyFormat,
keyData: JsonWebKey | ArrayBuffer, algorithm: EcKeyImportParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKey> {
if (format !== 'jwk') {
throw new Error(`Import key only supports jwk`);
return Promise.reject(new Error(`Import key only supports jwk`));
}
const ec = this.getCurve(algorithm.namedCurve);
const jwkKey: any = <JsonWebKey>keyData;
@ -144,7 +144,7 @@ export default class EllipticEdDsaProvider extends EllipticDsaProvider {
async onExportKey(format: KeyFormat, key: CryptoKey): Promise<JsonWebKey | ArrayBuffer> {
//const ec = this.getCurve((<any>key.algorithm).namedCurve);
if (format !== 'jwk') {
throw new Error(`Export key only supports jwk`);
return Promise.reject(new Error(`Export key only supports jwk`));
}
const ecKey: any = (<EllipticCurveKey>key).key;

Просмотреть файл

@ -21,12 +21,6 @@ export default class SubtleCryptoElliptic extends SubtleCrypto implements ISubtl
this.providers.set(<ProviderCrypto>new EllipticEcDsaProvider(crypto));
this.providers.set(new EllipticEdDsaProvider(crypto));
}
checkRequiredArguments(args: any[], size: number, methodName: string) {
if (methodName !== 'generateKey' && args.length !== size) {
throw new TypeError(`Failed to execute '${methodName}' on 'SubtleCrypto': ${size} arguments required, but only ${args.length} present`);
}
}
/**
* Returns the @class SubtleCrypto implementation for the nodes environment

Просмотреть файл

@ -0,0 +1,36 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import SubtleCryptoElliptic from '../src/SubtleCryptoElliptic';
import { Subtle } from 'verifiablecredentials-crypto-sdk-typescript-plugin';
describe('SubtleCryptoElliptic', () => {
it('should create a SubtleCryptoElliptic', () => {
const subtleCryptoElliptic = new SubtleCryptoElliptic(new Subtle());
expect(subtleCryptoElliptic.getSubtleCrypto().constructor.name).toEqual('SubtleCryptoElliptic');
});
it('should test algorithmTransform', () => {
let subtle = new SubtleCryptoElliptic(new Subtle());
let alg: any = {test: 'name'};
expect(subtle.algorithmTransform(alg)).toEqual(alg);
alg = {foo: 'fighters'};
expect(subtle.algorithmTransform(alg)).toEqual(alg);
});
it('should test keyImportTransform', () => {
let subtle: any = new SubtleCryptoElliptic(new Subtle());
let jwk: any = {test: 'name'};
expect(subtle.keyImportTransform(jwk)).toEqual(jwk);
jwk = {foo: 'fighters'};
expect(subtle.keyImportTransform(jwk)).toEqual(jwk);
});
it('should test keyExportTransform', () => {
let subtle: any = new SubtleCryptoElliptic(new Subtle());
let jwk: any = {test: 'name'};
expect(subtle.keyExportTransform(jwk)).toEqual(jwk);
jwk = {foo: 'fighters'};
expect(subtle.keyExportTransform(jwk)).toEqual(jwk);
});
});

Просмотреть файл

@ -6,6 +6,7 @@
import { SubtleCryptoElliptic, EllipticCurveKey } from '../src/index';
import base64url from 'base64url';
import { Subtle } from 'verifiablecredentials-crypto-sdk-typescript-plugin';
import EllipticEdDsaProvider from '../src/EllipticEdDsaProvider';
const algGenerate = {
@ -289,4 +290,11 @@ describe('ed25519 - EdDSA', () => {
});
expect(throws).toEqual(true);
});
it('should instantiate EllipticEdDsaProvider', () => {
const ellipticEdDsaProvider = new EllipticEdDsaProvider(crypto);
expect(ellipticEdDsaProvider.getCurve('ed25519')).toBeDefined();
expect(() => ellipticEdDsaProvider.getCurve('SECP256K1')).toThrowError(`The requested curve 'SECP256K1' is not supported in EllipticEcDsaProvider`);
});
});

Просмотреть файл

@ -6,6 +6,7 @@
import SubtleCryptoElliptic from '../src/SubtleCryptoElliptic';
import EllipticCurveKey from '../src/EllipticCurveKey';
import { Subtle } from 'verifiablecredentials-crypto-sdk-typescript-plugin';
import EllipticEcDsaProvider from '../src/EllipticEcDsaProvider';
// tslint:disable:mocha-no-side-effect-code
const EC = require('elliptic').ec;
@ -124,4 +125,45 @@ describe('secp256k1 - ECDSA', () => {
});
expect(throws).toEqual(true);
});
it('should instantiate EllipticEcDsaProvider', () => {
const ellipticEcDsaProvider = new EllipticEcDsaProvider(crypto);
expect(ellipticEcDsaProvider.getCurve('SECP256K1')).toBeDefined();
expect(() => ellipticEcDsaProvider.getCurve('EdDSA')).toThrowError(`The requested curve 'EdDSA' is not supported in EllipticEcDsaProvider`);
});
it('should generate key in EllipticEcDsaProvider', async () => {
const ellipticEcDsaProvider = new EllipticEcDsaProvider(crypto);
const ec = ellipticEcDsaProvider.getCurve('SECP256K1');
const keyPair = ec.genKeyPair();
const genKeyPairSpy: jasmine.Spy = spyOn(ec, 'genKeyPair').and.callFake(() => {
delete keyPair.pub;
return keyPair;
});
const algGenerate = {
name: 'ECDSA',
namedCurve: 'secp256k1'
};
expect((<CryptoKeyPair>await ellipticEcDsaProvider.generateKey(algGenerate, true, ['sign'])).privateKey).toBeDefined();
genKeyPairSpy.and.callFake(() => {
keyPair.pub = keyPair.getPublic();
return keyPair;
});
expect((<CryptoKeyPair>await ellipticEcDsaProvider.generateKey(algGenerate, true, ['sign'])).privateKey).toBeDefined();
});
it('should sign/verify in EllipticEcDsaProvider', async () => {
const ellipticEcDsaProvider = new EllipticEcDsaProvider(crypto);
let algGenerate: any = {
name: 'ECDSA',
namedCurve: 'secp256k1',
hash: undefined
};
const keyPair = (<CryptoKeyPair>await ellipticEcDsaProvider.generateKey(algGenerate, true, ['sign', 'verify']));
let signature = await ellipticEcDsaProvider.sign(algGenerate, keyPair.privateKey, new Uint8Array([1, 2, 3]));
expect(signature).toBeDefined();
expect(await ellipticEcDsaProvider.verify(algGenerate, keyPair.publicKey, signature, new Uint8Array([1, 2, 3])));
});
});

Просмотреть файл

@ -1,7 +1,7 @@
{
"name": "verifiablecredentials-crypto-sdk-typescript-plugin-factory",
"description": "Factory Package for crypto plugins.",
"version": "1.1.12-preview.0",
"version": "1.1.12-preview.1",
"repository": {
"type": "git",
"url": "https://github.com/microsoft/VerifiableCredentials-Crypto-SDK-Typescript.git"
@ -39,10 +39,10 @@
"dependencies": {
"@azure/identity": "1.0.0",
"lru-cache": "6.0.0",
"verifiablecredentials-crypto-sdk-typescript-keystore": "1.1.12-preview.0",
"verifiablecredentials-crypto-sdk-typescript-plugin": "1.1.12-preview.0",
"verifiablecredentials-crypto-sdk-typescript-plugin-cryptofactory-suites": "1.1.12-preview.0",
"verifiablecredentials-crypto-sdk-typescript-plugin-keyvault": "1.1.12-preview.0",
"verifiablecredentials-crypto-sdk-typescript-keystore": "1.1.12-preview.1",
"verifiablecredentials-crypto-sdk-typescript-plugin": "1.1.12-preview.1",
"verifiablecredentials-crypto-sdk-typescript-plugin-cryptofactory-suites": "1.1.12-preview.1",
"verifiablecredentials-crypto-sdk-typescript-plugin-keyvault": "1.1.12-preview.1",
"@types/node": "14.6.2",
"webcrypto-core": "1.1.8"
},

Просмотреть файл

@ -1,6 +1,6 @@
{
"name": "verifiablecredentials-crypto-sdk-typescript-plugin-keyvault",
"version": "1.1.12-preview.0",
"version": "1.1.12-preview.1",
"repository": {
"type": "git",
"url": "https://github.com/microsoft/VerifiableCredentials-Crypto-SDK-Typescript.git"
@ -50,10 +50,10 @@
"@azure/keyvault-keys": "4.0.2",
"@azure/keyvault-secrets": "4.0.2",
"lru-cache": "6.0.0",
"verifiablecredentials-crypto-sdk-typescript-keys": "1.1.12-preview.0",
"verifiablecredentials-crypto-sdk-typescript-keystore": "1.1.12-preview.0",
"verifiablecredentials-crypto-sdk-typescript-plugin": "1.1.12-preview.0",
"verifiablecredentials-crypto-sdk-typescript-plugin-elliptic": "1.1.12-preview.0",
"verifiablecredentials-crypto-sdk-typescript-keys": "1.1.12-preview.1",
"verifiablecredentials-crypto-sdk-typescript-keystore": "1.1.12-preview.1",
"verifiablecredentials-crypto-sdk-typescript-plugin": "1.1.12-preview.1",
"verifiablecredentials-crypto-sdk-typescript-plugin-elliptic": "1.1.12-preview.1",
"base64url": "3.0.1",
"clone": "2.1.2",
"webcrypto-core": "1.1.8"

Просмотреть файл

@ -135,7 +135,7 @@ export default class KeyStoreKeyVault implements IKeyStore {
keyContainerItem = options.publicKeyOnly ?
new RsaPublicKey(version.key ? version.key : version.value as any) : new RsaPrivateKey(version.key ? version.key : version.value as any);
} else {
throw new Error(`Non supported key type ${kty}`);
return Promise.reject(new Error(`Non supported key type ${kty}`));
}
} else {
if (kty === 'EC') {
@ -159,15 +159,14 @@ export default class KeyStoreKeyVault implements IKeyStore {
}
if (!container) {
throw new Error(`The secret with reference '${keyName}' has not usable secrets`);
return Promise.reject(new Error(`The secret with reference '${keyName}' has not usable secrets`));
}
return container;
} catch (e) {
console.error(`Could not retrieve ${JSON.stringify(keyReference)}. Error: ${e}`);
throw e;
return Promise.reject(e);
}
}
/**
@ -179,7 +178,7 @@ export default class KeyStoreKeyVault implements IKeyStore {
*/
async save(keyReference: KeyReference, key: CryptographicKey | string, options: KeyStoreOptions = new KeyStoreOptions()): Promise<void> {
if (!keyReference || !keyReference.keyReference) {
throw new Error(`Key reference needs to be specified`);
return Promise.reject(new Error(`Key reference needs to be specified`));
}
const keyName = keyReference.remoteKeyReference || keyReference.keyReference;

Просмотреть файл

@ -52,7 +52,7 @@ export default class KeyVaultEcdsaProvider extends KeyVaultProvider {
const kid = (<any>key.algorithm).kid;
if (!kid) {
throw new CryptoError(algorithm, 'Missing kid in algortihm');
return Promise.reject(new CryptoError(algorithm, 'Missing kid in algortihm'));
}
const client = (<KeyStoreKeyVault>this.keyStore).getCryptoClient(kid);
@ -65,7 +65,7 @@ export default class KeyVaultEcdsaProvider extends KeyVaultProvider {
// Added for legacy. Used by keys generated with crv: SECP256K1
signature = await client.sign(<any>'ECDSA256', new Uint8Array(hash));
} else {
throw exception;
return Promise.reject(exception);
}
}
@ -85,28 +85,28 @@ export default class KeyVaultEcdsaProvider extends KeyVaultProvider {
jwk: JsonWebKey, _algorithm: EcKeyImportParams, _extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKey> {
if (format !== 'jwk') {
throw new Error(`Import key only supports jwk`);
return Promise.reject(new Error(`Import key only supports jwk`));
}
if (jwk.kty?.toUpperCase() !== 'EC') {
throw new Error(`Import key only supports kty EC`);
return Promise.reject(new Error(`Import key only supports kty EC`));
}
if (jwk.crv?.toUpperCase() === 'SECP256K1') {
jwk.crv = 'P-256K';
} else if (jwk.crv?.toUpperCase() !== 'P-256K') {
throw new Error(`Import key only supports crv P-256K`);
return Promise.reject(new Error(`Import key only supports crv P-256K`));
}
if (!jwk.kid && jwk.kid!.startsWith('https://')) {
throw new Error(`Imported key must have a kid in the format https://<vault>/keys/<name>/<version>`);
if (!(jwk.kid && jwk.kid.startsWith('https://'))) {
return Promise.reject(new Error(`Imported key must have a kid in the format https://<vault>/keys/<name>/<version>`));
}
const kidParts = jwk.kid!.split('/');
let secretType: boolean = kidParts[3] === 'secrets';
if (!['keys', 'secrets'].includes(kidParts[3])) {
throw new Error(`Imported key must be of type keys or secrets`);
if (!(kidParts.length >= 5 && ['keys', 'secrets'].includes(kidParts[3]))) {
return Promise.reject(new Error(`Imported key must be of type keys or secrets`));
}
if (kidParts.length <= 5) {
@ -189,8 +189,13 @@ export default class KeyVaultEcdsaProvider extends KeyVaultProvider {
*/
async onExportKey(format: KeyFormat, key: CryptoKey): Promise<JsonWebKey> {
if (format !== 'jwk') {
throw new Error(`Export key only supports jwk`);
return Promise.reject(new Error(`Export key only supports jwk`));
}
return <Promise<JsonWebKey>>this.subtle.exportKey(format, key);
const jwk: any = await this.subtle.exportKey(format, key);
if (!jwk.kid) {
jwk.kid = (<any>key.algorithm).kid;
}
return <Promise<JsonWebKey>>jwk;
}
}

Просмотреть файл

@ -46,7 +46,7 @@ export default class KeyVaultRsaOaepProvider extends KeyVaultProvider {
async onDecrypt (algorithm: Algorithm, key: CryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
const kid = (<any>key.algorithm).kid;
if (!kid) {
throw new CryptoError(algorithm, 'Missing kid in algortihm');
return Promise.reject(new CryptoError(algorithm, 'Missing kid in algortihm'));
}
const client = (<KeyStoreKeyVault>this.keyStore).getCryptoClient(kid);

Просмотреть файл

@ -0,0 +1,20 @@
import { ClientSecretCredential } from '@azure/identity';
import { KeyReference, KeyStoreInMemory } from 'verifiablecredentials-crypto-sdk-typescript-keystore';
import { CryptoFactoryScope, Subtle } from 'verifiablecredentials-crypto-sdk-typescript-plugin';
import { CryptoFactoryKeyVault, KeyStoreKeyVault } from '../src';
describe('CryptoFactoryKeyVault', () => {
it('should create a CryptoFactoryKeyVault', () => {
const cache = new KeyStoreInMemory();
const credential = new ClientSecretCredential('tenant', 'clientid', 'secret');
const keyStore = new KeyStoreKeyVault(credential, 'https://example.vault.com', cache);
const subtle = new Subtle();
const crypto = new CryptoFactoryKeyVault(keyStore, subtle);
expect(crypto.getMessageSigner('ES256K', CryptoFactoryScope.Private, new KeyReference('key', 'key')).constructor.name).toEqual('SubtleCryptoKeyVault');
expect(crypto.getMessageSigner('ECDSA', CryptoFactoryScope.Private, new KeyReference('key', 'key')).constructor.name).toEqual('SubtleCryptoKeyVault');
expect(crypto.getKeyEncrypter('RSA-OAEP-256', CryptoFactoryScope.Private, new KeyReference('key', 'key')).constructor.name).toEqual('SubtleCryptoKeyVault');
expect(crypto.getKeyEncrypter('RSA-OAEP', CryptoFactoryScope.Private, new KeyReference('key', 'key')).constructor.name).toEqual('SubtleCryptoKeyVault');
expect(crypto.getMessageSigner('RSASSA-PKCS1-v1_5', CryptoFactoryScope.Private, new KeyReference('key', 'key')).constructor.name).toEqual('Subtle');
})
});

Просмотреть файл

@ -12,6 +12,8 @@ import { KeyClient } from '@azure/keyvault-keys';
import { SecretClient } from '@azure/keyvault-secrets';
import { CryptoKey } from 'webcrypto-core';
import Credentials from './Credentials';
import base64url from 'base64url';
const clone = require('clone');
// Sample config
const tenantId = Credentials.tenantGuid;
@ -44,10 +46,22 @@ afterEach(() => {
describe('KeyStoreKeyVault', () => {
const alg = { name: 'ECDSA', namedCurve: 'SECP256K1', hash: { name: 'SHA-256' } };
if (!keyVaultEnable) {
console.log('Key vault is enabled. Add your credentials to Credentials.ts')
console.log('Key vault is not enabled. Add your credentials to Credentials.ts')
return;
}
it('should create an instance', () => {
const cache = new KeyStoreInMemory();
const credential = new ClientSecretCredential(tenantId, clientId, clientSecret);
let vault = 'https://example.keyvault.com';
let keyStore = new KeyStoreKeyVault(credential, vault, cache);
expect(keyStore.cache).toEqual(cache);
expect((<any>keyStore).vaultUri).toEqual(vault + '/');
keyStore = new KeyStoreKeyVault(credential, vault + '/', cache);
expect(keyStore.cache).toEqual(cache);
expect((<any>keyStore).vaultUri).toEqual(vault + '/');
});
it('should list a named generated key', async () => {
const name = 'KvTest-KeyStoreKeyVault' + Math.random().toString(10).substr(2);
const cache = new KeyStoreInMemory();
@ -58,7 +72,9 @@ describe('KeyStoreKeyVault', () => {
await provider.onGenerateKey(alg, false, ['sign'], { keyReference: new KeyReference(name) });
let list = await keyStore.list('key', new KeyStoreOptions({ latestVersion: false }));
expect(list[name]).toBeDefined();
const key = await keyStore.get(new KeyReference(name, 'key'), new KeyStoreOptions({ latestVersion: false }));
// Two requests should hit cache
let key = await keyStore.get(new KeyReference(name, 'key'), new KeyStoreOptions({ latestVersion: false }));
key = await keyStore.get(new KeyReference(name, 'key'), new KeyStoreOptions({ latestVersion: false }));
expect(key).toBeDefined();
expect((await cache.list())[name]).toBeDefined();
} finally {
@ -66,6 +82,93 @@ describe('KeyStoreKeyVault', () => {
}
});
it('should list a named stored secret k', async () => {
const name = 'KvTest-KeyStoreKeyVault' + Math.random().toString(10).substr(2);
const cache = new KeyStoreInMemory();
const credential = new ClientSecretCredential(tenantId, clientId, clientSecret);
const keyStore = new KeyStoreKeyVault(credential, vaultUri, cache);
try {
const secret1 = base64url.encode(name);
const secret2 = base64url.encode(name + '2');
//save two versions
await keyStore.save(new KeyReference(name), secret1);
await keyStore.save(new KeyReference(name), secret2);
let list = await keyStore.list('secret', new KeyStoreOptions({ latestVersion: false }));
expect(list[name]).toBeDefined();
// get latest version only
let key = await keyStore.get(new KeyReference(name, 'secret'), new KeyStoreOptions({ latestVersion: true }));
expect(key.keys.length).toEqual(1);
//TODO BUG. k is reported as object
expect((await cache.list())[name]).toBeDefined();
// get all versions
key = await keyStore.get(new KeyReference(name, 'secret'), new KeyStoreOptions({ latestVersion: false }));
expect(key.keys.length).toEqual(2);
} finally {
await (<SecretClient>keyStore.getKeyStoreClient('secret')).beginDeleteSecret(name);
}
});
it('should list a named stored secret with EC', async () => {
let cleaned = false;
const name = 'KvTest-KeyStoreKeyVault' + Math.random().toString(10).substr(2);
const cache = new KeyStoreInMemory();
const credential = new ClientSecretCredential(tenantId, clientId, clientSecret);
const keyStore = new KeyStoreKeyVault(credential, vaultUri, cache);
const subtle = new Subtle();
try {
const keyPair = <CryptoKeyPair>await subtle.generateKey(<EcKeyGenParams>{ name: 'ECDSA', namedCurve: 'secp256k1', hash: { name: 'SHA-256' } }, true, ["sign", "verify"]);
const jwk: any = await subtle.exportKey('jwk', keyPair.privateKey);
//save two versions
await keyStore.save(new KeyReference(name), jwk);
// save okp version
let okp = clone(jwk);
okp.kty = 'OKP';
await keyStore.save(new KeyReference(name), jwk);
let list = await keyStore.list('secret', new KeyStoreOptions({ latestVersion: false }));
expect(list[name]).toBeDefined();
// get latest version only, second should
let key = await keyStore.get(new KeyReference(name, 'secret'), new KeyStoreOptions({ latestVersion: true }));
key = await keyStore.get(new KeyReference(name, 'secret'), new KeyStoreOptions({ latestVersion: true }));
expect(key.keys.length).toEqual(1);
//TODO BUG. k is reported as object
expect((await cache.list())[name]).toBeDefined();
// get all versions
key = await keyStore.get(new KeyReference(name, 'secret'), new KeyStoreOptions({ latestVersion: false }));
expect(key.keys.length).toEqual(2);
// get public key only
key = await keyStore.get(new KeyReference(name, 'secret'), new KeyStoreOptions({ publicKeyOnly: true }));
expect(key.keys.length).toEqual(1);
// negative cases
cleaned = true;
await (<SecretClient>keyStore.getKeyStoreClient('secret')).beginDeleteSecret(name);
const getKeyStoreClientSpy: jasmine.Spy = spyOn(keyStore, 'getKeyStoreClient').and.callFake(() => {
throw new Error('some error');
});
try {
await keyStore.get(new KeyReference(name, 'secret'), new KeyStoreOptions({ latestVersion: true }));
fail('get should have thrown');
} catch (exception) {
expect(exception.message).toEqual('some error');
}
} finally {
if (!cleaned) {
await (<SecretClient>keyStore.getKeyStoreClient('secret')).beginDeleteSecret(name);
}
}
});
it('should list a specific version of the key', async () => {
const name = 'KvTest-KeyStoreKeyVault' + Math.random().toString(10).substr(2);
const cache = new KeyStoreInMemory();
@ -78,11 +181,11 @@ describe('KeyStoreKeyVault', () => {
await provider.onGenerateKey(alg, false, ['sign'], { keyReference: new KeyReference(name) });
let parts = (<any>keyPair.publicKey.algorithm).kid.split('/');
const keyName = `${parts[parts.length-2]}/${parts[parts.length-1]}`;
const keyName = `${parts[parts.length - 2]}/${parts[parts.length - 1]}`;
let list = await keyStore.list('key', new KeyStoreOptions({ latestVersion: false }));
expect(list[name].kids[0].includes(keyName) || list[name].kids[1].includes(keyName)).toBeTruthy();
const key = await keyStore.get(new KeyReference(name, 'key',keyName), new KeyStoreOptions({ latestVersion: false }));
const key = await keyStore.get(new KeyReference(name, 'key', keyName), new KeyStoreOptions({ latestVersion: false }));
expect(key.keys.length).toEqual(1);
console.log(`name: ${keyName}`);
console.log(`${JSON.stringify(key.keys[0])}`);
@ -114,17 +217,20 @@ describe('KeyStoreKeyVault', () => {
const cache = new KeyStoreInMemory();
const credential = new ClientSecretCredential(tenantId, clientId, clientSecret);
const keyStore = new KeyStoreKeyVault(credential, vaultUri, cache);
let throwed = false;
await keyStore.save(new KeyReference(name, 'secret'), 'abcdefg');
let list = await keyStore.list('secret', new KeyStoreOptions({ latestVersion: false }));
expect(list[name]).toBeDefined();
try {
await keyStore.save(new KeyReference(name, 'secret'), 'abcdefg');
let list = await keyStore.list('secret', new KeyStoreOptions({ latestVersion: false }));
expect(list[name]).toBeDefined();
await cache.get(new KeyReference(name, 'secret'));
expect(throwed).toBeTruthy();
fail('Should have thrown during get: should set a secret');
} catch (err) {
throwed = true;
expect(err.message).toEqual(`${name} not found`)
}
try {
await keyStore.save(<any>undefined, '');
fail('Should have thrown during save: should set a secret');
} catch (err) {
expect(err.message).toEqual(`Key reference needs to be specified`)
} finally {
await (<SecretClient>keyStore.getKeyStoreClient('secret')).beginDeleteSecret(name);
}

Просмотреть файл

@ -8,9 +8,9 @@ import KeyVaultEcdsaProvider from '../src/plugin/KeyVaultEcdsaProvider';
import KeyVaultRsaOaepProvider from '../src/plugin/KeyVaultRsaOaepProvider';
import { KeyStoreOptions, KeyStoreInMemory, KeyReference } from 'verifiablecredentials-crypto-sdk-typescript-keystore';
import { KeyClient } from '@azure/keyvault-keys';
import { Subtle, CryptoFactoryScope, IKeyGenerationOptions } from 'verifiablecredentials-crypto-sdk-typescript-plugin';
import { Subtle, IKeyGenerationOptions } from 'verifiablecredentials-crypto-sdk-typescript-plugin';
import Credentials from './Credentials';
import { KeyVaultProvider, CryptoFactoryKeyVault, SubtleCryptoKeyVault } from '../src';
import { KeyVaultProvider, SubtleCryptoKeyVault } from '../src';
const clone = require('clone');
// Sample config
@ -20,7 +20,7 @@ const clientSecret = encodeURI(Credentials.clientSecret);
const vaultUri = Credentials.vaultUri;
const keyVaultEnable = vaultUri.startsWith('https://');
const subtle = new Subtle();
const subtleCrypto = new Subtle();
const random = (length: number) => Math.random().toString(36).substring(2, length + 2);
const logging = require('adal-node').Logging;
logging.setLoggingOptions({
@ -43,7 +43,7 @@ afterEach(() => {
describe('KeyVaultPlugin', () => {
if (!keyVaultEnable) {
console.log('Key vault is enabled. Add your credentials to Credentials.ts')
console.log('Key vault is not enabled. Add your credentials to Credentials.ts')
return;
}
@ -53,13 +53,25 @@ describe('KeyVaultPlugin', () => {
const cache = new KeyStoreInMemory();
const credential = new ClientSecretCredential(tenantId, clientId, clientSecret);
const keyStore = new KeyStoreKeyVault(credential, vaultUri, cache);
const plugin = new KeyVaultEcdsaProvider(subtle, keyStore);
const plugin = new KeyVaultEcdsaProvider(subtleCrypto, keyStore);
try {
const result: CryptoKeyPair = await plugin.onGenerateKey(alg, false, ['sign']);
expect((<any>result.publicKey).algorithm.namedCurve).toEqual('K-256');
expect(result.publicKey.algorithm.name).toEqual('ECDSA');
expect((<any>result.publicKey.algorithm).kid.startsWith('https')).toBeTruthy();
expect((<any>result.publicKey.algorithm).kid.includes(name)).toBeTruthy();
const keypair: CryptoKeyPair = await plugin.onGenerateKey(alg, false, ['sign']);
expect((<any>keypair.publicKey).algorithm.namedCurve).toEqual('K-256');
expect(keypair.publicKey.algorithm.name).toEqual('ECDSA');
expect((<any>keypair.publicKey.algorithm).kid.startsWith('https')).toBeTruthy();
expect((<any>keypair.publicKey.algorithm).kid.includes(name)).toBeTruthy();
const jwk: any = await plugin.exportKey('jwk', keypair.publicKey);
expect(jwk.kid.startsWith('https://')).toBeTruthy();
// negative cases
try {
await plugin.exportKey('raw', keypair.publicKey);
fail('export key raw should fail');
} catch(exception) {
expect(exception.message).toEqual('Export key only supports jwk');
}
} finally {
await (<KeyClient>keyStore.getKeyStoreClient('key')).beginDeleteKey(name);
}
@ -70,7 +82,7 @@ describe('KeyVaultPlugin', () => {
const cache = new KeyStoreInMemory();
const credential = new ClientSecretCredential(tenantId, clientId, clientSecret);
const keyStore = new KeyStoreKeyVault(credential, vaultUri, cache);
const plugin = new KeyVaultEcdsaProvider(subtle, keyStore);
const plugin = new KeyVaultEcdsaProvider(subtleCrypto, keyStore);
try {
let keyReference = new KeyReference(name, 'key');
let curve = 'P-256K';
@ -91,7 +103,7 @@ describe('KeyVaultPlugin', () => {
const cache = new KeyStoreInMemory();
const credential = new ClientSecretCredential(tenantId, clientId, clientSecret);
const keyStore = new KeyStoreKeyVault(credential, vaultUri, cache);
const plugin = new KeyVaultEcdsaProvider(subtle, keyStore);
const plugin = new KeyVaultEcdsaProvider(subtleCrypto, keyStore);
try {
let keyReference = new KeyReference(name, 'key', remoteName);
@ -113,19 +125,42 @@ describe('KeyVaultPlugin', () => {
const cache = new KeyStoreInMemory();
const credential = new ClientSecretCredential(tenantId, clientId, clientSecret);
const keyStore = new KeyStoreKeyVault(credential, vaultUri, cache);
const subtle = new SubtleCryptoKeyVault(new Subtle(), keyStore);
const subtleKv = new SubtleCryptoKeyVault(new Subtle(), keyStore);
try {
const keyReference = new KeyReference(name, 'key', remoteName);
const curve = 'P-256K';
const alg = { name: 'ECDSA', namedCurve: 'secp256k1', hash: { name: 'SHA-256' } };
const keypair = await subtle.generateKey(alg, false, ['sign', 'verify'], { keyReference, curve });
const keypair = await subtleKv.generateKey(alg, false, ['sign', 'verify'], { keyReference, curve });
const payload = Buffer.from('hello Houston');
const signature = await subtle.sign(alg, keypair.publicKey, payload);
const signature = await subtleKv.sign(alg, keypair.publicKey, payload);
expect(signature.byteLength).toEqual(64);
const jwk = await subtle.exportKey('jwk', keypair.publicKey);
const jwk = await subtleKv.exportKey('jwk', keypair.publicKey);
expect(jwk.kty).toEqual('EC');
// negative cases
let publicKey = clone(keypair.publicKey);
delete (<any>publicKey.algorithm).kid;
try {
await subtleKv.sign(alg, publicKey, payload);
fail('sign should throw');
} catch(exception) {
expect(exception.message).toEqual('Missing kid in algortihm');
}
let getCryptoClientSpy: jasmine.Spy = spyOn(keyStore, 'getCryptoClient').and.callFake(() => {
return {
sign: () => Promise.reject(new Error('spy signing error'))
};
});
try {
await subtleKv.sign(alg, keypair.publicKey, payload);
fail('sign should throw');
} catch(exception) {
expect(exception.message).toEqual('spy signing error');
}
} finally {
await (<KeyClient>keyStore.getKeyStoreClient('key')).beginDeleteKey(remoteName);
}
@ -139,7 +174,7 @@ describe('KeyVaultPlugin', () => {
try {
let list = await keyStore.list('key', new KeyStoreOptions({ latestVersion: false }));
const versions = list[name];
const plugin = new KeyVaultEcdsaProvider(subtle, keyStore);
const plugin = new KeyVaultEcdsaProvider(subtleCrypto, keyStore);
// Generate EC
let keyPair: CryptoKeyPair = await plugin.onGenerateKey(alg, false, ['sign', 'verify']);
@ -183,7 +218,7 @@ describe('KeyVaultPlugin', () => {
const credential = new ClientSecretCredential(tenantId, clientId, clientSecret);
const keyStore = new KeyStoreKeyVault(credential, vaultUri, cache);
try {
const plugin = new KeyVaultEcdsaProvider(subtle, keyStore);
const plugin = new KeyVaultEcdsaProvider(subtleCrypto, keyStore);
const payload = Buffer.from('test');
console.log(payload);
@ -198,8 +233,8 @@ describe('KeyVaultPlugin', () => {
const webCryptoAlg = clone(alg);
webCryptoAlg.namedCurve = 'K-256';
const jwk = await (await cache.get(new KeyReference(name, 'key'), keyPair.publicKey)).getKey<JsonWebKey>();
const cryptoKey = await subtle.importKey('jwk', jwk, webCryptoAlg, true, ['verify']);
const result = await subtle.verify(webCryptoAlg, cryptoKey, Buffer.from(signature), payload);
const cryptoKey = await subtleCrypto.importKey('jwk', jwk, webCryptoAlg, true, ['verify']);
const result = await subtleCrypto.verify(webCryptoAlg, cryptoKey, Buffer.from(signature), payload);
expect(result).toBeTruthy();
expect((await cache.list())[name]).toBeDefined();
} finally {
@ -215,15 +250,15 @@ describe('KeyVaultPlugin', () => {
const credential = new ClientSecretCredential(tenantId, clientId, clientSecret);
const keyStore = new KeyStoreKeyVault(credential, vaultUri, cache);
try {
const plugin = new KeyVaultEcdsaProvider(subtle, keyStore);
const plugin = new KeyVaultEcdsaProvider(subtleCrypto, keyStore);
const payload = Buffer.from('test');
console.log(payload);
// import reference key
const keyReference = new KeyReference(name, 'key');
let cryptoKey: any = <CryptoKey>await subtle.generateKey(alg, true, ['sign'], { keyReference });
let jwk: any = await subtle.exportKey('jwk', cryptoKey.privateKey);
let cryptoKey: any = <CryptoKey>await subtleCrypto.generateKey(alg, true, ['sign'], { keyReference });
let jwk: any = await subtleCrypto.exportKey('jwk', cryptoKey.privateKey);
jwk.kid = name;
await keyStore.save(keyReference, jwk, new KeyStoreOptions());
@ -231,17 +266,65 @@ describe('KeyVaultPlugin', () => {
const cachedPublic = await (await cache.get(keyReference)).getKey<JsonWebKey>();
cryptoKey = await plugin.importKey('jwk', cachedPublic, alg, false, ['sign', 'verify']);
const signature = await plugin.onSign(alg, cryptoKey, payload);
cryptoKey = await plugin.importKey('jwk', cachedPublic, alg, false, ['sign']);
const signature = await plugin.sign(alg, cryptoKey, payload);
// Set verify key
const webCryptoAlg = clone(alg);
webCryptoAlg.namedCurve = 'K-256';
jwk = (await cache.get(new KeyReference(name, 'key'), new KeyStoreOptions({ publicKeyOnly: true }))).getKey<JsonWebKey>();
cryptoKey = await subtle.importKey('jwk', jwk, webCryptoAlg, true, ['verify']);
const result = await subtle.verify(webCryptoAlg, cryptoKey, signature, payload);
cryptoKey = await subtleCrypto.importKey('jwk', jwk, webCryptoAlg, true, ['verify']);
const result = await subtleCrypto.verify(webCryptoAlg, cryptoKey, signature, payload);
expect(result).toBeTruthy();
expect((await cache.list())[name]).toBeDefined();
// negative cases
try {
await plugin.importKey('raw', new Uint8Array([1,2,3,4]), webCryptoAlg, true, ['sign']);
fail('import raw should fail');
} catch (exception) {
expect(exception.message).toEqual('Import key only supports jwk');
}
let clonedJwk = clone(jwk);
clonedJwk.kty = 'RSA'
try {
await plugin.importKey('jwk', clonedJwk, webCryptoAlg, true, ['sign']);
fail('import RSA should fail');
} catch (exception) {
expect(exception.message).toEqual('Import key only supports kty EC');
}
clonedJwk = clone(jwk);
clonedJwk.crv = 'ed25519';
try {
await plugin.importKey('jwk', clonedJwk, webCryptoAlg, true, ['sign']);
fail('import crv should fail');
} catch (exception) {
expect(exception.message).toEqual('Import key only supports crv P-256K');
}
clonedJwk = clone(jwk);
delete clonedJwk.kid;
try {
await plugin.importKey('jwk', clonedJwk, webCryptoAlg, true, ['sign']);
fail('import crv should fail');
} catch (exception) {
expect(exception.message).toEqual('Imported key must have a kid in the format https://<vault>/keys/<name>/<version>');
}
clonedJwk = clone(jwk);
clonedJwk.kid = 'vaultUri';
try {
await plugin.importKey('jwk', clonedJwk, webCryptoAlg, true, ['sign']);
fail('import crv should fail');
} catch (exception) {
expect(exception.message).toEqual('Imported key must have a kid in the format https://<vault>/keys/<name>/<version>');
}
clonedJwk = clone(jwk);
clonedJwk.kid = 'https://vault.com';
try {
await plugin.importKey('jwk', clonedJwk, webCryptoAlg, true, ['sign']);
fail('import crv should fail');
} catch (exception) {
expect(exception.message).toEqual('Imported key must be of type keys or secrets');
}
} finally {
await (<KeyClient>keyStore.getKeyStoreClient('key')).beginDeleteKey(name);
}
@ -266,7 +349,7 @@ describe('KeyVaultPlugin', () => {
const credential = new ClientSecretCredential(tenantId, clientId, clientSecret);
const keyStore = new KeyStoreKeyVault(credential, vaultUri, cache);
try {
const plugin = new KeyVaultEcdsaProvider(subtle, keyStore);
const plugin = new KeyVaultEcdsaProvider(subtleCrypto, keyStore);
// Generate EC
let keyReference = new KeyReference(name, 'key');
@ -303,19 +386,31 @@ describe('rsa-oaep', () => {
const credential = new ClientSecretCredential(tenantId, clientId, clientSecret);
const keyStore = new KeyStoreKeyVault(credential, vaultUri, cache);
try {
const plugin = new KeyVaultRsaOaepProvider(subtle, keyStore);
const plugin = new KeyVaultRsaOaepProvider(subtleCrypto, keyStore);
const payload = Buffer.from('hello houston');
// generate key
const keyPair: CryptoKeyPair = await plugin.onGenerateKey(alg, false, ['decrypt', 'encrypt']);
// Encrypt with subtle
const cipher = await subtle.encrypt(alg, keyPair.publicKey, payload);
const cipher = await subtleCrypto.encrypt(alg, keyPair.publicKey, payload);
// decrypt with key vault
const decrypt = await plugin.onDecrypt(alg, keyPair.publicKey, cipher);
expect(Buffer.from(decrypt)).toEqual(payload);
expect((await cache.list())[name]).toBeDefined();
// negative cases
let clonedPk = clone(keyPair.publicKey);
delete clonedPk.algorithm.kid;
try {
await plugin.decrypt(alg, clonedPk, cipher);
fail('decrypt RSA should fail');
} catch (exception) {
expect(exception.message).toEqual('Missing kid in algortihm');
}
} finally {
await (<KeyClient>keyStore.getKeyStoreClient('key')).beginDeleteKey(name);
}

Просмотреть файл

@ -0,0 +1,76 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { KeyStoreInMemory } from 'verifiablecredentials-crypto-sdk-typescript-keystore';
import { KeyStoreKeyVault, SubtleCryptoKeyVault } from '../src';
import { ClientSecretCredential } from '@azure/identity';
import { Subtle } from 'verifiablecredentials-crypto-sdk-typescript-plugin';
import Credentials from './Credentials';
describe('SubtleCryptoKeyVault', () => {
// Sample config
const tenantId = Credentials.tenantGuid;
const clientId = Credentials.clientId;
const clientSecret = encodeURI(Credentials.clientSecret);
const vaultUri = Credentials.vaultUri;
const credential = new ClientSecretCredential(tenantId, clientId, clientSecret);
const keyVaultEnable = vaultUri.startsWith('https://');
const cache = new KeyStoreInMemory();
const keyStore = new KeyStoreKeyVault(credential, vaultUri, cache);
const subtle = new Subtle();
const subtleKv: any = new SubtleCryptoKeyVault(subtle, keyStore);
if (!keyVaultEnable) {
console.log('Key vault is not enabled. Add your credentials to Credentials.ts')
return;
}
const genKey = async () => {
const cryptoKey = await subtleKv.generateKey(<EcKeyGenParams>{ name: "ECDSA", hash: { name: "SHA-256" }, namedCurve: 'secp256k1' }, true, ["sign", "verify"]);
return cryptoKey;
}
it('should create instance', () => {
let subtleKv: any = new SubtleCryptoKeyVault(subtle, keyStore);
expect(subtleKv.getSubtleCrypto().constructor.name).toEqual('SubtleCryptoKeyVault');
});
it('should generate key', async () => {
const cryptoKey = await genKey();
expect(cryptoKey).toBeDefined();
});
it('should test algorithmTransform', () => {
let alg: any = {test: 'name'};
expect(subtleKv.algorithmTransform(alg)).toEqual(alg);
alg = {foo: 'fighters'};
expect(subtleKv.algorithmTransform(alg)).toEqual(alg);
});
it('should test keyImportTransform', () => {
let jwk: any = { test: 'name' };
expect(subtleKv.keyImportTransform(jwk)).toEqual(jwk);
jwk = { foo: 'fighters' };
expect(subtleKv.keyImportTransform(jwk)).toEqual(jwk);
jwk = { crv: 'P-256K' };
expect(subtleKv.keyImportTransform(jwk)).toEqual({ crv: 'SECP256K1' });
jwk = { crv: 'SECP256K1' };
expect(subtleKv.keyImportTransform(jwk)).toEqual({ crv: 'SECP256K1' });
jwk = { crv: 'XXX' };
expect(subtleKv.keyImportTransform(jwk)).toEqual({ crv: 'XXX' });
});
it('should test keyExportTransform', () => {
let jwk: any = { test: 'name' };
expect(subtleKv.keyExportTransform(jwk)).toEqual(jwk);
jwk = { foo: 'fighters' };
expect(subtleKv.keyExportTransform(jwk)).toEqual(jwk);
jwk = { crv: 'P-256K' };
expect(subtleKv.keyExportTransform(jwk)).toEqual({ crv: 'SECP256K1' });
jwk = { crv: 'K-256' };
expect(subtleKv.keyExportTransform(jwk)).toEqual({ crv: 'SECP256K1' });
jwk = { crv: 'SECP256K1' };
expect(subtleKv.keyExportTransform(jwk)).toEqual({ crv: 'SECP256K1' });
});
});

Просмотреть файл

@ -36,9 +36,9 @@ export default class CryptoHelpers {
case 'SHA-384':
case 'SHA-512':
return cryptoFactory.getMessageDigest(jwa, scope, keyReference);
}
throw new Error(`Algorithm '${JSON.stringify(algorithm)}' is not supported`);
default:
throw new Error(`Algorithm '${JSON.stringify(algorithm)}' is not supported. Should be unreachable`);
}
}
/**
@ -82,7 +82,7 @@ export default class CryptoHelpers {
return { name: 'HMAC', hash: { name: `SHA-${jwa.toUpperCase().replace('HS', '')}` } };
}
throw new Error(`Algorithm ${JSON.stringify(jwa)} is not supported`);
throw new Error(`Algorithm '${jwa}' is not supported`);
}
/**
@ -91,7 +91,7 @@ export default class CryptoHelpers {
* @param hash Optional hash for the algorithm
*/
public static webCryptoToJwa(algorithm: any): string {
const hash: string = algorithm.hash || algorithm.name || 'SHA-256';
const hash: string = algorithm.hash || algorithm.name;
switch (algorithm.name.toUpperCase()) {
case 'RSASSA-PKCS1-V1_5':
return `RS${CryptoHelpers.getHash(hash)}`;
@ -105,7 +105,7 @@ export default class CryptoHelpers {
return `RSA-OAEP-256`;
case 'AES-GCM':
const length = algorithm.length || 128;
return `A${length}GCMKW`;
return `A${length}GCM`;
case 'HMAC':
return `HS256`;
@ -124,7 +124,7 @@ export default class CryptoHelpers {
* @param algorithm used for signature
*/
public static getKeyImportAlgorithm(algorithm: CryptoAlgorithm, jwk: PublicKey | JsonWebKey): string | RsaHashedImportParams | EcKeyImportParams | HmacImportParams | DhImportKeyParams {
const hash = (<any>algorithm).hash || (<any>algorithm).name || 'SHA-256';
const hash = (<any>algorithm).hash || (<any>algorithm).name;
const name = algorithm.name;
switch (algorithm.name.toUpperCase()) {
case 'RSASSA-PKCS1-V1_5':
@ -151,7 +151,7 @@ export default class CryptoHelpers {
if (hash.name) {
return (hash.name).toUpperCase().replace('SHA-', '');
}
return (hash || 'SHA-256').toUpperCase().replace('SHA-', '');
return '256';
}
private static getRegexMatch(matches: RegExpExecArray, index: number): string {

Просмотреть файл

@ -46,7 +46,7 @@ const SUPPORTED_CURVES = ['K-256', 'P-256K', 'secp256k1', 'ed25519'];
const pairwiseKeySeed = await crypto.sign(alg, key, Buffer.from(peerId));
if (SUPPORTED_CURVES.indexOf(algorithm.namedCurve) === -1) {
throw new CryptoError(algorithm, `Curve ${algorithm.namedCurve} is not supported`);
return Promise.reject(new CryptoError(algorithm, `Curve ${algorithm.namedCurve} is not supported`));
}
let privateKey = new BN(Buffer.from(pairwiseKeySeed));

Просмотреть файл

@ -50,7 +50,7 @@ import EcPairwiseKey from "./EcPairwiseKey";
return RsaPairwiseKey.generate(this.cryptoFactory, personaMasterKey, <RsaHashedKeyGenParams>algorithm, peerId);
default:
throw new CryptoError(algorithm, `Pairwise key for type '${keyType}' is not supported.`);
return Promise.reject(new CryptoError(algorithm, `Pairwise key for type '${keyType}' is not supported.`));
}
}

Просмотреть файл

@ -35,7 +35,7 @@ type PrimeDelegate = Array<(cryptoFactory: CryptoFactory, inx: number, key: Buff
// 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;
const keySize = algorithm.modulusLength || 2048;
// Get deterministic base number for p
const peerIdBuffer = Buffer.from(peerId);

Просмотреть файл

@ -24,7 +24,7 @@ import { ISubtleCrypto } from './index';
// tslint:disable-next-line:no-typeof-undefined
if (typeof window !== 'undefined') {
// return browser api
return <SubtleCrypto>window.crypto.subtle;
return <SubtleCrypto>(window.crypto?.subtle);
}
throw new CryptoError(<any>{}, 'window is not defined. Must be defined in browser.')

Просмотреть файл

@ -68,7 +68,7 @@ export default class SubtleCryptoExtension extends Subtle implements ISubtleCryp
const format: string = (<any>algorithm).format;
if (isElliptic && signature.byteLength <= 64 && format) {
if (format.toUpperCase() !== 'DER') {
throw new CryptoError(algorithm, 'Only DER format supported for signature');
return Promise.reject(new CryptoError(algorithm, 'Only DER format supported for signature'));
}
// DER format needed for signature, specied in algorithm

Просмотреть файл

@ -1,6 +1,6 @@
{
"name": "verifiablecredentials-crypto-sdk-typescript-plugin",
"version": "1.1.12-preview.0",
"version": "1.1.12-preview.1",
"description": "Package for plugeable crypto based on subtle crypto.",
"repository": {
"type": "git",
@ -35,8 +35,8 @@
"typescript": "4.0.3"
},
"dependencies": {
"verifiablecredentials-crypto-sdk-typescript-keys": "1.1.12-preview.0",
"verifiablecredentials-crypto-sdk-typescript-keystore": "1.1.12-preview.0",
"verifiablecredentials-crypto-sdk-typescript-keys": "1.1.12-preview.1",
"verifiablecredentials-crypto-sdk-typescript-keystore": "1.1.12-preview.1",
"@peculiar/webcrypto": "1.1.3",
"@types/node": "14.6.2",
"base64url": "^3.0.1",

Просмотреть файл

@ -31,7 +31,7 @@ describe('CryptoFactory', () => {
it('should change a crypto suite item',() => {
const keyStore = new KeyStoreInMemory();
const factory = new CryptoFactory(keyStore, SubtleCryptoNode.getSubtleCrypto());
let factory = new CryptoFactory(keyStore, SubtleCryptoNode.getSubtleCrypto());
const algorithm = 'ES256K-tobeinvented';
const subtleCrypto = new SubtleCryptoMock();
let keyReference = new KeyReference('', 'secret');
@ -39,6 +39,11 @@ describe('CryptoFactory', () => {
let keyEncrypters: any = factory.getKeyEncrypter(algorithm, CryptoFactoryScope.All, keyReference);
expect(keyEncrypters.ID).toBeUndefined();
factory.addKeyEncrypter(algorithm, {subtleCrypto: subtleCrypto, scope: CryptoFactoryScope.All, keyStoreType: ['secret']});
keyEncrypters = factory.getKeyEncrypter(algorithm, CryptoFactoryScope.Private, new KeyReference('', 'test'));
expect(keyEncrypters.ID).toBeUndefined();
factory = new CryptoFactory(keyStore, SubtleCryptoNode.getSubtleCrypto());
factory.addKeyEncrypter(algorithm, {subtleCrypto: subtleCrypto, scope: CryptoFactoryScope.Private, keyStoreType: ['secret']});
keyEncrypters = factory.getKeyEncrypter(algorithm, CryptoFactoryScope.Private, keyReference);
expect(keyEncrypters.ID).toEqual('SubtleCryptoMock');

Просмотреть файл

@ -0,0 +1,101 @@
import { SubtleCryptoNode, CryptoFactory, CryptoFactoryScope, CryptoHelpers } from '../lib/index';
import { KeyStoreInMemory, KeyReference } from 'verifiablecredentials-crypto-sdk-typescript-keystore';
describe('CryptoHelpers', () => {
it('should return getSubtleCryptoForAlgorithm', () => {
const keyStore = new KeyStoreInMemory();
let factory = new CryptoFactory(keyStore, SubtleCryptoNode.getSubtleCrypto());
let keyReference = new KeyReference('', 'secret');
let subtle = CryptoHelpers.getSubtleCryptoForAlgorithm(factory, {name: 'RSASSA-PKCS1-V1_5'}, CryptoFactoryScope.All, keyReference);
expect(subtle.constructor.name).toEqual('Subtle');
subtle = CryptoHelpers.getSubtleCryptoForAlgorithm(factory, {name: 'ECDSA'}, CryptoFactoryScope.All, keyReference);
expect(subtle.constructor.name).toEqual('Subtle');
subtle = CryptoHelpers.getSubtleCryptoForAlgorithm(factory, {name: 'EdDSA'}, CryptoFactoryScope.All, keyReference);
expect(subtle.constructor.name).toEqual('Subtle');
subtle = CryptoHelpers.getSubtleCryptoForAlgorithm(factory, {name: 'RSA-OAEP'}, CryptoFactoryScope.All, keyReference);
expect(subtle.constructor.name).toEqual('Subtle');
subtle = CryptoHelpers.getSubtleCryptoForAlgorithm(factory, {name: 'RSA-OAEP-256'}, CryptoFactoryScope.All, keyReference);
expect(subtle.constructor.name).toEqual('Subtle');
subtle = CryptoHelpers.getSubtleCryptoForAlgorithm(factory, {name: 'AES-GCM'}, CryptoFactoryScope.All, keyReference);
expect(subtle.constructor.name).toEqual('Subtle');
subtle = CryptoHelpers.getSubtleCryptoForAlgorithm(factory, {name: 'HMAC'}, CryptoFactoryScope.All, keyReference);
expect(subtle.constructor.name).toEqual('Subtle');
subtle = CryptoHelpers.getSubtleCryptoForAlgorithm(factory, {name: 'SHA-256'}, CryptoFactoryScope.All, keyReference);
expect(subtle.constructor.name).toEqual('Subtle');
subtle = CryptoHelpers.getSubtleCryptoForAlgorithm(factory, {name: 'SHA-384'}, CryptoFactoryScope.All, keyReference);
expect(subtle.constructor.name).toEqual('Subtle');
subtle = CryptoHelpers.getSubtleCryptoForAlgorithm(factory, {name: 'SHA-512'}, CryptoFactoryScope.All, keyReference);
expect(subtle.constructor.name).toEqual('Subtle');
// Negative cases
expect(() => CryptoHelpers.getSubtleCryptoForAlgorithm(factory, {name: 'SHA1'}, CryptoFactoryScope.All, keyReference)).toThrowError(`Algorithm '{"name":"SHA1"}' is not supported`);
const testSpy = { name: 'SHA-1' };
const webCryptoToJwaSpy: jasmine.Spy = spyOn(CryptoHelpers, 'webCryptoToJwa').and.callFake(() => 'SHA-1');
expect(() => CryptoHelpers.getSubtleCryptoForAlgorithm(factory, <any>testSpy, CryptoFactoryScope.All, keyReference)).toThrowError(`Algorithm '{"name":"SHA-1"}' is not supported. Should be unreachable`);
});
it('should return jwaToWebCrypto', () => {
expect(CryptoHelpers.jwaToWebCrypto('Rs256')).toEqual({ name: 'RSASSA-PKCS1-v1_5', modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: { name: 'SHA-256'} });
expect(CryptoHelpers.jwaToWebCrypto('Rs384')).toEqual({ name: 'RSASSA-PKCS1-v1_5', modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: { name: 'SHA-384'} });
expect(CryptoHelpers.jwaToWebCrypto('Rs512')).toEqual({ name: 'RSASSA-PKCS1-v1_5', modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: { name: 'SHA-512'} });
expect(CryptoHelpers.jwaToWebCrypto('RSA-OAEP-256')).toEqual({ name: 'RSA-OAEP', modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: 'SHA-256' });
expect(CryptoHelpers.jwaToWebCrypto('RSA-OAEP')).toEqual({ name: 'RSA-OAEP', modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: 'SHA-256' });
expect(CryptoHelpers.jwaToWebCrypto('A128GCM', [0,1,2], [3,4,5])).toEqual({ name: 'AES-GCM', iv: [0, 1, 2], additionalData: [3,4,5], tagLength: 128, length: 128});
expect(CryptoHelpers.jwaToWebCrypto('A192GCM', [0,1,2], [3,4,5])).toEqual({ name: 'AES-GCM', iv: [0, 1, 2], additionalData: [3,4,5], tagLength: 128, length: 192});
expect(CryptoHelpers.jwaToWebCrypto('A256GCM', [0,1,2], [3,4,5])).toEqual({ name: 'AES-GCM', iv: [0, 1, 2], additionalData: [3,4,5], tagLength: 128, length: 256});
expect(CryptoHelpers.jwaToWebCrypto('Es256k')).toEqual({ name: 'ECDSA', namedCurve: 'secp256k1', crv: 'secp256k1', hash: { name: 'SHA-256' } });
expect(CryptoHelpers.jwaToWebCrypto('EdDSA')).toEqual({ name: 'EdDSA', namedCurve: 'ed25519', crv: 'ed25519', hash: { name: 'SHA-256' } });
expect(CryptoHelpers.jwaToWebCrypto('SHA-256')).toEqual({ hash: { name: 'SHA-256'} });
expect(CryptoHelpers.jwaToWebCrypto('SHA-384')).toEqual({ hash: { name: 'SHA-384'} });
expect(CryptoHelpers.jwaToWebCrypto('SHA-512')).toEqual({ hash: { name: 'SHA-512'} });
expect(CryptoHelpers.jwaToWebCrypto('HS256')).toEqual({ name: 'HMAC', hash: { name: 'SHA-256'} });
expect(CryptoHelpers.jwaToWebCrypto('HS384')).toEqual({ name: 'HMAC', hash: { name: 'SHA-384'} });
expect(CryptoHelpers.jwaToWebCrypto('HS512')).toEqual({ name: 'HMAC', hash: { name: 'SHA-512'} });
// Negative cases
expect(() => CryptoHelpers.jwaToWebCrypto('SHA1')).toThrowError(`Algorithm 'SHA1' is not supported`);
});
it('should return webCryptoToJwa', () => {
expect(CryptoHelpers.webCryptoToJwa({ name: 'RSASSA-PKCS1-v1_5', modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: { name: 'SHA-256'} })).toEqual('RS256');
expect(CryptoHelpers.webCryptoToJwa({ name: 'RSASSA-PKCS1-v1_5', modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: { name: 'SHA-384'} })).toEqual('RS384');
expect(CryptoHelpers.webCryptoToJwa({ name: 'RSASSA-PKCS1-v1_5', modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: { name: 'SHA-512'} })).toEqual('RS512');
expect(CryptoHelpers.webCryptoToJwa({ name: 'RSA-OAEP', modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: 'SHA-256' })).toEqual('RSA-OAEP-256');
expect(CryptoHelpers.webCryptoToJwa({ name: 'RSA-OAEP', modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: 'SHA-256' })).toEqual('RSA-OAEP-256');
expect(CryptoHelpers.webCryptoToJwa({ name: 'AES-GCM', iv: [0, 1, 2], additionalData: [3,4,5], tagLength: 128, length: 128})).toEqual('A128GCM');
expect(CryptoHelpers.webCryptoToJwa({ name: 'AES-GCM', iv: [0, 1, 2], additionalData: [3,4,5], tagLength: 128, length: 192})).toEqual('A192GCM');
expect(CryptoHelpers.webCryptoToJwa({ name: 'AES-GCM', iv: [0, 1, 2], additionalData: [3,4,5], tagLength: 128, length: 256})).toEqual('A256GCM');
expect(CryptoHelpers.webCryptoToJwa({ name: 'ECDSA', namedCurve: 'secp256k1', crv: 'secp256k1', hash: { name: 'SHA-256' } })).toEqual('ES256K');
expect(CryptoHelpers.webCryptoToJwa({ name: 'EdDSA', namedCurve: 'ed25519', crv: 'ed25519', hash: { name: 'SHA-256' } })).toEqual('EdDSA');
expect(CryptoHelpers.webCryptoToJwa({ name: 'SHA-256' })).toEqual('SHA-256');
expect(CryptoHelpers.webCryptoToJwa({ name: 'SHA-384' })).toEqual('SHA-384');
expect(CryptoHelpers.webCryptoToJwa({ name: 'SHA-512' })).toEqual('SHA-512');
expect(CryptoHelpers.webCryptoToJwa({ name: 'HMAC', hash: { name: 'SHA-256'} })).toEqual('HS256');
// Negative cases
expect(() => CryptoHelpers.webCryptoToJwa({ name: 'SHA1' })).toThrowError(`Algorithm '{"name":"SHA1"}' is not supported`);
});
it('should return getKeyImportAlgorithm', () => {
let jwk: any = {crv: 'secp256k1'};
expect(CryptoHelpers.getKeyImportAlgorithm(<any>{ name: 'RSASSA-PKCS1-v1_5', modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: { name: 'SHA-256'} }, jwk)).toEqual(<any>{ name: 'RSASSA-PKCS1-v1_5', hash: { name: 'SHA-256'} });
expect(CryptoHelpers.getKeyImportAlgorithm(<any>{ name: 'RSASSA-PKCS1-v1_5', modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: { name: 'SHA-384'} }, jwk)).toEqual(<any>{ name: 'RSASSA-PKCS1-v1_5', hash: { name: 'SHA-384'} });
expect(CryptoHelpers.getKeyImportAlgorithm(<any>{ name: 'RSASSA-PKCS1-v1_5', modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: { name: 'SHA-512'} }, jwk)).toEqual(<any>{ name: 'RSASSA-PKCS1-v1_5', hash: { name: 'SHA-512'} });
expect(CryptoHelpers.getKeyImportAlgorithm(<any>{ name: 'RSA-OAEP', modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: 'SHA-256' }, jwk)).toEqual(<any>{ name: 'RSA-OAEP', hash: 'SHA-256' });
expect(CryptoHelpers.getKeyImportAlgorithm(<any>{ name: 'RSA-OAEP', modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: 'SHA-256' }, jwk)).toEqual(<any>{ name: 'RSA-OAEP', hash: 'SHA-256' });
expect(CryptoHelpers.getKeyImportAlgorithm(<any>{ name: 'ECDSA', namedCurve: 'secp256k1', crv: 'secp256k1', hash: { name: 'SHA-256' } }, jwk)).toEqual(<any>{ name: 'ECDSA', namedCurve: 'secp256k1', hash: { name: 'SHA-256' } });
jwk = {crv: 'ed25519'};
expect(CryptoHelpers.getKeyImportAlgorithm(<any>{ name: 'EdDSA', namedCurve: 'ed25519', crv: 'ed25519', hash: { name: 'SHA-256' } }, jwk)).toEqual(<any>{ name: 'EdDSA', namedCurve: 'ed25519', hash: { name: 'SHA-256' } });
expect(CryptoHelpers.getKeyImportAlgorithm(<any>{ name: 'SHA-256' }, jwk)).toEqual(<any>{ name: 'SHA-256', hash: 'SHA-256' });
expect(CryptoHelpers.getKeyImportAlgorithm(<any>{ name: 'SHA-384' }, jwk)).toEqual(<any>{ name: 'SHA-384', hash: 'SHA-384' });
expect(CryptoHelpers.getKeyImportAlgorithm(<any>{ name: 'SHA-512' }, jwk)).toEqual(<any>{ name: 'SHA-512', hash: 'SHA-512' });
expect(CryptoHelpers.getKeyImportAlgorithm(<any>{ name: 'HMAC', hash: { name: 'SHA-256'} }, jwk)).toEqual(<any>{ name: 'HMAC', hash: { name: 'SHA-256'} });
expect(CryptoHelpers.getKeyImportAlgorithm(<any>{ name: 'AES-GCM'}, jwk)).toEqual(<any>{ name: 'AES-GCM' });
// Negative cases
expect(() => CryptoHelpers.getKeyImportAlgorithm(<any>{ name: 'SHA-1'}, jwk)).toThrowError(`Algorithm '{"name":"SHA-1"}' is not supported`);
});
it('should test getHash', () => {
expect((<any>CryptoHelpers).getHash({'test': ''})).toEqual('256');
})
});

Просмотреть файл

@ -8,6 +8,7 @@ import {CryptoFactory, SubtleCryptoExtension, SubtleCryptoNode, PairwiseKey } fr
import { KeyStoreInMemory, KeyReference } from 'verifiablecredentials-crypto-sdk-typescript-keystore';
import { PrivateKey, EcPrivateKey, OctKey, KeyContainer } from 'verifiablecredentials-crypto-sdk-typescript-keys';
import base64url from 'base64url';
import RsaPairwiseKey from '../lib/Pairwise/RsaPairwiseKey';
class Helpers {
// Make sure we generate the same pairwise key
@ -235,4 +236,13 @@ describe('PairwiseKey', () => {
expect(1).toBe(results.filter(element => element === pairwiseKey.d).length);
}
});
it('should generate RSA pairwise', async () =>{
const keyStore = new KeyStoreInMemory();
const cryptoFactory = new CryptoFactory(keyStore, new SubtleCryptoNode().getSubtleCrypto());
const alg = { name: 'RSASSA-PKCS1-v1_5', publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: { name: 'SHA-256' }};
const rsaPairwiseKey: any = await RsaPairwiseKey.generate(cryptoFactory, Buffer.from('seed'), <any>alg, 'peer');
expect(base64url.toBuffer(rsaPairwiseKey.n).byteLength).toEqual(256);
});
});

Просмотреть файл

@ -0,0 +1,73 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Subtle } from '../lib';
describe('Subtle', () => {
it('should create instance', () => {
let subtle: any = new Subtle();
expect(subtle.getSubtleCrypto().constructor.name).toEqual('Subtle');
});
it('should test algorithmTransform', () => {
let subtle = new Subtle();
let alg: any = { test: 'name' };
expect(subtle.algorithmTransform(alg)).toEqual(alg);
alg = { namedCurve: 'P-256K' };
expect(subtle.algorithmTransform(alg)).toEqual({ namedCurve: 'K-256' });
alg = { namedCurve: 'SECP256K1' };
expect(subtle.algorithmTransform(alg)).toEqual({ namedCurve: 'K-256' });
alg = { crv: 'P-256K' };
expect(subtle.algorithmTransform(alg)).toEqual({ crv: 'K-256' });
alg = { crv: 'SECP256K1' };
expect(subtle.algorithmTransform(alg)).toEqual({ crv: 'K-256' });
alg = { crv: 'XXX' };
expect(subtle.algorithmTransform(alg)).toEqual({ crv: 'XXX' });
});
it('should test keyImportTransform', () => {
let subtle: any = new Subtle();
let jwk: any = { test: 'name' };
expect(subtle.keyImportTransform(jwk)).toEqual(jwk);
jwk = { foo: 'fighters' };
expect(subtle.keyImportTransform(jwk)).toEqual(jwk);
jwk = { crv: 'P-256K' };
expect(subtle.keyImportTransform(jwk)).toEqual({ crv: 'K-256' });
jwk = { crv: 'SECP256K1' };
expect(subtle.keyImportTransform(jwk)).toEqual({ crv: 'K-256' });
jwk = { crv: 'XXX' };
expect(subtle.keyImportTransform(jwk)).toEqual({ crv: 'XXX' });
});
it('should test keyExportTransform', () => {
let subtle: any = new Subtle();
let jwk: any = { test: 'name' };
expect(subtle.keyExportTransform(jwk)).toEqual(jwk);
jwk = { foo: 'fighters' };
expect(subtle.keyExportTransform(jwk)).toEqual(jwk);
jwk = { crv: 'P-256K' };
expect(subtle.keyExportTransform(jwk)).toEqual({ crv: 'SECP256K1' });
jwk = { crv: 'K-256' };
expect(subtle.keyExportTransform(jwk)).toEqual({ crv: 'SECP256K1' });
jwk = { crv: 'SECP256K1' };
expect(subtle.keyExportTransform(jwk)).toEqual({ crv: 'SECP256K1' });
});
it('should test digest', async() => {
let subtle: any = new Subtle();
let alg: any = { name: 'SHA-256' };
expect(await subtle.digest(alg, new Uint8Array([1,2,3,4]))).toBeDefined();
});
it('should test generate/export', async() => {
let subtle = new Subtle();
let alg: any = { name: "HMAC", hash: {name: "SHA-256"} };
const key = <CryptoKey>await subtle.generateKey(alg, true, ['sign']);
expect(key).toBeDefined();
let jwk: any = await subtle.exportKey('raw', key);
expect(new Uint8Array(jwk)[0]).toBeDefined();
let cryptoKey = await subtle.importKey('raw', jwk, alg, true, ['sign']);
expect(cryptoKey).toBeDefined();
jwk = await subtle.exportKey('jwk', key);
expect(jwk.k).toBeDefined();
jwk = await subtle.importKey('jwk', jwk, alg, true, ['sign']);
expect(jwk.type).toEqual('secret');
});
});

Просмотреть файл

@ -0,0 +1,31 @@
import { SubtleCryptoBrowser, Subtle } from '../lib';
describe('SubtleCryptoBrowser', () => {
it('should create a SubtleCryptoBrowser', () => {
const subtleBrowser = new SubtleCryptoBrowser();
expect(() => subtleBrowser.getSubtleCrypto()).toThrowError('window is not defined. Must be defined in browser.');
expect(() => SubtleCryptoBrowser.getSubtleCrypto()).toThrowError('window is not defined. Must be defined in browser.');
});
it('should test algorithmTransform', () => {
let subtle = new SubtleCryptoBrowser();
let alg: any = {test: 'name'};
expect(subtle.algorithmTransform(alg)).toEqual(alg);
alg = {foo: 'fighters'};
expect(subtle.algorithmTransform(alg)).toEqual(alg);
});
it('should test keyImportTransform', () => {
let subtle: any = new SubtleCryptoBrowser();
let jwk: any = {test: 'name'};
expect(subtle.keyImportTransform(jwk)).toEqual(jwk);
jwk = {foo: 'fighters'};
expect(subtle.keyImportTransform(jwk)).toEqual(jwk);
});
it('should test keyExportTransform', () => {
let subtle: any = new SubtleCryptoBrowser();
let jwk: any = {test: 'name'};
expect(subtle.keyExportTransform(jwk)).toEqual(jwk);
jwk = {foo: 'fighters'};
expect(subtle.keyExportTransform(jwk)).toEqual(jwk);
});
});

Просмотреть файл

@ -1,9 +0,0 @@
import { SubtleCryptoBrowser } from '../lib';
describe('SubtleCryptoBrowser', () => {
it('should create a SubtleCryptoBrowser', () => {
const subtleBrowser = new SubtleCryptoBrowser();
expect(() => subtleBrowser.getSubtleCrypto()).toThrowError('window is not defined. Must be defined in browser.');
});
});

Просмотреть файл

@ -6,7 +6,8 @@ import { SubtleCryptoNode, CryptoFactory, CryptoFactoryScope, CryptoHelpers, Sub
import { KeyStoreInMemory, KeyReference } from 'verifiablecredentials-crypto-sdk-typescript-keystore';
import EcPrivateKey from 'verifiablecredentials-crypto-sdk-typescript-keys/dist/lib/ec/EcPrivateKey';
import { PublicKey } from 'verifiablecredentials-crypto-sdk-typescript-keys';
import base64url from 'base64url';
const clone = require('clone');
describe('SubtleCryptoExtension', () => {
const keyStore = new KeyStoreInMemory();
@ -15,12 +16,26 @@ describe('SubtleCryptoExtension', () => {
it('should generate an ECDSA key', async () => {
const alg = CryptoHelpers.jwaToWebCrypto('Es256K');
const key: any = <CryptoKey>await generator.generateKey(
let key: any = <CryptoKey>await generator.generateKey(
alg,
true,
['sign', 'verify']
);
const jwk = await cryptoFactory.defaultCrypto.exportKey('jwk', key.privateKey);
let jwk = await cryptoFactory.defaultCrypto.exportKey('jwk', key.privateKey);
expect(jwk.d).toBeDefined();
expect(jwk.x).toBeDefined();
expect(jwk.y).toBeDefined();
expect(jwk.kty).toEqual('EC');
// pairwise
const keyReference = new KeyReference('seed');
await keyStore.save(keyReference, base64url(Buffer.from('ABCDEFG')));
jwk = await generator.generatePairwiseKey(
alg,
'seed',
'persona',
'peer'
);
expect(jwk.d).toBeDefined();
expect(jwk.x).toBeDefined();
expect(jwk.y).toBeDefined();
@ -52,9 +67,11 @@ describe('SubtleCryptoExtension', () => {
expect(jwk.k).toBeDefined();
expect(jwk.kty).toEqual('oct');
});
it('should sign a message', async () => {
it('should sign a message with ECDSA', async () => {
const keyStore = new KeyStoreInMemory();
const factory = new CryptoFactory(keyStore, SubtleCryptoNode.getSubtleCrypto());
const crypto = SubtleCryptoNode.getSubtleCrypto();
const factory = new CryptoFactory(keyStore, crypto);
const subtle = new SubtleCryptoExtension(factory);
const alg = { name: 'ECDSA', namedCurve: 'secp256k1', hash: { name: 'SHA-256' }, format: 'DER' };
@ -63,17 +80,126 @@ describe('SubtleCryptoExtension', () => {
const payload = Buffer.from('test');
let signature = await subtle.signByKeyStore(alg, new KeyReference('key'), payload);
expect(signature.byteLength).toBeGreaterThan(65);
const publicKey = (await keyStore.get(new KeyReference('key'), {publicKeyOnly: true})).getKey<PublicKey>();
const publicKey = (await keyStore.get(new KeyReference('key'), { publicKeyOnly: true })).getKey<PublicKey>();
let result = await subtle.verifyByJwk(alg, publicKey, signature, payload);
expect(result).toBeTruthy();
// verify by passing in crypto key
const cryptoKey = await subtle.importKey('jwk', <any>jwk, alg, true, ['sign', 'verify']);
let keyReference = new KeyReference('non-saved', 'signing', '', cryptoKey)
signature = await subtle.signByKeyStore(alg, keyReference, payload);
expect(signature.byteLength).toBeGreaterThan(65);
result = await subtle.verifyByJwk(alg, publicKey, signature, payload);
expect(result).toBeTruthy();
// without DER
delete (<any>alg).format;
signature = await subtle.signByKeyStore(alg, new KeyReference('key'), payload);
expect(signature.byteLength).toBeLessThanOrEqual(64);
result = await subtle.verifyByJwk(alg, publicKey, signature, payload);
expect(result).toBeTruthy();
// without DER if the underlying algorithm provides DER
const signSpy: jasmine.Spy = spyOn(crypto, 'sign').and.callFake((alg: any) => {
(<any>alg).format = 'DER';
const r = new Uint8Array(32);
r.fill(0xff);
const s = new Uint8Array(32);
s.fill(0xff);
return Promise.resolve(SubtleCryptoExtension.toDer([r, s]));
});
signature = await subtle.signByKeyStore(alg, keyReference, payload);
expect(signature.byteLength).toBeLessThanOrEqual(64);
// negative cases
// Only DER format supported for signature
const modifiedAlg = clone(alg);
(<any>modifiedAlg).format = 'DER';
(<any>modifiedAlg).name = 'EdDSA';
signSpy.and.callFake((alg: any) => {
(<any>alg).format = 'XXX';
return Promise.resolve(new ArrayBuffer(64));
});
try {
await subtle.signByKeyStore(modifiedAlg, keyReference, payload);
fail('Should throw signature error');
} catch (exception) {
console.log('test');
expect(exception.message).toEqual('Only DER format supported for signature');
}
// Verify failure
const verifySpy: jasmine.Spy = spyOn(crypto, 'verify').and.callFake(() => {
return Promise.resolve(false);
});
result = await subtle.verifyByJwk(alg, publicKey, signature, payload);
expect(result).toBeFalsy();
const getKeyImportAlgorithmSpy: jasmine.Spy = spyOn(CryptoHelpers, 'getKeyImportAlgorithm').and.callFake((alg: any) => {
// change the algorithm will break the signature
const keyImportAlgorithm = clone(alg);
alg.name = 'EDDSA';
return keyImportAlgorithm;
});
const importSpy: jasmine.Spy = spyOn(crypto, 'importKey').and.callFake(() => {
return Promise.resolve(cryptoKey);
});
result = await subtle.verifyByJwk(alg, publicKey, signature, payload);
expect(result).toBeFalsy();
});
it('should sign a message with RSA', async () => {
const keyStore = new KeyStoreInMemory();
const crypto = SubtleCryptoNode.getSubtleCrypto();
const factory = new CryptoFactory(keyStore, crypto);
const subtle = new SubtleCryptoExtension(factory);
const alg = { name: 'RSASSA-PKCS1-v1_5', modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: { name: 'SHA-256' }};
let keyReference = new KeyReference('key');
const keyPair = <CryptoKeyPair>(await subtle.generateKey(alg, true, ['sign', 'verify']));
const publicJwk = await subtle.exportKey('jwk', keyPair.publicKey);
const privateJwk = await subtle.exportKey('jwk', keyPair.privateKey);
await keyStore.save(keyReference, <any>privateJwk);
const payload = Buffer.from('test');
let signature = await subtle.signByKeyStore(alg, keyReference, payload);
expect(signature.byteLength).toEqual(256);
const publicKey = (await keyStore.get(keyReference, { publicKeyOnly: true })).getKey<PublicKey>();
let result = await subtle.verifyByJwk(alg, publicKey, signature, payload);
expect(result).toBeTruthy();
// Verify failure
const verifySpy: jasmine.Spy = spyOn(crypto, 'verify').and.callFake(() => {
return Promise.resolve(false);
});
result = await subtle.verifyByJwk(alg, publicKey, signature, payload);
expect(result).toBeFalsy();
});
it('should encrypt a message with RSA', async () => {
const keyStore = new KeyStoreInMemory();
const crypto = SubtleCryptoNode.getSubtleCrypto();
const factory = new CryptoFactory(keyStore, crypto);
const subtle = new SubtleCryptoExtension(factory);
const alg = { name: 'RSA-OAEP', modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: 'SHA-256' };
let keyReference = new KeyReference('key');
const keyPair = <CryptoKeyPair>(await subtle.generateKey(alg, true, ['encrypt', 'decrypt']));
const publicJwk = await subtle.exportKey('jwk', keyPair.publicKey);
const privateJwk = await subtle.exportKey('jwk', keyPair.privateKey);
await keyStore.save(keyReference, <any>privateJwk);
const payload = Buffer.from('test');
let cipher = await subtle.encryptByJwk(alg, publicJwk, payload);
expect(cipher).toBeDefined();
let decrypted = await subtle.decryptByKeyStore(alg, keyReference, cipher);
expect(new Uint8Array(decrypted)).toEqual(new Uint8Array(payload));
decrypted = await subtle.decryptByJwk(alg, privateJwk, cipher);
expect(new Uint8Array(decrypted)).toEqual(new Uint8Array(payload));
});
it('should sign a message with key reference options', async () => {
const keyStore = new KeyStoreInMemory();
const factory = new CryptoFactory(keyStore, SubtleCryptoNode.getSubtleCrypto());
@ -85,7 +211,7 @@ describe('SubtleCryptoExtension', () => {
const payload = Buffer.from('test');
let signature = await subtle.signByKeyStore(alg, new KeyReference('key'), payload);
expect(signature.byteLength).toBeGreaterThan(65);
const publicKey = (await keyStore.get(new KeyReference('key'), {publicKeyOnly: true})).getKey<PublicKey>();
const publicKey = (await keyStore.get(new KeyReference('key'), { publicKeyOnly: true })).getKey<PublicKey>();
let result = await subtle.verifyByJwk(alg, publicKey, signature, payload);
expect(result).toBeTruthy();
});
@ -102,6 +228,15 @@ describe('SubtleCryptoExtension', () => {
expect(Buffer.from(der).toString('hex').toUpperCase()).toEqual(expected);
});
it('should fail to decode DER', () => {
const signature = new Uint8Array(64);
signature.fill(0xff);
expect(() => SubtleCryptoExtension.fromDer(signature)).toThrowError('No DER format to decode');
signature[0] = 0x30;
expect(() => SubtleCryptoExtension.fromDer(signature)).toThrowError('Marker on index 2 must be 0x02');
});
it('should correctly roundtrip from R||S signature to DER signature', () => {
const scenarios = [
@ -200,7 +335,7 @@ describe('SubtleCryptoExtension', () => {
// roundtrip back to R||S
const derArray = new Uint8Array(der);
const roundtripRS = SubtleCryptoExtension.fromDer(derArray);
const r = SubtleCryptoExtension.toPaddedNumber(roundtripRS[0]);
const r = SubtleCryptoExtension.toPaddedNumber(roundtripRS[0], 32);
const s = SubtleCryptoExtension.toPaddedNumber(roundtripRS[1]);
const rsHex = Buffer.from(r).toString('hex').toUpperCase() + Buffer.from(s).toString('hex').toUpperCase();
expect(rsHex).toEqual(scenario.rs, scenario.scenario + " back to R||S");

Просмотреть файл

@ -0,0 +1,38 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { SubtleCryptoNode, CryptoFactory, CryptoFactoryScope, CryptoHelpers, SubtleCryptoExtension } from '../lib';
import { KeyStoreInMemory, KeyReference } from 'verifiablecredentials-crypto-sdk-typescript-keystore';
import EcPrivateKey from 'verifiablecredentials-crypto-sdk-typescript-keys/dist/lib/ec/EcPrivateKey';
import { PublicKey, JsonWebKey } from 'verifiablecredentials-crypto-sdk-typescript-keys';
import base64url from 'base64url';
describe('SubtleCryptoNode', () => {
it('should create instance', () => {
let subtle: any = new SubtleCryptoNode();
expect(subtle.getSubtleCrypto().constructor.name).toEqual('Subtle');
expect(SubtleCryptoNode.getSubtleCrypto().constructor.name).toEqual('Subtle');
});
it('should test algorithmTransform', () => {
let subtle = new SubtleCryptoNode();
let alg: any = {test: 'name'};
expect(subtle.algorithmTransform(alg)).toEqual(alg);
alg = {foo: 'fighters'};
expect(subtle.algorithmTransform(alg)).toEqual(alg);
});
it('should test keyImportTransform', () => {
let subtle: any = new SubtleCryptoNode();
let jwk: any = {test: 'name'};
expect(subtle.keyImportTransform(jwk)).toEqual(jwk);
jwk = {foo: 'fighters'};
expect(subtle.keyImportTransform(jwk)).toEqual(jwk);
});
it('should test keyExportTransform', () => {
let subtle: any = new SubtleCryptoNode();
let jwk: any = {test: 'name'};
expect(subtle.keyExportTransform(jwk)).toEqual(jwk);
jwk = {foo: 'fighters'};
expect(subtle.keyExportTransform(jwk)).toEqual(jwk);
});
});

Просмотреть файл

@ -107,7 +107,7 @@ export default class JoseProtocol implements IPayloadProtection {
const cipher: JweToken = JweToken.fromCryptoToken(token, options);
return cipher.serialize(protocolFormat);
default:
throw new CryptoProtocolError(JoseConstants.Jose, `Serialization format '${format}' is not supported`);
throw new CryptoProtocolError(JoseConstants.Jose, `Serialization format '${protocolFormat}' is not supported`);
}
}
@ -131,7 +131,7 @@ export default class JoseProtocol implements IPayloadProtection {
const jweProtectOptions = JweToken.fromPayloadProtectionOptions(options);
return JweToken.toCryptoToken(protocolFormat, JweToken.deserialize(token, jweProtectOptions), options);
default:
throw new CryptoProtocolError(JoseConstants.Jose, `Serialization format '${format}' is not supported`);
throw new CryptoProtocolError(JoseConstants.Jose, `Serialization format '${protocolFormat}' is not supported`);
}
}

Просмотреть файл

@ -443,7 +443,7 @@ export default class JweToken implements IJweGeneralJson {
let keyEncryptionAlgorithm: string | undefined = publicKey.alg;
if (!keyEncryptionAlgorithm) {
if (publicKey.kty == KeyType.EC) {
throw new Error('EC encryption not implemented');
return Promise.reject(new Error('EC encryption not implemented'));
} else {
// Default RSA algorithm
keyEncryptionAlgorithm = JoseConstants.RsaOaep256;
@ -553,7 +553,7 @@ export default class JweToken implements IJweGeneralJson {
}
if (!contentEncryptionKey) {
throw new CryptoProtocolError(JoseConstants.Jwe, 'Cannot decrypt the content encryption key because of missing key');
return Promise.reject(new CryptoProtocolError(JoseConstants.Jwe, 'Cannot decrypt the content encryption key because of missing key'));
}
// Decrypt content

Просмотреть файл

@ -559,10 +559,10 @@ export default class JwsToken implements IJwsGeneralJson {
}
if (!alg) {
throw new CryptoProtocolError(
return Promise.reject(new CryptoProtocolError(
JoseConstants.Jws,
'Unable to validate signature as no signature algorithm has been specified in the header.'
);
));
}
const algorithm = CryptoHelpers.jwaToWebCrypto(alg);
const encodedProtected = !protectedHeader ? '' : JoseHelpers.encodeHeader(protectedHeader);

Просмотреть файл

@ -1,6 +1,6 @@
{
"name": "verifiablecredentials-crypto-sdk-typescript-protocol-jose",
"version": "1.1.12-preview.0",
"version": "1.1.12-preview.1",
"repository": {
"type": "git",
"url": "https://github.com/microsoft/VerifiableCredentials-Crypto-SDK-Typescript.git"
@ -28,6 +28,7 @@
"devDependencies": {
"@types/jasmine": "^2.8.9",
"@types/node": "14.6.2",
"clone": "^2.1.2",
"jasmine": "3.6.3",
"jasmine-reporters": "^2.3.2",
"jasmine-spec-reporter": "^6.0.0",
@ -39,11 +40,11 @@
"typescript": "4.0.3"
},
"dependencies": {
"verifiablecredentials-crypto-sdk-typescript-keys": "1.1.12-preview.0",
"verifiablecredentials-crypto-sdk-typescript-keystore": "1.1.12-preview.0",
"verifiablecredentials-crypto-sdk-typescript-plugin": "1.1.12-preview.0",
"verifiablecredentials-crypto-sdk-typescript-protocols-common": "1.1.12-preview.0",
"verifiablecredentials-crypto-sdk-typescript-plugin-cryptofactory-suites": "1.1.12-preview.0",
"verifiablecredentials-crypto-sdk-typescript-keys": "1.1.12-preview.1",
"verifiablecredentials-crypto-sdk-typescript-keystore": "1.1.12-preview.1",
"verifiablecredentials-crypto-sdk-typescript-plugin": "1.1.12-preview.1",
"verifiablecredentials-crypto-sdk-typescript-protocols-common": "1.1.12-preview.1",
"verifiablecredentials-crypto-sdk-typescript-plugin-cryptofactory-suites": "1.1.12-preview.1",
"base64url": "^3.0.1",
"typescript-map": "0.0.7",
"webcrypto-core": "1.1.8"

Просмотреть файл

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { JoseHelpers } from "../lib/index";
import { CryptoProtocolError } from 'verifiablecredentials-crypto-sdk-typescript-protocols-common';
import { KeyType } from 'verifiablecredentials-crypto-sdk-typescript-keys';
import { JoseConstants, KeyType, KeyUse } from 'verifiablecredentials-crypto-sdk-typescript-keys';
import { TSMap } from 'typescript-map';
describe('JoseHelpers', () => {
@ -46,9 +46,21 @@ describe('JoseHelpers', () => {
it(`should return the key type for 'EC' via JWA`, () => {
expect(JoseHelpers.createTypeViaJwa('ES256K')).toEqual(KeyType.EC);
});
it(`should return the key type for 'RSA' via JWA`, () => {
expect(JoseHelpers.createTypeViaJwa('RS256')).toEqual(KeyType.RSA);
});
it('should create use via jwa', () => {
expect(JoseHelpers.createUseViaJwa(JoseConstants.Rs256)).toEqual(KeyUse.Signature);
expect(JoseHelpers.createUseViaJwa(JoseConstants.Rs384)).toEqual(KeyUse.Signature);
expect(JoseHelpers.createUseViaJwa(JoseConstants.Rs512)).toEqual(KeyUse.Signature);
expect(JoseHelpers.createUseViaJwa(JoseConstants.RsaOaep)).toEqual(KeyUse.Encryption);
expect(JoseHelpers.createUseViaJwa(JoseConstants.Es256K)).toEqual(KeyUse.Signature);
expect(JoseHelpers.createUseViaJwa('EdDSA')).toEqual(KeyUse.Signature);
expect(() => JoseHelpers.createUseViaJwa(JoseConstants.AesGcm128)).toThrowError(`The algorithm 'AES-GCM' is not supported`);
expect(() => JoseHelpers.createUseViaJwa(JoseConstants.AesGcm192)).toThrowError(`The algorithm 'AES-GCM' is not supported`);
expect(() => JoseHelpers.createUseViaJwa(JoseConstants.AesGcm256)).toThrowError(`The algorithm 'AES-GCM' is not supported`);
})
});

Просмотреть файл

@ -0,0 +1,26 @@
import { TSMap } from "typescript-map";
import { JoseConstants } from "verifiablecredentials-crypto-sdk-typescript-keys";
import { KeyStoreInMemory } from "verifiablecredentials-crypto-sdk-typescript-keystore";
import { CryptoFactory, SubtleCryptoNode } from "verifiablecredentials-crypto-sdk-typescript-plugin";
import { IPayloadProtectionOptions } from "verifiablecredentials-crypto-sdk-typescript-protocols-common";
import { JoseProtocol, JoseToken } from "../lib";
describe('JoseToken', () => {
const keyStore = new KeyStoreInMemory();
const cryptoFactory = new CryptoFactory(keyStore, SubtleCryptoNode.getSubtleCrypto())
const options: IPayloadProtectionOptions = {
cryptoFactory,
options: new TSMap<string, any>([
[JoseConstants.optionProtectedHeader, new TSMap([['typ', 'JWT']])]
]),
payloadProtection: new JoseProtocol()
};
it('should instantiate a token', () => {
let token = new JoseToken(options);
expect(() => token.tokenFormat()).toThrowError('The token format is not found');
token.set(JoseConstants.tokenFormat, 'JwsCompactJson');
expect(token.tokenFormat()).toEqual('JwsCompactJson');
})
});

Просмотреть файл

@ -4,11 +4,12 @@
*--------------------------------------------------------------------------------------------*/
import { JwsToken, JoseConstants, IJwsSigningOptions, JoseProtocol } from '../lib/index'
import { IPayloadProtectionOptions } from 'verifiablecredentials-crypto-sdk-typescript-protocols-common';
import { KeyStoreInMemory, ProtectionFormat, KeyReference, KeyStoreOptions } from 'verifiablecredentials-crypto-sdk-typescript-keystore';
import { CryptoProtocolError, IPayloadProtectionOptions } from 'verifiablecredentials-crypto-sdk-typescript-protocols-common';
import { KeyStoreInMemory, ProtectionFormat, KeyReference, KeyStoreOptions, CryptoError } from 'verifiablecredentials-crypto-sdk-typescript-keystore';
import { CryptoFactory, SubtleCryptoNode, SubtleCryptoExtension } from 'verifiablecredentials-crypto-sdk-typescript-plugin';
import { OctKey, PublicKey, KeyContainer } from 'verifiablecredentials-crypto-sdk-typescript-keys';
import { TSMap } from "typescript-map";
const clone = require('clone');
describe('JwsToken', () => {
it('should create a jws token', async () => {
@ -18,20 +19,59 @@ describe('JwsToken', () => {
await keyStore.save(new KeyReference(seedReference), new OctKey('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'));
const subtle = SubtleCryptoNode.getSubtleCrypto();
const options: IJwsSigningOptions = {
algorithm: <Algorithm> { name: 'ECDSA', namedCurve: 'secp256k1', hash: { name: 'SHA-256' } },
cryptoFactory: new CryptoFactory(keyStore, subtle)
};
const generate = new SubtleCryptoExtension(options.cryptoFactory);
algorithm: <Algorithm>{ name: 'ECDSA', namedCurve: 'secp256k1', 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(new KeyReference('key'), privateKey);
const jwsToken = new JwsToken(options);
const signature = await jwsToken.sign(new KeyReference('key'), Buffer.from(payload), ProtectionFormat.JwsGeneralJson);
expect(signature).toBeDefined();
const privateKey = await generate.generatePairwiseKey(options.algorithm, seedReference, 'did:personaId', 'did:peerId');
(<any>privateKey).alg = 'ES256K';
(<any>privateKey).defaultSignAlgorithm = 'ES256K';
await keyStore.save(new KeyReference('key'), privateKey);
const jwsToken = new JwsToken(options);
const signature = await jwsToken.sign(new KeyReference('key'), Buffer.from(payload), ProtectionFormat.JwsGeneralJson);
expect(signature).toBeDefined();
expect(jwsToken.serialize()).toBeDefined();
expect(jwsToken.serialize(ProtectionFormat.JwsGeneralJson)).toBeDefined();
// negative cases
expect(() => jwsToken.serialize(<any>'aaa')).toThrow(new CryptoProtocolError(JoseConstants.Jws,`The format 'JwsGeneralJson' is not supported`));
/*
let clonedJwsToken: JwsToken = clone(jwsToken);
clonedJwsToken.signatures[0] = <any>undefined;
expect(() => jwsToken.serialize()).toThrowError(`The format 'JwsGeneralJson' is not supported`);
*/
});
it('should create a jws token in compact', async () => {
const payload = 'test payload';
const keyStore = new KeyStoreInMemory();
const seedReference = 'seed';
await keyStore.save(new KeyReference(seedReference), new OctKey('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'));
const subtle = SubtleCryptoNode.getSubtleCrypto();
const options: IJwsSigningOptions = {
algorithm: <Algorithm>{ name: 'ECDSA', namedCurve: 'secp256k1', hash: { name: 'SHA-256' } },
cryptoFactory: new CryptoFactory(keyStore, subtle)
};
const generate = new SubtleCryptoExtension(options.cryptoFactory);
const privateKey = (<CryptoKeyPair>await generate.generateKey(options.algorithm, true, ['sign', 'verify'])).privateKey;
const jwk: any = await generate.exportKey('jwk', privateKey);
(<any>jwk).alg = 'ES256K';
(<any>jwk).defaultSignAlgorithm = 'ES256K';
await keyStore.save(new KeyReference('key'), jwk);
const jwsToken = new JwsToken(options);
const signature = await jwsToken.sign(new KeyReference('key'), Buffer.from(payload), ProtectionFormat.JwsCompactJson);
expect(signature).toBeDefined();
const serialized = signature.serialize();
expect(serialized.split('.').length).toEqual(3);
const token = JwsToken.deserialize(serialized);
expect(token.payload.length).toEqual(12);
});
it('should create a jws token with JWT header', async () => {
const payload = 'test payload';
const keyStore = new KeyStoreInMemory();
@ -39,20 +79,20 @@ describe('JwsToken', () => {
await keyStore.save(new KeyReference(seedReference), new OctKey('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'));
const subtle = SubtleCryptoNode.getSubtleCrypto();
const options: IJwsSigningOptions = {
protected: new TSMap([['typ', 'JWT']]),
algorithm: <Algorithm> { name: 'ECDSA', namedCurve: 'secp256k1', hash: { name: 'SHA-256' } },
cryptoFactory: new CryptoFactory(keyStore, subtle)
};
const generate = new SubtleCryptoExtension(options.cryptoFactory);
protected: new TSMap([['typ', 'JWT']]),
algorithm: <Algorithm>{ name: 'ECDSA', namedCurve: 'secp256k1', 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(new KeyReference('key'), privateKey);
const jwsToken = new JwsToken(options);
const signature = await jwsToken.sign(new KeyReference('key'), Buffer.from(payload), ProtectionFormat.JwsGeneralJson);
expect((<TSMap<string, string>>signature.signatures[0].protected).get('typ')).toEqual('JWT');
const privateKey = await generate.generatePairwiseKey(options.algorithm, seedReference, 'did:personaId', 'did:peerId');
(<any>privateKey).alg = 'ES256K';
(<any>privateKey).defaultSignAlgorithm = 'ES256K';
await keyStore.save(new KeyReference('key'), privateKey);
const jwsToken = new JwsToken(options);
const signature = await jwsToken.sign(new KeyReference('key'), Buffer.from(payload), ProtectionFormat.JwsGeneralJson);
expect((<TSMap<string, string>>signature.signatures[0].protected).get('typ')).toEqual('JWT');
});
it('should create a jws token by means of key reference options', async () => {
const payload = 'test payload';
@ -61,36 +101,36 @@ describe('JwsToken', () => {
await keyStore.save(new KeyReference(seedReference), new OctKey('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'));
const subtle = SubtleCryptoNode.getSubtleCrypto();
const options: IJwsSigningOptions = {
algorithm: <Algorithm> { name: 'ECDSA', namedCurve: 'secp256k1', hash: { name: 'SHA-256' } },
cryptoFactory: new CryptoFactory(keyStore, subtle)
};
const generate = new SubtleCryptoExtension(options.cryptoFactory);
algorithm: <Algorithm>{ name: 'ECDSA', namedCurve: 'secp256k1', 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(new KeyReference('key'), privateKey);
const jwsToken = new JwsToken(options);
const signature = await jwsToken.sign(new KeyReference('key'), Buffer.from(payload), ProtectionFormat.JwsGeneralJson);
expect(signature).toBeDefined();
const privateKey = await generate.generatePairwiseKey(options.algorithm, seedReference, 'did:personaId', 'did:peerId');
(<any>privateKey).alg = 'ES256K';
(<any>privateKey).defaultSignAlgorithm = 'ES256K';
await keyStore.save(new KeyReference('key'), privateKey);
const jwsToken = new JwsToken(options);
const signature = await jwsToken.sign(new KeyReference('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 payload = 'The true sign of intelligence is not knowledge but imagination.';
const keyStore = new KeyStoreInMemory();
await keyStore.save(new KeyReference('seed'), new OctKey('ABEE'));
const cryptoFactory = new CryptoFactory(keyStore, SubtleCryptoNode.getSubtleCrypto())
const options: IPayloadProtectionOptions = {
cryptoFactory,
options: new TSMap<string, any>([
[JoseConstants.optionProtectedHeader, new TSMap([['typ', 'JWT']]) ]
]),
payloadProtection: new JoseProtocol()
cryptoFactory,
options: new TSMap<string, any>([
[JoseConstants.optionProtectedHeader, new TSMap([['typ', 'JWT']])]
]),
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');
const privateKey = await generator.generatePairwiseKey(alg, 'seed', 'persona', 'peer');
expect((<any>privateKey).alg).toBeUndefined();
(<any>privateKey).alg = 'RS256';
await keyStore.save(new KeyReference('key'), privateKey);
@ -116,9 +156,11 @@ describe('JwsToken', () => {
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));
let staticDeserialize = JoseProtocol.deserialize(serialized, options);
expect(deserialized).toEqual(staticDeserialize);
// validate
const publicKeyContainer = (await keyStore.get(new KeyReference('key'), new KeyStoreOptions({publicKeyOnly: true}))).getKey<PublicKey>();
const publicKeyContainer = (await keyStore.get(new KeyReference('key'), new KeyStoreOptions({ publicKeyOnly: true }))).getKey<PublicKey>();
const result = await options.payloadProtection.verify([publicKeyContainer], Buffer.from(payload), signature, options);
expect(result.result).toBeTruthy();
@ -128,130 +170,161 @@ describe('JwsToken', () => {
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));
staticDeserialize = JoseProtocol.deserialize(serialized, options);
expect(deserialized).toEqual(staticDeserialize);
// 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));
staticDeserialize = JoseProtocol.deserialize(serialized, options);
expect(deserialized).toEqual(staticDeserialize);
// negative cases
let getProtectionFormatSpy: jasmine.Spy = spyOn(<JoseProtocol>options.payloadProtection, 'getProtectionFormat').and.callFake(() => {
throw new Error('spy bad format');
});
try {
options.payloadProtection.serialize(signature, 'JwsCompactJson', options);
} catch (exception) {
expect(exception.message).toEqual('spy bad format');
}
try {
options.payloadProtection.deserialize(serialized, 'JwsCompactJson', options);
} catch (exception) {
expect(exception.message).toEqual('spy bad format');
}
getProtectionFormatSpy.and.callFake(() => {
return 'aaa';
});
try {
options.payloadProtection.serialize(signature, 'JwsCompactJson', options);
} catch (exception) {
expect(exception.message).toEqual( `Serialization format 'aaa' is not supported`);
}
try {
options.payloadProtection.deserialize(serialized, 'JwsCompactJson', options);
} catch (exception) {
expect(exception.message).toEqual(`Serialization format 'aaa' is not supported`);
}
});
// 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(new KeyReference('seed'), 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'], ['kid', 'random']])]
]),
payloadProtection: new JoseProtocol()
};
const alg = { name: 'ECDSA', namedCurve: 'secp256k1', 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(new KeyReference('key'), privateKey);
let publicKey = await keyStore.get(new KeyReference('key'));
// sign
const signature = await options.payloadProtection.sign(new KeyReference('key'), Buffer.from(payload), 'JwsGeneralJson', options);
const signatures = signature.get(JoseConstants.tokenSignatures);
expect(signatures[0].protected.get('test')).toEqual('elo');
expect(signatures[0].protected.get('kid')).toEqual('random');
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(new KeyReference('key'), new KeyStoreOptions({ publicKeyOnly: 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();
// 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(new KeyReference('seed'), 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'], ['kid', 'random']]) ]
]),
payloadProtection: new JoseProtocol()
};
const alg = { name: 'ECDSA', namedCurve: 'secp256k1', 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(new KeyReference('key'), privateKey);
let publicKey = await keyStore.get(new KeyReference('key'));
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();
// sign
const signature = await options.payloadProtection.sign(new KeyReference('key'), Buffer.from(payload), 'JwsGeneralJson', options);
const signatures = signature.get(JoseConstants.tokenSignatures);
expect(signatures[0].protected.get('test')).toEqual('elo');
expect(signatures[0].protected.get('kid')).toEqual('random');
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(new KeyReference('key'), new KeyStoreOptions({publicKeyOnly: 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();
});
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();
});
});

Просмотреть файл

@ -1,6 +1,6 @@
{
"name": "verifiablecredentials-crypto-sdk-typescript-protocols-common",
"version": "1.1.12-preview.0",
"version": "1.1.12-preview.1",
"repository": {
"type": "git",
"url": "https://github.com/microsoft/VerifiableCredentials-Crypto-SDK-Typescript.git"
@ -39,9 +39,9 @@
"typescript": "4.0.3"
},
"dependencies": {
"verifiablecredentials-crypto-sdk-typescript-keys": "1.1.12-preview.0",
"verifiablecredentials-crypto-sdk-typescript-keystore": "1.1.12-preview.0",
"verifiablecredentials-crypto-sdk-typescript-plugin": "1.1.12-preview.0",
"verifiablecredentials-crypto-sdk-typescript-keys": "1.1.12-preview.1",
"verifiablecredentials-crypto-sdk-typescript-keystore": "1.1.12-preview.1",
"verifiablecredentials-crypto-sdk-typescript-plugin": "1.1.12-preview.1",
"base64url": "3.0.1",
"typescript-map": "0.0.7",
"webcrypto-core": "1.1.8"

Просмотреть файл

@ -20,11 +20,11 @@ export default class LongFormDid {
// See https://github.com/diafygi/webcrypto-examples for examples how to use the W3C web Crypto stamdard
if (!this.crypto.builder.signingKeyReference) {
throw new Error(`No signing key reference. Use CryptoBuilder.useSigningKeyReference.`)
return Promise.reject(new Error(`No signing key reference. Use CryptoBuilder.useSigningKeyReference.`));
}
if (!this.crypto.builder.recoveryKeyReference) {
throw new Error(`No recovery key reference. Use CryptoBuilder.useRecoveryKeyReference.`)
return Promise.reject(new Error(`No recovery key reference. Use CryptoBuilder.useRecoveryKeyReference.`));
}
let signingPublic = await (await this.crypto.builder.keyStore.get(this.crypto.builder.signingKeyReference, new KeyStoreOptions({publicKeyOnly: true}))).getKey<JsonWebKey>();

Просмотреть файл

@ -1,6 +1,6 @@
{
"name": "verifiablecredentials-crypto-sdk-typescript",
"version": "1.1.12-preview.0",
"version": "1.1.12-preview.1",
"repository": {
"type": "git",
"url": "https://github.com/microsoft/VerifiableCredentials-Crypto-SDK-Typescript.git"
@ -53,15 +53,15 @@
"jsonld": "2.0.2",
"typescript-map": "0.0.7",
"uuid": "^8.3.1",
"verifiablecredentials-crypto-sdk-typescript-keys": "1.1.12-preview.0",
"verifiablecredentials-crypto-sdk-typescript-keystore": "1.1.12-preview.0",
"verifiablecredentials-crypto-sdk-typescript-plugin": "1.1.12-preview.0",
"verifiablecredentials-crypto-sdk-typescript-plugin-cryptofactory-suites": "1.1.12-preview.0",
"verifiablecredentials-crypto-sdk-typescript-plugin-elliptic": "1.1.12-preview.0",
"verifiablecredentials-crypto-sdk-typescript-plugin-factory": "1.1.12-preview.0",
"verifiablecredentials-crypto-sdk-typescript-plugin-keyvault": "1.1.12-preview.0",
"verifiablecredentials-crypto-sdk-typescript-protocol-jose": "1.1.12-preview.0",
"verifiablecredentials-crypto-sdk-typescript-protocols-common": "1.1.12-preview.0",
"verifiablecredentials-crypto-sdk-typescript-keys": "1.1.12-preview.1",
"verifiablecredentials-crypto-sdk-typescript-keystore": "1.1.12-preview.1",
"verifiablecredentials-crypto-sdk-typescript-plugin": "1.1.12-preview.1",
"verifiablecredentials-crypto-sdk-typescript-plugin-cryptofactory-suites": "1.1.12-preview.1",
"verifiablecredentials-crypto-sdk-typescript-plugin-elliptic": "1.1.12-preview.1",
"verifiablecredentials-crypto-sdk-typescript-plugin-factory": "1.1.12-preview.1",
"verifiablecredentials-crypto-sdk-typescript-plugin-keyvault": "1.1.12-preview.1",
"verifiablecredentials-crypto-sdk-typescript-protocol-jose": "1.1.12-preview.1",
"verifiablecredentials-crypto-sdk-typescript-protocols-common": "1.1.12-preview.1",
"webcrypto-core": "1.1.8"
},
"nyc": {