123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171 |
- /*
- * 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 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 forge = require('node-forge');
- module.exports = DigitalEnvelopeOpener;
- /**
- * @class DigitalEnvelopeOpener
- * @classdesc The digital envelope opener API. To instantiate this object, use
- * the method {@link DigitalEnvelopeService.newOpener}.
- * @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 DigitalEnvelopeOpener(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 symmetricCipher_ = symmetricService.newCipher();
- var macHandler_ = symmetricService.newMacHandler();
- var asymmetricDecrypter_ = asymmetricService.newDecrypter();
- var privateKey_;
- /**
- * Initializes the Digital envelope opener with the provided private key.
- *
- * @function init
- * @memberof DigitalEnvelopeOpener
- * @param {string}
- * privateKey The private key, in PEM format.
- * @returns {DigitalEnvelopeOpener} A reference to this object, to
- * facilitate method chaining.
- * @throws {Error}
- * If the input data validation fails.
- */
- this.init = function(privateKey) {
- validator.checkIsNonEmptyString(
- privateKey,
- 'private key (PEM encoded) to initialize digital envelope opener');
- privateKey_ = privateKey;
- return this;
- };
- /**
- * Opens a digital envelope and retrieves its data. The envelope can be
- * opened with any private key corresponding to a public key that was used
- * to generate the envelope. Before using this method, the opener must have
- * been initialized with a private key, via the method
- * {@link DigitalEnvelopeOpener.init}.
- *
- * @function open
- * @memberof DigitalEnvelopeOpener
- * @param {DigitalEnvelope}
- * envelope The digital envelope to open.
- * @returns {Uint8Array} The data stored in the digital envelope.
- * @throws {Error}
- * If the input data validation fails or the digital envelope
- * could not be opened.
- */
- this.open = function(envelope) {
- if (typeof privateKey_ === 'undefined') {
- throw new Error(
- 'Could not open digital envelope; Envelope opener has not been initialized with any private key');
- }
- validator.checkIsInstanceOf(
- envelope, DigitalEnvelope, 'DigitalEnvelope',
- 'Digital envelope to open');
- try {
- // Retrieve data from digital envelope.
- var encryptedData = envelope.encryptedData;
- var mac = envelope.mac;
- var encryptedSecretKeyPairs = envelope.encryptedSecretKeyPairs;
- // Retrieve asymmetric encryption of bit-wise concatenation of encryption
- // and MAC secret keys that can be decrypted with private key provided as
- // input.
- var encryptedSecretKeyPair =
- getEncryptedSecretKeyPair(encryptedSecretKeyPairs, privateKey_);
- // Asymmetrically decrypt bit-wise concatenation of encryption and MAC
- // secret keys.
- var secretKeyPair = asymmetricDecrypter_.init(privateKey_)
- .decrypt(encryptedSecretKeyPair);
- // Extract secret keys from bit-wise concatenation.
- var encryptionSecretKeyLength =
- policy_.symmetric.secretKey.encryption.lengthBytes;
- var macSecretKeyLength = policy_.symmetric.secretKey.mac.lengthBytes;
- var encryptionSecretKey =
- bitwise.slice(secretKeyPair, 0, encryptionSecretKeyLength);
- var macSecretKey = bitwise.slice(
- secretKeyPair, encryptionSecretKeyLength,
- encryptionSecretKeyLength + macSecretKeyLength);
- // Check integrity of digital envelope.
- if (!macHandler_.init(macSecretKey).verify(mac, encryptedData)) {
- throw new Error('Integrity of digital envelope could not be verified.');
- }
- return symmetricCipher_.init(encryptionSecretKey).decrypt(encryptedData);
- } catch (error) {
- throw new Error('Digital envelope could not be opened; ' + error);
- }
- };
- function getEncryptedSecretKeyPair(encryptedSecretKeyPairs, privateKeyPem) {
- // Extract modulus and public exponent from private key.
- var privateKey = forge.pki.privateKeyFromPem(privateKeyPem);
- var modulus = privateKey.n;
- var publicExponent = privateKey.e;
- for (var i = 0; i < encryptedSecretKeyPairs.length; i++) {
- var publicKeyPem = encryptedSecretKeyPairs[i].publicKey;
- var publicKey = forge.pki.publicKeyFromPem(publicKeyPem);
- if (publicKey.n.equals(modulus) && publicKey.e.equals(publicExponent)) {
- return encryptedSecretKeyPairs[i].encryptedKeyPair;
- }
- }
- throw new Error(
- 'Could not find an asymmetric encryption of the secret key concatenation that could be decrypted using the provided private key.');
- }
- }
|