/*! * algorithms/rsassa.js - RSA Signatures * * Copyright (c) 2015 Cisco Systems, Inc. See LICENSE file. */ "use strict"; var forge = require("../deps/forge.js"), CONSTANTS = require("./constants"), helpers = require("./helpers.js"), rsaUtil = require("./rsa-util.js"); function nodePSSsupport() { return helpers.nodeCrypto && helpers.nodeCrypto.constants && helpers.nodeCrypto.constants.RSA_PSS_SALTLEN_DIGEST; } // ### RSASSA-PKCS1-v1_5 function rsassaV15SignFn(name) { var md = name.replace("RS", "SHA").toLowerCase(), hash = name.replace("RS", "SHA-"); var alg = { name: "RSASSA-PKCS1-V1_5", hash: { name: hash } }; // ### Fallback Implementation -- uses forge var fallback = function(key, pdata) { // create the digest var digest = forge.md[md].create(); digest.start(); digest.update(pdata); // sign it var pki = rsaUtil.convertToForge(key, false); var sig = pki.sign(digest, "RSASSA-PKCS1-V1_5"); sig = Buffer.from(sig, "binary"); return Promise.resolve({ data: pdata, mac: sig }); }; // ### WebCryptoAPI Implementation var webcrypto = function(key, pdata) { key = rsaUtil.convertToJWK(key, false); var promise; promise = helpers.subtleCrypto.importKey("jwk", key, alg, true, ["sign"]); promise = promise.then(function(key) { return helpers.subtleCrypto.sign(alg, key, pdata); }); promise = promise.then(function(result) { var sig = Buffer.from(result); return { data: pdata, mac: sig }; }); return promise; }; var nodejs; var nodeHash = "RSA-" + hash.replace("-", ""); if (helpers.nodeCrypto && helpers.nodeCrypto.getHashes().indexOf(nodeHash) > -1) { nodejs = function(key, pdata) { var sign = helpers.nodeCrypto.createSign(nodeHash); sign.update(pdata); return { data: pdata, mac: sign.sign(rsaUtil.convertToPem(key, false)) }; }; } return helpers.setupFallback(nodejs, webcrypto, fallback); } function rsassaV15VerifyFn(name) { var md = name.replace("RS", "SHA").toLowerCase(), hash = name.replace("RS", "SHA-"); var alg = { name: "RSASSA-PKCS1-V1_5", hash: { name: hash } }; // ### Fallback implementation -- uses forge var fallback = function(key, pdata, mac) { // create the digest var digest = forge.md[md].create(); digest.start(); digest.update(pdata); digest = digest.digest().bytes(); // verify it var pki = rsaUtil.convertToForge(key, true); var sig = mac.toString("binary"); var result = pki.verify(digest, sig, "RSASSA-PKCS1-V1_5"); if (!result) { return Promise.reject(new Error("verification failed")); } return Promise.resolve({ data: pdata, mac: mac, valid: true }); }; // ### WebCryptoAPI Implementation var webcrypto = function(key, pdata, mac) { key = rsaUtil.convertToJWK(key, true); var promise; promise = helpers.subtleCrypto.importKey("jwk", key, alg, true, ["verify"]); promise = promise.then(function(key) { return helpers.subtleCrypto.verify(alg, key, mac, pdata); }); promise = promise.then(function(result) { if (!result) { return Promise.reject(new Error("verification failed")); } return { data: pdata, mac: mac, valid: true }; }); return promise; }; var nodejs; if (helpers.nodeCrypto && helpers.nodeCrypto.getHashes().indexOf(md) > -1) { nodejs = function(key, pdata, mac) { var verify = helpers.nodeCrypto.createVerify(md); verify.update(pdata); verify.end(); var result = verify.verify(rsaUtil.convertToPem(key, true), mac); if (!result) { return Promise.reject(new Error("verification failed")); } return { data: pdata, mac: mac, valid: true }; }; } return helpers.setupFallback(nodejs, webcrypto, fallback); } // ### RSA-PSS function rsassaPssSignFn(name) { var md = name.replace("PS", "SHA").toLowerCase(), hash = name.replace("PS", "SHA-"); var alg = { name: "RSA-PSS", hash: { name: hash }, saltLength: CONSTANTS.HASHLENGTH[hash] / 8 }; // ### Fallback implementation -- uses forge var fallback = function (key, pdata) { // create the digest var digest = forge.md[md].create(); digest.start(); digest.update(pdata); // setup padding var pss = forge.pss.create({ md: forge.md[md].create(), mgf: forge.mgf.mgf1.create(forge.md[md].create()), saltLength: CONSTANTS.HASHLENGTH[hash] / 8 }); // sign it var pki = rsaUtil.convertToForge(key, false); var sig = pki.sign(digest, pss); sig = Buffer.from(sig, "binary"); return Promise.resolve({ data: pdata, mac: sig }); }; // ### WebCryptoAPI Implementation var webcrypto = function(key, pdata) { key = rsaUtil.convertToJWK(key, false); var promise; promise = helpers.subtleCrypto.importKey("jwk", key, alg, true, ["sign"]); promise = promise.then(function (key) { return helpers.subtleCrypto.sign(alg, key, pdata); }); promise = promise.then(function (result) { var sig = Buffer.from(result); return { data: pdata, mac: sig }; }); return promise; }; var nodejs; var nodeHash = "RSA-" + hash.replace("-", ""); if (nodePSSsupport()) { nodejs = function(key, pdata) { var sign = helpers.nodeCrypto.createSign(nodeHash); sign.update(pdata); var sig = sign.sign({ key: rsaUtil.convertToPem(key, false), padding: helpers.nodeCrypto.constants.RSA_PKCS1_PSS_PADDING, saltLength: helpers.nodeCrypto.constants.RSA_PSS_SALTLEN_DIGEST }); return { data: pdata, mac: sig }; }; } return helpers.setupFallback(nodejs, webcrypto, fallback); } function rsassaPssVerifyFn(name) { var md = name.replace("PS", "SHA").toLowerCase(), hash = name.replace("PS", "SHA-"); var alg = { name: "RSA-PSS", hash: { name: hash }, saltLength: CONSTANTS.HASHLENGTH[hash] / 8 }; // ### Fallback implementation -- uses forge var fallback = function (key, pdata, mac) { // create the digest var digest = forge.md[md].create(); digest.start(); digest.update(pdata); digest = digest.digest().bytes(); // setup padding var pss = forge.pss.create({ md: forge.md[md].create(), mgf: forge.mgf.mgf1.create(forge.md[md].create()), saltLength: CONSTANTS.HASHLENGTH[hash] / 8 }); // verify it var pki = rsaUtil.convertToForge(key, true); var sig = mac.toString("binary"); var result = pki.verify(digest, sig, pss); if (!result) { return Promise.reject(new Error("verification failed")); } return Promise.resolve({ data: pdata, mac: mac, valid: true }); }; // ### WebCryptoAPI Implementation var webcrypto = function(key, pdata, mac) { key = rsaUtil.convertToJWK(key, true); var promise; promise = helpers.subtleCrypto.importKey("jwk", key, alg, true, ["verify"]); promise = promise.then(function (key) { return helpers.subtleCrypto.verify(alg, key, mac, pdata); }); promise = promise.then(function (result) { if (!result) { return Promise.reject(new Error("verification failed")); } return { data: pdata, mac: mac, valid: true }; }); return promise; }; var nodejs; if (nodePSSsupport()) { nodejs = function(key, pdata, mac) { var verify = helpers.nodeCrypto.createVerify(md); verify.update(pdata); verify.end(); var result = verify.verify({ key: rsaUtil.convertToPem(key, true), padding: helpers.nodeCrypto.constants.RSA_PKCS1_PSS_PADDING, saltLength: helpers.nodeCrypto.constants.RSA_PSS_SALTLEN_DIGEST }, mac); if (!result) { return Promise.reject(new Error("verification failed")); } return { data: pdata, mac: mac, valid: true }; }; } return helpers.setupFallback(nodejs, webcrypto, fallback); } // ### Public API // * [name].sign // * [name].verify var rsassa = {}; [ "PS256", "PS384", "PS512" ].forEach(function(name) { rsassa[name] = { sign: rsassaPssSignFn(name), verify: rsassaPssVerifyFn(name) }; }); [ "RS256", "RS384", "RS512" ].forEach(function(name) { rsassa[name] = { sign: rsassaV15SignFn(name), verify: rsassaV15VerifyFn(name) }; }); module.exports = rsassa;