mesh: Refactor CCM

Unifies the Mesh CCM implementation parts for encryption and decryption
into a crypt and an auth step, reducing stack usage and code size.

This change also brings several performance improvements, most notably
reducing copying of the nonce and unrolling the 16 byte XOR operations.

Performance for the Mesh worst case of a 382 byte payload with 16 bytes
of additional data (full transport encrypt with virtual address) goes
from an average of 889us to 780us on nRF52840 with default optimization
flags.

this is port of 234888095f440f0504d6794ffc16aadb339232c9
This commit is contained in:
Krzysztof Kopyściński
2021-01-12 14:05:19 +01:00
committed by Łukasz Rymanowski
parent 07928f548a
commit e088a9d0e9
+123 -228
View File
@@ -194,83 +194,91 @@ int bt_mesh_id128(const u8_t n[16], const char *s, u8_t out[16])
return bt_mesh_k1(n, 16, salt, id128, out);
}
static int bt_mesh_ccm_decrypt(const u8_t key[16], u8_t nonce[13],
const u8_t *enc_msg, size_t msg_len,
const u8_t *aad, size_t aad_len,
u8_t *out_msg, size_t mic_size)
static inline void xor16(u8_t *dst, const u8_t *a, const u8_t *b)
{
u8_t msg[16], pmsg[16], cmic[16], cmsg[16], Xn[16], mic[16];
u16_t last_blk, blk_cnt;
size_t i, j;
int err;
dst[0] = a[0] ^ b[0];
dst[1] = a[1] ^ b[1];
dst[2] = a[2] ^ b[2];
dst[3] = a[3] ^ b[3];
dst[4] = a[4] ^ b[4];
dst[5] = a[5] ^ b[5];
dst[6] = a[6] ^ b[6];
dst[7] = a[7] ^ b[7];
dst[8] = a[8] ^ b[8];
dst[9] = a[9] ^ b[9];
dst[10] = a[10] ^ b[10];
dst[11] = a[11] ^ b[11];
dst[12] = a[12] ^ b[12];
dst[13] = a[13] ^ b[13];
dst[14] = a[14] ^ b[14];
dst[15] = a[15] ^ b[15];
}
if (msg_len < 1 || aad_len >= 0xff00) {
return -EINVAL;
}
/* pmsg is assumed to have the nonce already present in bytes 1-13 */
static int ccm_calculate_X0(const u8_t key[16], const u8_t *aad, u8_t aad_len,
size_t mic_size, u8_t msg_len, u8_t b[16],
u8_t X0[16])
{
int i, j, err;
/* C_mic = e(AppKey, 0x01 || nonce || 0x0000) */
pmsg[0] = 0x01;
memcpy(pmsg + 1, nonce, 13);
sys_put_be16(0x0000, pmsg + 14);
/* X_0 = e(AppKey, flags || nonce || length) */
b[0] = (((mic_size - 2) / 2) << 3) | ((!!aad_len) << 6) | 0x01;
err = bt_encrypt_be(key, pmsg, cmic);
if (err) {
return err;
}
sys_put_be16(msg_len, b + 14);
/* X_0 = e(AppKey, 0x09 || nonce || length) */
if (mic_size == sizeof(u64_t)) {
pmsg[0] = 0x19 | (aad_len ? 0x40 : 0x00);
} else {
pmsg[0] = 0x09 | (aad_len ? 0x40 : 0x00);
}
memcpy(pmsg + 1, nonce, 13);
sys_put_be16(msg_len, pmsg + 14);
err = bt_encrypt_be(key, pmsg, Xn);
err = bt_encrypt_be(key, b, X0);
if (err) {
return err;
}
/* If AAD is being used to authenticate, include it here */
if (aad_len) {
sys_put_be16(aad_len, pmsg);
sys_put_be16(aad_len, b);
for (i = 0; i < sizeof(u16_t); i++) {
pmsg[i] = Xn[i] ^ pmsg[i];
b[i] = X0[i] ^ b[i];
}
j = 0;
aad_len += sizeof(u16_t);
while (aad_len > 16) {
do {
pmsg[i] = Xn[i] ^ aad[j];
b[i] = X0[i] ^ aad[j];
i++, j++;
} while (i < 16);
aad_len -= 16;
i = 0;
err = bt_encrypt_be(key, pmsg, Xn);
err = bt_encrypt_be(key, b, X0);
if (err) {
return err;
}
}
for (; i < aad_len; i++, j++) {
pmsg[i] = Xn[i] ^ aad[j];
b[i] = X0[i] ^ aad[j];
}
for (i = aad_len; i < 16; i++) {
pmsg[i] = Xn[i];
b[i] = X0[i];
}
err = bt_encrypt_be(key, pmsg, Xn);
err = bt_encrypt_be(key, b, X0);
if (err) {
return err;
}
}
return 0;
}
static int ccm_auth(const u8_t key[16], u8_t nonce[13],
const u8_t *cleartext_msg, size_t msg_len, const u8_t *aad,
size_t aad_len, u8_t *mic, size_t mic_size)
{
u8_t b[16], Xn[16], s0[16];
u16_t blk_cnt, last_blk;
int err, j, i;
last_blk = msg_len % 16;
blk_cnt = (msg_len + 15) / 16;
@@ -278,72 +286,95 @@ static int bt_mesh_ccm_decrypt(const u8_t key[16], u8_t nonce[13],
last_blk = 16;
}
b[0] = 0x01;
memcpy(b + 1, nonce, 13);
/* S[0] = e(AppKey, 0x01 || nonce || 0x0000) */
sys_put_be16(0x0000, &b[14]);
err = bt_encrypt_be(key, b, s0);
if (err) {
return err;
}
ccm_calculate_X0(key, aad, aad_len, mic_size, msg_len, b, Xn);
for (j = 0; j < blk_cnt; j++) {
/* X_1 = e(AppKey, X_0 ^ Payload[0-15]) */
if (j + 1 == blk_cnt) {
/* C_1 = e(AppKey, 0x01 || nonce || 0x0001) */
pmsg[0] = 0x01;
memcpy(pmsg + 1, nonce, 13);
sys_put_be16(j + 1, pmsg + 14);
err = bt_encrypt_be(key, pmsg, cmsg);
if (err) {
return err;
}
/* Encrypted = Payload[0-15] ^ C_1 */
for (i = 0; i < last_blk; i++) {
msg[i] = enc_msg[(j * 16) + i] ^ cmsg[i];
b[i] = Xn[i] ^ cleartext_msg[(j * 16) + i];
}
memcpy(out_msg + (j * 16), msg, last_blk);
/* X_1 = e(AppKey, X_0 ^ Payload[0-15]) */
for (i = 0; i < last_blk; i++) {
pmsg[i] = Xn[i] ^ msg[i];
}
for (i = last_blk; i < 16; i++) {
pmsg[i] = Xn[i] ^ 0x00;
}
err = bt_encrypt_be(key, pmsg, Xn);
if (err) {
return err;
}
/* MIC = C_mic ^ X_1 */
for (i = 0; i < sizeof(mic); i++) {
mic[i] = cmic[i] ^ Xn[i];
}
memcpy(&b[i], &Xn[i], 16 - i);
} else {
/* C_1 = e(AppKey, 0x01 || nonce || 0x0001) */
pmsg[0] = 0x01;
memcpy(pmsg + 1, nonce, 13);
sys_put_be16(j + 1, pmsg + 14);
xor16(b, Xn, &cleartext_msg[j * 16]);
}
err = bt_encrypt_be(key, pmsg, cmsg);
if (err) {
return err;
}
err = bt_encrypt_be(key, b, Xn);
if (err) {
return err;
}
}
/* MIC = C_mic ^ X_1 */
for (i = 0; i < mic_size; i++) {
mic[i] = s0[i] ^ Xn[i];
}
/* Encrypted = Payload[0-15] ^ C_1 */
for (i = 0; i < 16; i++) {
msg[i] = enc_msg[(j * 16) + i] ^ cmsg[i];
}
return 0;
}
memcpy(out_msg + (j * 16), msg, 16);
static int ccm_crypt(const u8_t key[16], const u8_t nonce[13],
const u8_t *in_msg, u8_t *out_msg, size_t msg_len)
{
u8_t a_i[16], s_i[16];
u16_t last_blk, blk_cnt;
size_t i, j;
int err;
/* X_1 = e(AppKey, X_0 ^ Payload[0-15]) */
for (i = 0; i < 16; i++) {
pmsg[i] = Xn[i] ^ msg[i];
}
last_blk = msg_len % 16;
blk_cnt = (msg_len + 15) / 16;
if (!last_blk) {
last_blk = 16U;
}
err = bt_encrypt_be(key, pmsg, Xn);
if (err) {
return err;
a_i[0] = 0x01;
memcpy(&a_i[1], nonce, 13);
for (j = 0; j < blk_cnt; j++) {
/* S_1 = e(AppKey, 0x01 || nonce || 0x0001) */
sys_put_be16(j + 1, &a_i[14]);
err = bt_encrypt_be(key, a_i, s_i);
if (err) {
return err;
}
/* Encrypted = Payload[0-15] ^ C_1 */
if (j < blk_cnt - 1) {
xor16(&out_msg[j * 16], s_i, &in_msg[j * 16]);
} else {
for (i = 0; i < last_blk; i++) {
out_msg[(j * 16) + i] =
in_msg[(j * 16) + i] ^ s_i[i];
}
}
}
return 0;
}
static int bt_mesh_ccm_decrypt(const u8_t key[16], u8_t nonce[13],
const u8_t *enc_msg, size_t msg_len,
const u8_t *aad, size_t aad_len,
u8_t *out_msg, size_t mic_size)
{
u8_t mic[16];
if (msg_len == 0 || aad_len >= 0xff00) {
return -EINVAL;
}
ccm_crypt(key, nonce, enc_msg, out_msg, msg_len);
ccm_auth(key, nonce, out_msg, msg_len, aad, aad_len, mic, mic_size);
if (memcmp(mic, enc_msg + msg_len, mic_size)) {
return -EBADMSG;
@@ -357,10 +388,7 @@ static int bt_mesh_ccm_encrypt(const u8_t key[16], u8_t nonce[13],
const u8_t *aad, size_t aad_len,
u8_t *out_msg, size_t mic_size)
{
u8_t pmsg[16], cmic[16], cmsg[16], mic[16], Xn[16];
u16_t blk_cnt, last_blk;
size_t i, j;
int err;
u8_t *mic = out_msg + msg_len;
BT_DBG("key %s", bt_hex(key, 16));
BT_DBG("nonce %s", bt_hex(nonce, 13));
@@ -372,142 +400,9 @@ static int bt_mesh_ccm_encrypt(const u8_t key[16], u8_t nonce[13],
return -EINVAL;
}
/* C_mic = e(AppKey, 0x01 || nonce || 0x0000) */
pmsg[0] = 0x01;
memcpy(pmsg + 1, nonce, 13);
sys_put_be16(0x0000, pmsg + 14);
ccm_auth(key, nonce, out_msg, msg_len, aad, aad_len, mic, mic_size);
err = bt_encrypt_be(key, pmsg, cmic);
if (err) {
return err;
}
/* X_0 = e(AppKey, 0x09 || nonce || length) */
if (mic_size == sizeof(u64_t)) {
pmsg[0] = 0x19 | (aad_len ? 0x40 : 0x00);
} else {
pmsg[0] = 0x09 | (aad_len ? 0x40 : 0x00);
}
memcpy(pmsg + 1, nonce, 13);
sys_put_be16(msg_len, pmsg + 14);
err = bt_encrypt_be(key, pmsg, Xn);
if (err) {
return err;
}
/* If AAD is being used to authenticate, include it here */
if (aad_len) {
sys_put_be16(aad_len, pmsg);
for (i = 0; i < sizeof(u16_t); i++) {
pmsg[i] = Xn[i] ^ pmsg[i];
}
j = 0;
aad_len += sizeof(u16_t);
while (aad_len > 16) {
do {
pmsg[i] = Xn[i] ^ aad[j];
i++, j++;
} while (i < 16);
aad_len -= 16;
i = 0;
err = bt_encrypt_be(key, pmsg, Xn);
if (err) {
return err;
}
}
for (; i < aad_len; i++, j++) {
pmsg[i] = Xn[i] ^ aad[j];
}
for (i = aad_len; i < 16; i++) {
pmsg[i] = Xn[i];
}
err = bt_encrypt_be(key, pmsg, Xn);
if (err) {
return err;
}
}
last_blk = msg_len % 16;
blk_cnt = (msg_len + 15) / 16;
if (!last_blk) {
last_blk = 16;
}
for (j = 0; j < blk_cnt; j++) {
if (j + 1 == blk_cnt) {
/* X_1 = e(AppKey, X_0 ^ Payload[0-15]) */
for (i = 0; i < last_blk; i++) {
pmsg[i] = Xn[i] ^ msg[(j * 16) + i];
}
for (i = last_blk; i < 16; i++) {
pmsg[i] = Xn[i] ^ 0x00;
}
err = bt_encrypt_be(key, pmsg, Xn);
if (err) {
return err;
}
/* MIC = C_mic ^ X_1 */
for (i = 0; i < sizeof(mic); i++) {
mic[i] = cmic[i] ^ Xn[i];
}
/* C_1 = e(AppKey, 0x01 || nonce || 0x0001) */
pmsg[0] = 0x01;
memcpy(pmsg + 1, nonce, 13);
sys_put_be16(j + 1, pmsg + 14);
err = bt_encrypt_be(key, pmsg, cmsg);
if (err) {
return err;
}
/* Encrypted = Payload[0-15] ^ C_1 */
for (i = 0; i < last_blk; i++) {
out_msg[(j * 16) + i] =
msg[(j * 16) + i] ^ cmsg[i];
}
} else {
/* X_1 = e(AppKey, X_0 ^ Payload[0-15]) */
for (i = 0; i < 16; i++) {
pmsg[i] = Xn[i] ^ msg[(j * 16) + i];
}
err = bt_encrypt_be(key, pmsg, Xn);
if (err) {
return err;
}
/* C_1 = e(AppKey, 0x01 || nonce || 0x0001) */
pmsg[0] = 0x01;
memcpy(pmsg + 1, nonce, 13);
sys_put_be16(j + 1, pmsg + 14);
err = bt_encrypt_be(key, pmsg, cmsg);
if (err) {
return err;
}
/* Encrypted = Payload[0-15] ^ C_N */
for (i = 0; i < 16; i++) {
out_msg[(j * 16) + i] =
msg[(j * 16) + i] ^ cmsg[i];
}
}
}
memcpy(out_msg + msg_len, mic, mic_size);
ccm_crypt(key, nonce, msg, out_msg, msg_len);
return 0;
}