symmetric.mac.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  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. cryptolib.modules.symmetric = cryptolib.modules.symmetric || {};
  9. /**
  10. * Defines the MACs of the module.
  11. *
  12. * @namespace symmetric/mac
  13. */
  14. cryptolib.modules.symmetric.mac = function(box) {
  15. 'use strict';
  16. box.symmetric = box.symmetric || {};
  17. box.symmetric.mac = {};
  18. /**
  19. * Allows a symmetric MAC factory to be obtained.
  20. *
  21. * @exports symmetric/mac/factory
  22. */
  23. box.symmetric.mac.factory = {};
  24. var policies = {
  25. mac: {
  26. hash: box.policies.symmetric.mac.hash,
  27. provider: box.policies.symmetric.mac.provider
  28. }
  29. };
  30. var converters;
  31. var exceptions;
  32. cryptolib('commons', function(box) {
  33. converters = new box.commons.utils.Converters();
  34. exceptions = box.commons.exceptions;
  35. });
  36. /**
  37. * Factory class that allows a MAC to be obtained.
  38. *
  39. * @class
  40. */
  41. box.symmetric.mac.factory.MacFactory = function() {};
  42. box.symmetric.mac.factory.MacFactory.prototype = {
  43. /**
  44. * Create a new MAC.
  45. * <p>
  46. * The particular MAC that is returned depends on the policies.
  47. *
  48. * @function
  49. * @returns a new MAC.
  50. */
  51. create: function() {
  52. try {
  53. if (policies.mac.provider === Config.symmetric.mac.provider.FORGE) {
  54. return this.createCryptoForgeMac();
  55. } else {
  56. throw new exceptions.CryptoLibException(
  57. 'No suitable provider was provided.');
  58. }
  59. } catch (error) {
  60. throw new exceptions.CryptoLibException(
  61. 'A CryptoMac could not be obtained.', error);
  62. }
  63. },
  64. /**
  65. * Create a new CryptoForgeMac.
  66. *
  67. * @function
  68. * @returns {symmetric/mac.CryptoForgeMac} a new CryptoForgeMac.
  69. */
  70. createCryptoForgeMac: function() {
  71. return new CryptoForgeMac();
  72. }
  73. };
  74. /**
  75. * Represents a MAC.
  76. *
  77. * @class
  78. * @memberof symmetric/mac
  79. */
  80. function CryptoForgeMac() {
  81. this.hmacDigester = forge.hmac.create();
  82. }
  83. CryptoForgeMac.prototype = {
  84. /**
  85. * Generates MAC for some data.
  86. *
  87. * @function
  88. * @param secretKeyBase64
  89. * {String} Secret key, as string in Base64 encoded format
  90. * @param arrayDataBase64
  91. * {String} The input data for the MAC, as an array of string
  92. * in Base64 encoded format. It is assumed that the array is
  93. * not empty.
  94. * @returns The generated MAC, in Base64 encoded format.
  95. */
  96. generate: function(secretKeyBase64, arrayDataBase64) {
  97. var secretKey = converters.base64Decode(secretKeyBase64);
  98. var secretKeyBuffer = new forge.util.ByteBuffer(secretKey);
  99. try {
  100. this.hmacDigester.start(policies.mac.hash, secretKeyBuffer);
  101. } catch (error) {
  102. throw new exceptions.CryptoLibException(
  103. 'MAC message digester could not be initialized.', error);
  104. }
  105. try {
  106. if (arrayDataBase64.length < 1) {
  107. throw new exceptions.CryptoLibException(
  108. 'The array of data should have at least one element');
  109. }
  110. var dataB64;
  111. for (var i = 0; i < arrayDataBase64.length; i++) {
  112. dataB64 = arrayDataBase64[i];
  113. var data = converters.base64Decode(dataB64);
  114. this.hmacDigester.update(data);
  115. }
  116. } catch (error) {
  117. throw new exceptions.CryptoLibException(
  118. 'MAC message digester could not be updated.', error);
  119. }
  120. try {
  121. var hmacMessageDigest = this.hmacDigester.digest();
  122. return converters.base64Encode(hmacMessageDigest.getBytes());
  123. } catch (error) {
  124. throw new exceptions.CryptoLibException(
  125. 'MAC message digest could not be generated.', error);
  126. }
  127. },
  128. /**
  129. * Verify that a given MAC is indeed the MAC for the given data, using
  130. * the given secret key.
  131. *
  132. * @function
  133. * @param macBase64
  134. * {String} The MAC to be verified, as string in Base64
  135. * encoded format.
  136. * @param secretKeyBase64
  137. * {String} Secret key, as string in Base64 encoded format
  138. * @param arrayDataBase64
  139. * {String} The input data for the MAC, as an array of string
  140. * in Base64 encoded format. It is assumed that the array is
  141. * not empty.
  142. *
  143. * @returns True if the MAC is the MAC of the given data and SecretKey,
  144. * false otherwise.
  145. */
  146. verify: function(macBase64, secretKeyBase64, arrayDataBase64) {
  147. if (typeof macBase64 === 'undefined' || macBase64 === null) {
  148. return false;
  149. }
  150. var macBase64ToVerify = this.generate(secretKeyBase64, arrayDataBase64);
  151. if (macBase64.length !== macBase64ToVerify.length) {
  152. return false;
  153. }
  154. var equals = true;
  155. for (var i = 0; i < macBase64ToVerify.length; i++) {
  156. if (macBase64ToVerify.charCodeAt(i) !== macBase64.charCodeAt(i)) {
  157. equals = false;
  158. }
  159. }
  160. return equals;
  161. }
  162. };
  163. };