summaryrefslogtreecommitdiffstats
path: root/src/direct_bt/SMPCrypto.cpp
blob: e4cd2d9ca54e8fae7f0b0f937ef423055c90274a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
/**
 * Derived from zephyr/subsys/bluetooth/host/smp.c
 *
 * Currently disabled since no use w/o private jau::uint256_t dhkey. *
 *
 * Copyright (c) 2022 Gothel Software e.K.
 * Copyright (c) 2017 Nordic Semiconductor ASA
 * Copyright (c) 2015-2016 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * _and_
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#include <memory>
#include <cstdint>

#include <jau/debug.hpp>

#include "SMPCrypto.hpp"

#define USE_SMP_CRYPTO_AES128_ 1
// #define USE_SMP_CRYPTO_CMAC_ 1
#define USE_SMP_CRYPTO_F5_ 0

inline constexpr const bool USE_SMP_CRYPTO_IRK = true;
inline constexpr const bool USE_SMP_CRYPTO_F5 = false;

#include <tinycrypt/constants.h>

#if USE_SMP_CRYPTO_AES128_
    #include <tinycrypt/aes.h>
#endif
#if USE_SMP_CRYPTO_CMAC_
    // #include <tinycrypt/utils.h>
    #include <tinycrypt/cmac_mode.h>
#endif

namespace direct_bt {

/**
 * @brief Swap one buffer content into another
 *
 * Copy the content of src buffer into dst buffer in reversed order,
 * i.e.: src[n] will be put in dst[end-n]
 * Where n is an index and 'end' the last index in both arrays.
 * The 2 memory pointers must be pointing to different areas, and have
 * a minimum size of given length.
 *
 * @param dst A valid pointer on a memory area where to copy the data in
 * @param src A valid pointer on a memory area where to copy the data from
 * @param length Size of both dst and src memory areas
 */
static inline void sys_memcpy_swap(void *dst, const void *src, size_t length)
{
    uint8_t *pdst = (uint8_t *)dst;
    const uint8_t *psrc = (const uint8_t *)src;

    /**
    __ASSERT(((psrc < pdst && (psrc + length) <= pdst) ||
          (psrc > pdst && (pdst + length) <= psrc)),
         "Source and destination buffers must not overlap"); */

    psrc += length - 1;

    for (; length > 0; length--) {
        *pdst++ = *psrc--;
    }
}

/**
 * @brief Swap buffer content
 *
 * In-place memory swap, where final content will be reversed.
 * I.e.: buf[n] will be put in buf[end-n]
 * Where n is an index and 'end' the last index of buf.
 *
 * @param buf A valid pointer on a memory area to swap
 * @param length Size of buf memory area
 */
static inline void sys_mem_swap(void *buf, size_t length)
{
    size_t i;

    for (i = 0; i < (length/2); i++) {
        uint8_t tmp = ((uint8_t *)buf)[i];

        ((uint8_t *)buf)[i] = ((uint8_t *)buf)[length - 1 - i];
        ((uint8_t *)buf)[length - 1 - i] = tmp;
    }
}

static int bt_encrypt_le(const uint8_t key[16], const uint8_t plaintext[16],
                         uint8_t enc_data[16])
{
    struct tc_aes_key_sched_struct s;
    uint8_t tmp[16];

    // BT_DBG("key %s", bt_hex(key, 16));
    // BT_DBG("plaintext %s", bt_hex(plaintext, 16));

    sys_memcpy_swap(tmp, key, 16);

    if (tc_aes128_set_encrypt_key(&s, tmp) == TC_CRYPTO_FAIL) {
        return -EINVAL;
    }

    sys_memcpy_swap(tmp, plaintext, 16);

    if (tc_aes_encrypt(enc_data, tmp, &s) == TC_CRYPTO_FAIL) {
        return -EINVAL;
    }

    sys_mem_swap(enc_data, 16);

    // BT_DBG("enc_data %s", bt_hex(enc_data, 16));

    return 0;
}

static int smp_crypto_ah(const uint8_t irk[16], const uint8_t r[3], uint8_t out[3])
{
    uint8_t res[16];
    int err;

    // BT_DBG("irk %s", bt_hex(irk, 16));
    // BT_DBG("r %s", bt_hex(r, 3));

    /* r' = padding || r */
    std::memcpy(res, r, 3);
    (void)std::memset(res + 3, 0, 13);

    err = bt_encrypt_le(irk, res, res);
    if (err) {
        return err;
    }

    /* The output of the random address function ah is:
     *      ah(h, r) = e(k, r') mod 2^24
     * The output of the security function e is then truncated to 24 bits
     * by taking the least significant 24 bits of the output of e as the
     * result of ah.
     */
    std::memcpy(out, res, 3);

    return 0;
}

bool smp_crypto_rpa_irk_matches(const jau::uint128dp_t irk, const EUI48& rpa) noexcept {
    if constexpr ( !USE_SMP_CRYPTO_IRK ) {
        return false;
    }
    // DBG_PRINT("IRK %s bdaddr %s", bt_hex(irk, 16), bt_addr_str(addr));
    uint8_t hash[3];
    int err = smp_crypto_ah(irk.data, &rpa.b[3], hash);
    if (err) {
        return false;
    }
    return !memcmp(rpa.b, hash, 3);
}

#if USE_SMP_CRYPTO_F5_

/**
 * Cypher based Message Authentication Code (CMAC) with AES 128 bit
 * @param key 128-bit key
 * @param in message to be authenticated
 * @param len length of message in octets
 * @param out 128-bit message authentication code
 * @return
 */
static bool bt_smp_aes_cmac(const jau::uint128dp_t& key, const uint8_t *in, size_t len, jau::uint128dp_t& out)
{
#if defined(USE_SMP_CRYPTO_CMAC_) && defined(USE_SMP_CRYPTO_AES128_)
    struct tc_aes_key_sched_struct sched;
    struct tc_cmac_struct state;

    if (tc_cmac_setup(&state, key.data, &sched) == TC_CRYPTO_FAIL) {
        return false;
    }
    if (tc_cmac_update(&state, in, len) == TC_CRYPTO_FAIL) {
        return false;
    }
    if (tc_cmac_final(out.data, &state) == TC_CRYPTO_FAIL) {
        return false;
    }
    return true;
#else
    (void)key;
    (void)in;
    (void)len;
    (void)out;
    return false;
#endif
}

/**
 * SMP F5 algo according to BT Core Spec
 * @param w dhkey in littleEndian, which we have no access to!
 * @param n1 rrnd in littleEndian
 * @param n2 prnd in littleEndian
 * @param a1 init_address (master)
 * @param a2 responder_address (Slave)
 * @param mackey result in littleEndian
 * @param ltk result in littleEndian
 * @return
 */
bool smp_crypto_f5(const jau::uint256_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
{
    if constexpr ( !USE_SMP_CRYPTO_F5 ) {
        return false;
    }

    /** Salt random number in MSB, see BT Core Spec */
    static const jau::uint128dp_t salt( { 0x6c, 0x88, 0x83, 0x91, 0xaa, 0xf5,
                                        0xa5, 0x38, 0x60, 0x37, 0x0b, 0xdb,
                                        0x5a, 0x60, 0x83, 0xbe } );

    /** Value bag, all in MSB */
    uint8_t m[53] = { 0x00, /* counter */
              0x62, 0x74, 0x6c, 0x65, /* keyID 'btle' in MSB */
              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*n1*/
              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*n2*/
              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* a1 */
              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* a2 */
              0x01, 0x00 /* length 256 in MSB */ };
    uint8_t ws[32];
    jau::uint128dp_t t, temp_;

    DBG_PRINT("w %s", jau::bytesHexString(w.data, 0, 32, true /* lsbFirst */).c_str());
    DBG_PRINT("n1 %s", jau::bytesHexString(n1.data, 0, 16, true /* lsbFirst */).c_str());
    DBG_PRINT("n2 %s", jau::bytesHexString(n2.data, 0, 16, true /* lsbFirst */).c_str());

    jau::bswap(ws, w.data, 32); // little -> big

    if( bt_smp_aes_cmac(salt, ws, 32, t) ) {
        return false;
    }
    DBG_PRINT("t %s", jau::bytesHexString(t, 0, 16, false /* lsbFirst */).c_str());

    jau::bswap(m + 5, n1.data, 16); // little -> big
    jau::bswap(m + 21, n2.data, 16); // little -> big
    m[37] = a1.type;
    if constexpr ( jau::isLittleEndian() ) {
        jau::bswap(m + 38, a1.address.b, 6); // little -> big
    }
    m[44] = a2.type;
    if constexpr ( jau::isLittleEndian() ) {
        jau::bswap(m + 45, a2.address.b, 6); // little -> big
    }

    if( !bt_smp_aes_cmac(t, m, sizeof(m), temp_) ) { // temp_ received mackey in bigEndian
        return false;
    }
    mackey = jau::bswap(temp_); // big -> little
    DBG_PRINT("mackey %1s", jau::bytesHexString(mackey.data, 0, 16, true /* lsbFirst */).c_str());

    /* counter for ltk is 1 */
    m[0] = 0x01;

    if( !bt_smp_aes_cmac(t, m, sizeof(m), temp_) ) { // temp_ received ltk in bigEndian
        return false;
    }
    ltk = jau::bswap(temp_); // big -> little
    DBG_PRINT("ltk %s", jau::bytesHexString(ltk.data, 0, 16, true /* lsbFirst */).c_str());

    return true;
}

#endif /* USE_SMP_CRYPTO_F5_ */

} // namespace direct_bt