You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

128 lines
4.2 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]++;
}
}
#endif // !defined(USE_ORIGINAL_AES)