/* * 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 forge = require('node-forge'); var BigInteger = forge.jsbn.BigInteger; /** * Input data validation utility for this module. Only intended for internal * use. */ module.exports = { /** * Checks if a value is defined. * * @function checkIsDefined * @private * @param {Object} * value The value to check. * @param {string} * label The error message label for the value. * @throws {TypeError} * If the value is undefined. */ checkIsDefined: function(value, label) { if (typeof value === 'undefined') { throw new TypeError(label + ' is undefined.'); } }, /** * Checks if a value is not null. * * @function checkIsNotNull * @private * @param {Object} * value The value to check. * @param {string} * label The error message label for the value. * @throws {TypeError} * If the value is null. */ checkIsNotNull: function(value, label) { if (value === null) { throw new TypeError(label + ' is null.'); } }, /** * Checks if a value is defined and not null. * * @function checkIsDefinedAndNotNull * @private * @param {Object} * value The value to check. * @param {string} * label The error message label for the value. * @throws {TypeError} * If the value is not defined or it is null. */ checkIsDefinedAndNotNull: function(value, label) { this.checkIsDefined(value, label); this.checkIsNotNull(value, label); }, /** * Checks if a value is of an expected type. * * @function checkIsType * @private * @param {Object} * value The value to check. * @param {string} * type The expected type of the value. * @param {string} * label The error message label for the value. * @throws {TypeError} * If the value is not of the expected type. */ checkIsType: function(value, type, label) { var typeFound = typeof value; if (typeFound !== type) { throw new TypeError( 'Expected ' + label + ' to have type \'' + type + '\' ; Found: \'' + typeFound + '\''); } }, /** * Checks if a value is an instance of an Object. * * @function checkIsInstanceOf * @private * @param {Object} * value The value to check. * @param {Object} * obj The Object to check against. * @param {string} * objName The Object name, for error handling purposes. * @param {string} * label The error message label for the value. * @throws {TypeError} * If the object is undefined, null, or it is not an instance of * the Object. */ checkIsInstanceOf: function(value, obj, objName, label) { this.checkIsDefinedAndNotNull(value, label); if (!(value instanceof obj)) { throw new TypeError(label + ' is not an instance of Object ' + objName); } }, /** * Checks if a value is an object. * * @function checkIsObject * @private * @param {Object} * value The value to check. * @param {string} * label The error message label for the value. * @throws {TypeError} * If the value is not an object. */ checkIsObject: function(value, label) { if (typeof value !== 'object') { throw new TypeError(label + ' is not an object.'); } }, /** * Checks if a value is an object and has properties. * * @function checkIsObjectWithProperties * @private * @param {Object} * value The value to check. * @param {string} * label The error message label for the value. * @throws {TypeError} * If the value is not an object or it has no properties. */ checkIsObjectWithProperties: function(value, label) { this.checkIsDefinedAndNotNull(value, label); this.checkIsObject(value, label); if (!Object.getOwnPropertyNames(value).length) { throw new TypeError(label + ' does not have any properties.'); } }, /** * Checks if a value is a function. * * @function checkIsFunction * @private * @param {Object} * value The value to check. * @param {string} * label The error message label for the value. * @throws {TypeError} * If the value is not a function. */ checkIsFunction: function(value, label) { if (typeof value !== 'function') { throw new TypeError(label + ' is not a function.'); } }, /** * Checks if a value is a function and has properties. * * @function checkIsFunctionWithProperties * @private * @param {Object} * value The value to check. * @param {string} * label The error message label for the value. * @throws {TypeError} * If the value is not a function or it has no properties. */ checkIsFunctionWithProperties: function(value, label) { this.checkIsDefinedAndNotNull(value, label); this.checkIsFunction(value, label); if (!Object.getOwnPropertyNames(value).length) { throw new TypeError(label + ' does not have any properties.'); } }, /** * Checks if a value is a positive number. * * @function checkIsPositiveNumber * @private * @param {Object} * value The value to check. * @param {string} * label The error message label for the value. * @throws {TypeError} * If the value is not a positive number. */ checkIsPositiveNumber: function(value, label) { this.checkIsType(value, 'number', label); if (value === 0 || value < 0) { throw new TypeError(label + ' must be at least 1. Found: ' + value); } }, /** * Checks if a value is an Array object. * * @function checkIsArray * @private * @param {Array} * array The value to check. * @param {string} * label The error message label for the value. * @throws {TypeError} * If the value is not an {Array} object. */ checkIsArray: function(value, label) { this.checkIsDefinedAndNotNull(value, 'Array of ' + label); if (value.constructor !== Array) { throw new TypeError('Array of ' + label + ' is not of type Array.'); } }, /** * Checks if a value is a non-empty Array object. * * @function checkIsNonEmptyArray * @private * @param {Array} * array The value to check. * @param {string} * label The error message label for the value. * @throws {TypeError} * If the value is not a non-empty {Array} object. */ checkIsNonEmptyArray: function(value, label) { this.checkIsArray(value, label); if (value.length < 1) { throw new TypeError('Array of ' + label + ' is empty.'); } }, /** * Checks if two arrays contain the same number of values. * * @function checkArrayLengthsEqual * @private * @param {Array} * array1 The first array to check. * @param {string} * label1 The error message label for the first array. * @param {Array} * array2 The second array to check. * @param {string} * label2 The error message label for the second array. * @throws {TypeError} * If one or both arrays are not valid or they do not contain * the same number of values. */ checkArrayLengthsEqual: function(array1, label1, array2, label2) { this.checkIsArray(array1, label1); this.checkIsArray(array2, label2); var array1Length = array1.length; var array2Length = array2.length; if (array1Length !== array2Length) { throw new TypeError( 'Expected array length of ' + label1 + ' to equal array length of ' + label2 + ': ' + array2Length + ' ; Found: ' + array1Length); } }, /** * Checks if a value is a non-empty string. * * @function checkIsNonEmptyString * @private * @param {string} * value The value to check. * @param {string} * label The error message label for the value. * @throws {TypeError} * If the value is not a non-empty string. */ checkIsNonEmptyString: function(value, label) { this.checkIsType(value, 'string', label); if (value.length === 0) { throw new TypeError(label + ' is empty.'); } }, /** * Checks if a value is an array of strings. * * @function checkIsStringArray * @private * @param {string[]} * value The value to check. * @param {string} * label The error message label for the value. * @throws {TypeError} * If value is not an array of strings. */ checkIsStringArray: function(value, label) { this.checkIsNonEmptyArray(value, label); for (var i = 0; i < value.length; i++) { this.checkIsType(value[i], 'string', 'Element ' + i + ' of ' + label); } }, /** * Checks if a value is an array of non-empty strings. * * @function checkIsNonEmptyStringArray * @private * @param {string[]} * value The value to check. * @param {string} * label The error message label for the value. * @throws {TypeError} * If value is not an array of non-empty strings. */ checkIsNonEmptyStringArray: function(value, label) { this.checkIsNonEmptyArray(value, label); for (var i = 0; i < value.length; i++) { this.checkIsNonEmptyString(value[i], 'Element ' + i + ' of ' + label); } }, /** * Checks if a value is a BigInteger object. * * @function checkIsBigInteger * @private * @param {Object} * value The value to check. * @param {string} * label The error message label for the value. * @throws {TypeError} * If the value is not a BigInteger object. */ checkIsBigInteger: function(value, label) { this.checkIsDefinedAndNotNull(value, label); if (typeof value.abs === 'undefined') { throw new TypeError(label + ' is not a BigInteger object.'); } }, /** * Checks if a value is a positive BigInteger object. * * @function checkIsPositiveBigInteger * @private * @param {Object} * value The value to check. * @param {string} * label The error message label for the value. * @throws {TypeError} * If the value is not a positive {BigInteger} object. */ checkIsPositiveBigInteger: function(value, label) { this.checkIsBigInteger(value, label); if (value.compareTo(BigInteger.ONE) < 0) { throw new TypeError(label + ' must not be less than 1; Found: ' + value); } }, /** * Checks if a value is a valid JSON object. * * @function checkIsJsonObject * @private * @param {Object} * value The value to check. * @param {string} * label The error message label for the value. * @throws {TypeError} * If the value is a valid JSON object. */ checkIsJsonObject: function(value, label) { try { JSON.parse(value); } catch (error) { throw new TypeError(label + ' is not a valid JSON object.'); } }, /** * Checks if a value is a valid JSON string. * * @function checkIsJsonString * @private * @param {Object} * value The value to check. * @param {string} * label The error message label for the value. * @throws {TypeError} * If the value is a valid JSON string. */ checkIsJsonString: function(value, label) { this.checkIsNonEmptyString(value, label); this.checkIsJsonObject(value, label); }, /** * Checks the validity of a ZpGroupElements object. * * @function checkZpGroupElement * @private * @param {ZpGroupElement} * element The object to check. * @param {string} * label The error message label for the object. * @param {Zpsubgroup} * [group] Optional parameter pertaining to a Zp subgroup. If * this option is provided, the modulus and order of the element * will be checked against those of the group. * @throws {TypeError} * If the value is not a valid ZpGroupElement object. */ checkZpGroupElement: function(element, label, group) { this.checkIsObjectWithProperties(element, label); if (typeof group !== 'undefined') { var groupP = group.p; var groupQ = group.q; var elementP = element.p; if (!elementP.equals(groupP)) { throw new TypeError( 'Expected modulus p of ' + label + ' to equal modulus of Zp subgroup provided as input: ' + groupP + ' ; Found: ' + elementP); } var elementQ = element.q; if (!elementQ.equals(groupQ)) { throw new TypeError( 'Expected order q of ' + label + ' to equal order of Zp subgroup provided as input: ' + groupQ + ' ; Found: ' + elementQ); } } }, /** * Checks the validity of an Exponent object. * * @function checkExponent * @private * @param {Exponent} * exponent The object to check. * @param {string} * label The error message label for the object. * @param {Zpsubgroup} * [q] Optional parameter pertaining to a Zp subgroup order. If * this option is provided, the order of the exponent will be * checked against that order. * @throws {TypeError} * If the value is not a valid Exponent value. */ checkExponent: function(exponent, label, q) { this.checkIsObjectWithProperties(exponent, label); if (typeof q !== 'undefined') { var exponentQ = exponent.q; if (!exponentQ.equals(q)) { throw new TypeError( 'Expected order q of ' + label + ' to equal order of Zp subgroup provided as input: ' + q + ' ; Found: ' + exponentQ); } } }, /** * Checks the validity of an array of ZpGroupElement objects. * * @function checkZpGroupElements * @private * @param {ZpGroupElement[]} * elements The array to check. * @param {string} * label The error message label for the array. * @param {Zpsubgroup} * [group] Optional parameter pertaining to a Zp subgroup. If * this option is provided, the modulus and order of each element * will be checked against those of the group. * @throws {TypeError} * If the array of ZpGroupElement objects is not valid. */ checkZpGroupElements: function(elements, label, group) { this.checkIsNonEmptyArray(elements, label); for (var i = 0; i < elements.length; i++) { this.checkZpGroupElement( elements[i], 'element ' + i + ' of ' + label, group); } }, /** * Checks the validity of an array of Exponent objects. * * @function checkExponents * @private * @param {Exponent[]} * exponents The array to check. * @param {string} * label The error message label for the array. * @param {Zpsubgroup} * [q] Optional parameter pertaining to a Zp subgroup order. If * this option is provided, the order of the exponent will be * checked against that order. * @throws {TypeError} * If the array of {Exponent} objectss is not valid. */ checkExponents: function(exponents, label, q) { this.checkIsNonEmptyArray(exponents, label); for (var i = 0; i < exponents.length; i++) { this.checkExponent(exponents[i], 'exponent ' + i + ' of ' + label, q); } }, /** * Checks the validity of an ElGamalPublicKey object. * * @function checkElGamalPublicKey * @private * @param {ElGamalPublicKey} * publicKey The object to check. * @param {string} * label The error message label for the object. * @param {Zpsubgroup} * [group] Optional parameter pertaining to a Zp subgroup. If * this option is provided, the modulus and order of the public * key will be checked against those of the group. * @throws {TypeError} * If the ElGamalPublicKey object is not valid. */ checkElGamalPublicKey: function(publicKey, label, group) { this.checkIsObjectWithProperties(publicKey, label); if (typeof group !== 'undefined') { var groupP = group.p; var groupQ = group.q; var publicKeyP = publicKey.group.p; if (!publicKeyP.equals(groupP)) { throw new TypeError( 'Expected modulus p of ' + label + ' to equal modulus of Zp subgroup provided as input: ' + groupP + ' ; Found: ' + publicKeyP); } var publicKeyQ = publicKey.group.q; if (!publicKeyQ.equals(groupQ)) { throw new TypeError( 'Expected order q of ' + label + ' to equal order of Zp subgroup provided as input: ' + groupQ + ' ; Found: ' + publicKeyQ); } } }, /** * Checks the validity of an ElGamalPrivateKey object. * * @function checkElGamalPrivateKey * @private * @param {ElGamalPrivateKey} * privateKey The object to check. * @param {string} * label The error message label for the object. * @param {Zpsubgroup} * [group] Optional parameter pertaining to a Zp subgroup. If * this option is provided, the order of the private key will be * checked against that of the group. * @throws {TypeError} * If the ElGamalPrivateKey object is not valid. */ checkElGamalPrivateKey: function(privateKey, label, group) { this.checkIsObjectWithProperties(privateKey, label); if (typeof group !== 'undefined') { var groupQ = group.q; var privateKeyQ = privateKey.group.q; if (!privateKeyQ.equals(groupQ)) { throw new TypeError( 'Expected order q of ' + label + ' to equal order of Zp subgroup provided as input: ' + groupQ + ' ; Found: ' + privateKeyQ); } } }, /** * Checks the validity of an ElGamalEncryptedElements object. * * @function checkElGamalEncryptedElements * @private * @param {ElGamalEncryptedElements} * encryptedElements The object to check. * @param {string} * label The error message label for the object. * @param {Zpsubgroup} * [group] Optional parameter pertaining to a Zp subgroup. If * this option is provided, the modulus and order of the value's * secret and ElGamal encrypted elements will be checked against * those of the group. * @throws {TypeError} * If the {ElGamalEncryptedElements} object is not valid. */ checkElGamalEncryptedElements: function(encryptedElements, label, group) { this.checkIsObjectWithProperties(encryptedElements, label); if (typeof group !== 'undefined') { var groupP = group.p; var groupQ = group.q; var gamma = encryptedElements.gamma; var gammaP = gamma.p; if (!gammaP.equals(groupP)) { throw new TypeError( 'Expected modulus p of gamma element of ' + label + ' to equal modulus of Zp subgroup provided as input: ' + groupP + ' ; Found: ' + gammaP); } var gammaQ = gamma.q; if (!gammaQ.equals(groupQ)) { throw new TypeError( 'Expected order q of gamma element of ' + label + ' to equal order of Zp subgroup provided as input: ' + groupQ + ' ; Found: ' + gammaQ); } } var secret = encryptedElements.secret; if (typeof secret !== 'undefined') { this.checkExponent(secret, group); } }, /** * Checks the validity of the input data for a progress meter. * * @function checkProgressMeterData * @private * @param {function} * callback The progress meter callback function. * @param {number} * minCheckInterval The progress percent minimum check interval. * @param {string} * label The error message label for the check. * @throws {TypeError} * If one or more of the progress meter input data are not * valid. */ checkProgressMeterData: function(callback, minCheckInterval, label) { this.checkIsFunctionWithProperties( callback, label + 'progress meter callback function'); if (typeof minCheckInterval !== 'undefined') { this.checkIsPositiveNumber( minCheckInterval, label + ' progress percent minimum check interval'); } } };