I am copying a larger than normal list of "interested parties" of this patch to (finally) convert the Bluetooth Mesh daemon to using the ELL cipher and checksum APIs.
This will have one major side effect in that it will no longer support kernels older than v4.9 (I am personally using v5.0.17). I am told that (some?) Freescale platforms also will have problems regardless of kernel version.
This does not change any functionality in the mesh daemon otherwise.
Brian Gix (1):
mesh: Convert crypto to use ELL wrappers
Makefile.am | 4 +
mesh/crypto.c | 518 ++++++----------------------------------------------------
2 files changed, 58 insertions(+), 464 deletions(-)
--
2.14.5
Use ELL implementations of aead-ccm(aes), ecb(aes) and cmac(aes). These
standard encryption and hashing routines are provided by ELL.
---
Makefile.am | 4 +
mesh/crypto.c | 518 ++++++----------------------------------------------------
2 files changed, 58 insertions(+), 464 deletions(-)
diff --git a/Makefile.am b/Makefile.am
index f84a1faba..2583fbdd9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -116,6 +116,8 @@ ell_headers = ell/util.h \
ell/random.h \
ell/signal.h \
ell/timeout.h \
+ ell/cipher.h \
+ ell/checksum.h \
ell/io.h \
ell/idle.h \
ell/main.h \
@@ -138,6 +140,8 @@ ell_sources = ell/private.h ell/missing.h \
ell/main.c \
ell/strv.c \
ell/string.c \
+ ell/cipher.c \
+ ell/checksum.c \
ell/dbus-private.h \
ell/dbus.c \
ell/dbus-message.c \
diff --git a/mesh/crypto.c b/mesh/crypto.c
index 085e72798..3fa0df246 100644
--- a/mesh/crypto.c
+++ b/mesh/crypto.c
@@ -44,193 +44,52 @@
/* Multiply used Zero array */
static const uint8_t zero[16] = { 0, };
-static int alg_new(int fd, const void *keyval, socklen_t keylen,
- size_t mic_size)
+static bool aes_ecb_one(const uint8_t key[16], const uint8_t in[16],
+ uint8_t out[16])
{
- if (setsockopt(fd, SOL_ALG, ALG_SET_KEY, keyval, keylen) < 0)
- return -1;
+ void *cipher;
+ bool result = false;
- if (mic_size &&
- setsockopt(fd, SOL_ALG, ALG_SET_AEAD_AUTHSIZE,
- NULL, mic_size) < 0)
- return -1;
+ cipher = l_cipher_new(L_CIPHER_AES, key, 16);
- /* 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;
+ if (cipher) {
+ result = l_cipher_encrypt(cipher, in, out, 16);
+ l_cipher_free(cipher);
}
- 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;
}
-bool mesh_aes_ecb_one(const uint8_t key[16],
- const uint8_t plaintext[16], uint8_t encrypted[16])
-{
- return aes_ecb_one(key, plaintext, encrypted);
-}
-
-/* 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,
+static bool aes_cmac(void *checksum, 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)
+ if (!l_checksum_update(checksum, msg, msg_len))
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;
+ if (16 == l_checksum_get_digest(checksum, res, 16))
+ return true;
- fd = aes_cmac_setup(N);
- return fd;
+ return false;
}
static bool aes_cmac_one(const uint8_t key[16], const void *msg,
size_t msg_len, uint8_t res[16])
{
+ void *checksum;
bool result;
- int fd;
- fd = aes_cmac_setup(key);
- if (fd < 0)
+ checksum = l_checksum_new_cmac_aes(key, 16);
+ if (!checksum)
return false;
- result = aes_cmac(fd, msg, msg_len, res);
+ result = l_checksum_update(checksum, msg, msg_len);
- aes_cmac_destroy(fd);
+ if (result) {
+ ssize_t len = l_checksum_get_digest(checksum, res, 16);
+ result = !!(len == 16);
+ }
+
+ l_checksum_free(checksum);
return result;
}
@@ -247,161 +106,22 @@ bool mesh_crypto_aes_ccm_encrypt(const uint8_t nonce[13], const uint8_t key[16],
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;
+ void *cipher;
bool result;
- size_t i, j;
- int fd;
-
- /* Mesh limits AAD length to 16 */
- if (aad_len > 16)
- 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);
- l_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 == 8)
- pmsg[0] = 0x19 | (aad_len ? 0x40 : 0x00);
- else
- pmsg[0] = 0x09 | (aad_len ? 0x40 : 0x00);
-
- memcpy(pmsg + 1, nonce, 13);
- l_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) {
- l_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);
+ cipher = l_aead_cipher_new(L_AEAD_CIPHER_AES_CCM, key, 16, mic_size);
- aad_len -= 16;
- i = 0;
+ result = l_aead_cipher_encrypt(cipher, msg, msg_len, aad, aad_len,
+ nonce, 13, out_msg, msg_len + mic_size);
- 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);
- l_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);
- l_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 4:
- *(uint32_t *)out_mic = l_get_be32(mic);
- break;
- case 8:
- *(uint64_t *)out_mic = l_get_be64(mic);
- break;
- default:
- /* Unsupported MIC size */
- result = false;
- }
+ if (result && out_mic) {
+ if (mic_size == 4)
+ *(uint32_t *)out_mic = l_get_be32(out_msg + msg_len);
+ else
+ *(uint64_t *)out_mic = l_get_be64(out_msg + msg_len);
}
-done:
- aes_ecb_destroy(fd);
+ l_aead_cipher_free(cipher);
return result;
}
@@ -412,156 +132,26 @@ bool mesh_crypto_aes_ccm_decrypt(const uint8_t nonce[13], const uint8_t key[16],
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;
+ void *cipher;
bool result;
- size_t i, j;
- int fd;
-
- if (enc_msg_len < 5 || aad_len > 16)
- 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);
- l_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 == 8)
- pmsg[0] = 0x19 | (aad_len ? 0x40 : 0x00);
- else
- pmsg[0] = 0x09 | (aad_len ? 0x40 : 0x00);
+ size_t out_msg_len = enc_msg_len - mic_size;
- memcpy(pmsg + 1, nonce, 13);
- l_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) {
- l_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;
- }
+ cipher = l_aead_cipher_new(L_AEAD_CIPHER_AES_CCM, key, 16, mic_size);
- for (i = 0; i < aad_len; i++, j++)
- pmsg[i] = Xn[i] ^ aad[j];
+ result = l_aead_cipher_decrypt(cipher, enc_msg, enc_msg_len,
+ aad, aad_len, nonce, 13,
+ out_msg, out_msg_len);
- 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);
- l_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);
- l_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;
- }
- }
-
- if (out_mic) {
- switch (mic_size) {
- case 4:
- *(uint32_t *)out_mic = l_get_be32(mic);
- break;
- case 8:
- *(uint64_t *)out_mic = l_get_be64(mic);
- break;
- default:
- /* Unsupported MIC size */
- result = false;
- }
+ if (result && out_mic) {
+ if (mic_size == 4)
+ *(uint32_t *)out_mic =
+ l_get_be32(enc_msg + enc_msg_len - mic_size);
+ else
+ *(uint64_t *)out_mic =
+ l_get_be64(enc_msg + enc_msg_len - mic_size);
}
-done:
- aes_ecb_destroy(fd);
+ l_aead_cipher_free(cipher);
return result;
}
@@ -582,7 +172,7 @@ bool mesh_crypto_k2(const uint8_t n[16], const uint8_t *p, size_t p_len,
uint8_t enc_key[16],
uint8_t priv_key[16])
{
- int fd;
+ void *checksum;
uint8_t output[16];
uint8_t t[16];
uint8_t *stage;
@@ -598,14 +188,14 @@ bool mesh_crypto_k2(const uint8_t n[16], const uint8_t *p, size_t p_len,
if (!aes_cmac_one(stage, n, 16, t))
goto fail;
- fd = aes_cmac_N_start(t);
- if (fd < 0)
+ checksum = l_checksum_new_cmac_aes(t, 16);
+ if (!checksum)
goto fail;
memcpy(stage, p, p_len);
stage[p_len] = 1;
- if (!aes_cmac(fd, stage, p_len + 1, output))
+ if (!aes_cmac(checksum, stage, p_len + 1, output))
goto done;
net_id[0] = output[15] & 0x7f;
@@ -614,7 +204,7 @@ bool mesh_crypto_k2(const uint8_t n[16], const uint8_t *p, size_t p_len,
memcpy(stage + 16, p, p_len);
stage[p_len + 16] = 2;
- if (!aes_cmac(fd, stage, p_len + 16 + 1, output))
+ if (!aes_cmac(checksum, stage, p_len + 16 + 1, output))
goto done;
memcpy(enc_key, output, 16);
@@ -623,14 +213,14 @@ bool mesh_crypto_k2(const uint8_t n[16], const uint8_t *p, size_t p_len,
memcpy(stage + 16, p, p_len);
stage[p_len + 16] = 3;
- if (!aes_cmac(fd, stage, p_len + 16 + 1, output))
+ if (!aes_cmac(checksum, stage, p_len + 16 + 1, output))
goto done;
memcpy(priv_key, output, 16);
success = true;
done:
- aes_cmac_destroy(fd);
+ l_checksum_free(checksum);
fail:
l_free(stage);
--
2.14.5
Hi,
On 05/29, Brian Gix wrote:
> This will have one major side effect in that it will no longer support
> kernels older than v4.9 (I am personally using v5.0.17). I am told
> that (some?) Freescale platforms also will have problems regardless of
> kernel version.
Alright, so I agree that it would be up to the vendor (i.e. me ;) to
provide a patch for older kernels.
I think the most sensible option would be to patch ELL to use userspace
fallback, instead of patching bluez. So if you could just make sure that
an appropriate unit test exists within ELL, I have no objections.
--
Michał Lowas-Rzechonek <[email protected]>
Silvair http://silvair.com
Jasnogórska 44, 31-358 Krakow, POLAND
Hi Michał,
>
> Hi,
>
> On 05/29, Brian Gix wrote:
> > This will have one major side effect in that it will no longer support
> > kernels older than v4.9 (I am personally using v5.0.17). I am told
> > that (some?) Freescale platforms also will have problems regardless of
> > kernel version.
>
> Alright, so I agree that it would be up to the vendor (i.e. me ;) to provide a
> patch for older kernels.
>
> I think the most sensible option would be to patch ELL to use userspace fallback,
> instead of patching bluez. So if you could just make sure that an appropriate
> unit test exists within ELL, I have no objections.
The unit test for AEAD in .../ell/unit/test-cipher.c already makes this report on encryption failures:
if (!success) {
printf("* Some kernel versions before v4.9 have a known AEAD\n"
"* bug. If the system running this test is using a\n"
"* v4.8 or earlier kernel, a failure here is likely\n"
"* due to that kernel bug.\n");
}
So I will likely be applying the patch as written by this weekend to convert all of mesh crypto to ELL.
Any patching (to ELL or BlueZ) will again become the responsibility of the vendor offering support to these older platforms. To emphasize this, I will probably also add a note to .../bluez/mesh/README to emphasize both the problem with these older platforms, usage of the ELL unit test to identify, and the required work-around.
Best Regards,
Brian
Patch Applied
On Wed, 2019-05-29 at 10:28 -0700, Brian Gix wrote:
> I am copying a larger than normal list of "interested parties" of
> this patch to (finally) convert the Bluetooth Mesh daemon to using
> the ELL cipher and checksum APIs.
>
> This will have one major side effect in that it will no longer
> support kernels older than v4.9 (I am personally using v5.0.17). I
> am told that (some?) Freescale platforms also will have problems
> regardless of kernel version.
>
> This does not change any functionality in the mesh daemon otherwise.
>
> Brian Gix (1):
> mesh: Convert crypto to use ELL wrappers
>
> Makefile.am | 4 +
> mesh/crypto.c | 518 ++++++----------------------------------------
> ------------
> 2 files changed, 58 insertions(+), 464 deletions(-)
>