You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

146 lines
3.7 KiB

4 years ago
  1. /**
  2. * Hash-based Message Authentication Code implementation. Requires a message
  3. * digest object that can be obtained, for example, from forge.md.sha1 or
  4. * forge.md.md5.
  5. *
  6. * @author Dave Longley
  7. *
  8. * Copyright (c) 2010-2012 Digital Bazaar, Inc. All rights reserved.
  9. */
  10. var forge = require('./forge');
  11. require('./md');
  12. require('./util');
  13. /* HMAC API */
  14. var hmac = module.exports = forge.hmac = forge.hmac || {};
  15. /**
  16. * Creates an HMAC object that uses the given message digest object.
  17. *
  18. * @return an HMAC object.
  19. */
  20. hmac.create = function() {
  21. // the hmac key to use
  22. var _key = null;
  23. // the message digest to use
  24. var _md = null;
  25. // the inner padding
  26. var _ipadding = null;
  27. // the outer padding
  28. var _opadding = null;
  29. // hmac context
  30. var ctx = {};
  31. /**
  32. * Starts or restarts the HMAC with the given key and message digest.
  33. *
  34. * @param md the message digest to use, null to reuse the previous one,
  35. * a string to use builtin 'sha1', 'md5', 'sha256'.
  36. * @param key the key to use as a string, array of bytes, byte buffer,
  37. * or null to reuse the previous key.
  38. */
  39. ctx.start = function(md, key) {
  40. if(md !== null) {
  41. if(typeof md === 'string') {
  42. // create builtin message digest
  43. md = md.toLowerCase();
  44. if(md in forge.md.algorithms) {
  45. _md = forge.md.algorithms[md].create();
  46. } else {
  47. throw new Error('Unknown hash algorithm "' + md + '"');
  48. }
  49. } else {
  50. // store message digest
  51. _md = md;
  52. }
  53. }
  54. if(key === null) {
  55. // reuse previous key
  56. key = _key;
  57. } else {
  58. if(typeof key === 'string') {
  59. // convert string into byte buffer
  60. key = forge.util.createBuffer(key);
  61. } else if(forge.util.isArray(key)) {
  62. // convert byte array into byte buffer
  63. var tmp = key;
  64. key = forge.util.createBuffer();
  65. for(var i = 0; i < tmp.length; ++i) {
  66. key.putByte(tmp[i]);
  67. }
  68. }
  69. // if key is longer than blocksize, hash it
  70. var keylen = key.length();
  71. if(keylen > _md.blockLength) {
  72. _md.start();
  73. _md.update(key.bytes());
  74. key = _md.digest();
  75. }
  76. // mix key into inner and outer padding
  77. // ipadding = [0x36 * blocksize] ^ key
  78. // opadding = [0x5C * blocksize] ^ key
  79. _ipadding = forge.util.createBuffer();
  80. _opadding = forge.util.createBuffer();
  81. keylen = key.length();
  82. for(var i = 0; i < keylen; ++i) {
  83. var tmp = key.at(i);
  84. _ipadding.putByte(0x36 ^ tmp);
  85. _opadding.putByte(0x5C ^ tmp);
  86. }
  87. // if key is shorter than blocksize, add additional padding
  88. if(keylen < _md.blockLength) {
  89. var tmp = _md.blockLength - keylen;
  90. for(var i = 0; i < tmp; ++i) {
  91. _ipadding.putByte(0x36);
  92. _opadding.putByte(0x5C);
  93. }
  94. }
  95. _key = key;
  96. _ipadding = _ipadding.bytes();
  97. _opadding = _opadding.bytes();
  98. }
  99. // digest is done like so: hash(opadding | hash(ipadding | message))
  100. // prepare to do inner hash
  101. // hash(ipadding | message)
  102. _md.start();
  103. _md.update(_ipadding);
  104. };
  105. /**
  106. * Updates the HMAC with the given message bytes.
  107. *
  108. * @param bytes the bytes to update with.
  109. */
  110. ctx.update = function(bytes) {
  111. _md.update(bytes);
  112. };
  113. /**
  114. * Produces the Message Authentication Code (MAC).
  115. *
  116. * @return a byte buffer containing the digest value.
  117. */
  118. ctx.getMac = function() {
  119. // digest is done like so: hash(opadding | hash(ipadding | message))
  120. // here we do the outer hashing
  121. var inner = _md.digest().bytes();
  122. _md.start();
  123. _md.update(_opadding);
  124. _md.update(inner);
  125. return _md.digest();
  126. };
  127. // alias for getMac
  128. ctx.digest = ctx.getMac;
  129. return ctx;
  130. };