/* * Copyright 2018 Scytl Secure Electronic Voting SA * * All rights reserved * * See our extended copyright notice in *file 'Copyright.txt' which is part of this source code package */ cryptolib.modules.asymmetric = cryptolib.modules.asymmetric || {}; /** @namespace asymmetric/signer */ cryptolib.modules.asymmetric.signer = function(box) { 'use strict'; box.asymmetric = box.asymmetric || {}; box.asymmetric.signer = {}; /** * A module that holds asymmetric signature functionalities. * * @exports asymmetric/signer/factory */ box.asymmetric.signer.factory = {}; var policies = { algorithm: box.policies.asymmetric.signer.algorithm, provider: box.policies.asymmetric.signer.provider, hash: box.policies.asymmetric.signer.hash, padding: box.policies.asymmetric.signer.padding, publicExponent: box.policies.asymmetric.signer.publicExponent }; var converters, exceptions, randomFactory; var f = function(box) { converters = new box.commons.utils.Converters(); exceptions = box.commons.exceptions; randomFactory = new box.primitives.securerandom.factory.SecureRandomFactory(); }; f.policies = { primitives: { secureRandom: {provider: box.policies.asymmetric.signer.secureRandom.provider} } }; cryptolib('commons', 'primitives.securerandom', f); /** * A factory class for creating a digital signer. * * @class */ box.asymmetric.signer.factory.SignerFactory = function() {}; box.asymmetric.signer.factory.SignerFactory.prototype = { getCryptoSigner: function() { try { if (policies.provider === Config.asymmetric.signer.provider.FORGE) { var secureRandom = randomFactory.getCryptoRandomBytes(); return this.getCryptoForgeSigner(secureRandom); } else { throw new exceptions.CryptoLibException( 'No suitable provider for the signer was provided.'); } } catch (error) { throw new exceptions.CryptoLibException( 'A CryptoSigner could not be obtained.', error); } }, /** * @function * @return {asymmetric/signer.CryptoForgeSigner} */ getCryptoForgeSigner: function(secureRandom) { return new CryptoForgeSigner(secureRandom); } }; /** * A digital signer. * * @class * @memberof asymmetric/signer * @param secureRandom */ function CryptoForgeSigner(secureRandom) { this.digester = null; this.secureRandom = secureRandom; try { if (policies.algorithm !== Config.asymmetric.signer.algorithm.RSA) { throw new exceptions.CryptoLibException( 'The given algorithm is not supported'); } if (policies.hash === Config.asymmetric.signer.hash.SHA256) { this.digester = forge.md.sha256.create(); } else { var errorMessage = 'Message digester type \'' + policies.hash + '\' not recognized by signer.'; throw new exceptions.CryptoLibException(errorMessage); } if (typeof(policies.padding) !== 'undefined' && policies.padding.PSS.name === Config.asymmetric.signer.padding.PSS.name) { var paddingHash; if (policies.padding.PSS.hash === Config.asymmetric.signer.padding.PSS.hash.SHA256) { paddingHash = forge.md.sha256.create(); } var paddingMgf; if (policies.padding.PSS.mask.MGF1.name === Config.asymmetric.signer.padding.PSS.mask.MGF1.name && policies.padding.PSS.mask.MGF1.hash === Config.asymmetric.signer.padding.PSS.mask.MGF1.hash.SHA256) { var mgfHash = forge.md.sha256.create(); paddingMgf = forge.mgf.mgf1.create(mgfHash); } this.padding = forge.pss.create({ md: paddingHash, mgf: paddingMgf, saltLength: policies.padding.PSS.saltLengthBytes, prng: this.secureRandom }); } } catch (error) { throw new exceptions.CryptoLibException( 'CryptoForgeSigner could not be created.', error); } } CryptoForgeSigner.prototype = { /** * Digitally signs some data. * * @function * @param privateKeyPem * Private key, as string in PEM format. * @param arrayDataBase64 * Data to sign, as an array of string in Base64 encoded * format. * @returns signature, in Base64 encoded format. */ sign: function(privateKeyPem, arrayDataBase64) { try { var privateKey = forge.pki.privateKeyFromPem(privateKeyPem); if (privateKey.e.toString() !== policies.publicExponent.toString()) { throw new exceptions.CryptoLibException( 'Private key has not been generated with the required exponent : ' + policies.publicExponent); } if (!arrayDataBase64 || arrayDataBase64.length < 1) { throw new exceptions.CryptoLibException( 'The array of data in Base64 should contain at least one element.'); } this.digester.start(); var dataB64; var data; for (var i = 0; i < arrayDataBase64.length; i++) { dataB64 = arrayDataBase64[i]; if (dataB64 === undefined || dataB64 === null) { throw new exceptions.CryptoLibException( 'The input data contained an element that was not defined or empty.'); } data = converters.base64Decode(dataB64); this.digester.update(data); } var signature = privateKey.sign(this.digester, this.padding); return converters.base64Encode(signature); } catch (error) { throw new exceptions.CryptoLibException( 'Signature could not be generated.', error); } }, /** * Verifies the digital signature of some data. * * @function * * @param signatureB64 * Signature, as string in Base64 encoded format. * @param publicKeyPem * Public key, as string in PEM format. * @param arrayDataBase64 * Data that was signed, as an array of string in Base64 * encoded format. * * @returns boolean indicating whether signature verification was * successful. */ verify: function(signatureB64, publicKeyPem, arrayDataBase64) { try { var publicKey = forge.pki.publicKeyFromPem(publicKeyPem); if (publicKey.e.toString() !== policies.publicExponent.toString()) { throw new exceptions.CryptoLibException( 'Public key has not been generated with the required exponent : ' + policies.publicExponent); } if (!arrayDataBase64 || arrayDataBase64.length < 1) { throw new exceptions.CryptoLibException( 'The array of data in Base64 should contain at least one element.'); } this.digester.start(); var dataB64; for (var i = 0; i < arrayDataBase64.length; i++) { dataB64 = arrayDataBase64[i]; if (dataB64 === undefined || dataB64 === null) { throw new exceptions.CryptoLibException( 'The input data contained an element that was not defined or empty.'); } var data = converters.base64Decode(dataB64); this.digester.update(data); } var signature = converters.base64Decode(signatureB64); return publicKey.verify( this.digester.digest().getBytes(), signature, this.padding); } catch (error) { throw new exceptions.CryptoLibException( 'Signature could not be verified.', error); } } }; };