mirror of
https://git.kernel.org/pub/scm/bluetooth/bluez.git
synced 2024-11-15 16:24:28 +08:00
1169 lines
24 KiB
C
1169 lines
24 KiB
C
/*
|
|
*
|
|
* BlueZ - Bluetooth protocol stack for Linux
|
|
*
|
|
* Copyright (C) 2017 Intel Corporation. All rights reserved.
|
|
*
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <sys/socket.h>
|
|
|
|
#include <linux/if_alg.h>
|
|
|
|
#include <glib.h>
|
|
|
|
#ifndef SOL_ALG
|
|
#define SOL_ALG 279
|
|
#endif
|
|
|
|
#ifndef ALG_SET_AEAD_AUTHSIZE
|
|
#define ALG_SET_AEAD_AUTHSIZE 5
|
|
#endif
|
|
|
|
#include "src/shared/util.h"
|
|
#include "mesh/mesh-net.h"
|
|
#include "mesh/crypto.h"
|
|
|
|
static int alg_new(int fd, const void *keyval, socklen_t keylen,
|
|
size_t mic_size)
|
|
{
|
|
if (setsockopt(fd, SOL_ALG, ALG_SET_KEY, keyval, keylen) < 0) {
|
|
g_printerr("key");
|
|
return -1;
|
|
}
|
|
|
|
if (mic_size &&
|
|
setsockopt(fd, SOL_ALG,
|
|
ALG_SET_AEAD_AUTHSIZE, NULL, mic_size) < 0) {
|
|
g_printerr("taglen");
|
|
return -1;
|
|
}
|
|
|
|
/* FIXME: This should use accept4() with SOCK_CLOEXEC */
|
|
return accept(fd, NULL, 0);
|
|
}
|
|
|
|
static bool alg_encrypt(int fd, const void *inbuf, size_t inlen,
|
|
void *outbuf, size_t outlen)
|
|
{
|
|
__u32 alg_op = ALG_OP_ENCRYPT;
|
|
char cbuf[CMSG_SPACE(sizeof(alg_op))];
|
|
struct cmsghdr *cmsg;
|
|
struct msghdr msg;
|
|
struct iovec iov;
|
|
ssize_t len;
|
|
|
|
memset(cbuf, 0, sizeof(cbuf));
|
|
memset(&msg, 0, sizeof(msg));
|
|
|
|
msg.msg_control = cbuf;
|
|
msg.msg_controllen = sizeof(cbuf);
|
|
|
|
cmsg = CMSG_FIRSTHDR(&msg);
|
|
cmsg->cmsg_level = SOL_ALG;
|
|
cmsg->cmsg_type = ALG_SET_OP;
|
|
cmsg->cmsg_len = CMSG_LEN(sizeof(alg_op));
|
|
memcpy(CMSG_DATA(cmsg), &alg_op, sizeof(alg_op));
|
|
|
|
iov.iov_base = (void *) inbuf;
|
|
iov.iov_len = inlen;
|
|
|
|
msg.msg_iov = &iov;
|
|
msg.msg_iovlen = 1;
|
|
|
|
len = sendmsg(fd, &msg, 0);
|
|
if (len < 0)
|
|
return false;
|
|
|
|
len = read(fd, outbuf, outlen);
|
|
if (len < 0)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static int aes_ecb_setup(const uint8_t key[16])
|
|
{
|
|
struct sockaddr_alg salg;
|
|
int fd, nfd;
|
|
|
|
fd = socket(PF_ALG, SOCK_SEQPACKET | SOCK_CLOEXEC, 0);
|
|
if (fd < 0)
|
|
return -1;
|
|
|
|
memset(&salg, 0, sizeof(salg));
|
|
salg.salg_family = AF_ALG;
|
|
strcpy((char *) salg.salg_type, "skcipher");
|
|
strcpy((char *) salg.salg_name, "ecb(aes)");
|
|
|
|
if (bind(fd, (struct sockaddr *) &salg, sizeof(salg)) < 0) {
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
|
|
nfd = alg_new(fd, key, 16, 0);
|
|
|
|
close(fd);
|
|
|
|
return nfd;
|
|
}
|
|
|
|
static bool aes_ecb(int fd, const uint8_t plaintext[16], uint8_t encrypted[16])
|
|
{
|
|
return alg_encrypt(fd, plaintext, 16, encrypted, 16);
|
|
}
|
|
|
|
static void aes_ecb_destroy(int fd)
|
|
{
|
|
close(fd);
|
|
}
|
|
|
|
static bool aes_ecb_one(const uint8_t key[16],
|
|
const uint8_t plaintext[16], uint8_t encrypted[16])
|
|
{
|
|
bool result;
|
|
int fd;
|
|
|
|
fd = aes_ecb_setup(key);
|
|
if (fd < 0)
|
|
return false;
|
|
|
|
result = aes_ecb(fd, plaintext, encrypted);
|
|
|
|
aes_ecb_destroy(fd);
|
|
|
|
return result;
|
|
}
|
|
|
|
/* Maximum message length that can be passed to aes_cmac */
|
|
#define CMAC_MSG_MAX (64 + 64 + 17)
|
|
|
|
static int aes_cmac_setup(const uint8_t key[16])
|
|
{
|
|
struct sockaddr_alg salg;
|
|
int fd, nfd;
|
|
|
|
fd = socket(PF_ALG, SOCK_SEQPACKET | SOCK_CLOEXEC, 0);
|
|
if (fd < 0)
|
|
return -1;
|
|
|
|
memset(&salg, 0, sizeof(salg));
|
|
salg.salg_family = AF_ALG;
|
|
strcpy((char *) salg.salg_type, "hash");
|
|
strcpy((char *) salg.salg_name, "cmac(aes)");
|
|
|
|
if (bind(fd, (struct sockaddr *) &salg, sizeof(salg)) < 0) {
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
|
|
nfd = alg_new(fd, key, 16, 0);
|
|
|
|
close(fd);
|
|
|
|
return nfd;
|
|
}
|
|
|
|
static bool aes_cmac(int fd, const uint8_t *msg,
|
|
size_t msg_len, uint8_t res[16])
|
|
{
|
|
ssize_t len;
|
|
|
|
if (msg_len > CMAC_MSG_MAX)
|
|
return false;
|
|
|
|
len = send(fd, msg, msg_len, 0);
|
|
if (len < 0)
|
|
return false;
|
|
|
|
len = read(fd, res, 16);
|
|
if (len < 0)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static void aes_cmac_destroy(int fd)
|
|
{
|
|
close(fd);
|
|
}
|
|
|
|
static int aes_cmac_N_start(const uint8_t N[16])
|
|
{
|
|
int fd;
|
|
|
|
fd = aes_cmac_setup(N);
|
|
return fd;
|
|
}
|
|
|
|
static bool aes_cmac_one(const uint8_t key[16], const void *msg,
|
|
size_t msg_len, uint8_t res[16])
|
|
{
|
|
bool result;
|
|
int fd;
|
|
|
|
fd = aes_cmac_setup(key);
|
|
if (fd < 0)
|
|
return false;
|
|
|
|
result = aes_cmac(fd, msg, msg_len, res);
|
|
|
|
aes_cmac_destroy(fd);
|
|
|
|
return result;
|
|
}
|
|
|
|
bool mesh_crypto_aes_cmac(const uint8_t key[16], const uint8_t *msg,
|
|
size_t msg_len, uint8_t res[16])
|
|
{
|
|
return aes_cmac_one(key, msg, msg_len, res);
|
|
}
|
|
|
|
bool mesh_crypto_aes_ccm_encrypt(const uint8_t nonce[13], const uint8_t key[16],
|
|
const uint8_t *aad, uint16_t aad_len,
|
|
const uint8_t *msg, uint16_t msg_len,
|
|
uint8_t *out_msg, void *out_mic,
|
|
size_t mic_size)
|
|
{
|
|
uint8_t pmsg[16], cmic[16], cmsg[16];
|
|
uint8_t mic[16], Xn[16];
|
|
uint16_t blk_cnt, last_blk;
|
|
bool result;
|
|
size_t i, j;
|
|
int fd;
|
|
|
|
if (aad_len >= 0xff00) {
|
|
g_printerr("Unsupported AAD size");
|
|
return false;
|
|
}
|
|
|
|
fd = aes_ecb_setup(key);
|
|
if (fd < 0)
|
|
return false;
|
|
|
|
/* C_mic = e(AppKey, 0x01 || nonce || 0x0000) */
|
|
pmsg[0] = 0x01;
|
|
memcpy(pmsg + 1, nonce, 13);
|
|
put_be16(0x0000, pmsg + 14);
|
|
|
|
result = aes_ecb(fd, pmsg, cmic);
|
|
if (!result)
|
|
goto done;
|
|
|
|
/* X_0 = e(AppKey, 0x09 || nonce || length) */
|
|
if (mic_size == sizeof(uint64_t))
|
|
pmsg[0] = 0x19 | (aad_len ? 0x40 : 0x00);
|
|
else
|
|
pmsg[0] = 0x09 | (aad_len ? 0x40 : 0x00);
|
|
|
|
memcpy(pmsg + 1, nonce, 13);
|
|
put_be16(msg_len, pmsg + 14);
|
|
|
|
result = aes_ecb(fd, pmsg, Xn);
|
|
if (!result)
|
|
goto done;
|
|
|
|
/* If AAD is being used to authenticate, include it here */
|
|
if (aad_len) {
|
|
put_be16(aad_len, pmsg);
|
|
|
|
for (i = 0; i < sizeof(uint16_t); i++)
|
|
pmsg[i] = Xn[i] ^ pmsg[i];
|
|
|
|
j = 0;
|
|
aad_len += sizeof(uint16_t);
|
|
while (aad_len > 16) {
|
|
do {
|
|
pmsg[i] = Xn[i] ^ aad[j];
|
|
i++, j++;
|
|
} while (i < 16);
|
|
|
|
aad_len -= 16;
|
|
i = 0;
|
|
|
|
result = aes_ecb(fd, pmsg, Xn);
|
|
if (!result)
|
|
goto done;
|
|
}
|
|
|
|
for (i = 0; i < aad_len; i++, j++)
|
|
pmsg[i] = Xn[i] ^ aad[j];
|
|
|
|
for (i = aad_len; i < 16; i++)
|
|
pmsg[i] = Xn[i];
|
|
|
|
result = aes_ecb(fd, pmsg, Xn);
|
|
if (!result)
|
|
goto done;
|
|
}
|
|
|
|
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;
|
|
|
|
result = aes_ecb(fd, pmsg, Xn);
|
|
if (!result)
|
|
goto done;
|
|
|
|
/* 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);
|
|
put_be16(j + 1, pmsg + 14);
|
|
|
|
result = aes_ecb(fd, pmsg, cmsg);
|
|
if (!result)
|
|
goto done;
|
|
|
|
if (out_msg) {
|
|
/* 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];
|
|
|
|
result = aes_ecb(fd, pmsg, Xn);
|
|
if (!result)
|
|
goto done;
|
|
|
|
/* C_1 = e(AppKey, 0x01 || nonce || 0x0001) */
|
|
pmsg[0] = 0x01;
|
|
memcpy(pmsg + 1, nonce, 13);
|
|
put_be16(j + 1, pmsg + 14);
|
|
|
|
result = aes_ecb(fd, pmsg, cmsg);
|
|
if (!result)
|
|
goto done;
|
|
|
|
if (out_msg) {
|
|
/* Encrypted = Payload[0-15] ^ C_N */
|
|
for (i = 0; i < 16; i++)
|
|
out_msg[(j * 16) + i] =
|
|
msg[(j * 16) + i] ^ cmsg[i];
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
if (out_msg)
|
|
memcpy(out_msg + msg_len, mic, mic_size);
|
|
|
|
if (out_mic) {
|
|
switch (mic_size) {
|
|
case sizeof(uint32_t):
|
|
*(uint32_t *)out_mic = get_be32(mic);
|
|
break;
|
|
case sizeof(uint64_t):
|
|
*(uint64_t *)out_mic = get_be64(mic);
|
|
break;
|
|
default:
|
|
g_printerr("Unsupported MIC size");
|
|
}
|
|
}
|
|
|
|
done:
|
|
aes_ecb_destroy(fd);
|
|
|
|
return result;
|
|
}
|
|
|
|
bool mesh_crypto_aes_ccm_decrypt(const uint8_t nonce[13], const uint8_t key[16],
|
|
const uint8_t *aad, uint16_t aad_len,
|
|
const uint8_t *enc_msg, uint16_t enc_msg_len,
|
|
uint8_t *out_msg, void *out_mic,
|
|
size_t mic_size)
|
|
{
|
|
uint8_t msg[16], pmsg[16], cmic[16], cmsg[16], Xn[16];
|
|
uint8_t mic[16];
|
|
uint16_t msg_len = enc_msg_len - mic_size;
|
|
uint16_t last_blk, blk_cnt;
|
|
bool result;
|
|
size_t i, j;
|
|
int fd;
|
|
|
|
if (enc_msg_len < 5 || aad_len >= 0xff00)
|
|
return false;
|
|
|
|
fd = aes_ecb_setup(key);
|
|
if (fd < 0)
|
|
return false;
|
|
|
|
/* C_mic = e(AppKey, 0x01 || nonce || 0x0000) */
|
|
pmsg[0] = 0x01;
|
|
memcpy(pmsg + 1, nonce, 13);
|
|
put_be16(0x0000, pmsg + 14);
|
|
|
|
result = aes_ecb(fd, pmsg, cmic);
|
|
if (!result)
|
|
goto done;
|
|
|
|
/* X_0 = e(AppKey, 0x09 || nonce || length) */
|
|
if (mic_size == sizeof(uint64_t))
|
|
pmsg[0] = 0x19 | (aad_len ? 0x40 : 0x00);
|
|
else
|
|
pmsg[0] = 0x09 | (aad_len ? 0x40 : 0x00);
|
|
|
|
memcpy(pmsg + 1, nonce, 13);
|
|
put_be16(msg_len, pmsg + 14);
|
|
|
|
result = aes_ecb(fd, pmsg, Xn);
|
|
if (!result)
|
|
goto done;
|
|
|
|
/* If AAD is being used to authenticate, include it here */
|
|
if (aad_len) {
|
|
put_be16(aad_len, pmsg);
|
|
|
|
for (i = 0; i < sizeof(uint16_t); i++)
|
|
pmsg[i] = Xn[i] ^ pmsg[i];
|
|
|
|
j = 0;
|
|
aad_len += sizeof(uint16_t);
|
|
while (aad_len > 16) {
|
|
do {
|
|
pmsg[i] = Xn[i] ^ aad[j];
|
|
i++, j++;
|
|
} while (i < 16);
|
|
|
|
aad_len -= 16;
|
|
i = 0;
|
|
|
|
result = aes_ecb(fd, pmsg, Xn);
|
|
if (!result)
|
|
goto done;
|
|
}
|
|
|
|
for (i = 0; i < aad_len; i++, j++)
|
|
pmsg[i] = Xn[i] ^ aad[j];
|
|
|
|
for (i = aad_len; i < 16; i++)
|
|
pmsg[i] = Xn[i];
|
|
|
|
result = aes_ecb(fd, pmsg, Xn);
|
|
if (!result)
|
|
goto done;
|
|
}
|
|
|
|
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) {
|
|
/* C_1 = e(AppKey, 0x01 || nonce || 0x0001) */
|
|
pmsg[0] = 0x01;
|
|
memcpy(pmsg + 1, nonce, 13);
|
|
put_be16(j + 1, pmsg + 14);
|
|
|
|
result = aes_ecb(fd, pmsg, cmsg);
|
|
if (!result)
|
|
goto done;
|
|
|
|
/* Encrypted = Payload[0-15] ^ C_1 */
|
|
for (i = 0; i < last_blk; i++)
|
|
msg[i] = enc_msg[(j * 16) + i] ^ cmsg[i];
|
|
|
|
if (out_msg)
|
|
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;
|
|
|
|
result = aes_ecb(fd, pmsg, Xn);
|
|
if (!result)
|
|
goto done;
|
|
|
|
/* MIC = C_mic ^ X_1 */
|
|
for (i = 0; i < sizeof(mic); i++)
|
|
mic[i] = cmic[i] ^ Xn[i];
|
|
} else {
|
|
/* C_1 = e(AppKey, 0x01 || nonce || 0x0001) */
|
|
pmsg[0] = 0x01;
|
|
memcpy(pmsg + 1, nonce, 13);
|
|
put_be16(j + 1, pmsg + 14);
|
|
|
|
result = aes_ecb(fd, pmsg, cmsg);
|
|
if (!result)
|
|
goto done;
|
|
|
|
/* Encrypted = Payload[0-15] ^ C_1 */
|
|
for (i = 0; i < 16; i++)
|
|
msg[i] = enc_msg[(j * 16) + i] ^ cmsg[i];
|
|
|
|
if (out_msg)
|
|
memcpy(out_msg + (j * 16), msg, 16);
|
|
|
|
/* X_1 = e(AppKey, X_0 ^ Payload[0-15]) */
|
|
for (i = 0; i < 16; i++)
|
|
pmsg[i] = Xn[i] ^ msg[i];
|
|
|
|
result = aes_ecb(fd, pmsg, Xn);
|
|
if (!result)
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
switch (mic_size) {
|
|
case sizeof(uint32_t):
|
|
if (out_mic)
|
|
*(uint32_t *)out_mic = get_be32(mic);
|
|
else if (get_be32(enc_msg + enc_msg_len - mic_size) !=
|
|
get_be32(mic))
|
|
result = false;
|
|
break;
|
|
|
|
case sizeof(uint64_t):
|
|
if (out_mic)
|
|
*(uint64_t *)out_mic = get_be64(mic);
|
|
else if (get_be64(enc_msg + enc_msg_len - mic_size) !=
|
|
get_be64(mic))
|
|
result = false;
|
|
break;
|
|
|
|
default:
|
|
g_printerr("Unsupported MIC size");
|
|
result = false;
|
|
}
|
|
|
|
done:
|
|
aes_ecb_destroy(fd);
|
|
|
|
return result;
|
|
}
|
|
|
|
bool mesh_crypto_k1(const uint8_t ikm[16], const uint8_t salt[16],
|
|
const void *info, size_t info_len, uint8_t okm[16])
|
|
{
|
|
uint8_t res[16];
|
|
|
|
if (!aes_cmac_one(salt, ikm, 16, res))
|
|
return false;
|
|
|
|
return aes_cmac_one(res, info, info_len, okm);
|
|
}
|
|
|
|
bool mesh_crypto_k2(const uint8_t n[16], const uint8_t *p, size_t p_len,
|
|
uint8_t net_id[1],
|
|
uint8_t enc_key[16],
|
|
uint8_t priv_key[16])
|
|
{
|
|
int fd;
|
|
uint8_t output[16];
|
|
uint8_t t[16];
|
|
uint8_t *stage;
|
|
bool success = false;
|
|
|
|
stage = g_malloc(sizeof(output) + p_len + 1);
|
|
if (stage == NULL)
|
|
return false;
|
|
|
|
if (!mesh_crypto_s1("smk2", 4, stage))
|
|
goto fail;
|
|
|
|
if (!aes_cmac_one(stage, n, 16, t))
|
|
goto fail;
|
|
|
|
fd = aes_cmac_N_start(t);
|
|
if (fd < 0)
|
|
goto fail;
|
|
|
|
memcpy(stage, p, p_len);
|
|
stage[p_len] = 1;
|
|
|
|
if(!aes_cmac(fd, stage, p_len + 1, output))
|
|
goto done;
|
|
|
|
net_id[0] = output[15] & 0x7f;
|
|
|
|
memcpy(stage, output, 16);
|
|
memcpy(stage + 16, p, p_len);
|
|
stage[p_len + 16] = 2;
|
|
|
|
if(!aes_cmac(fd, stage, p_len + 16 + 1, output))
|
|
goto done;
|
|
|
|
memcpy(enc_key, output, 16);
|
|
|
|
memcpy(stage, output, 16);
|
|
memcpy(stage + 16, p, p_len);
|
|
stage[p_len + 16] = 3;
|
|
|
|
if(!aes_cmac(fd, stage, p_len + 16 + 1, output))
|
|
goto done;
|
|
|
|
memcpy(priv_key, output, 16);
|
|
success = true;
|
|
|
|
done:
|
|
aes_cmac_destroy(fd);
|
|
fail:
|
|
g_free(stage);
|
|
|
|
return success;
|
|
}
|
|
|
|
static bool crypto_128(const uint8_t n[16], const char *s, uint8_t out128[16])
|
|
{
|
|
uint8_t id128[] = { 'i', 'd', '1', '2', '8', 0x01 };
|
|
uint8_t salt[16];
|
|
|
|
if (!mesh_crypto_s1(s, 4, salt))
|
|
return false;
|
|
|
|
return mesh_crypto_k1(n, salt, id128, sizeof(id128), out128);
|
|
}
|
|
|
|
bool mesh_crypto_nkik(const uint8_t n[16], uint8_t identity_key[16])
|
|
{
|
|
return crypto_128(n, "nkik", identity_key);
|
|
}
|
|
|
|
static bool identity_calc(const uint8_t net_key[16], uint16_t addr,
|
|
bool check, uint8_t id[16])
|
|
{
|
|
uint8_t id_key[16];
|
|
uint8_t tmp[16];
|
|
|
|
if (!mesh_crypto_nkik(net_key, id_key))
|
|
return false;
|
|
|
|
memset(tmp, 0, sizeof(tmp));
|
|
put_be16(addr, tmp + 14);
|
|
|
|
if (check) {
|
|
memcpy(tmp + 6, id + 8, 8);
|
|
} else {
|
|
mesh_get_random_bytes(tmp + 6, 8);
|
|
memcpy(id + 8, tmp + 6, 8);
|
|
}
|
|
|
|
if (!aes_ecb_one(id_key, tmp, tmp))
|
|
return false;
|
|
|
|
if (check)
|
|
return (memcmp(id, tmp + 8, 8) == 0);
|
|
|
|
memcpy(id, tmp + 8, 8);
|
|
return true;
|
|
}
|
|
|
|
bool mesh_crypto_identity(const uint8_t net_key[16], uint16_t addr,
|
|
uint8_t id[16])
|
|
{
|
|
return identity_calc(net_key, addr, false, id);
|
|
}
|
|
|
|
bool mesh_crypto_identity_check(const uint8_t net_key[16], uint16_t addr,
|
|
uint8_t id[16])
|
|
{
|
|
return identity_calc(net_key, addr, true, id);
|
|
}
|
|
|
|
bool mesh_crypto_nkbk(const uint8_t n[16], uint8_t beacon_key[16])
|
|
{
|
|
return crypto_128(n, "nkbk", beacon_key);
|
|
}
|
|
|
|
bool mesh_crypto_k3(const uint8_t n[16], uint8_t out64[8])
|
|
{
|
|
uint8_t tmp[16];
|
|
uint8_t t[16];
|
|
uint8_t id64[] = { 'i', 'd', '6', '4', 0x01 };
|
|
|
|
if (!mesh_crypto_s1("smk3", 4, tmp))
|
|
return false;
|
|
|
|
if (!aes_cmac_one(tmp, n, 16, t))
|
|
return false;
|
|
|
|
if (!aes_cmac_one(t, id64, sizeof(id64), tmp))
|
|
return false;
|
|
|
|
memcpy(out64, tmp + 8, 8);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool mesh_crypto_k4(const uint8_t a[16], uint8_t out6[1])
|
|
{
|
|
uint8_t tmp[16];
|
|
uint8_t t[16];
|
|
uint8_t id6[] = { 'i', 'd', '6', 0x01 };
|
|
|
|
if (!mesh_crypto_s1("smk4", 4, tmp))
|
|
return false;
|
|
|
|
if (!aes_cmac_one(tmp, a, 16, t))
|
|
return false;
|
|
|
|
if (!aes_cmac_one(t, id6, sizeof(id6), tmp))
|
|
return false;
|
|
|
|
out6[0] = tmp[15] & 0x3f;
|
|
return true;
|
|
}
|
|
|
|
bool mesh_crypto_beacon_cmac(const uint8_t encryption_key[16],
|
|
const uint8_t network_id[8],
|
|
uint32_t iv_index, bool kr, bool iu,
|
|
uint64_t *cmac)
|
|
{
|
|
uint8_t msg[13], tmp[16];
|
|
|
|
if (!cmac)
|
|
return false;
|
|
|
|
msg[0] = kr ? 0x01 : 0x00;
|
|
msg[0] |= iu ? 0x02 : 0x00;
|
|
memcpy(msg + 1, network_id, 8);
|
|
put_be32(iv_index, msg + 9);
|
|
|
|
if (!aes_cmac_one(encryption_key, msg, 13, tmp))
|
|
return false;
|
|
|
|
*cmac = get_be64(tmp);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool mesh_crypto_network_nonce(bool ctl, uint8_t ttl, uint32_t seq,
|
|
uint16_t src, uint32_t iv_index,
|
|
uint8_t nonce[13])
|
|
{
|
|
nonce[0] = 0;
|
|
nonce[1] = (ttl & TTL_MASK) | (ctl ? CTL : 0x00);
|
|
nonce[2] = (seq >> 16) & 0xff;
|
|
nonce[3] = (seq >> 8) & 0xff;
|
|
nonce[4] = seq & 0xff;
|
|
|
|
/* SRC */
|
|
put_be16(src, nonce + 5);
|
|
|
|
put_be16(0, nonce + 7);
|
|
|
|
/* IV Index */
|
|
put_be32(iv_index, nonce + 9);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool mesh_crypto_network_encrypt(bool ctl, uint8_t ttl,
|
|
uint32_t seq, uint16_t src,
|
|
uint32_t iv_index,
|
|
const uint8_t net_key[16],
|
|
const uint8_t *enc_msg, uint8_t enc_msg_len,
|
|
uint8_t *out, void *net_mic)
|
|
{
|
|
uint8_t nonce[13];
|
|
|
|
if (!mesh_crypto_network_nonce(ctl, ttl, seq, src, iv_index, nonce))
|
|
return false;
|
|
|
|
return mesh_crypto_aes_ccm_encrypt(nonce, net_key,
|
|
NULL, 0, enc_msg,
|
|
enc_msg_len, out,
|
|
net_mic,
|
|
ctl ? sizeof(uint64_t) : sizeof(uint32_t));
|
|
}
|
|
|
|
bool mesh_crypto_network_decrypt(bool ctl, uint8_t ttl,
|
|
uint32_t seq, uint16_t src,
|
|
uint32_t iv_index,
|
|
const uint8_t net_key[16],
|
|
const uint8_t *enc_msg, uint8_t enc_msg_len,
|
|
uint8_t *out, void *net_mic, size_t mic_size)
|
|
{
|
|
uint8_t nonce[13];
|
|
|
|
if (!mesh_crypto_network_nonce(ctl, ttl, seq, src, iv_index, nonce))
|
|
return false;
|
|
|
|
return mesh_crypto_aes_ccm_decrypt(nonce, net_key, NULL, 0,
|
|
enc_msg, enc_msg_len, out,
|
|
net_mic, mic_size);
|
|
}
|
|
|
|
bool mesh_crypto_application_nonce(uint32_t seq, uint16_t src,
|
|
uint16_t dst, uint32_t iv_index,
|
|
bool aszmic, uint8_t nonce[13])
|
|
{
|
|
nonce[0] = 0x01;
|
|
nonce[1] = aszmic ? 0x80 : 0x00;
|
|
nonce[2] = (seq & 0x00ff0000) >> 16;
|
|
nonce[3] = (seq & 0x0000ff00) >> 8;
|
|
nonce[4] = (seq & 0x000000ff);
|
|
nonce[5] = (src & 0xff00) >> 8;
|
|
nonce[6] = (src & 0x00ff);
|
|
nonce[7] = (dst & 0xff00) >> 8;
|
|
nonce[8] = (dst & 0x00ff);
|
|
put_be32(iv_index, nonce + 9);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool mesh_crypto_device_nonce(uint32_t seq, uint16_t src,
|
|
uint16_t dst, uint32_t iv_index,
|
|
bool aszmic, uint8_t nonce[13])
|
|
{
|
|
nonce[0] = 0x02;
|
|
nonce[1] = aszmic ? 0x80 : 0x00;
|
|
nonce[2] = (seq & 0x00ff0000) >> 16;
|
|
nonce[3] = (seq & 0x0000ff00) >> 8;
|
|
nonce[4] = (seq & 0x000000ff);
|
|
nonce[5] = (src & 0xff00) >> 8;
|
|
nonce[6] = (src & 0x00ff);
|
|
nonce[7] = (dst & 0xff00) >> 8;
|
|
nonce[8] = (dst & 0x00ff);
|
|
put_be32(iv_index, nonce + 9);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool mesh_crypto_application_encrypt(uint8_t key_id, uint32_t seq, uint16_t src,
|
|
uint16_t dst, uint32_t iv_index,
|
|
const uint8_t app_key[16],
|
|
const uint8_t *aad, uint8_t aad_len,
|
|
const uint8_t *msg, uint8_t msg_len,
|
|
uint8_t *out, void *app_mic,
|
|
size_t mic_size)
|
|
{
|
|
uint8_t nonce[13];
|
|
bool aszmic = (mic_size == sizeof(uint64_t)) ? true : false;
|
|
|
|
if (!key_id && !mesh_crypto_device_nonce(seq, src, dst,
|
|
iv_index, aszmic, nonce))
|
|
return false;
|
|
|
|
if (key_id && !mesh_crypto_application_nonce(seq, src, dst,
|
|
iv_index, aszmic, nonce))
|
|
return false;
|
|
|
|
return mesh_crypto_aes_ccm_encrypt(nonce, app_key, aad, aad_len,
|
|
msg, msg_len,
|
|
out, app_mic, mic_size);
|
|
}
|
|
|
|
bool mesh_crypto_application_decrypt(uint8_t key_id, uint32_t seq, uint16_t src,
|
|
uint16_t dst, uint32_t iv_index,
|
|
const uint8_t app_key[16],
|
|
const uint8_t *aad, uint8_t aad_len,
|
|
const uint8_t *enc_msg, uint8_t enc_msg_len,
|
|
uint8_t *out, void *app_mic, size_t mic_size)
|
|
{
|
|
uint8_t nonce[13];
|
|
bool aszmic = (mic_size == sizeof(uint64_t)) ? true : false;
|
|
|
|
if (!key_id && !mesh_crypto_device_nonce(seq, src, dst,
|
|
iv_index, aszmic, nonce))
|
|
return false;
|
|
|
|
if (key_id && !mesh_crypto_application_nonce(seq, src, dst,
|
|
iv_index, aszmic, nonce))
|
|
return false;
|
|
|
|
return mesh_crypto_aes_ccm_decrypt(nonce, app_key,
|
|
aad, aad_len, enc_msg,
|
|
enc_msg_len, out,
|
|
app_mic, mic_size);
|
|
}
|
|
|
|
bool mesh_crypto_session_key(const uint8_t secret[32],
|
|
const uint8_t salt[16],
|
|
uint8_t session_key[16])
|
|
{
|
|
const uint8_t prsk[4] = "prsk";
|
|
|
|
if (!aes_cmac_one(salt, secret, 32, session_key))
|
|
return false;
|
|
|
|
return aes_cmac_one(session_key, prsk, 4, session_key);
|
|
}
|
|
|
|
bool mesh_crypto_nonce(const uint8_t secret[32],
|
|
const uint8_t salt[16],
|
|
uint8_t nonce[13])
|
|
{
|
|
const uint8_t prsn[4] = "prsn";
|
|
uint8_t tmp[16];
|
|
bool result;
|
|
|
|
if (!aes_cmac_one(salt, secret, 32, tmp))
|
|
return false;
|
|
|
|
result = aes_cmac_one(tmp, prsn, 4, tmp);
|
|
|
|
if (result)
|
|
memcpy(nonce, tmp + 3, 13);
|
|
|
|
return result;
|
|
}
|
|
|
|
bool mesh_crypto_s1(const void *info, size_t len, uint8_t salt[16])
|
|
{
|
|
const uint8_t zero[16] = {0};
|
|
|
|
return aes_cmac_one(zero, info, len, salt);
|
|
}
|
|
|
|
bool mesh_crypto_prov_prov_salt(const uint8_t conf_salt[16],
|
|
const uint8_t prov_rand[16],
|
|
const uint8_t dev_rand[16],
|
|
uint8_t prov_salt[16])
|
|
{
|
|
const uint8_t zero[16] = {0};
|
|
uint8_t tmp[16 * 3];
|
|
|
|
memcpy(tmp, conf_salt, 16);
|
|
memcpy(tmp + 16, prov_rand, 16);
|
|
memcpy(tmp + 32, dev_rand, 16);
|
|
|
|
return aes_cmac_one(zero, tmp, sizeof(tmp), prov_salt);
|
|
}
|
|
|
|
bool mesh_crypto_prov_conf_key(const uint8_t secret[32],
|
|
const uint8_t salt[16],
|
|
uint8_t conf_key[16])
|
|
{
|
|
const uint8_t prck[4] = "prck";
|
|
|
|
if (!aes_cmac_one(salt, secret, 32, conf_key))
|
|
return false;
|
|
|
|
return aes_cmac_one(conf_key, prck, 4, conf_key);
|
|
}
|
|
|
|
bool mesh_crypto_device_key(const uint8_t secret[32],
|
|
const uint8_t salt[16],
|
|
uint8_t device_key[16])
|
|
{
|
|
const uint8_t prdk[4] = "prdk";
|
|
|
|
if (!aes_cmac_one(salt, secret, 32, device_key))
|
|
return false;
|
|
|
|
return aes_cmac_one(device_key, prdk, 4, device_key);
|
|
}
|
|
|
|
bool mesh_crypto_virtual_addr(const uint8_t virtual_label[16],
|
|
uint16_t *addr)
|
|
{
|
|
uint8_t tmp[16];
|
|
|
|
if (!mesh_crypto_s1("vtad", 4, tmp))
|
|
return false;
|
|
|
|
if (!addr || !aes_cmac_one(tmp, virtual_label, 16, tmp))
|
|
return false;
|
|
|
|
*addr = (get_be16(tmp + 14) & 0x3fff) | 0x8000;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool mesh_crypto_packet_encode(uint8_t *packet, uint8_t packet_len,
|
|
const uint8_t network_key[16],
|
|
uint32_t iv_index,
|
|
const uint8_t privacy_key[16])
|
|
{
|
|
uint8_t network_nonce[13] = { 0x00, 0x00 };
|
|
uint8_t privacy_counter[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, };
|
|
uint8_t tmp[16];
|
|
int i;
|
|
|
|
/* Detect Proxy packet by CTL == true && DST == 0x0000 */
|
|
if ((packet[1] & CTL) && get_be16(packet + 7) == 0)
|
|
network_nonce[0] = 0x03;
|
|
else
|
|
/* CTL + TTL */
|
|
network_nonce[1] = packet[1];
|
|
|
|
/* Seq Num */
|
|
network_nonce[2] = packet[2];
|
|
network_nonce[3] = packet[3];
|
|
network_nonce[4] = packet[4];
|
|
|
|
/* SRC */
|
|
network_nonce[5] = packet[5];
|
|
network_nonce[6] = packet[6];
|
|
|
|
/* DST not available */
|
|
network_nonce[7] = 0;
|
|
network_nonce[8] = 0;
|
|
|
|
/* IV Index */
|
|
put_be32(iv_index, network_nonce + 9);
|
|
|
|
/* Check for Long net-MIC */
|
|
if (packet[1] & CTL) {
|
|
if (!mesh_crypto_aes_ccm_encrypt(network_nonce, network_key,
|
|
NULL, 0,
|
|
packet + 7, packet_len - 7 - 8,
|
|
packet + 7, NULL, sizeof(uint64_t)))
|
|
return false;
|
|
} else {
|
|
if (!mesh_crypto_aes_ccm_encrypt(network_nonce, network_key,
|
|
NULL, 0,
|
|
packet + 7, packet_len - 7 - 4,
|
|
packet + 7, NULL, sizeof(uint32_t)))
|
|
return false;
|
|
}
|
|
|
|
put_be32(iv_index, privacy_counter + 5);
|
|
memcpy(privacy_counter + 9, packet + 7, 7);
|
|
|
|
if (!aes_ecb_one(privacy_key, privacy_counter, tmp))
|
|
return false;
|
|
|
|
for (i = 0; i < 6; i++)
|
|
packet[1 + i] ^= tmp[i];
|
|
|
|
return true;
|
|
}
|
|
|
|
bool mesh_crypto_packet_decode(const uint8_t *packet, uint8_t packet_len,
|
|
bool proxy, uint8_t *out, uint32_t iv_index,
|
|
const uint8_t network_key[16],
|
|
const uint8_t privacy_key[16])
|
|
{
|
|
uint8_t privacy_counter[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, };
|
|
uint8_t network_nonce[13] = { 0x00, 0x00, };
|
|
uint8_t tmp[16];
|
|
uint16_t src;
|
|
int i;
|
|
|
|
if (packet_len < 14)
|
|
return false;
|
|
|
|
put_be32(iv_index, privacy_counter + 5);
|
|
memcpy(privacy_counter + 9, packet + 7, 7);
|
|
|
|
if (!aes_ecb_one(privacy_key, privacy_counter, tmp))
|
|
return false;
|
|
|
|
memcpy(out, packet, packet_len);
|
|
for (i = 0; i < 6; i++)
|
|
out[1 + i] ^= tmp[i];
|
|
|
|
src = get_be16(out + 5);
|
|
|
|
/* Pre-check SRC address for illegal values */
|
|
if (!src || src >= 0x8000)
|
|
return false;
|
|
|
|
/* Detect Proxy packet by CTL == true && proxy == true */
|
|
if ((out[1] & CTL) && proxy)
|
|
network_nonce[0] = 0x03;
|
|
else
|
|
/* CTL + TTL */
|
|
network_nonce[1] = out[1];
|
|
|
|
/* Seq Num */
|
|
network_nonce[2] = out[2];
|
|
network_nonce[3] = out[3];
|
|
network_nonce[4] = out[4];
|
|
|
|
/* SRC */
|
|
network_nonce[5] = out[5];
|
|
network_nonce[6] = out[6];
|
|
|
|
/* DST not available */
|
|
network_nonce[7] = 0;
|
|
network_nonce[8] = 0;
|
|
|
|
/* IV Index */
|
|
put_be32(iv_index, network_nonce + 9);
|
|
|
|
/* Check for Long MIC */
|
|
if (out[1] & CTL) {
|
|
uint64_t mic;
|
|
|
|
if (!mesh_crypto_aes_ccm_decrypt(network_nonce, network_key,
|
|
NULL, 0, packet + 7, packet_len - 7,
|
|
out + 7, &mic, sizeof(mic)))
|
|
return false;
|
|
|
|
mic ^= get_be64(out + packet_len - 8);
|
|
put_be64(mic, out + packet_len - 8);
|
|
|
|
if (mic)
|
|
return false;
|
|
} else {
|
|
uint32_t mic;
|
|
|
|
if (!mesh_crypto_aes_ccm_decrypt(network_nonce, network_key,
|
|
NULL, 0, packet + 7, packet_len - 7,
|
|
out + 7, &mic, sizeof(mic)))
|
|
return false;
|
|
|
|
mic ^= get_be32(out + packet_len - 4);
|
|
put_be32(mic, out + packet_len - 4);
|
|
|
|
if (mic)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool mesh_get_random_bytes(void *buf, size_t num_bytes)
|
|
{
|
|
ssize_t len;
|
|
int fd;
|
|
|
|
fd = open("/dev/urandom", O_RDONLY);
|
|
if (fd < 0)
|
|
return false;
|
|
|
|
len = read(fd, buf, num_bytes);
|
|
|
|
close(fd);
|
|
|
|
if (len < 0)
|
|
return false;
|
|
|
|
return true;
|
|
}
|