1 /*
2 * Copyright (c) 2016, The OpenThread Authors.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. Neither the name of the copyright holder nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 /**
30 * @file
31 * This file implements AES-CCM.
32 */
33
34 #include "aes_ccm.hpp"
35
36 #include "common/code_utils.hpp"
37 #include "common/debug.hpp"
38 #include "common/encoding.hpp"
39
40 namespace ot {
41 namespace Crypto {
42
SetKey(const uint8_t * aKey,uint16_t aKeyLength)43 void AesCcm::SetKey(const uint8_t *aKey, uint16_t aKeyLength)
44 {
45 Key cryptoKey;
46
47 cryptoKey.Set(aKey, aKeyLength);
48 SetKey(cryptoKey);
49 }
50
SetKey(const Mac::KeyMaterial & aMacKey)51 void AesCcm::SetKey(const Mac::KeyMaterial &aMacKey)
52 {
53 Key cryptoKey;
54
55 aMacKey.ConvertToCryptoKey(cryptoKey);
56 SetKey(cryptoKey);
57 }
58
Init(uint32_t aHeaderLength,uint32_t aPlainTextLength,uint8_t aTagLength,const void * aNonce,uint8_t aNonceLength)59 void AesCcm::Init(uint32_t aHeaderLength,
60 uint32_t aPlainTextLength,
61 uint8_t aTagLength,
62 const void *aNonce,
63 uint8_t aNonceLength)
64 {
65 const uint8_t *nonceBytes = reinterpret_cast<const uint8_t *>(aNonce);
66 uint8_t blockLength = 0;
67 uint32_t len;
68 uint8_t L;
69 uint8_t i;
70
71 // Tag length must be even and within [kMinTagLength, kMaxTagLength]
72 OT_ASSERT(((aTagLength & 0x1) == 0) && (kMinTagLength <= aTagLength) && (aTagLength <= kMaxTagLength));
73
74 L = 0;
75
76 for (len = aPlainTextLength; len; len >>= 8)
77 {
78 L++;
79 }
80
81 if (L <= 1)
82 {
83 L = 2;
84 }
85
86 if (aNonceLength > 13)
87 {
88 aNonceLength = 13;
89 }
90
91 // increase L to match nonce len
92 if (L < (15 - aNonceLength))
93 {
94 L = 15 - aNonceLength;
95 }
96
97 // decrease nonceLength to match L
98 if (aNonceLength > (15 - L))
99 {
100 aNonceLength = 15 - L;
101 }
102
103 // setup initial block
104
105 // write flags
106 mBlock[0] = (static_cast<uint8_t>((aHeaderLength != 0) << 6) | static_cast<uint8_t>(((aTagLength - 2) >> 1) << 3) |
107 static_cast<uint8_t>(L - 1));
108
109 // write nonce
110 memcpy(&mBlock[1], nonceBytes, aNonceLength);
111
112 // write len
113 len = aPlainTextLength;
114
115 for (i = sizeof(mBlock) - 1; i > aNonceLength; i--)
116 {
117 mBlock[i] = len & 0xff;
118 len >>= 8;
119 }
120
121 // encrypt initial block
122 mEcb.Encrypt(mBlock, mBlock);
123
124 // process header
125 if (aHeaderLength > 0)
126 {
127 // process length
128 if (aHeaderLength < (65536U - 256U))
129 {
130 mBlock[blockLength++] ^= aHeaderLength >> 8;
131 mBlock[blockLength++] ^= aHeaderLength >> 0;
132 }
133 else
134 {
135 mBlock[blockLength++] ^= 0xff;
136 mBlock[blockLength++] ^= 0xfe;
137 mBlock[blockLength++] ^= aHeaderLength >> 24;
138 mBlock[blockLength++] ^= aHeaderLength >> 16;
139 mBlock[blockLength++] ^= aHeaderLength >> 8;
140 mBlock[blockLength++] ^= aHeaderLength >> 0;
141 }
142 }
143
144 // init counter
145 mCtr[0] = L - 1;
146 memcpy(&mCtr[1], nonceBytes, aNonceLength);
147 memset(&mCtr[aNonceLength + 1], 0, sizeof(mCtr) - aNonceLength - 1);
148
149 mNonceLength = aNonceLength;
150 mHeaderLength = aHeaderLength;
151 mHeaderCur = 0;
152 mPlainTextLength = aPlainTextLength;
153 mPlainTextCur = 0;
154 mBlockLength = blockLength;
155 mCtrLength = sizeof(mCtrPad);
156 mTagLength = aTagLength;
157 }
158
Header(const void * aHeader,uint32_t aHeaderLength)159 void AesCcm::Header(const void *aHeader, uint32_t aHeaderLength)
160 {
161 const uint8_t *headerBytes = reinterpret_cast<const uint8_t *>(aHeader);
162
163 OT_ASSERT(mHeaderCur + aHeaderLength <= mHeaderLength);
164
165 // process header
166 for (unsigned i = 0; i < aHeaderLength; i++)
167 {
168 if (mBlockLength == sizeof(mBlock))
169 {
170 mEcb.Encrypt(mBlock, mBlock);
171 mBlockLength = 0;
172 }
173
174 mBlock[mBlockLength++] ^= headerBytes[i];
175 }
176
177 mHeaderCur += aHeaderLength;
178
179 if (mHeaderCur == mHeaderLength)
180 {
181 // process remainder
182 if (mBlockLength != 0)
183 {
184 mEcb.Encrypt(mBlock, mBlock);
185 }
186
187 mBlockLength = 0;
188 }
189 }
190
Payload(void * aPlainText,void * aCipherText,uint32_t aLength,Mode aMode)191 void AesCcm::Payload(void *aPlainText, void *aCipherText, uint32_t aLength, Mode aMode)
192 {
193 uint8_t *plaintextBytes = reinterpret_cast<uint8_t *>(aPlainText);
194 uint8_t *ciphertextBytes = reinterpret_cast<uint8_t *>(aCipherText);
195 uint8_t byte;
196
197 OT_ASSERT(mPlainTextCur + aLength <= mPlainTextLength);
198
199 for (unsigned i = 0; i < aLength; i++)
200 {
201 if (mCtrLength == 16)
202 {
203 for (int j = sizeof(mCtr) - 1; j > mNonceLength; j--)
204 {
205 if (++mCtr[j])
206 {
207 break;
208 }
209 }
210
211 mEcb.Encrypt(mCtr, mCtrPad);
212 mCtrLength = 0;
213 }
214
215 if (aMode == kEncrypt)
216 {
217 byte = plaintextBytes[i];
218 ciphertextBytes[i] = byte ^ mCtrPad[mCtrLength++];
219 }
220 else
221 {
222 byte = ciphertextBytes[i] ^ mCtrPad[mCtrLength++];
223 plaintextBytes[i] = byte;
224 }
225
226 if (mBlockLength == sizeof(mBlock))
227 {
228 mEcb.Encrypt(mBlock, mBlock);
229 mBlockLength = 0;
230 }
231
232 mBlock[mBlockLength++] ^= byte;
233 }
234
235 mPlainTextCur += aLength;
236
237 if (mPlainTextCur >= mPlainTextLength)
238 {
239 if (mBlockLength != 0)
240 {
241 mEcb.Encrypt(mBlock, mBlock);
242 }
243
244 // reset counter
245 memset(&mCtr[mNonceLength + 1], 0, sizeof(mCtr) - mNonceLength - 1);
246 }
247 }
248
249 #if !OPENTHREAD_RADIO
Payload(Message & aMessage,uint16_t aOffset,uint16_t aLength,Mode aMode)250 void AesCcm::Payload(Message &aMessage, uint16_t aOffset, uint16_t aLength, Mode aMode)
251 {
252 Message::MutableChunk chunk;
253
254 aMessage.GetFirstChunk(aOffset, aLength, chunk);
255
256 while (chunk.GetLength() > 0)
257 {
258 Payload(chunk.GetBytes(), chunk.GetBytes(), chunk.GetLength(), aMode);
259 aMessage.GetNextChunk(aLength, chunk);
260 }
261 }
262 #endif
263
Finalize(void * aTag)264 void AesCcm::Finalize(void *aTag)
265 {
266 uint8_t *tagBytes = reinterpret_cast<uint8_t *>(aTag);
267
268 OT_ASSERT(mPlainTextCur == mPlainTextLength);
269
270 mEcb.Encrypt(mCtr, mCtrPad);
271
272 for (int i = 0; i < mTagLength; i++)
273 {
274 tagBytes[i] = mBlock[i] ^ mCtrPad[i];
275 }
276 }
277
GenerateNonce(const Mac::ExtAddress & aAddress,uint32_t aFrameCounter,uint8_t aSecurityLevel,uint8_t * aNonce)278 void AesCcm::GenerateNonce(const Mac::ExtAddress &aAddress,
279 uint32_t aFrameCounter,
280 uint8_t aSecurityLevel,
281 uint8_t *aNonce)
282 {
283 memcpy(aNonce, aAddress.m8, sizeof(Mac::ExtAddress));
284 aNonce += sizeof(Mac::ExtAddress);
285
286 BigEndian::WriteUint32(aFrameCounter, aNonce);
287 aNonce += sizeof(uint32_t);
288
289 aNonce[0] = aSecurityLevel;
290 }
291
292 } // namespace Crypto
293 } // namespace ot
294