123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592 |
- /*
- * 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 = cryptolib.modules || {};
- /**
- * Defines the generation and opening of digital envelopes.
- */
- cryptolib.modules.digitalenvelope = function(box) {
- 'use strict';
- box.digitalenvelope = box.digitalenvelope || {};
- box.digitalenvelope.factory = {};
- var policies = {
- symmetric: {
- secretkey: {
- encryption: {
- lengthBytes: box.policies.digitalenvelope.symmetric.secretkey
- .encryption.lengthBytes
- },
- mac: {
- lengthBytes:
- box.policies.digitalenvelope.symmetric.secretkey.mac.lengthBytes
- }
- }
- }
- };
- var exceptions;
- var converters;
- var bitOperators;
- var secretKeyFactory;
- var macDigester;
- var symmetricCipher;
- var asymmetricCipher;
- var f = function(box) {
- exceptions = box.commons.exceptions;
- converters = new box.commons.utils.Converters();
- bitOperators = new box.commons.utils.BitOperators();
- secretKeyFactory = new box.symmetric.secretkey.factory.SecretKeyFactory();
- macDigester = (new box.symmetric.mac.factory.MacFactory()).create();
- symmetricCipher =
- (new box.symmetric.cipher.factory.SymmetricCipherFactory())
- .getCryptoSymmetricCipher();
- asymmetricCipher =
- (new box.asymmetric.cipher.factory.AsymmetricCipherFactory())
- .getCryptoAsymmetricCipher();
- };
- f.policies = {
- symmetric: {
- secretkey: {
- encryption: {
- lengthBytes: box.policies.digitalenvelope.symmetric.secretkey
- .encryption.lengthBytes
- },
- mac: {
- lengthBytes:
- box.policies.digitalenvelope.symmetric.secretkey.mac.lengthBytes
- },
- secureRandom: {
- provider: box.policies.digitalenvelope.symmetric.secretkey
- .secureRandom.provider
- }
- },
- cipher: {
- provider: box.policies.digitalenvelope.symmetric.cipher.provider,
- algorithm: {
- AES128_GCM: {
- name: box.policies.digitalenvelope.symmetric.cipher.algorithm
- .AES128_GCM.name,
- keyLengthBytes: box.policies.digitalenvelope.symmetric.cipher
- .algorithm.AES128_GCM.keyLengthBytes,
- tagLengthBytes: box.policies.digitalenvelope.symmetric.cipher
- .algorithm.AES128_GCM.tagLengthBytes
- }
- },
- initializationVectorLengthBytes:
- box.policies.digitalenvelope.symmetric.cipher
- .initializationVectorLengthBytes,
- secureRandom: {
- provider: box.policies.digitalenvelope.symmetric.cipher.secureRandom
- .provider
- }
- },
- mac: {
- hash: box.policies.digitalenvelope.symmetric.mac.hash,
- provider: box.policies.digitalenvelope.symmetric.mac.provider
- }
- },
- asymmetric: {
- cipher: {
- algorithm: box.policies.digitalenvelope.asymmetric.cipher.algorithm,
- secretKeyLengthBytes:
- box.policies.digitalenvelope.asymmetric.cipher.secretKeyLengthBytes,
- ivLengthBytes:
- box.policies.digitalenvelope.asymmetric.cipher.ivLengthBytes,
- tagLengthBytes:
- box.policies.digitalenvelope.asymmetric.cipher.tagLengthBytes,
- deriver: box.policies.digitalenvelope.asymmetric.cipher.deriver,
- hash: box.policies.digitalenvelope.asymmetric.cipher.hash,
- symmetricCipher:
- box.policies.digitalenvelope.asymmetric.cipher.symmetricCipher,
- provider: box.policies.digitalenvelope.asymmetric.cipher.provider,
- secureRandom: {
- provider: box.policies.digitalenvelope.asymmetric.cipher.secureRandom
- .provider
- }
- }
- }
- };
- cryptolib('commons', 'symmetric', 'asymmetric.cipher', f);
- /**
- * Defines a factory for the creation of digital envelope generators and
- * openers.
- *
- * @class DigitalEnvelopeFactory
- * @memberof digitalenvelope
- */
- box.digitalenvelope.factory.DigitalEnvelopeFactory = function() {
- };
- box.digitalenvelope.factory.DigitalEnvelopeFactory.prototype = {
- /**
- * Obtains a new digital envelope generator.
- *
- * @function getDigitalEnvelopeGenerator
- * @return {DigitalEnvelopeGenerator} the digital envelope generator.
- */
- getDigitalEnvelopeGenerator: function() {
- try {
- return new box.digitalenvelope.DigitalEnvelopeGenerator();
- } catch (error) {
- throw new exceptions.CryptoLibException(
- 'Could not obtain a digital envelope generator.', error);
- }
- },
- /**
- * Obtains new a digital envelope opener.
- *
- * @function getDigitalEnvelopeOpener
- * @return {DigitalEnvelopeOpener} the digital envelope opener.
- */
- getDigitalEnvelopeOpener: function() {
- try {
- return new box.digitalenvelope.DigitalEnvelopeOpener();
- } catch (error) {
- throw new exceptions.CryptoLibException(
- 'Could not obtain a digital envelope opener.', error);
- }
- }
- };
- /**
- * Defines a digital envelope generator.
- *
- * @class DigitalEnvelopeGenerator
- * @memberof digitalenvelope
- */
- box.digitalenvelope.DigitalEnvelopeGenerator = function() {
- };
- box.digitalenvelope.DigitalEnvelopeGenerator.prototype = {
- /**
- * Generates a digital envelope for some data. The envelope can be
- * generated with one or more public keys, each of whose corresponding
- * private key can be used to open the envelope.
- *
- * @function generate
- * @param dataBase64
- * {string} the data to store in the digital envelope, as a
- * string in Base64 encoded format.
- * @param publicKeysPem
- * {string[]} a list of one or more public keys used to
- * generate the digital envelope, as an array of strings in
- * PEM format.
- * @return {DigitalEnvelope} the generated digital envelope.
- */
- generate: function(dataBase64, publicKeysPem) {
- try {
- validateGeneratorInput(dataBase64, publicKeysPem);
- // Generate encryption secret key and use it to symmetrically encrypt
- // data.
- var secretKeyForEncryptionBase64 =
- secretKeyFactory.getCryptoSecretKeyGeneratorForEncryption()
- .generate();
- var encryptedDataBase64 =
- symmetricCipher.encrypt(secretKeyForEncryptionBase64, dataBase64);
- // Generate MAC secret key and use it to generate MAC of symmetrically
- // Base64 encryption of encrypted data.
- var secretKeyForMacBase64 =
- secretKeyFactory.getCryptoSecretKeyGeneratorForMac().generate();
- var encryptedDataBase64Base64 =
- converters.base64Encode(encryptedDataBase64);
- var macBase64 = macDigester.generate(
- secretKeyForMacBase64, [encryptedDataBase64Base64]);
- // Construct bit-wise concatenation of encryption and MAC secret keys.
- var secretKeyForEncryption =
- converters.base64Decode(secretKeyForEncryptionBase64);
- var secretKeyForMac = converters.base64Decode(secretKeyForMacBase64);
- var secretKeyConcatenation =
- secretKeyForEncryption.toString() + secretKeyForMac.toString();
- var secretKeyConcatenationBase64 =
- converters.base64Encode(secretKeyConcatenation);
- // 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 secretKeyEncryptions = [];
- for (var i = 0; i < publicKeysPem.length; i++) {
- var publicKeyPem = publicKeysPem[i];
- var encryptedSecretKeyConcatentionBase64 = asymmetricCipher.encrypt(
- publicKeyPem, secretKeyConcatenationBase64);
- secretKeyEncryptions.push(new SecretKeyEncryption(
- encryptedSecretKeyConcatentionBase64, publicKeyPem));
- }
- return new DigitalEnvelope(
- encryptedDataBase64, macBase64, secretKeyEncryptions);
- } catch (error) {
- throw new exceptions.CryptoLibException(
- 'Digital envelope could not be generated.', error);
- }
- }
- };
- /**
- * Defines a digital envelope opener.
- *
- * @class DigitalEnvelopeOpener
- * @memberof digitalenvelope
- */
- box.digitalenvelope.DigitalEnvelopeOpener = function() {
- this._getEncryptedSecretKeyConcatenation = function(
- secretKeyEncryptions, privateKeyPem) {
- // Extract modulus and public exponent from private key.
- var privateKey = box.forge.pki.privateKeyFromPem(privateKeyPem);
- var modulus = privateKey.n;
- var publicExponent = privateKey.e;
- for (var i = 0; i < secretKeyEncryptions.length; i++) {
- var publicKeyPem = secretKeyEncryptions[i].getPublicKey();
- var publicKey = box.forge.pki.publicKeyFromPem(publicKeyPem);
- if (publicKey.n.equals(modulus) && publicKey.e.equals(publicExponent)) {
- return secretKeyEncryptions[i].getEncryptedSecretKeyConcatenation();
- }
- }
- throw new exceptions.CryptoLibException(
- 'Could not find an asymmetric encryption of the secret key concatenation that could be decrypted using the private key provided.');
- };
- };
- box.digitalenvelope.DigitalEnvelopeOpener.prototype = {
- /**
- * 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.
- *
- * @function open
- * @param digitalEnvelope
- * {DigitalEnvelope} the digital envelope to open.
- * @param privateKeyPem
- * {string} the private key used to open the digital
- * envelope, as a string in PEM format.
- * @return {string} the data stored in the digital envelope, as a string
- * in Base64 encoded format.
- */
- open: function(digitalEnvelope, privateKeyPem) {
- try {
- validateOpenerInput(digitalEnvelope, privateKeyPem);
- // Retrieve data from digital envelope.
- var encryptedDataBase64 = digitalEnvelope.getEncryptedData();
- var macBase64 = digitalEnvelope.getMac();
- var secretKeyEncryptions = digitalEnvelope.getSecretKeyEncryptions();
- // Retrieve asymmetric encryption of bit-wise concatenation of
- // encryption and MAC secret keys that can be decrypted with private
- // key provided as input.
- var encryptedSecretKeyConcatenationBase64 =
- this._getEncryptedSecretKeyConcatenation(
- secretKeyEncryptions, privateKeyPem);
- // Asymmetrically decrypt bit-wise concatenation of encryption and MAC
- // secret keys.
- var secretKeyConcatenationBase64 = asymmetricCipher.decrypt(
- privateKeyPem, encryptedSecretKeyConcatenationBase64);
- var secretKeyConcatenation =
- converters.base64Decode(secretKeyConcatenationBase64);
- // Extract secret keys from bit-wise concatenation.
- var secretKeyForEncryptionLength =
- policies.symmetric.secretkey.encryption.lengthBytes;
- var secretKeyForMacLength =
- policies.symmetric.secretkey.mac.lengthBytes;
- var secretKeyForEncryption = bitOperators.extract(
- secretKeyConcatenation, 0, secretKeyForEncryptionLength);
- var secretKeyForEncryptionBase64 =
- converters.base64Encode(secretKeyForEncryption);
- var secretKeyForMac = bitOperators.extract(
- secretKeyConcatenation, secretKeyForEncryptionLength,
- secretKeyForEncryptionLength + secretKeyForMacLength);
- var secretKeyForMacBase64 = converters.base64Encode(secretKeyForMac);
- // Check integrity of digital envelope.
- var encryptedDataBase64Base64 =
- converters.base64Encode(encryptedDataBase64);
- if (!macDigester.verify(
- macBase64, secretKeyForMacBase64,
- [encryptedDataBase64Base64])) {
- throw new exceptions.CryptoLibException(
- 'Integrity of digital envelope could not be verified.');
- }
- return symmetricCipher.decrypt(
- secretKeyForEncryptionBase64, encryptedDataBase64);
- } catch (error) {
- throw new exceptions.CryptoLibException(
- 'Digital envelope could not be opened.', error);
- }
- }
- };
- /**
- * Container class for a digital envelope.
- *
- * @class DigitalEnvelope
- * @param encryptedDataBase64
- * {string} the symmetrically encrypted data, as a string in
- * Base64 encoded format.
- * @param macBase64
- * {string} the MAC of the symmetrically encrypted data, as a
- * string in Base64 encoded format.
- * @param secretKeyEncryptions
- * {SecretKeyEncryption[]} the list of asymmetric encryptions of
- * the secret key concatenation, each encryption having been
- * created using a different one of the public keys provided as
- * input to the digital envelope generator.
- * @memberof digitalenvelope
- */
- function DigitalEnvelope(
- encryptedDataBase64, macBase64, secretKeyEncryptions) {
- this.encryptedDataBase64 = encryptedDataBase64;
- this.macBase64 = macBase64;
- this.secretKeyEncryptions = secretKeyEncryptions;
- var _encryptedSecretKeyConcatsBase64 = [];
- var _publicKeysPem = [];
- for (var i = 0; i < secretKeyEncryptions.length; i++) {
- _encryptedSecretKeyConcatsBase64[i] =
- secretKeyEncryptions[i].getEncryptedSecretKeyConcatenation();
- _publicKeysPem[i] = secretKeyEncryptions[i].getPublicKey();
- }
- this._getEncryptedSecretKeyConcatsBase64 = function() {
- return _encryptedSecretKeyConcatsBase64;
- };
- this._getPublicKeysPem = function() {
- return _publicKeysPem;
- };
- }
- DigitalEnvelope.prototype = {
- /**
- * @function getEncryptedData
- * @return {string} the symmetrically encrypted data, as a string in
- * Base64 encoded format.
- */
- getEncryptedData: function() {
- return this.encryptedDataBase64;
- },
- /**
- * @function getMac
- * @return {string} the MAC of the symmetrically encrypted data, as a
- * string in Base64 encoded format.
- */
- getMac: function() {
- return this.macBase64;
- },
- /**
- * @function getSecretKeyEncryptions
- * @return {SecretKeyEncryption[]} the list of asymmetric encryptions of
- * the secret key concatenation.
- */
- getSecretKeyEncryptions: function() {
- return this.secretKeyEncryptions;
- },
- /**
- * Generates a string representation of the digital envelope.
- * <p>
- * Note: in order to permit interoperability between libraries, this
- * representation should match the equivalent representation in Java.
- *
- * @function stringify
- * @return {string} the string representation of the digital envelope.
- */
- stringify: function() {
- return JSON.stringify({
- digitalEnvelope: {
- encryptedDataBase64: this.encryptedDataBase64,
- macBase64: this.macBase64,
- encryptedSecretKeyConcatsBase64:
- this._getEncryptedSecretKeyConcatsBase64(),
- publicKeysPem: this._getPublicKeysPem()
- }
- });
- }
- };
- /**
- * Container class for the asymmetric encryption of the bit-wise
- * concatenation of the secret key used to symmetrically encrypt the data of
- * a digital envelope and the secret key used to generate an MAC for
- * checking the integrity of the symmetric encryption. The public key used
- * to perform the asymmetric encryption of the secret key concatenation is
- * also included.
- *
- * @class SecretKeyEncryption
- * @param encryptedSecretKeyConcatBase64
- * {string} the asymmetric encryption of the secret key
- * concatenation, using the public key provided as input, as a
- * string in Base64 encoded format.
- * @param publicKeyPem
- * {string} the public key, of type
- * {@link java.security.PublicKey}, used to asymmetrically
- * encrypt the secret key concatenation.
- * @memberof digitalenvelope
- */
- function SecretKeyEncryption(encryptedSecretKeyConcatBase64, publicKeyPem) {
- /**
- * @function getEncryptedSecretKeyConcatenationBase64
- * @return {string} the asymmetrically encrypted secret key
- * concatenation, as a string in Base64 encoded format.
- */
- var getEncryptedSecretKeyConcatenation = function() {
- return encryptedSecretKeyConcatBase64;
- };
- /**
- * @function getPublicKey
- * @return {string} the public key used to asymmetrically encrypt the
- * secret key concatenation, as a string in PEM format.
- */
- var getPublicKey = function() {
- return publicKeyPem;
- };
- return {
- getEncryptedSecretKeyConcatenation: getEncryptedSecretKeyConcatenation,
- getPublicKey: getPublicKey
- };
- }
- /**
- * Deserializes a digital envelope.
- *
- * @function deserialize
- * @param serializedDigitalEnvelope
- * {string} the serialized digital envelope.
- * @return {DigitalEnvelope} the digital envelope after deserialization.
- * @memberof digitalenvelope
- */
- box.digitalenvelope.deserialize = function(serializedDigitalEnvelope) {
- validateDeserializerInput(serializedDigitalEnvelope);
- var digitalEnvelopeJson =
- JSON.parse(serializedDigitalEnvelope).digitalEnvelope;
- var encryptedDataBase64 = digitalEnvelopeJson.encryptedDataBase64;
- var macBase64 = digitalEnvelopeJson.macBase64;
- var encryptedSecretKeyConcatsBase64 =
- digitalEnvelopeJson.encryptedSecretKeyConcatsBase64;
- var publicKeysPem = digitalEnvelopeJson.publicKeysPem;
- var secretKeyEncryptions = [];
- for (var i = 0; i < encryptedSecretKeyConcatsBase64.length; i++) {
- secretKeyEncryptions.push(new SecretKeyEncryption(
- encryptedSecretKeyConcatsBase64[i], publicKeysPem[i]));
- }
- return new DigitalEnvelope(
- encryptedDataBase64, macBase64, secretKeyEncryptions);
- };
- function validateGeneratorInput(dataBase64, publicKeysPem) {
- if (typeof dataBase64 === 'undefined') {
- throw new exceptions.CryptoLibException(
- 'Data provided to digital envelope generator is undefined.');
- }
- if (converters.base64Decode(dataBase64) === '') {
- throw new exceptions.CryptoLibException(
- 'Data provided to digital envelope generator is empty.');
- }
- if (typeof dataBase64 !== 'string') {
- throw new exceptions.CryptoLibException(
- 'Base64 encoded data provided to digital envelope generator is not a String.');
- }
- if (typeof publicKeysPem === 'undefined') {
- throw new exceptions.CryptoLibException(
- 'Public key array provided to digital envelope generator is undefined.');
- }
- if (!(publicKeysPem instanceof Array)) {
- throw new exceptions.CryptoLibException(
- 'Public key array provided to digital envelope generator is not of type Array.');
- }
- if (publicKeysPem.length === 0) {
- throw new exceptions.CryptoLibException(
- 'Public key array provided to digital envelope generator is empty.');
- }
- }
- function validateOpenerInput(digitalEnvelope, privateKeyPem) {
- if (typeof digitalEnvelope === 'undefined') {
- throw new exceptions.CryptoLibException(
- 'Digital envelope provided to digital envelope opener is undefined.');
- }
- if (!(digitalEnvelope instanceof Object)) {
- throw new exceptions.CryptoLibException(
- 'Digital envelope provided to digital envelope generator is not of type Object.');
- }
- if (typeof privateKeyPem === 'undefined') {
- throw new exceptions.CryptoLibException(
- 'Private key provided to digital envelope opener is undefined.');
- }
- if (typeof privateKeyPem !== 'string') {
- throw new exceptions.CryptoLibException(
- 'Private key PEM provided to digital envelope opener is not of type String.');
- }
- }
- function validateDeserializerInput(serializedDigitalEnvelope) {
- if (typeof serializedDigitalEnvelope === 'undefined') {
- throw new exceptions.CryptoLibException(
- 'Serialized object provided to digital envelope deserializer is undefined.');
- }
- if (typeof serializedDigitalEnvelope !== 'string') {
- throw new exceptions.CryptoLibException(
- 'Serialized object provided to digital envelope deserializer is not of type String.');
- }
- if (serializedDigitalEnvelope.length === 0) {
- throw new exceptions.CryptoLibException(
- 'Serialized object provided to digital envelope deserializer is empty.');
- }
- }
- };
|