@microsoft/useragent-sdk
Version:
SDK for building decentralized identity wallets and enterprise agents.
154 lines (131 loc) • 9.29 kB
text/typescript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import base64url from "base64url";
import JwsToken from "../../../../../src/crypto/protocols/jose/jws/JwsToken";
import KeyStoreInMemory from "../../../../../src/crypto/keyStore/KeyStoreInMemory";
import CryptoFactory from "../../../../../src/crypto/plugin/CryptoFactory";
import SubtleCryptoNode from "../../../../../src/crypto/plugin/SubtleCryptoNode";
import { ProtectionFormat } from "../../../../../src/crypto/keyStore/ProtectionFormat";
import RsaPrivateKey from "../../../../../src/crypto/keys/rsa/RsaPrivateKey";
import { KeyOperation } from "../../../../../src/crypto/keys/PublicKey";
import { IJwsSigningOptions } from "../../../../../src/crypto/protocols/jose/IJoseOptions";
import CryptoFactoryNode from "../../../../../src/crypto/plugin/CryptoFactoryNode";
import OkpPrivateKey from "../../../../../src/crypto/keys/ec/OkpPrivateKey";
import KeyContainer from "../../../../../src/crypto/keys/KeyContainer";
describe('JwsToken standard RSA', () => {
it('should run RFC 7515 A.2. Example JWS Using RSASSA-PKCS1-v1_5 SHA-256', async () => {
const payload = '{"iss":"joe",\r\n'+
' "exp":1300819380,\r\n'+
' "http://example.com/is_root":true}';
const payloadBuffer = Buffer.from([123, 34, 105, 115, 115, 34, 58, 34, 106, 111, 101, 34, 44, 13, 10,
32, 34, 101, 120, 112, 34, 58, 49, 51, 48, 48, 56, 49, 57, 51, 56, 48, 44, 13, 10,
32, 34, 104, 116, 116, 112, 58, 47, 47, 101, 120, 97,
109, 112, 108, 101, 46, 99, 111, 109, 47, 105, 115, 95, 114, 111,
111, 116, 34, 58, 116, 114, 117, 101, 125]);
expect(payload).toBeDefined();
expect(payloadBuffer).toBeDefined();
const keyStore = new KeyStoreInMemory();
const subtle = SubtleCryptoNode.getSubtleCrypto();
const options: IJwsSigningOptions = {
algorithm: <Algorithm>{name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256'},
cryptoFactory: new CryptoFactory(keyStore, subtle)
};
const privateKey = {
e: 'AQAB',
d: 'Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97IjlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYTCBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLhBOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ',
n: 'ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddxHmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMsD1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSHSXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdVMTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ',
p: '4BzEEOtIpmVdVEZNCqS7baC4crd0pqnRH_5IB3jw3bcxGn6QLvnEtfdUdiYrqBdss1l58BQ3KhooKeQTa9AB0Hw_Py5PJdTJNPY8cQn7ouZ2KKDcmnPGBY5t7yLc1QlQ5xHdwW1VhvKn-nXqhJTBgIPgtldC-KDV5z-y2XDwGUc',
q: 'uQPEfgmVtjL0Uyyx88GZFF1fOunH3-7cepKmtH4pxhtCoHqpWmT8YAmZxaewHgHAjLYsp1ZSe7zFYHj7C6ul7TjeLQeZD_YwD66t62wDmpe_HlB-TnBA-njbglfIsRLtXlnDzQkv5dTltRJ11BKBBypeeF6689rjcJIDEz9RWdc',
dp: 'BwKfV3Akq5_MFZDFZCnW-wzl-CCo83WoZvnLQwCTeDv8uzluRSnm71I3QCLdhrqE2e9YkxvuxdBfpT_PI7Yz-FOKnu1R6HsJeDCjn12Sk3vmAktV2zb34MCdy7cpdTh_YVr7tss2u6vneTwrA86rZtu5Mbr1C1XsmvkxHQAdYo0',
dq: 'h_96-mK1R_7glhsum81dZxjTnYynPbZpHziZjeeHcXYsXaaMwkOlODsWa7I9xXDoRwbKgB719rrmI2oKr6N3Do9U0ajaHF-NKJnwgjMd2w9cjz3_-kyNlxAr2v4IKhGNpmM5iIgOS1VZnOZ68m6_pbLBSp3nssTdlqvd0tIiTHU',
qi: 'IYd7DHOhrWvxkwPQsRM2tOgrjbcrfvtQJipd-DlcxyVuuM9sQLdgjVk2oy26F0EmpScGLq2MowX7fhd_QJQ3ydy5cY7YIBi87w93IKLEdfnbJtoOPLUW0ITrJReOgo1cq9SbsxYawBgfp_gh6A5603k2-ZQwVK0JKSHuLFkuQ3U',
key_ops: [KeyOperation.Sign],
alg: 'RS256'
};
const key = new RsaPrivateKey(privateKey);
await keyStore.save('key', new KeyContainer(key));
const jwsToken = new JwsToken(options);
const signature = await jwsToken.sign('key', payloadBuffer, ProtectionFormat.JwsCompactJson);
expect(signature).toBeDefined();
const encodedPayload = 'eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ';
const encodedProtected = 'eyJhbGciOiJSUzI1NiJ9';
const encodedSignature = 'cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZmh7AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjbKBYNX4BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHlb1L07Qe7K0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZESc6BfI7noOPqvhJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AXLIhWkWywlVmtVrBp0igcN_IoypGlUPQGe77Rw';
expect(base64url.encode(signature.signatures[0].signature)).toEqual(encodedSignature);
// Verify signature
const success = await signature.verify([key.getPublicKey()]);
expect(success).toBeTruthy();
const compact = signature.serialize(ProtectionFormat.JwsCompactJson);
expect(compact).toEqual(`${encodedProtected}.${encodedPayload}.${encodedSignature}`);
const general = signature.serialize(ProtectionFormat.JwsGeneralJson);
let parsed = JSON.parse(general);
expect(parsed.payload).toEqual(encodedPayload);
expect(parsed.signatures[0].signature).toEqual(encodedSignature);
expect(parsed.signatures[0].protected).toEqual(encodedProtected);
// the header should be undefined. commented out for moment to get to identiverse - todo
//expect(parsed.signatures[0].header).toBeUndefined()
const flat = signature.serialize(ProtectionFormat.JwsFlatJson);
parsed = JSON.parse(flat);
expect(parsed.payload).toEqual(encodedPayload);
expect(parsed.signature).toEqual(encodedSignature);
expect(parsed.protected).toEqual(encodedProtected);
// the header should be undefined. commented out for moment to get to identiverse - todo
// expect(parsed.header).toBeUndefined();
});
});
describe('JwsToken standard ed25519', () => {
it('should run RFC rfc8037 A.4. Ed25519 Signing', async () => {
const payload = 'Example of Ed25519 signing';
const payloadBuffer = Buffer.from(payload);
const keyStore = new KeyStoreInMemory();
const factory = new CryptoFactoryNode(keyStore, SubtleCryptoNode.getSubtleCrypto());
const subtle = factory.getMessageSigner('ed25519');
const options: IJwsSigningOptions = {
algorithm: <Algorithm>{name: 'EDDSA', hash: 'SHA-256'},
cryptoFactory: factory
};
const priv = [0x9d, 0x61, 0xb1, 0x9d , 0xef , 0xfd , 0x5a , 0x60 , 0xba , 0x84 , 0x4a , 0xf4 , 0x92 , 0xec , 0x2c , 0xc4,
0x44 , 0x49 , 0xc5 , 0x69 , 0x7b , 0x32 , 0x69 , 0x19 , 0x70 , 0x3b , 0xac , 0x03 , 0x1c , 0xae , 0x7f , 0x60];
const testPriv = [];
for (let inx=priv.length - 1; inx > 0 ; inx --) {
testPriv.push(priv[inx]);
}
const privateKey = {
'kty': 'OKP',
'alg': 'EdDSA',
'crv': 'Ed25519',
'd': base64url.encode(Buffer.from(priv)),
'x': '11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo'
};
const key = new OkpPrivateKey(privateKey);
await keyStore.save('key', new KeyContainer(key));
const jwsToken = new JwsToken(options);
const signature = await jwsToken.sign('key', payloadBuffer, ProtectionFormat.JwsCompactJson);
expect(signature).toBeDefined();
const encodedPayload = 'RXhhbXBsZSBvZiBFZDI1NTE5IHNpZ25pbmc';
const encodedProtected = 'eyJhbGciOiJFZERTQSJ9';
const encodedSignature = 'hgyY0il_MGCjP0JzlnLWG1PPOt7-09PGcvMg3AIbQR6dWbhijcNR4ki4iylGjg5BhVsPt9g7sVvpAr_MuM0KAg';
const calcSignature = base64url.encode(signature.signatures[0].signature);
expect(calcSignature).toEqual(encodedSignature);
// Verify signature
const success = await signature.verify([key.getPublicKey()]);
expect(success).toBeTruthy();
const compact = signature.serialize(ProtectionFormat.JwsCompactJson);
expect(compact).toEqual(`${encodedProtected}.${encodedPayload}.${encodedSignature}`);
const general = signature.serialize(ProtectionFormat.JwsGeneralJson);
let parsed = JSON.parse(general);
expect(parsed.payload).toEqual(encodedPayload);
expect(parsed.signatures[0].signature).toEqual(encodedSignature);
expect(parsed.signatures[0].protected).toEqual(encodedProtected);
// the header should be undefined. commented out for moment to get to identiverse - todo
//expect(parsed.signatures[0].header).toBeUndefined()
const flat = signature.serialize(ProtectionFormat.JwsFlatJson);
parsed = JSON.parse(flat);
expect(parsed.payload).toEqual(encodedPayload);
expect(parsed.signature).toEqual(encodedSignature);
expect(parsed.protected).toEqual(encodedProtected);
// the header should be undefined. commented out for moment to get to identiverse - todo
// expect(parsed.header).toBeUndefined();
});
});