123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349 |
- /*
- * 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 Pkcs12KeyStore = require('./pkcs12-keystore');
- var cryptoPolicy = require('scytl-cryptopolicy');
- var elGamal = require('scytl-elgamal');
- var symmetric = require('scytl-symmetric');
- var pbkdf = require('scytl-pbkdf');
- var constants = require('./constants');
- var validator = require('./input-validator');
- var codec = require('scytl-codec');
- var forge = require('node-forge');
- module.exports = ScytlKeyStore;
- var BigInteger = forge.jsbn.BigInteger;
- /**
- * @class ScytlKeyStore
- * @classdesc Encapsulates a Scytl key store. To instantiate this object, use
- * the method {@link KeyStoreService.newScytlKeyStore}.
- * @hideconstructor
- * @param {Object|string}
- * keyStore The provided Scytl key store, as an object with expected
- * properties <b>OR</b> its JSON string representation.
- * @param {string}
- * password The password to load the Scytl key store.
- * @param {Object}
- * [options] An object containing optional arguments.
- * @param {Policy}
- * [options.policy=Default policy] The cryptographic policy to use.
- * @param {PbkdfRandomService}
- * [options.pbkdfRandomService=Created internally] The PBKDF service
- * to use.
- * @param {SymmetricCryptographyService}
- * [options.symmetricCryptographyService=Created internally] The
- * symmetric cryptography service to use.
- * @param {ElGamalCryptographyService}
- * [options.elGamalCryptographyService=Created internally] The
- * ElGamal cryptography service to use.
- * @throws {Error}
- * If the input data validation fails or the underlying key store
- * could not be loaded.
- */
- function ScytlKeyStore(keyStore, password, options) {
- var scytlKeyStore = keyStore;
- if (typeof keyStore === 'string') {
- validator.checkIsJsonString(
- keyStore, 'Scytl key store (in JSON format) to load');
- scytlKeyStore = JSON.parse(keyStore);
- }
- checkScytlKeyStore(scytlKeyStore);
- validator.checkIsType(password, 'string', 'Password to load Scytl key store');
- options = options || {};
- var policy_;
- if (options.policy) {
- policy_ = options.policy;
- } else {
- policy_ = cryptoPolicy.newIntance();
- }
- var pbkdfService;
- if (options.pbkdfService) {
- pbkdfService = options.pbkdfService;
- } else {
- pbkdfService = pbkdf.newService({policy: policy_});
- }
- var symmetricService_;
- if (options.symmetricCryptographyService) {
- symmetricService_ = options.symmetricCryptographyService;
- } else {
- symmetricService_ = symmetric.newService({policy: policy_});
- }
- var elGamalService_;
- if (options.elGamalCryptographyService) {
- elGamalService_ = options.elGamalCryptographyService;
- } else {
- elGamalService_ = elGamal.newService();
- }
- var pbkdfDeriver_ = pbkdfService.newDeriver();
- var symmetricCipher_ = symmetricService_.newCipher();
- var derivedKeys_ = {};
- var derivedPasswords_ = {};
- var pkcs12KeyStore_;
- try {
- pkcs12KeyStore_ =
- new Pkcs12KeyStore(scytlKeyStore.store, getDerivedPassword(password));
- } catch (error) {
- throw new Error('Could not load underlying PKCS12 key store; ' + error);
- }
- /**
- * Retrieves a private key stored inside the Scytl key store, given the
- * key's storage alias name and password.
- *
- * @function getPrivateKey
- * @memberof ScytlKeyStore
- * @param {string}
- * alias The storage alias name of the private key to retrieve.
- * @param {string}
- * password The storage password of the private key.
- * @returns {string} The private key, in PEM format.
- * @throws {Error}
- * If the input data validation fails or the private key could
- * not be retrieved.
- */
- this.getPrivateKey = function(alias, password) {
- validator.checkIsNonEmptyString(
- alias,
- 'Storage alias name of private key to retrieve from Scytl key store');
- validator.checkIsType(
- password, 'string',
- 'Password of private key to retrieve from Scytl key store');
- try {
- return pkcs12KeyStore_.getPrivateKey(alias, getDerivedPassword(password));
- } catch (error) {
- throw new Error(
- 'Could not retrieve private key with alias \'' + alias +
- '\' from Scytl key store; ' + error);
- }
- };
- /**
- * Retrieves a certificate stored inside the Scytl key store, given the
- * storage alias name of the certificate or that of its associated private
- * key entry.
- *
- * @function getCertificate
- * @memberof ScytlKeyStore
- * @param {string}
- * alias The storage alias name of the certificate or that of its
- * associated private key entry.
- * @returns {string} The certificate, in PEM format.
- * @throws {Error}
- * If the input data validation fails or the certificate could
- * not be retrieved.
- */
- this.getCertificate = function(alias) {
- validator.checkIsNonEmptyString(
- alias,
- 'Storage alias name of certificate to retrieve from Scytl key store,');
- try {
- return pkcs12KeyStore_.getCertificate(alias);
- } catch (error) {
- throw new Error(
- 'Could not retrieve certificate with alias \'' + alias +
- '\' from Scytl key store; ' + error);
- }
- };
- /**
- * Retrieves a certificate stored inside the Scytl key store, given the
- * certificate's subject common name.
- *
- * @function getCertificateBySubject
- * @memberof ScytlKeyStore
- * @param {string}
- * subjectCn The subject common name of the certificate.
- * @returns {string} The certificate, in PEM format.
- * @throws {Error}
- * If the input data validation fails or the certificate could
- * not be retrieved.
- */
- this.getCertificateBySubject = function(subjectCn) {
- validator.checkIsNonEmptyString(
- subjectCn,
- 'Subject common name of certificate to retrieve from Scytl key store');
- try {
- return pkcs12KeyStore_.getCertificateBySubject(subjectCn);
- } catch (error) {
- throw new Error(
- 'Could not retrieve certificate with subject common name \'' +
- subjectCn + '\' from Scytl key store; ' + error);
- }
- };
- /**
- * Retrieves a certificate chain stored inside the Scytl key store, given
- * the storage alias name of the chain's associated private key entry.
- *
- * @function getCertificateChain
- * @memberof ScytlKeyStore
- * @param {string}
- * alias The storage alias name of the chain's associated private
- * key entry.
- * @returns {string[]} The certificate chain, as an array of strings in PEM
- * format.
- * @throws {Error}
- * If the input data validation fails or the certificate chain
- * could not be retrieved.
- */
- this.getCertificateChain = function(alias) {
- validator.checkIsNonEmptyString(
- alias,
- 'Storage alias name of certificate chain to retrieve from Scytl key store');
- try {
- return pkcs12KeyStore_.getCertificateChain(alias);
- } catch (error) {
- throw new Error(
- 'Could not retrieve certificate chain with alias \'' + alias +
- '\' from Scytl key store; ' + error);
- }
- };
- /**
- * Retrieves a secret key stored inside the Scytl key store, given the key's
- * storage alias name and password.
- *
- * @function getSecretKey
- * @memberof ScytlKeyStore
- * @param {string}
- * alias The storage alias name of the secret key to retrieve.
- * @param {string}
- * password The storage password of the secret key.
- * @returns {Uint8Array} The secret key.
- * @throws {Error}
- * If the input data validation fails or the secret key could
- * not be retrieved.
- */
- this.getSecretKey = function(alias, password) {
- validator.checkIsNonEmptyString(
- alias,
- 'Storage alias name of secret key to retrieve from Scytl key store');
- validator.checkIsType(
- password, 'string',
- 'Password of secret key to retrieve from key store');
- var encryptedKey = scytlKeyStore.secrets[alias];
- checkEncryptedKey(encryptedKey, 'secret', alias);
- var secretKey = getKey(password, encryptedKey, alias, 'secret');
- return codec.binaryDecode(secretKey);
- };
- /**
- * Retrieves an ElGamal private key stored inside the Scytl key store, given
- * the key's storage alias name and password.
- *
- * @function getElGamalPrivateKey
- * @memberof ScytlKeyStore
- * @param {string}
- * alias The storage alias name of the ElGamal private key to
- * retrieve.
- * @param {string}
- * password The storage password of the ElGamal private key.
- * @returns {ElGamalPrivateKey} The ElGamal private key.
- * @throws {Error}
- * If the input data validation fails or the ElGamal private key
- * could not be retrieved.
- */
- this.getElGamalPrivateKey = function(alias, password) {
- validator.checkIsNonEmptyString(
- alias,
- 'Storage alias name of ElGamal private key to retrieve from Scytl key store');
- validator.checkIsType(
- password, 'string',
- 'Password of ElGamal private key to retrieve from Scytl key store');
- var encryptedKey = scytlKeyStore.egPrivKeys[alias];
- checkEncryptedKey(encryptedKey, 'ElGamal private', alias);
- var elGamalPrivateKeyJson =
- getKey(password, encryptedKey, alias, 'ElGamal private');
- return elGamalService_.newPrivateKey(elGamalPrivateKeyJson);
- };
- function getDerivedPassword(password) {
- if (!derivedPasswords_.hasOwnProperty(password)) {
- var derivedKeyBytes = getDerivedKey(password);
- var derivedKeyBigInteger = new BigInteger(derivedKeyBytes);
- var derivedPassword =
- derivedKeyBigInteger.toString(constants.CHARACTER_MAX_RADIX);
- derivedPasswords_[password] = derivedPassword;
- }
- return derivedPasswords_[password];
- }
- function getDerivedKey(password) {
- if (!derivedKeys_.hasOwnProperty(password)) {
- derivedKeys_[password] = pbkdfDeriver_.derive(
- password, codec.base64Decode(scytlKeyStore.salt));
- }
- return derivedKeys_[password];
- }
- function checkEncryptedKey(key, keyType, alias) {
- if (!key) {
- throw new Error(
- 'Could not find ' + keyType + ' key with storge alias name \'' +
- alias + '\'');
- }
- }
- function getKey(password, encryptedKey, alias, keyType) {
- var aliasAndKey = symmetricCipher_.init(getDerivedKey(password))
- .decrypt(codec.base64Decode(encryptedKey));
- var aliasAndKeyBinaryEncoded = codec.binaryEncode(aliasAndKey);
- var decryptedAlias = aliasAndKeyBinaryEncoded.slice(0, alias.length);
- if (decryptedAlias !== alias) {
- throw new Error(
- 'Expected decrypted alias for ' + keyType + ' key to be \'' + alias +
- '\'; Found \'' + decryptedAlias +
- '\' Check password provided to retrieve key from store.');
- }
- return aliasAndKeyBinaryEncoded.slice(alias.length, aliasAndKey.length);
- }
- function checkScytlKeyStore(scytlKeyStore) {
- validator.checkIsObjectWithProperties(
- scytlKeyStore, 'Scytl key store to load');
- if (typeof scytlKeyStore.salt === 'undefined') {
- throw new Error('Field \'salt\' is undefined in Scytl key store to load');
- }
- if (typeof scytlKeyStore.store === 'undefined') {
- throw new Error(
- 'Field \'store\' is undefined in Scytl key store to load');
- }
- }
- }
|