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 <limits.h>
37
38 #include "common/code_utils.hpp"
39 #include "common/debug.hpp"
40 #include "common/encoding.hpp"
41
42 namespace ot {
43 namespace Crypto {
44
SetKey(const uint8_t * aKey,uint16_t aKeyLength)45 void AesCcm::SetKey(const uint8_t *aKey, uint16_t aKeyLength)
46 {
47 Key cryptoKey;
48
49 cryptoKey.Set(aKey, aKeyLength);
50 SetKey(cryptoKey);
51 }
52
SetKey(const Mac::KeyMaterial & aMacKey)53 void AesCcm::SetKey(const Mac::KeyMaterial &aMacKey)
54 {
55 Key cryptoKey;
56
57 aMacKey.ConvertToCryptoKey(cryptoKey);
58 SetKey(cryptoKey);
59 }
60
Init(uint32_t aHeaderLength,uint32_t aPlainTextLength,uint8_t aTagLength,const void * aNonce,uint8_t aNonceLength)61 void AesCcm::Init(uint32_t aHeaderLength,
62 uint32_t aPlainTextLength,
63 uint8_t aTagLength,
64 const void *aNonce,
65 uint8_t aNonceLength)
66 {
67 const uint8_t *nonceBytes = reinterpret_cast<const uint8_t *>(aNonce);
68 uint8_t blockLength = 0;
69 uint32_t len;
70 uint8_t L;
71 uint8_t i;
72
73 // Tag length must be even and within [kMinTagLength, kMaxTagLength]
74 OT_ASSERT(((aTagLength & 0x1) == 0) && (kMinTagLength <= aTagLength) && (aTagLength <= kMaxTagLength));
75
76 L = 0;
77
78 for (len = aPlainTextLength; len; len >>= 8)
79 {
80 L++;
81 }
82
83 if (L <= 1)
84 {
85 L = 2;
86 }
87
88 if (aNonceLength > 13)
89 {
90 aNonceLength = 13;
91 }
92
93 // increase L to match nonce len
94 if (L < (15 - aNonceLength))
95 {
96 L = 15 - aNonceLength;
97 }
98
99 // decrease nonceLength to match L
100 if (aNonceLength > (15 - L))
101 {
102 aNonceLength = 15 - L;
103 }
104
105 // setup initial block
106
107 // write flags
108 mBlock[0] = (static_cast<uint8_t>((aHeaderLength != 0) << 6) | static_cast<uint8_t>(((aTagLength - 2) >> 1) << 3) |
109 static_cast<uint8_t>(L - 1));
110
111 // write nonce
112 memcpy(&mBlock[1], nonceBytes, aNonceLength);
113
114 // write len
115 len = aPlainTextLength;
116
117 for (i = sizeof(mBlock) - 1; i > aNonceLength; i--)
118 {
119 mBlock[i] = len & 0xff;
120 len >>= 8;
121 }
122
123 // encrypt initial block
124 mEcb.Encrypt(mBlock, mBlock);
125
126 // process header
127 if (aHeaderLength > 0)
128 {
129 // process length
130 if (aHeaderLength < (65536U - 256U))
131 {
132 mBlock[blockLength++] ^= aHeaderLength >> 8;
133 mBlock[blockLength++] ^= aHeaderLength >> 0;
134 }
135 else
136 {
137 mBlock[blockLength++] ^= 0xff;
138 mBlock[blockLength++] ^= 0xfe;
139 mBlock[blockLength++] ^= aHeaderLength >> 24;
140 mBlock[blockLength++] ^= aHeaderLength >> 16;
141 mBlock[blockLength++] ^= aHeaderLength >> 8;
142 mBlock[blockLength++] ^= aHeaderLength >> 0;
143 }
144 }
145
146 // init counter
147 mCtr[0] = L - 1;
148 memcpy(&mCtr[1], nonceBytes, aNonceLength);
149 memset(&mCtr[aNonceLength + 1], 0, sizeof(mCtr) - aNonceLength - 1);
150
151 mNonceLength = aNonceLength;
152 mHeaderLength = aHeaderLength;
153 mHeaderCur = 0;
154 mPlainTextLength = aPlainTextLength;
155 mPlainTextCur = 0;
156 mBlockLength = blockLength;
157 mCtrLength = sizeof(mCtrPad);
158 mTagLength = aTagLength;
159 }
160
Header(const void * aHeader,uint32_t aHeaderLength)161 void AesCcm::Header(const void *aHeader, uint32_t aHeaderLength)
162 {
163 const uint8_t *headerBytes = reinterpret_cast<const uint8_t *>(aHeader);
164
165 OT_ASSERT(mHeaderCur + aHeaderLength <= mHeaderLength);
166
167 // process header
168 for (unsigned i = 0; i < aHeaderLength; i++)
169 {
170 if (mBlockLength == sizeof(mBlock))
171 {
172 mEcb.Encrypt(mBlock, mBlock);
173 mBlockLength = 0;
174 }
175
176 mBlock[mBlockLength++] ^= headerBytes[i];
177 }
178
179 mHeaderCur += aHeaderLength;
180
181 if (mHeaderCur == mHeaderLength)
182 {
183 // process remainder
184 if (mBlockLength != 0)
185 {
186 mEcb.Encrypt(mBlock, mBlock);
187 }
188
189 mBlockLength = 0;
190 }
191 }
192
Payload(void * aPlainText,void * aCipherText,uint32_t aLength,Mode aMode)193 void AesCcm::Payload(void *aPlainText, void *aCipherText, uint32_t aLength, Mode aMode)
194 {
195 uint8_t *plaintextBytes = reinterpret_cast<uint8_t *>(aPlainText);
196 uint8_t *ciphertextBytes = reinterpret_cast<uint8_t *>(aCipherText);
197 uint8_t byte;
198
199 OT_ASSERT(mPlainTextCur + aLength <= mPlainTextLength);
200
201 for (unsigned i = 0; i < aLength; i++)
202 {
203 if (mCtrLength == 16)
204 {
205 for (int j = sizeof(mCtr) - 1; j > mNonceLength; j--)
206 {
207 if (++mCtr[j])
208 {
209 break;
210 }
211 }
212
213 mEcb.Encrypt(mCtr, mCtrPad);
214 mCtrLength = 0;
215 }
216
217 if (aMode == kEncrypt)
218 {
219 byte = plaintextBytes[i];
220 ciphertextBytes[i] = byte ^ mCtrPad[mCtrLength++];
221 }
222 else
223 {
224 byte = ciphertextBytes[i] ^ mCtrPad[mCtrLength++];
225 plaintextBytes[i] = byte;
226 }
227
228 if (mBlockLength == sizeof(mBlock))
229 {
230 mEcb.Encrypt(mBlock, mBlock);
231 mBlockLength = 0;
232 }
233
234 mBlock[mBlockLength++] ^= byte;
235 }
236
237 mPlainTextCur += aLength;
238
239 if (mPlainTextCur >= mPlainTextLength)
240 {
241 if (mBlockLength != 0)
242 {
243 mEcb.Encrypt(mBlock, mBlock);
244 }
245
246 // reset counter
247 memset(&mCtr[mNonceLength + 1], 0, sizeof(mCtr) - mNonceLength - 1);
248 }
249 }
250
251 #if !OPENTHREAD_RADIO
Payload(Message & aMessage,uint16_t aOffset,uint16_t aLength,Mode aMode)252 void AesCcm::Payload(Message &aMessage, uint16_t aOffset, uint16_t aLength, Mode aMode)
253 {
254 Message::MutableChunk chunk;
255
256 aMessage.GetFirstChunk(aOffset, aLength, chunk);
257
258 while (chunk.GetLength() > 0)
259 {
260 Payload(chunk.GetBytes(), chunk.GetBytes(), chunk.GetLength(), aMode);
261 aMessage.GetNextChunk(aLength, chunk);
262 }
263 }
264 #endif
265
Finalize(void * aTag)266 void AesCcm::Finalize(void *aTag)
267 {
268 uint8_t *tagBytes = reinterpret_cast<uint8_t *>(aTag);
269
270 OT_ASSERT(mPlainTextCur == mPlainTextLength);
271
272 mEcb.Encrypt(mCtr, mCtrPad);
273
274 for (int i = 0; i < mTagLength; i++)
275 {
276 tagBytes[i] = mBlock[i] ^ mCtrPad[i];
277 }
278 }
279
GenerateNonce(const Mac::ExtAddress & aAddress,uint32_t aFrameCounter,uint8_t aSecurityLevel,uint8_t * aNonce)280 void AesCcm::GenerateNonce(const Mac::ExtAddress &aAddress,
281 uint32_t aFrameCounter,
282 uint8_t aSecurityLevel,
283 uint8_t *aNonce)
284 {
285 memcpy(aNonce, aAddress.m8, sizeof(Mac::ExtAddress));
286 aNonce += sizeof(Mac::ExtAddress);
287
288 Encoding::BigEndian::WriteUint32(aFrameCounter, aNonce);
289 aNonce += sizeof(uint32_t);
290
291 aNonce[0] = aSecurityLevel;
292 }
293
294 } // namespace Crypto
295 } // namespace ot
296