1 /** 2 * CMAC 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.cmac; 12 13 import botan.constants; 14 15 static if (BOTAN_HAS_CMAC): 16 17 import botan.utils.types; 18 import botan.mac.mac; 19 import botan.block.block_cipher; 20 import botan.utils.loadstor; 21 import botan.utils.xor_buf; 22 import botan.utils.mem_ops; 23 import std.conv : to; 24 /** 25 * CMAC, also known as OMAC1 26 */ 27 final class CMAC : MessageAuthenticationCode, BufferedComputation, SymmetricAlgorithm 28 { 29 public: 30 /* 31 * Return the name of this type 32 */ 33 override @property string name() const 34 { 35 return "CMAC(" ~ m_cipher.name ~ ")"; 36 } 37 38 override @property size_t outputLength() const { return m_cipher.blockSize(); } 39 /* 40 * Return a clone of this object 41 */ 42 override MessageAuthenticationCode clone() const 43 { 44 return new CMAC(m_cipher.clone()); 45 } 46 47 /* 48 * Clear memory of sensitive data 49 */ 50 void clear() 51 { 52 m_cipher.clear(); 53 zeroise(m_state); 54 zeroise(m_buffer); 55 zeroise(m_B); 56 zeroise(m_P); 57 m_position = 0; 58 } 59 60 KeyLengthSpecification keySpec() const 61 { 62 return m_cipher.keySpec(); 63 } 64 65 /** 66 * CMAC's polynomial doubling operation 67 * Params: 68 * input = the input 69 */ 70 static SecureVector!ubyte polyDouble(const ref SecureVector!ubyte input) 71 { 72 const bool top_carry = (input[0] & 0x80) != 0; 73 74 SecureVector!ubyte output = input.dup; 75 76 ubyte carry = 0; 77 for (size_t i = output.length; i != 0; --i) 78 { 79 ubyte temp = output[i-1]; 80 output.ptr[i-1] = cast(ubyte)( (temp << 1) | carry ); 81 carry = (temp >> 7); 82 } 83 84 if (top_carry) 85 { 86 switch(input.length) 87 { 88 case 8: 89 output.ptr[output.length-1] ^= 0x1B; 90 break; 91 case 16: 92 output.ptr[output.length-1] ^= 0x87; 93 break; 94 case 32: 95 output.ptr[output.length-2] ^= 0x4; 96 output.ptr[output.length-1] ^= 0x25; 97 break; 98 case 64: 99 output.ptr[output.length-2] ^= 0x1; 100 output.ptr[output.length-1] ^= 0x25; 101 break; 102 default: 103 throw new Exception("Unsupported CMAC size: " ~ input.length.to!string); 104 } 105 } 106 107 return output.move; 108 } 109 110 /** 111 * Params: 112 * cipher = the underlying block cipher to use 113 */ 114 this(BlockCipher cipher) 115 { 116 m_cipher = cipher; 117 if (m_cipher.blockSize() != 8 && m_cipher.blockSize() != 16 && 118 m_cipher.blockSize() != 32 && m_cipher.blockSize() != 64) 119 { 120 throw new InvalidArgument("CMAC cannot use the " ~ to!string(m_cipher.blockSize() * 8) ~ " bit cipher " ~ m_cipher.name); 121 } 122 123 m_state.resize(outputLength()); 124 m_buffer.resize(outputLength()); 125 m_B.resize(outputLength()); 126 m_P.resize(outputLength()); 127 m_position = 0; 128 } 129 130 protected: 131 /* 132 * Update an CMAC Calculation 133 */ 134 override void addData(const(ubyte)* input, size_t length) 135 { 136 bufferInsert(m_buffer, m_position, input, length); 137 if (m_position + length > outputLength()) 138 { 139 xorBuf(m_state, m_buffer, outputLength()); 140 m_cipher.encrypt(m_state); 141 input += (outputLength() - m_position); 142 length -= (outputLength() - m_position); 143 while (length > outputLength()) 144 { 145 xorBuf(m_state, input, outputLength()); 146 m_cipher.encrypt(m_state); 147 input += outputLength(); 148 length -= outputLength(); 149 } 150 copyMem(m_buffer.ptr, input, length); 151 m_position = 0; 152 } 153 m_position += length; 154 } 155 156 /* 157 * Finalize an CMAC Calculation 158 */ 159 override void finalResult(ubyte* mac) 160 { 161 xorBuf(m_state, m_buffer, m_position); 162 163 if (m_position == outputLength()) 164 { 165 xorBuf(m_state, m_B, outputLength()); 166 } 167 else 168 { 169 m_state.ptr[m_position] ^= 0x80; 170 xorBuf(m_state, m_P, outputLength()); 171 } 172 173 m_cipher.encrypt(m_state); 174 175 for (size_t i = 0; i != outputLength(); ++i) 176 mac[i] = m_state[i]; 177 178 zeroise(m_state); 179 zeroise(m_buffer); 180 m_position = 0; 181 } 182 183 /* 184 * CMAC Key Schedule 185 */ 186 override void keySchedule(const(ubyte)* key, size_t length) 187 { 188 clear(); 189 m_cipher.setKey(key, length); 190 m_cipher.encrypt(m_B); 191 m_B = polyDouble(m_B); 192 m_P = polyDouble(m_B); 193 } 194 195 196 Unique!BlockCipher m_cipher; 197 SecureVector!ubyte m_buffer, m_state, m_B, m_P; 198 size_t m_position; 199 }