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.
		
		
		
		
		
			
		
			
				
					147 lines
				
				3.7 KiB
			
		
		
			
		
	
	
					147 lines
				
				3.7 KiB
			| 
											2 years ago
										 | /** | ||
|  |  * 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; | ||
|  | }; |