205 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			205 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /**
 | |
|  * 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.
 | |
|  */
 | |
| (function() {
 | |
| /* ########## Begin module implementation ########## */
 | |
| function initModule(forge) {
 | |
| 
 | |
| /* HMAC API */
 | |
| var hmac = 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) {
 | |
|     console.log('forge key start', typeof key, Object.prototype.toString.apply(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]);
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       console.log('forge key', key);
 | |
| 
 | |
|       // 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;
 | |
| };
 | |
| 
 | |
| } // end module implementation
 | |
| 
 | |
| /* ########## Begin module wrapper ########## */
 | |
| var name = 'hmac';
 | |
| if(typeof define !== 'function') {
 | |
|   // NodeJS -> AMD
 | |
|   if(typeof module === 'object' && module.exports) {
 | |
|     var nodeJS = true;
 | |
|     define = function(ids, factory) {
 | |
|       factory(require, module);
 | |
|     };
 | |
|   } else {
 | |
|     // <script>
 | |
|     if(typeof forge === 'undefined') {
 | |
|       forge = {};
 | |
|     }
 | |
|     return initModule(forge);
 | |
|   }
 | |
| }
 | |
| // AMD
 | |
| var deps;
 | |
| var defineFunc = function(require, module) {
 | |
|   module.exports = function(forge) {
 | |
|     var mods = deps.map(function(dep) {
 | |
|       return require(dep);
 | |
|     }).concat(initModule);
 | |
|     // handle circular dependencies
 | |
|     forge = forge || {};
 | |
|     forge.defined = forge.defined || {};
 | |
|     if(forge.defined[name]) {
 | |
|       return forge[name];
 | |
|     }
 | |
|     forge.defined[name] = true;
 | |
|     for(var i = 0; i < mods.length; ++i) {
 | |
|       mods[i](forge);
 | |
|     }
 | |
|     return forge[name];
 | |
|   };
 | |
| };
 | |
| var tmpDefine = define;
 | |
| define = function(ids, factory) {
 | |
|   deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
 | |
|   if(nodeJS) {
 | |
|     delete define;
 | |
|     return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
 | |
|   }
 | |
|   define = tmpDefine;
 | |
|   return define.apply(null, Array.prototype.slice.call(arguments, 0));
 | |
| };
 | |
| define(['require', 'module', './md', './util'], function() {
 | |
|   defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
 | |
| });
 | |
| })();
 |