Update unit tests for key vault

This commit is contained in:
Ronny Bjones 2021-01-13 10:02:48 +00:00
Родитель 6c18f93c2a
Коммит 5a9b7e4795
17 изменённых файлов: 326 добавлений и 115 удалений

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

@ -1,3 +1,8 @@
# version 1.1.12-preview.1
## Keyvault exportKey did not add the kid in the jwk.
**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",

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

@ -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": {

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

@ -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;
}
}

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

@ -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');
})
});

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

@ -46,11 +46,11 @@ 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', () =>{
it('should create an instance', () => {
const cache = new KeyStoreInMemory();
const credential = new ClientSecretCredential(tenantId, clientId, clientSecret);
let vault = 'https://example.keyvault.com';
@ -74,6 +74,7 @@ describe('KeyStoreKeyVault', () => {
expect(list[name]).toBeDefined();
// 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 {
@ -88,11 +89,11 @@ describe('KeyStoreKeyVault', () => {
const keyStore = new KeyStoreKeyVault(credential, vaultUri, cache);
try {
const secret1 = base64url.encode(name);
const secret2 = base64url.encode(name+'2');
const secret2 = base64url.encode(name + '2');
//save two versions
await keyStore.save( new KeyReference(name), secret1);
await keyStore.save( new KeyReference(name), secret2);
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();
@ -111,6 +112,7 @@ describe('KeyStoreKeyVault', () => {
}
});
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);
@ -122,18 +124,19 @@ describe('KeyStoreKeyVault', () => {
const jwk: any = await subtle.exportKey('jwk', keyPair.privateKey);
//save two versions
await keyStore.save( new KeyReference(name), jwk);
await keyStore.save(new KeyReference(name), jwk);
// save okp version
let okp = clone(jwk);
okp.kty = 'OKP';
await keyStore.save( new KeyReference(name), jwk);
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
// 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();
@ -147,17 +150,22 @@ describe('KeyStoreKeyVault', () => {
expect(key.keys.length).toEqual(1);
// negative cases
/** ROB TODO
getKeyStoreClientSpy: jasmine.Spy = spyOn(keyStore, 'getKeyStoreClient').and.callFake(() => () => {
return {
keys: {
}
}
});
*/
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 {
await (<SecretClient>keyStore.getKeyStoreClient('secret')).beginDeleteSecret(name);
if (!cleaned) {
await (<SecretClient>keyStore.getKeyStoreClient('secret')).beginDeleteSecret(name);
}
}
});
@ -173,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])}`);
@ -209,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' });
});
});

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

@ -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",

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

@ -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"
@ -39,11 +39,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"

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

@ -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"

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

@ -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": {