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 }