/* * 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 */ /* jshint node:true */ 'use strict'; var DigitalEnvelope = require('./envelope'); var EncryptedSecretKeyPair = require('./encrypted-secret-keypair'); var cryptoPolicy = require('scytl-cryptopolicy'); var asymmetric = require('scytl-asymmetric'); var symmetric = require('scytl-symmetric'); var validator = require('./input-validator'); var bitwise = require('scytl-bitwise'); var codec = require('scytl-codec'); module.exports = DigitalEnvelopeGenerator; /** * @class DigitalEnvelopeGenerator * @classdesc The digital envelope generator API. To instantiate this object, * use the method {@link DigitalEnvelopeService.newGenerator}. * @hideconstructor * @param {Object} * [options] An object containing optional arguments. * @param {Policy} * [options.policy=Default policy] The cryptographic policy to use. * @param {SymmetricCryptographyService} * [options.symmetricCryptographyService=Created internally] The * symmetric cryptography service to use. * @param {AsymmetricCryptographyService} * [options.asymmetricCryptographyService=Created internally] The * asymmetric cryptography service to use. */ function DigitalEnvelopeGenerator(options) { options = options || {}; var policy = cryptoPolicy.newInstance(); if (options.policy) { policy.symmetric = options.policy.digitalEnvelope.symmetric; policy.asymmetric.cipher = options.policy.digitalEnvelope.asymmetric.cipher; } var symmetricService; if (options.symmetricCryptographyService) { symmetricService = options.symmetricCryptographyService; } else { symmetricService = symmetric.newService({policy: policy}); } var asymmetricService; if (options.asymmetricCryptographyService) { asymmetricService = options.asymmetricCryptographyService; } else { asymmetricService = asymmetric.newService({policy: policy}); } var secretKeyGenerator_ = symmetricService.newKeyGenerator(); var symmetricCipher_ = symmetricService.newCipher(); var macHandler_ = symmetricService.newMacHandler(); var asymmetricEncrypter_ = asymmetricService.newEncrypter(); var publicKeys_; /** * Initializes the Digital envelope generator with one or more public keys. * * @function init * @memberof DigitalEnvelopeGenerator * @param {...string} * arguments A comma separated list of the public keys, each in * PEM format. * @returns {DigitalEnvelopeGenerator} A reference to this object, to * facilitate method chaining. * @throws {Error} * If the input data validation fails. */ this.init = function() { publicKeys_ = Array.prototype.slice.call(arguments); validator.checkIsNonEmptyStringArray( publicKeys_, 'public keys (PEM encoded) to initialize digital envelope generator'); return this; }; /** * Generates a digital envelope, given the data to store in the envelope. * The envelope can be generated with one or more public keys, each of whose * corresponding private key can be used to open the envelope. Before using * this method, the generator must have been initialized with at least one * public key, via the method {@link DigitalEnvelopeGenerator.init}. * * @function generate * @memberof DigitalEnvelopeGenerator * @param {Uint8Array| * string} data The data to store in the digital envelope. Data * of type string will be UTF-8 encoded. * @returns {DigitalEnvelope} The generated digital envelope. * @throws {Error} * If the input data validation fails or the digital envelope * could not be generated. */ this.generate = function(data) { if (typeof publicKeys_ === 'undefined') { throw new Error( 'Could not generate digital envelope; Envelope generator has not been initialized with any public key'); } if (typeof data === 'string') { data = codec.utf8Encode(data); } validator.checkIsInstanceOf( data, Uint8Array, 'Uint8Array', 'Data to store in digital envelope'); try { // Generate encryption secret key and use it to symmetrically encrypt // data. var encryptionSecretKey = secretKeyGenerator_.nextEncryptionKey(); var encryptedData = symmetricCipher_.init(encryptionSecretKey).encrypt(data); // Generate MAC secret key and use it to generate MAC of symmetrically // encrypted data. var macSecretKey = secretKeyGenerator_.nextMacKey(); var mac = macHandler_.init(macSecretKey).generate(encryptedData); // Construct bit-wise concatenation of encryption and MAC secret keys. var secretKeyPair = bitwise.concatenate(encryptionSecretKey, macSecretKey); // Generate list of asymmetric encryptions of the secret key // concatenation, each encryption created using a different one of the // public keys provided as input for the digital envelope generation. var encryptedSecretKeyPairs = []; for (var i = 0; i < publicKeys_.length; i++) { var publicKeyPem = publicKeys_[i]; var encryptedSecretKeyPair = asymmetricEncrypter_.init(publicKeyPem).encrypt(secretKeyPair); encryptedSecretKeyPairs.push( new EncryptedSecretKeyPair(encryptedSecretKeyPair, publicKeyPem)); } return new DigitalEnvelope(encryptedData, mac, encryptedSecretKeyPairs); } catch (error) { throw new Error('Digital envelope could not be generated. ' + error); } }; }