UNPKG

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