| /** | 
|  * Hash-based Message Authentication Code implementation. Requires a message | 
|  * digest object that can be obtained, for example, from forge.md.sha1 or | 
|  * forge.md.md5. | 
|  * | 
|  * @author Dave Longley | 
|  * | 
|  * Copyright (c) 2010-2012 Digital Bazaar, Inc. All rights reserved. | 
|  */ | 
| var forge = require('./forge'); | 
| require('./md'); | 
| require('./util'); | 
|   | 
| /* HMAC API */ | 
| var hmac = module.exports = forge.hmac = forge.hmac || {}; | 
|   | 
| /** | 
|  * Creates an HMAC object that uses the given message digest object. | 
|  * | 
|  * @return an HMAC object. | 
|  */ | 
| hmac.create = function() { | 
|   // the hmac key to use | 
|   var _key = null; | 
|   | 
|   // the message digest to use | 
|   var _md = null; | 
|   | 
|   // the inner padding | 
|   var _ipadding = null; | 
|   | 
|   // the outer padding | 
|   var _opadding = null; | 
|   | 
|   // hmac context | 
|   var ctx = {}; | 
|   | 
|   /** | 
|    * Starts or restarts the HMAC with the given key and message digest. | 
|    * | 
|    * @param md the message digest to use, null to reuse the previous one, | 
|    *           a string to use builtin 'sha1', 'md5', 'sha256'. | 
|    * @param key the key to use as a string, array of bytes, byte buffer, | 
|    *           or null to reuse the previous key. | 
|    */ | 
|   ctx.start = function(md, key) { | 
|     if(md !== null) { | 
|       if(typeof md === 'string') { | 
|         // create builtin message digest | 
|         md = md.toLowerCase(); | 
|         if(md in forge.md.algorithms) { | 
|           _md = forge.md.algorithms[md].create(); | 
|         } else { | 
|           throw new Error('Unknown hash algorithm "' + md + '"'); | 
|         } | 
|       } else { | 
|         // store message digest | 
|         _md = md; | 
|       } | 
|     } | 
|   | 
|     if(key === null) { | 
|       // reuse previous key | 
|       key = _key; | 
|     } else { | 
|       if(typeof key === 'string') { | 
|         // convert string into byte buffer | 
|         key = forge.util.createBuffer(key); | 
|       } else if(forge.util.isArray(key)) { | 
|         // convert byte array into byte buffer | 
|         var tmp = key; | 
|         key = forge.util.createBuffer(); | 
|         for(var i = 0; i < tmp.length; ++i) { | 
|           key.putByte(tmp[i]); | 
|         } | 
|       } | 
|   | 
|       // if key is longer than blocksize, hash it | 
|       var keylen = key.length(); | 
|       if(keylen > _md.blockLength) { | 
|         _md.start(); | 
|         _md.update(key.bytes()); | 
|         key = _md.digest(); | 
|       } | 
|   | 
|       // mix key into inner and outer padding | 
|       // ipadding = [0x36 * blocksize] ^ key | 
|       // opadding = [0x5C * blocksize] ^ key | 
|       _ipadding = forge.util.createBuffer(); | 
|       _opadding = forge.util.createBuffer(); | 
|       keylen = key.length(); | 
|       for(var i = 0; i < keylen; ++i) { | 
|         var tmp = key.at(i); | 
|         _ipadding.putByte(0x36 ^ tmp); | 
|         _opadding.putByte(0x5C ^ tmp); | 
|       } | 
|   | 
|       // if key is shorter than blocksize, add additional padding | 
|       if(keylen < _md.blockLength) { | 
|         var tmp = _md.blockLength - keylen; | 
|         for(var i = 0; i < tmp; ++i) { | 
|           _ipadding.putByte(0x36); | 
|           _opadding.putByte(0x5C); | 
|         } | 
|       } | 
|       _key = key; | 
|       _ipadding = _ipadding.bytes(); | 
|       _opadding = _opadding.bytes(); | 
|     } | 
|   | 
|     // digest is done like so: hash(opadding | hash(ipadding | message)) | 
|   | 
|     // prepare to do inner hash | 
|     // hash(ipadding | message) | 
|     _md.start(); | 
|     _md.update(_ipadding); | 
|   }; | 
|   | 
|   /** | 
|    * Updates the HMAC with the given message bytes. | 
|    * | 
|    * @param bytes the bytes to update with. | 
|    */ | 
|   ctx.update = function(bytes) { | 
|     _md.update(bytes); | 
|   }; | 
|   | 
|   /** | 
|    * Produces the Message Authentication Code (MAC). | 
|    * | 
|    * @return a byte buffer containing the digest value. | 
|    */ | 
|   ctx.getMac = function() { | 
|     // digest is done like so: hash(opadding | hash(ipadding | message)) | 
|     // here we do the outer hashing | 
|     var inner = _md.digest().bytes(); | 
|     _md.start(); | 
|     _md.update(_opadding); | 
|     _md.update(inner); | 
|     return _md.digest(); | 
|   }; | 
|   // alias for getMac | 
|   ctx.digest = ctx.getMac; | 
|   | 
|   return ctx; | 
| }; |