Skip to content

Commit 4fa9526

Browse files
committed
feat(srv/nbus2): Add WIP of the NBUS2 protocol MAC service
1 parent dbf87a2 commit 4fa9526

10 files changed

+862
-0
lines changed

services/Kconfig

+4
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ menu "Low-level platform drivers"
5959
bool "NBUS peripheral bus driver for STM32 FDCAN"
6060
default n
6161

62+
config SERVICE_NBUS2
63+
bool "NBUS2 protocol MAC"
64+
default n
65+
6266
config SERVICE_NBUS_SWITCH
6367
bool "NBUS CAN-FD switch"
6468
default n

services/nbus2/SConscript

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Import("env")
2+
Import("objs")
3+
Import("conf")
4+
5+
if conf["SERVICE_NBUS2"] == "y":
6+
objs.append(env.Object(File(Glob("*.c"))))

services/nbus2/blake2s-siv.c

+124
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/* SPDX-License-Identifier: BSD-2-Clause
2+
*
3+
* Blake2s-SIV implementation
4+
*
5+
* Copyright (c) 2018-2023, Marek Koza (qyx@krtko.org)
6+
* All rights reserved.
7+
*/
8+
9+
#include <stdint.h>
10+
#include <stddef.h>
11+
#include <string.h>
12+
13+
#include <main.h>
14+
#include <blake2.h>
15+
16+
#include "blake2s-siv.h"
17+
18+
/**
19+
* @brief A SIV-mode authenticated encryption based on the Blake2s PRF
20+
*
21+
* The idea comes from https://web.cs.ucdavis.edu/~rogaway/papers/siv.pdf:
22+
* - the input is a key, header and a plaintext
23+
* - output of the first phase is a MAC, serving as a SIV (synthetic IV)
24+
* - output of the second phase is a ciphertext using a key and the SIV as an IV
25+
*
26+
* Using the SIV mode in this use case have the advantage of not requiring
27+
* keeping a message counter value between reboots. In an embedded device this
28+
* could be a significant challenge. We also save some bytes by not requiring
29+
* and explicit IV.
30+
*
31+
* Although the original paper doesn't use Blake2s, we made this change
32+
* considering the following:
33+
*
34+
* - only a single PRF is used to do a key derivation (to get an encryption key Ke
35+
* and a MAC key Km from a single shared key), a MAC (a keyed Blake2s can be
36+
* used as a MAC, even without the HMAC construct) and encryption (Blake2s
37+
* is used in a stream cipher mode where B2S(Ke | counter) is used to generate
38+
* a cipherstream). This reduces code and memory footprint.
39+
* - it serves as an "obstruction layer" on layer 2/3. A proper encryption with
40+
* a key exchange is done on higher layers. We may tolerate some fuckups here.
41+
* - don't roll your own crypto, huh!
42+
*
43+
* encryption:
44+
*
45+
* input key is K, input message is M, ciphertext is C
46+
* derive Ke, Km as (Ke | Km) = H(K)
47+
* compute a MAC serving as a IV: SIV = H(Km, M)
48+
* construct cipherstream as i:0->n, Cs = H(Ke, SIV | (i as a big endian uint32)) | H(..) | H(..)
49+
* encrypt C = M^Cs | SIV
50+
*
51+
* decryption:
52+
*
53+
* input key is K, ciphertext is C, output message is M
54+
* derive Ke, Km as (Ke | Km) = H(K)
55+
* construct cipherstream as i:0->n, Cs = H(Ke, SIV | (i as a big endian uint32)) | H(..) | H(..)
56+
* split the input Me | SIV = C
57+
* decrypt the message M = Me^Cs
58+
* validate MAC H(Km, M) == SIV?, return M if matches
59+
*/
60+
61+
62+
void b2s_derive_keys(const uint8_t *key, size_t len, uint8_t ke[B2S_KE_LEN], uint8_t km[B2S_KM_LEN]) {
63+
/* No length constraints for the key except it must be non-empty. */
64+
u_assert(key != NULL);
65+
u_assert(len > 0);
66+
67+
uint8_t res[B2S_KE_LEN + B2S_KM_LEN] = {0};
68+
blake2s(res, B2S_KE_LEN + B2S_KM_LEN, key, len, NULL, 0);
69+
70+
/* Now split the key. */
71+
memcpy(ke, res, B2S_KE_LEN);
72+
memcpy(km, res + B2S_KE_LEN, B2S_KM_LEN);
73+
}
74+
75+
76+
void b2s_crypt(uint8_t *buf, size_t len, const uint8_t *siv, size_t siv_len, const uint8_t ke[B2S_KE_LEN]) {
77+
u_assert(buf != NULL);
78+
u_assert(len > 0);
79+
u_assert(siv != NULL);
80+
u_assert(siv_len >= 4);
81+
u_assert(ke != NULL);
82+
83+
/* Block counter */
84+
uint8_t i = 0;
85+
while (len > 0) {
86+
/* Generate a BLAKE2S_OUTBYTES of keystream. */
87+
uint8_t keystream[BLAKE2S_OUTBYTES] = {0};
88+
blake2s_state s;
89+
blake2s_init_key(&s, BLAKE2S_OUTBYTES, ke, B2S_KE_LEN);
90+
blake2s_update(&s, siv, siv_len);
91+
uint8_t b[4] = {0, 0, 0, i};
92+
blake2s_update(&s, b, sizeof(b));
93+
blake2s_final(&s, keystream, BLAKE2S_OUTBYTES);
94+
95+
/* Crunch BLAKE2S_OUTBYTES or less in one step. */
96+
size_t block_len = len;
97+
if (block_len > BLAKE2S_OUTBYTES) {
98+
block_len = BLAKE2S_OUTBYTES;
99+
}
100+
for (size_t j = 0; j < block_len; j++) {
101+
buf[j] = buf[j] ^ keystream[j];
102+
}
103+
104+
/* Advance to the next block. */
105+
buf += block_len;
106+
len -= block_len;
107+
i++;
108+
}
109+
/* Now len = 0 and buf is at the end. */
110+
}
111+
112+
113+
void b2s_siv(const uint8_t *buf, size_t len, uint8_t *siv, size_t siv_len, const uint8_t km[B2S_KM_LEN]) {
114+
u_assert(buf != NULL);
115+
u_assert(len > 0);
116+
u_assert(siv != NULL);
117+
u_assert(siv_len >= 4);
118+
u_assert(km != NULL);
119+
120+
blake2s_state s;
121+
blake2s_init_key(&s, BLAKE2S_OUTBYTES, km, B2S_KM_LEN);
122+
blake2s_update(&s, buf, len);
123+
blake2s_final(&s, siv, siv_len);
124+
}

services/nbus2/blake2s-siv.h

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/* SPDX-License-Identifier: BSD-2-Clause
2+
*
3+
* Blake2s-SIV implementation
4+
*
5+
* Copyright (c) 2018-2023, Marek Koza (qyx@krtko.org)
6+
* All rights reserved.
7+
*/
8+
9+
#pragma once
10+
11+
#include <stdint.h>
12+
#include <stddef.h>
13+
14+
#define B2S_KE_LEN 16
15+
#define B2S_KM_LEN 16
16+
17+
/**
18+
* @brief Defive Ke and Km keys from an arbitrary-legnth key
19+
*/
20+
void b2s_derive_keys(const uint8_t *key, size_t len, uint8_t ke[B2S_KE_LEN], uint8_t km[B2S_KM_LEN]);
21+
22+
23+
/**
24+
* @brief Encrypt a message
25+
*
26+
* The encryption is reversible. This single function can be used bot for
27+
* encryption and decryption.
28+
*/
29+
void b2s_crypt(uint8_t *buf, size_t len, const uint8_t *siv, size_t siv_len, const uint8_t ke[B2S_KE_LEN]);
30+
31+
32+
/**
33+
* @brief Produce a message SIV
34+
*/
35+
void b2s_siv(const uint8_t *buf, size_t len, uint8_t *siv, size_t siv_len, const uint8_t km[B2S_KM_LEN]);
36+

services/nbus2/halfsiphash.c

+166
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
2+
/*
3+
SipHash reference C implementation
4+
5+
Copyright (c) 2016 Jean-Philippe Aumasson <jeanphilippe.aumasson@gmail.com>
6+
7+
To the extent possible under law, the author(s) have dedicated all copyright
8+
and related and neighboring rights to this software to the public domain
9+
worldwide. This software is distributed without any warranty.
10+
11+
You should have received a copy of the CC0 Public Domain Dedication along
12+
with
13+
this software. If not, see
14+
<http://creativecommons.org/publicdomain/zero/1.0/>.
15+
*/
16+
#include "halfsiphash.h"
17+
#include <assert.h>
18+
#include <stddef.h>
19+
#include <stdint.h>
20+
21+
/* default: SipHash-2-4 */
22+
#ifndef cROUNDS
23+
#define cROUNDS 2
24+
#endif
25+
#ifndef dROUNDS
26+
#define dROUNDS 4
27+
#endif
28+
29+
#define ROTL(x, b) (uint32_t)(((x) << (b)) | ((x) >> (32 - (b))))
30+
31+
#define U32TO8_LE(p, v) \
32+
(p)[0] = (uint8_t)((v)); \
33+
(p)[1] = (uint8_t)((v) >> 8); \
34+
(p)[2] = (uint8_t)((v) >> 16); \
35+
(p)[3] = (uint8_t)((v) >> 24);
36+
37+
#define U8TO32_LE(p) \
38+
(((uint32_t)((p)[0])) | ((uint32_t)((p)[1]) << 8) | \
39+
((uint32_t)((p)[2]) << 16) | ((uint32_t)((p)[3]) << 24))
40+
41+
#define SIPROUND \
42+
do { \
43+
v0 += v1; \
44+
v1 = ROTL(v1, 5); \
45+
v1 ^= v0; \
46+
v0 = ROTL(v0, 16); \
47+
v2 += v3; \
48+
v3 = ROTL(v3, 8); \
49+
v3 ^= v2; \
50+
v0 += v3; \
51+
v3 = ROTL(v3, 7); \
52+
v3 ^= v0; \
53+
v2 += v1; \
54+
v1 = ROTL(v1, 13); \
55+
v1 ^= v2; \
56+
v2 = ROTL(v2, 16); \
57+
} while (0)
58+
59+
60+
#ifdef DEBUG_SIPHASH
61+
#include <stdio.h>
62+
63+
#define TRACE \
64+
do { \
65+
printf("(%3zu) v0 %08" PRIx32 "\n", inlen, v0); \
66+
printf("(%3zu) v1 %08" PRIx32 "\n", inlen, v1); \
67+
printf("(%3zu) v2 %08" PRIx32 "\n", inlen, v2); \
68+
printf("(%3zu) v3 %08" PRIx32 "\n", inlen, v3); \
69+
} while (0)
70+
#else
71+
#define TRACE
72+
#endif
73+
74+
/*
75+
Computes a SipHash value
76+
*in: pointer to input data (read-only)
77+
inlen: input data length in bytes (any size_t value)
78+
*k: pointer to the key data (read-only), must be 8 bytes
79+
*out: pointer to output data (write-only), outlen bytes must be allocated
80+
outlen: length of the output in bytes, must be 4 or 8
81+
*/
82+
int halfsiphash(const void *in, const size_t inlen, const void *k, uint8_t *out,
83+
const size_t outlen) {
84+
85+
const unsigned char *ni = (const unsigned char *)in;
86+
const unsigned char *kk = (const unsigned char *)k;
87+
88+
assert((outlen == 4) || (outlen == 8));
89+
uint32_t v0 = 0;
90+
uint32_t v1 = 0;
91+
uint32_t v2 = UINT32_C(0x6c796765);
92+
uint32_t v3 = UINT32_C(0x74656462);
93+
uint32_t k0 = U8TO32_LE(kk);
94+
uint32_t k1 = U8TO32_LE(kk + 4);
95+
uint32_t m;
96+
int i;
97+
const unsigned char *end = ni + inlen - (inlen % sizeof(uint32_t));
98+
const int left = inlen & 3;
99+
uint32_t b = ((uint32_t)inlen) << 24;
100+
v3 ^= k1;
101+
v2 ^= k0;
102+
v1 ^= k1;
103+
v0 ^= k0;
104+
105+
if (outlen == 8)
106+
v1 ^= 0xee;
107+
108+
for (; ni != end; ni += 4) {
109+
m = U8TO32_LE(ni);
110+
v3 ^= m;
111+
112+
TRACE;
113+
for (i = 0; i < cROUNDS; ++i)
114+
SIPROUND;
115+
116+
v0 ^= m;
117+
}
118+
119+
switch (left) {
120+
case 3:
121+
b |= ((uint32_t)ni[2]) << 16;
122+
/* FALLTHRU */
123+
case 2:
124+
b |= ((uint32_t)ni[1]) << 8;
125+
/* FALLTHRU */
126+
case 1:
127+
b |= ((uint32_t)ni[0]);
128+
break;
129+
case 0:
130+
break;
131+
}
132+
133+
v3 ^= b;
134+
135+
TRACE;
136+
for (i = 0; i < cROUNDS; ++i)
137+
SIPROUND;
138+
139+
v0 ^= b;
140+
141+
if (outlen == 8)
142+
v2 ^= 0xee;
143+
else
144+
v2 ^= 0xff;
145+
146+
TRACE;
147+
for (i = 0; i < dROUNDS; ++i)
148+
SIPROUND;
149+
150+
b = v1 ^ v3;
151+
U32TO8_LE(out, b);
152+
153+
if (outlen == 4)
154+
return 0;
155+
156+
v1 ^= 0xdd;
157+
158+
TRACE;
159+
for (i = 0; i < dROUNDS; ++i)
160+
SIPROUND;
161+
162+
b = v1 ^ v3;
163+
U32TO8_LE(out + 4, b);
164+
165+
return 0;
166+
}

services/nbus2/halfsiphash.h

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
SipHash reference C implementation
3+
4+
Copyright (c) 2012-2021 Jean-Philippe Aumasson
5+
<jeanphilippe.aumasson@gmail.com>
6+
Copyright (c) 2012-2014 Daniel J. Bernstein <djb@cr.yp.to>
7+
8+
To the extent possible under law, the author(s) have dedicated all copyright
9+
and related and neighboring rights to this software to the public domain
10+
worldwide. This software is distributed without any warranty.
11+
12+
You should have received a copy of the CC0 Public Domain Dedication along
13+
with
14+
this software. If not, see
15+
<http://creativecommons.org/publicdomain/zero/1.0/>.
16+
*/
17+
18+
#include <inttypes.h>
19+
#include <string.h>
20+
21+
int halfsiphash(const void *in, const size_t inlen, const void *k, uint8_t *out,
22+
const size_t outlen);

0 commit comments

Comments
 (0)