UNPKG

cloudworker-proxy

Version:
242 lines (221 loc) 11.8 kB
"use strict"; const assert = require('assert'); const webcrypto = require('./config'); const checkAlgorithms = require('./helper').checkAlgorithms; describe("WebCrypto RSA", () => { var TEST_MESSAGE = Buffer.from("1234567890123456"); var KEYS = [ { alg: "RSASSA-PKCS1-v1_5", usages: ["sign", "verify"] }, { alg: "RSA-PSS", usages: ["sign", "verify"] }, { alg: "RSA-OAEP", usages: ["encrypt", "decrypt"] }, { alg: "RSA-OAEP", usages: ["wrapKey", "unwrapKey"] }, ]; var DIGEST = ["SHA-1", "SHA-256", "SHA-384", "SHA-512"]; var PUBLIC_EXPONENT = [new Uint8Array([3]), new Uint8Array([1, 0, 1])]; var MODULUS_LENGTH = [1024, 2048, /*4096*/]; var keys = []; context("Generate key", () => { it("Params", done => { webcrypto.subtle.generateKey( { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256", modulusLength: 1024, publicExponent: new Uint8Array([1, 0, 1]) }, false, ["sign"] ) .then(keyPair => { let pkey = keyPair.privateKey; assert.equal(pkey.type, "private"); assert.equal(pkey.algorithm.name, "RSASSA-PKCS1-v1_5"); assert.equal(pkey.algorithm.hash.name, "SHA-256"); assert.equal(pkey.algorithm.modulusLength, 1024); assert.equal(pkey.algorithm.publicExponent.length, 3); assert.equal(pkey.algorithm.publicExponent[0], 1); assert.equal(pkey.extractable, false); let pubkey = keyPair.publicKey; assert.equal(pubkey.type, "public"); assert.equal(pubkey.algorithm.name, "RSASSA-PKCS1-v1_5"); assert.equal(pubkey.algorithm.hash.name, "SHA-256"); assert.equal(pubkey.algorithm.modulusLength, 1024); assert.equal(pubkey.algorithm.publicExponent.length, 3); assert.equal(pubkey.algorithm.publicExponent[0], 1); assert.equal(pubkey.extractable, true); }) .then(done, done); }); // Keys KEYS.forEach(key => { // Digest DIGEST.forEach(digest => { // publicExponent PUBLIC_EXPONENT.forEach(pubExp => { // modulusLength MODULUS_LENGTH.forEach(modLen => { var keyName = `${key.alg} ${digest} e:${pubExp.length === 1 ? 3 : 65535} n:${modLen}` var keyTemplate = { name: keyName, privateKey: null, publicKey: null, usages: key.usages, } keys.push(keyTemplate); it(keyName, done => { var alg = { name: key.alg, hash: { name: digest }, modulusLength: modLen, publicExponent: pubExp }; webcrypto.subtle.generateKey(alg, true, key.usages) .then(keyPair => { assert.equal(!!(keyPair.privateKey || keyPair.publicKey), true, "KeyPair is empty"); // save keys for next tests keyTemplate.privateKey = keyPair.privateKey; keyTemplate.publicKey = keyPair.publicKey; return Promise.resolve(); }) .then(done, done); }).timeout(modLen === 2048 ? 4000 : 2000); }); }); }); }); }); context("Sign/Verify", () => { keys.filter(key => key.usages.some(usage => usage === "sign")) .forEach(key => { it(key.name, done => { // TODO: Add label webcrypto.subtle.sign({ name: key.privateKey.algorithm.name, saltLength: 8 }, key.privateKey, TEST_MESSAGE) .then(sig => { assert.equal(!!sig, true, "Has no signature value"); assert.notEqual(sig.length, 0, "Has empty signature value"); return webcrypto.subtle.verify({ name: key.publicKey.algorithm.name, saltLength: 8 }, key.publicKey, sig, TEST_MESSAGE) }) .then(v => assert.equal(v, true, "Signature is not valid")) .then(done, done); }); }); }); context("Encrypt/Decrypt", () => { // Select keys for encrypt keys.filter(key => key.usages.some(usage => usage === "encrypt")) .forEach(key => { // Label [null, new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8])].forEach(label => { it(`${label ? "label\t" : "no label"}\t${key.name}`, done => { webcrypto.subtle.encrypt({ name: key.privateKey.algorithm.name, label: label }, key.publicKey, TEST_MESSAGE) .catch(e => { if (e.message.indexOf("RSA_padding_add_PKCS1_OAEP_mgf1") > -1) return Promise.reject(); return Promise.reject(e); }) .then(enc => { assert.equal(!!enc, true, "Has no encrypted value"); assert.notEqual(enc.length, 0, "Has empty encrypted value"); return webcrypto.subtle.decrypt({ name: key.publicKey.algorithm.name, label: label }, key.privateKey, enc) }) .then(dec => { assert.equal(Buffer.from(dec).toString(), TEST_MESSAGE.toString(), "Decrypted message is not valid") }) .then(done, done); }); }); }); }); context("Export/Import", () => { // Keys keys.forEach(key => { // Format ["jwk", "spki", "pkcs8"].forEach(format => { it(`${format}\t${key.name}`, done => { var promise = Promise.resolve(); // Check public and private keys [key.privateKey, key.publicKey].forEach(_key => { if ((format === "spki" && _key.type === "public") || (format === "pkcs8" && _key.type === "private") || format === "jwk") promise = promise.then(() => { return webcrypto.subtle.exportKey(format, _key) .then(jwk => { assert.equal(!!jwk, true, "Has no jwk value"); // TODO assert JWK params return webcrypto.subtle.importKey(format, jwk, _key.algorithm, true, _key.usages); }) }) .then(k => { assert.equal(!!k, true, "Imported key is empty"); checkAlgorithms(_key.algorithm, k.algorithm); }); }); promise.then(done, done); }); }); }); }); context("Wrap/Unwrap", () => { var aesKeys = [{}, {}, {}]; before(done => { var promise = Promise.resolve(); [128, 192, 256].forEach((length, index) => { var keyTemplate = aesKeys[index]; promise = promise.then(() => { return webcrypto.subtle.generateKey({ name: "AES-CBC", length: length }, true, ["encrypt", "decrypt"]) .then(key => { keyTemplate.key = key; // return Promise.resolve(); }); }); }); promise.then(done, done); }); // Keys keys.filter(key => key.usages.some(usage => "wrapKey" === usage)) .forEach(key => { // AES keys aesKeys.forEach(aes => { // Format ["raw"].forEach(format => { [null, new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8])].forEach(label => { it(`${label ? "label\t" : "no label"}\t${key.name}`, done => { var _alg = { name: key.publicKey.algorithm.name, label: label }; webcrypto.subtle.wrapKey(format, aes.key, key.publicKey, _alg) .catch(e => { if (e.message.indexOf("RSA_padding_add_PKCS1_OAEP_mgf1") > -1) return Promise.reject(); return Promise.reject(e); }) .then(enc => { assert.equal(!!enc, true, "Has no encrypted value"); return webcrypto.subtle.unwrapKey(format, enc, key.privateKey, _alg, aes.key.algorithm, true, aes.key.usages); }) .then(key => { assert.equal(!!key, true, "Has no unwrapped key"); }) .then(done, done); }); }); }); }); }); }); context("Algorithm parameters", () => { it("modulusLength/publicExponent for imported key", (done) => { webcrypto.subtle.generateKey({ name: "RSASSA-PKCS1-v1_5", hash: "SHA-1", publicExponent: new Uint8Array([1, 0, 1]), modulusLength: 2048 }, true, ["sign", "verify"]) .then((keyPair) => { const key = keyPair.privateKey; assert.equal(ArrayBuffer.isView(key.algorithm.publicExponent), true); assert.equal(key.algorithm.publicExponent.length === 3, true); assert.equal(key.algorithm.modulusLength === 2048, true); assert.equal(key.type === "private", true); return webcrypto.subtle.exportKey("pkcs8", key); }) .then((raw) => { return webcrypto.subtle.importKey("pkcs8", raw, { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" }, false, ["sign"]); }) .then((key) => { assert.equal(ArrayBuffer.isView(key.algorithm.publicExponent), true); assert.equal(key.algorithm.publicExponent.length === 3, true); assert.equal(key.algorithm.modulusLength === 2048, true); assert.equal(key.type === "private", true); }) .then(done, done); }); }); });