146 lines
4.8 KiB
C
146 lines
4.8 KiB
C
/*******************************************************************************
|
|
* Copyright (c) 2016 Matthijs Kooijman
|
|
*
|
|
* LICENSE
|
|
*
|
|
* Permission is hereby granted, free of charge, to anyone
|
|
* obtaining a copy of this document and accompanying files,
|
|
* to do whatever they want with them without any restriction,
|
|
* including, but not limited to, copying, modification and
|
|
* redistribution.
|
|
*
|
|
* NO WARRANTY OF ANY KIND IS PROVIDED.
|
|
*******************************************************************************/
|
|
|
|
/*
|
|
* The original LMIC AES implementation integrates raw AES encryption
|
|
* with CMAC and AES-CTR in a single piece of code. Most other AES
|
|
* implementations (only) offer raw single block AES encryption, so this
|
|
* file contains an implementation of CMAC and AES-CTR, and offers the
|
|
* same API through the os_aes() function as the original AES
|
|
* implementation. This file assumes that there is an encryption
|
|
* function available with this signature:
|
|
*
|
|
* extern "C" void lmic_aes_encrypt(u1_t *data, u1_t *key);
|
|
*
|
|
* That takes a single 16-byte buffer and encrypts it wit the given
|
|
* 16-byte key.
|
|
*/
|
|
|
|
#include "../lmic/oslmic.h"
|
|
|
|
#if !defined(USE_ORIGINAL_AES)
|
|
|
|
// This should be defined elsewhere
|
|
void lmic_aes_encrypt(u1_t *data, u1_t *key);
|
|
|
|
// global area for passing parameters (aux, key)
|
|
u4_t AESAUX[16/sizeof(u4_t)];
|
|
u4_t AESKEY[16/sizeof(u4_t)];
|
|
|
|
// Shift the given buffer left one bit
|
|
static void shift_left(xref2u1_t buf, u1_t len) {
|
|
while (len--) {
|
|
u1_t next = len ? buf[1] : 0;
|
|
|
|
u1_t val = (*buf << 1);
|
|
if (next & 0x80)
|
|
val |= 1;
|
|
*buf++ = val;
|
|
}
|
|
}
|
|
|
|
// Apply RFC4493 CMAC, using AESKEY as the key. If prepend_aux is true,
|
|
// AESAUX is prepended to the message. AESAUX is used as working memory
|
|
// in any case. The CMAC result is returned in AESAUX as well.
|
|
static void os_aes_cmac(xref2u1_t buf, u2_t len, u1_t prepend_aux) {
|
|
if (prepend_aux)
|
|
lmic_aes_encrypt(AESaux, AESkey);
|
|
else
|
|
memset (AESaux, 0, 16);
|
|
|
|
while (len > 0) {
|
|
u1_t need_padding = 0;
|
|
for (u1_t i = 0; i < 16; ++i, ++buf, --len) {
|
|
if (len == 0) {
|
|
// The message is padded with 0x80 and then zeroes.
|
|
// Since zeroes are no-op for xor, we can just skip them
|
|
// and leave AESAUX unchanged for them.
|
|
AESaux[i] ^= 0x80;
|
|
need_padding = 1;
|
|
break;
|
|
}
|
|
AESaux[i] ^= *buf;
|
|
}
|
|
|
|
if (len == 0) {
|
|
// Final block, xor with K1 or K2. K1 and K2 are calculated
|
|
// by encrypting the all-zeroes block and then applying some
|
|
// shifts and xor on that.
|
|
u1_t final_key[16];
|
|
memset(final_key, 0, sizeof(final_key));
|
|
lmic_aes_encrypt(final_key, AESkey);
|
|
|
|
// Calculate K1
|
|
u1_t msb = final_key[0] & 0x80;
|
|
shift_left(final_key, sizeof(final_key));
|
|
if (msb)
|
|
final_key[sizeof(final_key)-1] ^= 0x87;
|
|
|
|
// If the final block was not complete, calculate K2 from K1
|
|
if (need_padding) {
|
|
msb = final_key[0] & 0x80;
|
|
shift_left(final_key, sizeof(final_key));
|
|
if (msb)
|
|
final_key[sizeof(final_key)-1] ^= 0x87;
|
|
}
|
|
|
|
// Xor with K1 or K2
|
|
for (u1_t i = 0; i < sizeof(final_key); ++i)
|
|
AESaux[i] ^= final_key[i];
|
|
}
|
|
|
|
lmic_aes_encrypt(AESaux, AESkey);
|
|
}
|
|
}
|
|
|
|
// Run AES-CTR using the key in AESKEY and using AESAUX as the
|
|
// counter block. The last byte of the counter block will be incremented
|
|
// for every block. The given buffer will be encrypted in place.
|
|
static void os_aes_ctr (xref2u1_t buf, u2_t len) {
|
|
u1_t ctr[16];
|
|
while (len) {
|
|
// Encrypt the counter block with the selected key
|
|
memcpy(ctr, AESaux, sizeof(ctr));
|
|
lmic_aes_encrypt(ctr, AESkey);
|
|
|
|
// Xor the payload with the resulting ciphertext
|
|
for (u1_t i = 0; i < 16 && len > 0; i++, len--, buf++)
|
|
*buf ^= ctr[i];
|
|
|
|
// Increment the block index byte
|
|
AESaux[15]++;
|
|
}
|
|
}
|
|
|
|
u4_t os_aes (u1_t mode, xref2u1_t buf, u2_t len) {
|
|
switch (mode & ~AES_MICNOAUX) {
|
|
case AES_MIC:
|
|
os_aes_cmac(buf, len, /* prepend_aux */ !(mode & AES_MICNOAUX));
|
|
return os_rmsbf4(AESaux);
|
|
|
|
case AES_ENC:
|
|
// TODO: Check / handle when len is not a multiple of 16
|
|
for (u1_t i = 0; i < len; i += 16)
|
|
lmic_aes_encrypt(buf+i, AESkey);
|
|
break;
|
|
|
|
case AES_CTR:
|
|
os_aes_ctr(buf, len);
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#endif // !defined(USE_ORIGINAL_AES)
|