1 /** 2 * HMAC 3 * 4 * Copyright: 5 * (C) 1999-2007,2014 Jack Lloyd 6 * (C) 2014-2015 Etienne Cimon 7 * 8 * License: 9 * Botan is released under the Simplified BSD License (see LICENSE.md) 10 */ 11 module botan.mac.hmac; 12 13 import botan.constants; 14 static if (BOTAN_HAS_HMAC || BOTAN_HAS_PBE_PKCS_V20): 15 16 public import botan.mac.mac; 17 import botan.hash.hash; 18 import std.algorithm : fill; 19 import botan.utils.xor_buf; 20 import botan.utils.mem_ops; 21 import std.range : refRange; 22 /** 23 * HMAC 24 */ 25 final class HMAC : MessageAuthenticationCode 26 { 27 public: 28 /* 29 * Clear memory of sensitive data 30 */ 31 override void clear() 32 { 33 m_hash.clear(); 34 zap(m_ikey); 35 zap(m_okey); 36 } 37 38 /* 39 * Return the name of this type 40 */ 41 override @property string name() const 42 { 43 return "HMAC(" ~ m_hash.name ~ ")"; 44 } 45 46 /* 47 * Return a clone of this object 48 */ 49 override MessageAuthenticationCode clone() const 50 { 51 return new HMAC(m_hash.clone()); 52 } 53 54 override @property size_t outputLength() const { return m_hash.outputLength; } 55 56 KeyLengthSpecification keySpec() const 57 { 58 // Absurd max length here is to support PBKDF2 59 return KeyLengthSpecification(0, 512); 60 } 61 62 /** 63 * Params: 64 * hash = the hash to use for HMACing 65 */ 66 this(HashFunction hash) 67 { 68 m_hash = hash; 69 if (m_hash.hashBlockSize == 0) 70 throw new InvalidArgument("HMAC cannot be used with " ~ m_hash.name); 71 } 72 protected: 73 /* 74 * Update a HMAC Calculation 75 */ 76 override void addData(const(ubyte)* input, size_t length) 77 { 78 m_hash.update(input, length); 79 } 80 81 /* 82 * Finalize a HMAC Calculation 83 */ 84 override void finalResult(ubyte* mac) 85 { 86 m_hash.flushInto(mac); 87 m_hash.update(m_okey); 88 m_hash.update(mac, outputLength()); 89 m_hash.flushInto(mac); 90 m_hash.update(m_ikey); 91 } 92 93 /* 94 * HMAC Key Schedule 95 */ 96 override void keySchedule(const(ubyte)* key, size_t length) 97 { 98 m_hash.clear(); 99 100 m_ikey.resize(m_hash.hashBlockSize); 101 m_okey.resize(m_hash.hashBlockSize); 102 103 fill(m_ikey.ptr[0 .. m_ikey.length], cast(ubyte)0x36); 104 fill(m_okey.ptr[0 .. m_okey.length], cast(ubyte)0x5C); 105 106 if (length > m_hash.hashBlockSize) 107 { 108 SecureVector!ubyte hmac_key = m_hash.process(key, length); 109 xorBuf(m_ikey, hmac_key, hmac_key.length); 110 xorBuf(m_okey, hmac_key, hmac_key.length); 111 } 112 else 113 { 114 xorBuf(m_ikey, key, length); 115 xorBuf(m_okey, key, length); 116 } 117 118 m_hash.update(m_ikey); 119 } 120 121 Unique!HashFunction m_hash; 122 SecureVector!ubyte m_ikey, m_okey; 123 }