Direct-BT v3.3.0-1-gc2d430c
Direct-BT - Direct Bluetooth Programming.
SMPCrypto.cpp
Go to the documentation of this file.
1/**
2 * Derived from zephyr/subsys/bluetooth/host/smp.c
3 *
4 * Currently disabled since no use w/o private jau::uint256_t dhkey. *
5 *
6 * Copyright (c) 2022 Gothel Software e.K.
7 * Copyright (c) 2017 Nordic Semiconductor ASA
8 * Copyright (c) 2015-2016 Intel Corporation
9 *
10 * SPDX-License-Identifier: Apache-2.0
11 *
12 * _and_
13 *
14 * Permission is hereby granted, free of charge, to any person obtaining
15 * a copy of this software and associated documentation files (the
16 * "Software"), to deal in the Software without restriction, including
17 * without limitation the rights to use, copy, modify, merge, publish,
18 * distribute, sublicense, and/or sell copies of the Software, and to
19 * permit persons to whom the Software is furnished to do so, subject to
20 * the following conditions:
21 *
22 * The above copyright notice and this permission notice shall be
23 * included in all copies or substantial portions of the Software.
24 *
25 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 */
33#include <memory>
34#include <cstdint>
35
36#include <jau/debug.hpp>
37
38#include "SMPCrypto.hpp"
39
40#define USE_SMP_CRYPTO_AES128_ 1
41// #define USE_SMP_CRYPTO_CMAC_ 1
42#define USE_SMP_CRYPTO_F5_ 0
43
44inline constexpr const bool USE_SMP_CRYPTO_IRK = true;
45inline constexpr const bool USE_SMP_CRYPTO_F5 = false;
46
47#include <tinycrypt/constants.h>
48
49#if USE_SMP_CRYPTO_AES128_
50 #include <tinycrypt/aes.h>
51#endif
52#if USE_SMP_CRYPTO_CMAC_
53 // #include <tinycrypt/utils.h>
54 #include <tinycrypt/cmac_mode.h>
55#endif
56
57namespace direct_bt {
58
59/**
60 * @brief Swap one buffer content into another
61 *
62 * Copy the content of src buffer into dst buffer in reversed order,
63 * i.e.: src[n] will be put in dst[end-n]
64 * Where n is an index and 'end' the last index in both arrays.
65 * The 2 memory pointers must be pointing to different areas, and have
66 * a minimum size of given length.
67 *
68 * @param dst A valid pointer on a memory area where to copy the data in
69 * @param src A valid pointer on a memory area where to copy the data from
70 * @param length Size of both dst and src memory areas
71 */
72static inline void sys_memcpy_swap(void *dst, const void *src, size_t length)
73{
74 uint8_t *pdst = (uint8_t *)dst;
75 const uint8_t *psrc = (const uint8_t *)src;
76
77 /**
78 __ASSERT(((psrc < pdst && (psrc + length) <= pdst) ||
79 (psrc > pdst && (pdst + length) <= psrc)),
80 "Source and destination buffers must not overlap"); */
81
82 psrc += length - 1;
83
84 for (; length > 0; length--) {
85 *pdst++ = *psrc--;
86 }
87}
88
89/**
90 * @brief Swap buffer content
91 *
92 * In-place memory swap, where final content will be reversed.
93 * I.e.: buf[n] will be put in buf[end-n]
94 * Where n is an index and 'end' the last index of buf.
95 *
96 * @param buf A valid pointer on a memory area to swap
97 * @param length Size of buf memory area
98 */
99static inline void sys_mem_swap(void *buf, size_t length)
100{
101 size_t i;
102
103 for (i = 0; i < (length/2); i++) {
104 uint8_t tmp = ((uint8_t *)buf)[i];
105
106 ((uint8_t *)buf)[i] = ((uint8_t *)buf)[length - 1 - i];
107 ((uint8_t *)buf)[length - 1 - i] = tmp;
108 }
109}
110
111static int bt_encrypt_le(const uint8_t key[16], const uint8_t plaintext[16],
112 uint8_t enc_data[16])
113{
114 struct tc_aes_key_sched_struct s;
115 uint8_t tmp[16];
116
117 // BT_DBG("key %s", bt_hex(key, 16));
118 // BT_DBG("plaintext %s", bt_hex(plaintext, 16));
119
120 sys_memcpy_swap(tmp, key, 16);
121
122 if (tc_aes128_set_encrypt_key(&s, tmp) == TC_CRYPTO_FAIL) {
123 return -EINVAL;
124 }
125
126 sys_memcpy_swap(tmp, plaintext, 16);
127
128 if (tc_aes_encrypt(enc_data, tmp, &s) == TC_CRYPTO_FAIL) {
129 return -EINVAL;
130 }
131
132 sys_mem_swap(enc_data, 16);
133
134 // BT_DBG("enc_data %s", bt_hex(enc_data, 16));
135
136 return 0;
137}
138
139static int smp_crypto_ah(const uint8_t irk[16], const uint8_t r[3], uint8_t out[3])
140{
141 uint8_t res[16];
142 int err;
143
144 // BT_DBG("irk %s", bt_hex(irk, 16));
145 // BT_DBG("r %s", bt_hex(r, 3));
146
147 /* r' = padding || r */
148 std::memcpy(res, r, 3);
149 (void)std::memset(res + 3, 0, 13);
150
151 err = bt_encrypt_le(irk, res, res);
152 if (err) {
153 return err;
154 }
155
156 /* The output of the random address function ah is:
157 * ah(h, r) = e(k, r') mod 2^24
158 * The output of the security function e is then truncated to 24 bits
159 * by taking the least significant 24 bits of the output of e as the
160 * result of ah.
161 */
162 std::memcpy(out, res, 3);
163
164 return 0;
165}
166
167bool smp_crypto_rpa_irk_matches(const jau::uint128dp_t irk, const EUI48& rpa) noexcept {
168 if constexpr ( !USE_SMP_CRYPTO_IRK ) {
169 return false;
170 }
171 // DBG_PRINT("IRK %s bdaddr %s", bt_hex(irk, 16), bt_addr_str(addr));
172 uint8_t hash[3];
173 int err = smp_crypto_ah(irk.data, &rpa.b[3], hash);
174 if (err) {
175 return false;
176 }
177 return !memcmp(rpa.b, hash, 3);
178}
179
180#if USE_SMP_CRYPTO_F5_
181
182/**
183 * Cypher based Message Authentication Code (CMAC) with AES 128 bit
184 * @param key 128-bit key
185 * @param in message to be authenticated
186 * @param len length of message in octets
187 * @param out 128-bit message authentication code
188 * @return
189 */
190static bool bt_smp_aes_cmac(const jau::uint128dp_t& key, const uint8_t *in, size_t len, jau::uint128dp_t& out)
191{
192#if defined(USE_SMP_CRYPTO_CMAC_) && defined(USE_SMP_CRYPTO_AES128_)
193 struct tc_aes_key_sched_struct sched;
194 struct tc_cmac_struct state;
195
196 if (tc_cmac_setup(&state, key.data, &sched) == TC_CRYPTO_FAIL) {
197 return false;
198 }
199 if (tc_cmac_update(&state, in, len) == TC_CRYPTO_FAIL) {
200 return false;
201 }
202 if (tc_cmac_final(out.data, &state) == TC_CRYPTO_FAIL) {
203 return false;
204 }
205 return true;
206#else
207 (void)key;
208 (void)in;
209 (void)len;
210 (void)out;
211 return false;
212#endif
213}
214
215/**
216 * SMP F5 algo according to BT Core Spec
217 * @param w dhkey in littleEndian, which we have no access to!
218 * @param n1 rrnd in littleEndian
219 * @param n2 prnd in littleEndian
220 * @param a1 init_address (master)
221 * @param a2 responder_address (Slave)
222 * @param mackey result in littleEndian
223 * @param ltk result in littleEndian
224 * @return
225 */
226bool smp_crypto_f5(const jau::uint256_t w, const jau::uint128dp_t n1, const jau::uint128dp_t n2,
227 const BDAddressAndType& a1, const BDAddressAndType& a2,
228 jau::uint128dp_t& mackey, jau::uint128dp_t& ltk) noexcept
229{
230 if constexpr ( !USE_SMP_CRYPTO_F5 ) {
231 return false;
232 }
233
234 /** Salt random number in MSB, see BT Core Spec */
235 static const jau::uint128dp_t salt( { 0x6c, 0x88, 0x83, 0x91, 0xaa, 0xf5,
236 0xa5, 0x38, 0x60, 0x37, 0x0b, 0xdb,
237 0x5a, 0x60, 0x83, 0xbe } );
238
239 /** Value bag, all in MSB */
240 uint8_t m[53] = { 0x00, /* counter */
241 0x62, 0x74, 0x6c, 0x65, /* keyID 'btle' in MSB */
242 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*n1*/
243 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
244 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*n2*/
245 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
246 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* a1 */
247 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* a2 */
248 0x01, 0x00 /* length 256 in MSB */ };
249 uint8_t ws[32];
250 jau::uint128dp_t t, temp_;
251
252 DBG_PRINT("w %s", jau::bytesHexString(w.data, 0, 32, true /* lsbFirst */).c_str());
253 DBG_PRINT("n1 %s", jau::bytesHexString(n1.data, 0, 16, true /* lsbFirst */).c_str());
254 DBG_PRINT("n2 %s", jau::bytesHexString(n2.data, 0, 16, true /* lsbFirst */).c_str());
255
256 jau::bswap(ws, w.data, 32); // little -> big
257
258 if( bt_smp_aes_cmac(salt, ws, 32, t) ) {
259 return false;
260 }
261 DBG_PRINT("t %s", jau::bytesHexString(t, 0, 16, false /* lsbFirst */).c_str());
262
263 jau::bswap(m + 5, n1.data, 16); // little -> big
264 jau::bswap(m + 21, n2.data, 16); // little -> big
265 m[37] = a1.type;
266 if constexpr ( jau::isLittleEndian() ) {
267 jau::bswap(m + 38, a1.address.b, 6); // little -> big
268 }
269 m[44] = a2.type;
270 if constexpr ( jau::isLittleEndian() ) {
271 jau::bswap(m + 45, a2.address.b, 6); // little -> big
272 }
273
274 if( !bt_smp_aes_cmac(t, m, sizeof(m), temp_) ) { // temp_ received mackey in bigEndian
275 return false;
276 }
277 mackey = jau::bswap(temp_); // big -> little
278 DBG_PRINT("mackey %1s", jau::bytesHexString(mackey.data, 0, 16, true /* lsbFirst */).c_str());
279
280 /* counter for ltk is 1 */
281 m[0] = 0x01;
282
283 if( !bt_smp_aes_cmac(t, m, sizeof(m), temp_) ) { // temp_ received ltk in bigEndian
284 return false;
285 }
286 ltk = jau::bswap(temp_); // big -> little
287 DBG_PRINT("ltk %s", jau::bytesHexString(ltk.data, 0, 16, true /* lsbFirst */).c_str());
288
289 return true;
290}
291
292#endif /* USE_SMP_CRYPTO_F5_ */
293
294} // namespace direct_bt
constexpr const bool USE_SMP_CRYPTO_IRK
Definition: SMPCrypto.cpp:44
constexpr const bool USE_SMP_CRYPTO_F5
Definition: SMPCrypto.cpp:45
#define DBG_PRINT(...)
Use for environment-variable environment::DEBUG conditional debug messages, prefix '[elapsed_time] De...
Definition: debug.hpp:52
constexpr uint16_t bswap(uint16_t const source) noexcept
Definition: byte_util.hpp:88
std::string bytesHexString(const void *data, const nsize_t offset, const nsize_t length, const bool lsbFirst, const bool lowerCase=true) noexcept
Produce a hexadecimal string representation of the given byte values.
static void sys_memcpy_swap(void *dst, const void *src, size_t length)
Swap one buffer content into another.
Definition: SMPCrypto.cpp:72
static int bt_encrypt_le(const uint8_t key[16], const uint8_t plaintext[16], uint8_t enc_data[16])
Definition: SMPCrypto.cpp:111
bool smp_crypto_rpa_irk_matches(const jau::uint128dp_t irk, const EUI48 &rpa) noexcept
Returns true if the given IRK matches the given random private address (RPA).
Definition: SMPCrypto.cpp:167
static int smp_crypto_ah(const uint8_t irk[16], const uint8_t r[3], uint8_t out[3])
Definition: SMPCrypto.cpp:139
static void sys_mem_swap(void *buf, size_t length)
Swap buffer content.
Definition: SMPCrypto.cpp:99
bool smp_crypto_f5(const jau::uint256dp_t w, const jau::uint128dp_t n1, const jau::uint128dp_t n2, const BDAddressAndType &a1, const BDAddressAndType &a2, jau::uint128dp_t &mackey, jau::uint128dp_t &ltk) noexcept
A packed 48 bit EUI-48 identifier, formerly known as MAC-48 or simply network device MAC address (Med...
Definition: eui48.hpp:324
A 128-bit packed uint8_t data array.
Definition: int_types.hpp:114
uint8_t data[16]
Definition: int_types.hpp:114