input-validator.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696
  1. /*
  2. * Copyright 2018 Scytl Secure Electronic Voting SA
  3. *
  4. * All rights reserved
  5. *
  6. * See our extended copyright notice in *file 'Copyright.txt' which is part of this source code package
  7. */
  8. /* jshint node:true */
  9. 'use strict';
  10. var forge = require('node-forge');
  11. var BigInteger = forge.jsbn.BigInteger;
  12. /**
  13. * Input data validation utility for this module. Only intended for internal
  14. * use.
  15. */
  16. module.exports = {
  17. /**
  18. * Checks if a value is defined.
  19. *
  20. * @function checkIsDefined
  21. * @private
  22. * @param {Object}
  23. * value The value to check.
  24. * @param {string}
  25. * label The error message label for the value.
  26. * @throws {TypeError}
  27. * If the value is undefined.
  28. */
  29. checkIsDefined: function(value, label) {
  30. if (typeof value === 'undefined') {
  31. throw new TypeError(label + ' is undefined.');
  32. }
  33. },
  34. /**
  35. * Checks if a value is not null.
  36. *
  37. * @function checkIsNotNull
  38. * @private
  39. * @param {Object}
  40. * value The value to check.
  41. * @param {string}
  42. * label The error message label for the value.
  43. * @throws {TypeError}
  44. * If the value is null.
  45. */
  46. checkIsNotNull: function(value, label) {
  47. if (value === null) {
  48. throw new TypeError(label + ' is null.');
  49. }
  50. },
  51. /**
  52. * Checks if a value is defined and not null.
  53. *
  54. * @function checkIsDefinedAndNotNull
  55. * @private
  56. * @param {Object}
  57. * value The value to check.
  58. * @param {string}
  59. * label The error message label for the value.
  60. * @throws {TypeError}
  61. * If the value is not defined or it is null.
  62. */
  63. checkIsDefinedAndNotNull: function(value, label) {
  64. this.checkIsDefined(value, label);
  65. this.checkIsNotNull(value, label);
  66. },
  67. /**
  68. * Checks if a value is of an expected type.
  69. *
  70. * @function checkIsType
  71. * @private
  72. * @param {Object}
  73. * value The value to check.
  74. * @param {string}
  75. * type The expected type of the value.
  76. * @param {string}
  77. * label The error message label for the value.
  78. * @throws {TypeError}
  79. * If the value is not of the expected type.
  80. */
  81. checkIsType: function(value, type, label) {
  82. var typeFound = typeof value;
  83. if (typeFound !== type) {
  84. throw new TypeError(
  85. 'Expected ' + label + ' to have type \'' + type + '\' ; Found: \'' +
  86. typeFound + '\'');
  87. }
  88. },
  89. /**
  90. * Checks if a value is an instance of an Object.
  91. *
  92. * @function checkIsInstanceOf
  93. * @private
  94. * @param {Object}
  95. * value The value to check.
  96. * @param {Object}
  97. * obj The Object to check against.
  98. * @param {string}
  99. * objName The Object name, for error handling purposes.
  100. * @param {string}
  101. * label The error message label for the value.
  102. * @throws {TypeError}
  103. * If the object is undefined, null, or it is not an instance of
  104. * the Object.
  105. */
  106. checkIsInstanceOf: function(value, obj, objName, label) {
  107. this.checkIsDefinedAndNotNull(value, label);
  108. if (!(value instanceof obj)) {
  109. throw new TypeError(label + ' is not an instance of Object ' + objName);
  110. }
  111. },
  112. /**
  113. * Checks if a value is an object.
  114. *
  115. * @function checkIsObject
  116. * @private
  117. * @param {Object}
  118. * value The value to check.
  119. * @param {string}
  120. * label The error message label for the value.
  121. * @throws {TypeError}
  122. * If the value is not an object.
  123. */
  124. checkIsObject: function(value, label) {
  125. if (typeof value !== 'object') {
  126. throw new TypeError(label + ' is not an object.');
  127. }
  128. },
  129. /**
  130. * Checks if a value is an object and has properties.
  131. *
  132. * @function checkIsObjectWithProperties
  133. * @private
  134. * @param {Object}
  135. * value The value to check.
  136. * @param {string}
  137. * label The error message label for the value.
  138. * @throws {TypeError}
  139. * If the value is not an object or it has no properties.
  140. */
  141. checkIsObjectWithProperties: function(value, label) {
  142. this.checkIsDefinedAndNotNull(value, label);
  143. this.checkIsObject(value, label);
  144. if (!Object.getOwnPropertyNames(value).length) {
  145. throw new TypeError(label + ' does not have any properties.');
  146. }
  147. },
  148. /**
  149. * Checks if a value is a function.
  150. *
  151. * @function checkIsFunction
  152. * @private
  153. * @param {Object}
  154. * value The value to check.
  155. * @param {string}
  156. * label The error message label for the value.
  157. * @throws {TypeError}
  158. * If the value is not a function.
  159. */
  160. checkIsFunction: function(value, label) {
  161. if (typeof value !== 'function') {
  162. throw new TypeError(label + ' is not a function.');
  163. }
  164. },
  165. /**
  166. * Checks if a value is a function and has properties.
  167. *
  168. * @function checkIsFunctionWithProperties
  169. * @private
  170. * @param {Object}
  171. * value The value to check.
  172. * @param {string}
  173. * label The error message label for the value.
  174. * @throws {TypeError}
  175. * If the value is not a function or it has no properties.
  176. */
  177. checkIsFunctionWithProperties: function(value, label) {
  178. this.checkIsDefinedAndNotNull(value, label);
  179. this.checkIsFunction(value, label);
  180. if (!Object.getOwnPropertyNames(value).length) {
  181. throw new TypeError(label + ' does not have any properties.');
  182. }
  183. },
  184. /**
  185. * Checks if a value is a positive number.
  186. *
  187. * @function checkIsPositiveNumber
  188. * @private
  189. * @param {Object}
  190. * value The value to check.
  191. * @param {string}
  192. * label The error message label for the value.
  193. * @throws {TypeError}
  194. * If the value is not a positive number.
  195. */
  196. checkIsPositiveNumber: function(value, label) {
  197. this.checkIsType(value, 'number', label);
  198. if (value === 0 || value < 0) {
  199. throw new TypeError(label + ' must be at least 1. Found: ' + value);
  200. }
  201. },
  202. /**
  203. * Checks if a value is an Array object.
  204. *
  205. * @function checkIsArray
  206. * @private
  207. * @param {Array}
  208. * array The value to check.
  209. * @param {string}
  210. * label The error message label for the value.
  211. * @throws {TypeError}
  212. * If the value is not an {Array} object.
  213. */
  214. checkIsArray: function(value, label) {
  215. this.checkIsDefinedAndNotNull(value, 'Array of ' + label);
  216. if (value.constructor !== Array) {
  217. throw new TypeError('Array of ' + label + ' is not of type Array.');
  218. }
  219. },
  220. /**
  221. * Checks if a value is a non-empty Array object.
  222. *
  223. * @function checkIsNonEmptyArray
  224. * @private
  225. * @param {Array}
  226. * array The value to check.
  227. * @param {string}
  228. * label The error message label for the value.
  229. * @throws {TypeError}
  230. * If the value is not a non-empty {Array} object.
  231. */
  232. checkIsNonEmptyArray: function(value, label) {
  233. this.checkIsArray(value, label);
  234. if (value.length < 1) {
  235. throw new TypeError('Array of ' + label + ' is empty.');
  236. }
  237. },
  238. /**
  239. * Checks if two arrays contain the same number of values.
  240. *
  241. * @function checkArrayLengthsEqual
  242. * @private
  243. * @param {Array}
  244. * array1 The first array to check.
  245. * @param {string}
  246. * label1 The error message label for the first array.
  247. * @param {Array}
  248. * array2 The second array to check.
  249. * @param {string}
  250. * label2 The error message label for the second array.
  251. * @throws {TypeError}
  252. * If one or both arrays are not valid or they do not contain
  253. * the same number of values.
  254. */
  255. checkArrayLengthsEqual: function(array1, label1, array2, label2) {
  256. this.checkIsArray(array1, label1);
  257. this.checkIsArray(array2, label2);
  258. var array1Length = array1.length;
  259. var array2Length = array2.length;
  260. if (array1Length !== array2Length) {
  261. throw new TypeError(
  262. 'Expected array length of ' + label1 + ' to equal array length of ' +
  263. label2 + ': ' + array2Length + ' ; Found: ' + array1Length);
  264. }
  265. },
  266. /**
  267. * Checks if a value is a non-empty string.
  268. *
  269. * @function checkIsNonEmptyString
  270. * @private
  271. * @param {string}
  272. * value The value to check.
  273. * @param {string}
  274. * label The error message label for the value.
  275. * @throws {TypeError}
  276. * If the value is not a non-empty string.
  277. */
  278. checkIsNonEmptyString: function(value, label) {
  279. this.checkIsType(value, 'string', label);
  280. if (value.length === 0) {
  281. throw new TypeError(label + ' is empty.');
  282. }
  283. },
  284. /**
  285. * Checks if a value is an array of strings.
  286. *
  287. * @function checkIsStringArray
  288. * @private
  289. * @param {string[]}
  290. * value The value to check.
  291. * @param {string}
  292. * label The error message label for the value.
  293. * @throws {TypeError}
  294. * If value is not an array of strings.
  295. */
  296. checkIsStringArray: function(value, label) {
  297. this.checkIsNonEmptyArray(value, label);
  298. for (var i = 0; i < value.length; i++) {
  299. this.checkIsType(value[i], 'string', 'Element ' + i + ' of ' + label);
  300. }
  301. },
  302. /**
  303. * Checks if a value is an array of non-empty strings.
  304. *
  305. * @function checkIsNonEmptyStringArray
  306. * @private
  307. * @param {string[]}
  308. * value The value to check.
  309. * @param {string}
  310. * label The error message label for the value.
  311. * @throws {TypeError}
  312. * If value is not an array of non-empty strings.
  313. */
  314. checkIsNonEmptyStringArray: function(value, label) {
  315. this.checkIsNonEmptyArray(value, label);
  316. for (var i = 0; i < value.length; i++) {
  317. this.checkIsNonEmptyString(value[i], 'Element ' + i + ' of ' + label);
  318. }
  319. },
  320. /**
  321. * Checks if a value is a BigInteger object.
  322. *
  323. * @function checkIsBigInteger
  324. * @private
  325. * @param {Object}
  326. * value The value to check.
  327. * @param {string}
  328. * label The error message label for the value.
  329. * @throws {TypeError}
  330. * If the value is not a BigInteger object.
  331. */
  332. checkIsBigInteger: function(value, label) {
  333. this.checkIsDefinedAndNotNull(value, label);
  334. if (typeof value.abs === 'undefined') {
  335. throw new TypeError(label + ' is not a BigInteger object.');
  336. }
  337. },
  338. /**
  339. * Checks if a value is a positive BigInteger object.
  340. *
  341. * @function checkIsPositiveBigInteger
  342. * @private
  343. * @param {Object}
  344. * value The value to check.
  345. * @param {string}
  346. * label The error message label for the value.
  347. * @throws {TypeError}
  348. * If the value is not a positive {BigInteger} object.
  349. */
  350. checkIsPositiveBigInteger: function(value, label) {
  351. this.checkIsBigInteger(value, label);
  352. if (value.compareTo(BigInteger.ONE) < 0) {
  353. throw new TypeError(label + ' must not be less than 1; Found: ' + value);
  354. }
  355. },
  356. /**
  357. * Checks if a value is a valid JSON object.
  358. *
  359. * @function checkIsJsonObject
  360. * @private
  361. * @param {Object}
  362. * value The value to check.
  363. * @param {string}
  364. * label The error message label for the value.
  365. * @throws {TypeError}
  366. * If the value is a valid JSON object.
  367. */
  368. checkIsJsonObject: function(value, label) {
  369. try {
  370. JSON.parse(value);
  371. } catch (error) {
  372. throw new TypeError(label + ' is not a valid JSON object.');
  373. }
  374. },
  375. /**
  376. * Checks if a value is a valid JSON string.
  377. *
  378. * @function checkIsJsonString
  379. * @private
  380. * @param {Object}
  381. * value The value to check.
  382. * @param {string}
  383. * label The error message label for the value.
  384. * @throws {TypeError}
  385. * If the value is a valid JSON string.
  386. */
  387. checkIsJsonString: function(value, label) {
  388. this.checkIsNonEmptyString(value, label);
  389. this.checkIsJsonObject(value, label);
  390. },
  391. /**
  392. * Checks the validity of a ZpGroupElements object.
  393. *
  394. * @function checkZpGroupElement
  395. * @private
  396. * @param {ZpGroupElement}
  397. * element The object to check.
  398. * @param {string}
  399. * label The error message label for the object.
  400. * @param {Zpsubgroup}
  401. * [group] Optional parameter pertaining to a Zp subgroup. If
  402. * this option is provided, the modulus and order of the element
  403. * will be checked against those of the group.
  404. * @throws {TypeError}
  405. * If the value is not a valid ZpGroupElement object.
  406. */
  407. checkZpGroupElement: function(element, label, group) {
  408. this.checkIsObjectWithProperties(element, label);
  409. if (typeof group !== 'undefined') {
  410. var groupP = group.p;
  411. var groupQ = group.q;
  412. var elementP = element.p;
  413. if (!elementP.equals(groupP)) {
  414. throw new TypeError(
  415. 'Expected modulus p of ' + label +
  416. ' to equal modulus of Zp subgroup provided as input: ' + groupP +
  417. ' ; Found: ' + elementP);
  418. }
  419. var elementQ = element.q;
  420. if (!elementQ.equals(groupQ)) {
  421. throw new TypeError(
  422. 'Expected order q of ' + label +
  423. ' to equal order of Zp subgroup provided as input: ' + groupQ +
  424. ' ; Found: ' + elementQ);
  425. }
  426. }
  427. },
  428. /**
  429. * Checks the validity of an Exponent object.
  430. *
  431. * @function checkExponent
  432. * @private
  433. * @param {Exponent}
  434. * exponent The object to check.
  435. * @param {string}
  436. * label The error message label for the object.
  437. * @param {Zpsubgroup}
  438. * [q] Optional parameter pertaining to a Zp subgroup order. If
  439. * this option is provided, the order of the exponent will be
  440. * checked against that order.
  441. * @throws {TypeError}
  442. * If the value is not a valid Exponent value.
  443. */
  444. checkExponent: function(exponent, label, q) {
  445. this.checkIsObjectWithProperties(exponent, label);
  446. if (typeof q !== 'undefined') {
  447. var exponentQ = exponent.q;
  448. if (!exponentQ.equals(q)) {
  449. throw new TypeError(
  450. 'Expected order q of ' + label +
  451. ' to equal order of Zp subgroup provided as input: ' + q +
  452. ' ; Found: ' + exponentQ);
  453. }
  454. }
  455. },
  456. /**
  457. * Checks the validity of an array of ZpGroupElement objects.
  458. *
  459. * @function checkZpGroupElements
  460. * @private
  461. * @param {ZpGroupElement[]}
  462. * elements The array to check.
  463. * @param {string}
  464. * label The error message label for the array.
  465. * @param {Zpsubgroup}
  466. * [group] Optional parameter pertaining to a Zp subgroup. If
  467. * this option is provided, the modulus and order of each element
  468. * will be checked against those of the group.
  469. * @throws {TypeError}
  470. * If the array of ZpGroupElement objects is not valid.
  471. */
  472. checkZpGroupElements: function(elements, label, group) {
  473. this.checkIsNonEmptyArray(elements, label);
  474. for (var i = 0; i < elements.length; i++) {
  475. this.checkZpGroupElement(
  476. elements[i], 'element ' + i + ' of ' + label, group);
  477. }
  478. },
  479. /**
  480. * Checks the validity of an array of Exponent objects.
  481. *
  482. * @function checkExponents
  483. * @private
  484. * @param {Exponent[]}
  485. * exponents The array to check.
  486. * @param {string}
  487. * label The error message label for the array.
  488. * @param {Zpsubgroup}
  489. * [q] Optional parameter pertaining to a Zp subgroup order. If
  490. * this option is provided, the order of the exponent will be
  491. * checked against that order.
  492. * @throws {TypeError}
  493. * If the array of {Exponent} objectss is not valid.
  494. */
  495. checkExponents: function(exponents, label, q) {
  496. this.checkIsNonEmptyArray(exponents, label);
  497. for (var i = 0; i < exponents.length; i++) {
  498. this.checkExponent(exponents[i], 'exponent ' + i + ' of ' + label, q);
  499. }
  500. },
  501. /**
  502. * Checks the validity of an ElGamalPublicKey object.
  503. *
  504. * @function checkElGamalPublicKey
  505. * @private
  506. * @param {ElGamalPublicKey}
  507. * publicKey The object to check.
  508. * @param {string}
  509. * label The error message label for the object.
  510. * @param {Zpsubgroup}
  511. * [group] Optional parameter pertaining to a Zp subgroup. If
  512. * this option is provided, the modulus and order of the public
  513. * key will be checked against those of the group.
  514. * @throws {TypeError}
  515. * If the ElGamalPublicKey object is not valid.
  516. */
  517. checkElGamalPublicKey: function(publicKey, label, group) {
  518. this.checkIsObjectWithProperties(publicKey, label);
  519. if (typeof group !== 'undefined') {
  520. var groupP = group.p;
  521. var groupQ = group.q;
  522. var publicKeyP = publicKey.group.p;
  523. if (!publicKeyP.equals(groupP)) {
  524. throw new TypeError(
  525. 'Expected modulus p of ' + label +
  526. ' to equal modulus of Zp subgroup provided as input: ' + groupP +
  527. ' ; Found: ' + publicKeyP);
  528. }
  529. var publicKeyQ = publicKey.group.q;
  530. if (!publicKeyQ.equals(groupQ)) {
  531. throw new TypeError(
  532. 'Expected order q of ' + label +
  533. ' to equal order of Zp subgroup provided as input: ' + groupQ +
  534. ' ; Found: ' + publicKeyQ);
  535. }
  536. }
  537. },
  538. /**
  539. * Checks the validity of an ElGamalPrivateKey object.
  540. *
  541. * @function checkElGamalPrivateKey
  542. * @private
  543. * @param {ElGamalPrivateKey}
  544. * privateKey The object to check.
  545. * @param {string}
  546. * label The error message label for the object.
  547. * @param {Zpsubgroup}
  548. * [group] Optional parameter pertaining to a Zp subgroup. If
  549. * this option is provided, the order of the private key will be
  550. * checked against that of the group.
  551. * @throws {TypeError}
  552. * If the ElGamalPrivateKey object is not valid.
  553. */
  554. checkElGamalPrivateKey: function(privateKey, label, group) {
  555. this.checkIsObjectWithProperties(privateKey, label);
  556. if (typeof group !== 'undefined') {
  557. var groupQ = group.q;
  558. var privateKeyQ = privateKey.group.q;
  559. if (!privateKeyQ.equals(groupQ)) {
  560. throw new TypeError(
  561. 'Expected order q of ' + label +
  562. ' to equal order of Zp subgroup provided as input: ' + groupQ +
  563. ' ; Found: ' + privateKeyQ);
  564. }
  565. }
  566. },
  567. /**
  568. * Checks the validity of an ElGamalEncryptedElements object.
  569. *
  570. * @function checkElGamalEncryptedElements
  571. * @private
  572. * @param {ElGamalEncryptedElements}
  573. * encryptedElements The object to check.
  574. * @param {string}
  575. * label The error message label for the object.
  576. * @param {Zpsubgroup}
  577. * [group] Optional parameter pertaining to a Zp subgroup. If
  578. * this option is provided, the modulus and order of the value's
  579. * secret and ElGamal encrypted elements will be checked against
  580. * those of the group.
  581. * @throws {TypeError}
  582. * If the {ElGamalEncryptedElements} object is not valid.
  583. */
  584. checkElGamalEncryptedElements: function(encryptedElements, label, group) {
  585. this.checkIsObjectWithProperties(encryptedElements, label);
  586. if (typeof group !== 'undefined') {
  587. var groupP = group.p;
  588. var groupQ = group.q;
  589. var gamma = encryptedElements.gamma;
  590. var gammaP = gamma.p;
  591. if (!gammaP.equals(groupP)) {
  592. throw new TypeError(
  593. 'Expected modulus p of gamma element of ' + label +
  594. ' to equal modulus of Zp subgroup provided as input: ' + groupP +
  595. ' ; Found: ' + gammaP);
  596. }
  597. var gammaQ = gamma.q;
  598. if (!gammaQ.equals(groupQ)) {
  599. throw new TypeError(
  600. 'Expected order q of gamma element of ' + label +
  601. ' to equal order of Zp subgroup provided as input: ' + groupQ +
  602. ' ; Found: ' + gammaQ);
  603. }
  604. }
  605. var secret = encryptedElements.secret;
  606. if (typeof secret !== 'undefined') {
  607. this.checkExponent(secret, group);
  608. }
  609. },
  610. /**
  611. * Checks the validity of the input data for a progress meter.
  612. *
  613. * @function checkProgressMeterData
  614. * @private
  615. * @param {function}
  616. * callback The progress meter callback function.
  617. * @param {number}
  618. * minCheckInterval The progress percent minimum check interval.
  619. * @param {string}
  620. * label The error message label for the check.
  621. * @throws {TypeError}
  622. * If one or more of the progress meter input data are not
  623. * valid.
  624. */
  625. checkProgressMeterData: function(callback, minCheckInterval, label) {
  626. this.checkIsFunctionWithProperties(
  627. callback, label + 'progress meter callback function');
  628. if (typeof minCheckInterval !== 'undefined') {
  629. this.checkIsPositiveNumber(
  630. minCheckInterval, label + ' progress percent minimum check interval');
  631. }
  632. }
  633. };