Initial Commit

This commit is contained in:
cnlohr
2024-03-24 17:33:38 -07:00
commit 0cc3b139d0
82 changed files with 22400 additions and 0 deletions
+626
View File
@@ -0,0 +1,626 @@
// NOTE: https://github.com/cnlohr/lorasoft/lib/LoRa-SDR-Code.h - IS UPSTREAM VERSION
/* This code is lifted from LoRa-SDR. I found this copyright at the top
of one of their files:
// Copyright (c) 2016-2016 Lime Microsystems
// Copyright (c) 2016-2016 Arne Hennig
// SPDX-License-Identifier: BSL-1.0
// from https://github.com/myriadrf/LoRa-SDR/blob/master/LoRaEncoder.cpp
Hopefully that covers some of the other stuff.
For any portions of this file not covered under the above licensing terms, it
they shall be governed by the MIT License
Copyright (c) 2024 Charles Lohr "CNLohr"
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef _LORAENCODER_H
#define _LORAENCODER_H
// From LoRaCodes.hpp
#include <string.h>
/***********************************************************************
* Defines
**********************************************************************/
#define HEADER_RDD 4
#define N_HEADER_SYMBOLS (HEADER_RDD + 4)
/***********************************************************************
* Round functions
**********************************************************************/
static unsigned roundUp(unsigned num, unsigned factor)
{
return ((num + factor - 1) / factor) * factor;
}
/***********************************************************************
* Simple 8-bit checksum routine
**********************************************************************/
static uint8_t checksum8(const uint8_t *p, const size_t len)
{
uint8_t acc = 0;
for (size_t i = 0; i < len; i++)
{
acc = (acc >> 1) + ((acc & 0x1) << 7); //rotate
acc += p[i]; //add
}
return acc;
}
static uint8_t headerChecksum(const uint8_t *h) {
int a0 = (h[0] >> 4) & 0x1;
int a1 = (h[0] >> 5) & 0x1;
int a2 = (h[0] >> 6) & 0x1;
int a3 = (h[0] >> 7) & 0x1;
int b0 = (h[0] >> 0) & 0x1;
int b1 = (h[0] >> 1) & 0x1;
int b2 = (h[0] >> 2) & 0x1;
int b3 = (h[0] >> 3) & 0x1;
int c0 = (h[1] >> 0) & 0x1;
int c1 = (h[1] >> 1) & 0x1;
int c2 = (h[1] >> 2) & 0x1;
int c3 = (h[1] >> 3) & 0x1;
uint8_t res;
res = (a0 ^ a1 ^ a2 ^ a3) << 4;
res |= (a3 ^ b1 ^ b2 ^ b3 ^ c0) << 3;
res |= (a2 ^ b0 ^ b3 ^ c1 ^ c3) << 2;
res |= (a1 ^ b0 ^ b2 ^ c0 ^ c1 ^ c2) << 1;
res |= a0 ^ b1 ^ c0 ^ c1 ^ c2 ^ c3;
return res;
}
static uint16_t crc16sx(uint16_t crc, const uint16_t poly) {
for (int i = 0; i < 8; i++) {
if (crc & 0x8000) {
crc = (crc << 1) ^ poly;
}
else {
crc <<= 1;
}
}
return crc;
}
static uint8_t xsum8(uint8_t t) {
t ^= t >> 4;
t ^= t >> 2;
t ^= t >> 1;
return (t & 1);
}
/***********************************************************************
* CRC reverse engineered from Sx1272 data stream.
* Modified CCITT crc with masking of the output with an 8bit lfsr
**********************************************************************/
static uint16_t sx1272DataChecksum(const uint8_t *data, int length) {
uint16_t res = 0;
uint8_t v = 0xff;
uint16_t crc = 0;
for (int i = 0; i < length; i++) {
crc = crc16sx(res, 0x1021);
v = xsum8(v & 0xB8) | (v << 1);
res = crc ^ data[i];
}
res ^= v;
v = xsum8(v & 0xB8) | (v << 1);
res ^= v << 8;
return res;
}
/***********************************************************************
* http://www.semtech.com/images/datasheet/AN1200.18_AG.pdf
**********************************************************************/
static void SX1232RadioComputeWhitening( uint8_t *buffer, uint16_t bufferSize )
{
uint8_t WhiteningKeyMSB; // Global variable so the value is kept after starting the
uint8_t WhiteningKeyLSB; // de-whitening process
WhiteningKeyMSB = 0x01; // Init value for the LFSR, these values should be initialize only
WhiteningKeyLSB = 0xFF; // at the start of a whitening or a de-whitening process
// *buffer is a char pointer indicating the data to be whiten / de-whiten
// buffersize is the number of char to be whiten / de-whiten
// >> The whitened / de-whitened data are directly placed into the pointer
uint8_t i = 0;
uint16_t j = 0;
uint8_t WhiteningKeyMSBPrevious = 0; // 9th bit of the LFSR
for( j = 0; j < bufferSize; j++ ) // byte counter
{
buffer[j] ^= WhiteningKeyLSB; // XOR between the data and the whitening key
for( i = 0; i < 8; i++ ) // 8-bit shift between each byte
{
WhiteningKeyMSBPrevious = WhiteningKeyMSB;
WhiteningKeyMSB = ( WhiteningKeyLSB & 0x01 ) ^ ( ( WhiteningKeyLSB >> 5 ) & 0x01 );
WhiteningKeyLSB= ( ( WhiteningKeyLSB >> 1 ) & 0xFF ) | ( ( WhiteningKeyMSBPrevious << 7 ) & 0x80 );
}
}
}
/***********************************************************************
* Whitening generator reverse engineered from Sx1272 data stream.
* Each bit of a codeword is combined with the output from a different position in the whitening sequence.
**********************************************************************/
static void Sx1272ComputeWhitening(uint8_t *buffer, uint16_t bufferSize, const int bitOfs, const int RDD) {
static const int ofs0[8] = {6,4,2,0,-112,-114,-302,-34 }; // offset into sequence for each bit
static const int ofs1[5] = {6,4,2,0,-360 }; // different offsets used for single parity mode (1 == RDD)
static const int whiten_len = 510; // length of whitening sequence
static const uint64_t whiten_seq[8] = { // whitening sequence
0x0102291EA751AAFFL,0xD24B050A8D643A17L,0x5B279B671120B8F4L,0x032B37B9F6FB55A2L,
0x994E0F87E95E2D16L,0x7CBCFC7631984C26L,0x281C8E4F0DAEF7F9L,0x1741886EB7733B15L
};
const int *ofs = (1 == RDD) ? ofs1 : ofs0;
int i, j;
for (j = 0; j < bufferSize; j++) {
uint8_t x = 0;
for (i = 0; i < 4 + RDD; i++) {
int t = (ofs[i] + j + bitOfs + whiten_len) % whiten_len;
if (whiten_seq[t >> 6] & ((uint64_t)1 << (t & 0x3F))) {
x |= 1 << i;
}
}
buffer[j] ^= x;
}
}
/***********************************************************************
* Whitening generator reverse engineered from Sx1272 data stream.
* Same as above but using the actual interleaved LFSRs.
**********************************************************************/
static void Sx1272ComputeWhiteningLfsr(uint8_t *buffer, uint16_t bufferSize, const int bitOfs, const size_t RDD) {
static const uint64_t seed1[2] = {0x6572D100E85C2EFF,0xE85C2EFFFFFFFFFF}; // lfsr start values
static const uint64_t seed2[2] = {0x05121100F8ECFEEF,0xF8ECFEEFEFEFEFEF}; // lfsr start values for single parity mode (1 == RDD)
const uint8_t m = 0xff >> (4 - RDD);
uint64_t r[2] = {(1 == RDD)?seed2[0]:seed1[0],(1 == RDD)?seed2[1]:seed1[1]};
int i,j;
for (i = 0; i < bitOfs;i++){
r[i & 1] = (r[i & 1] >> 8) | (((r[i & 1] >> 32) ^ (r[i & 1] >> 24) ^ (r[i & 1] >> 16) ^ r[i & 1]) << 56); // poly: 0x1D
}
for (j = 0; j < bufferSize; j++,i++) {
buffer[j] ^= r[i & 1] & m;
r[i & 1] = (r[i & 1] >> 8) | (((r[i & 1] >> 32) ^ (r[i & 1] >> 24) ^ (r[i & 1] >> 16) ^ r[i & 1]) << 56);
}
}
/***********************************************************************
* https://en.wikipedia.org/wiki/Gray_code
**********************************************************************/
/*
* This function converts an unsigned binary
* number to reflected binary Gray code.
*
* The operator >> is shift right. The operator ^ is exclusive or.
*/
static unsigned short binaryToGray16(unsigned short num)
{
return num ^ (num >> 1);
}
/*
* A more efficient version, for Gray codes of 16 or fewer bits.
*/
static unsigned short grayToBinary16(unsigned short num)
{
num = num ^ (num >> 8);
num = num ^ (num >> 4);
num = num ^ (num >> 2);
num = num ^ (num >> 1);
return num;
}
/***********************************************************************
* Encode a 4 bit word into a 8 bits with parity
* Non standard version used in sx1272.
* https://en.wikipedia.org/wiki/Hamming_code
**********************************************************************/
static unsigned char encodeHamming84sx(const unsigned char x)
{
int d0 = (x >> 0) & 0x1;
int d1 = (x >> 1) & 0x1;
int d2 = (x >> 2) & 0x1;
int d3 = (x >> 3) & 0x1;
int b = x & 0xf;
b |= (d0 ^ d1 ^ d2) << 4;
b |= (d1 ^ d2 ^ d3) << 5;
b |= (d0 ^ d1 ^ d3) << 6;
b |= (d0 ^ d2 ^ d3) << 7;
return b;
}
/***********************************************************************
* Decode 8 bits into a 4 bit word with single bit correction.
* Non standard version used in sx1272.
* Set error to true when a parity error was detected
* Set bad to true when the result could not be corrected
**********************************************************************/
static unsigned char decodeHamming84sx(const unsigned char b, int * error, int * bad)
{
int b0 = (b >> 0) & 0x1;
int b1 = (b >> 1) & 0x1;
int b2 = (b >> 2) & 0x1;
int b3 = (b >> 3) & 0x1;
int b4 = (b >> 4) & 0x1;
int b5 = (b >> 5) & 0x1;
int b6 = (b >> 6) & 0x1;
int b7 = (b >> 7) & 0x1;
int p0 = (b0 ^ b1 ^ b2 ^ b4);
int p1 = (b1 ^ b2 ^ b3 ^ b5);
int p2 = (b0 ^ b1 ^ b3 ^ b6);
int p3 = (b0 ^ b2 ^ b3 ^ b7);
int parity = (p0 << 0) | (p1 << 1) | (p2 << 2) | (p3 << 3);
if (parity != 0) *error = 1;
switch (parity & 0xf)
{
case 0xD: return (b ^ 1) & 0xf;
case 0x7: return (b ^ 2) & 0xf;
case 0xB: return (b ^ 4) & 0xf;
case 0xE: return (b ^ 8) & 0xf;
case 0x0:
case 0x1:
case 0x2:
case 0x4:
case 0x8: return b & 0xf;
default: *bad = 1; return b & 0xf;
}
}
/***********************************************************************
* Encode a 4 bit word into a 7 bits with parity.
* Non standard version used in sx1272.
**********************************************************************/
static unsigned char encodeHamming74sx(const unsigned char x)
{
int d0 = (x >> 0) & 0x1;
int d1 = (x >> 1) & 0x1;
int d2 = (x >> 2) & 0x1;
int d3 = (x >> 3) & 0x1;
unsigned char b = x & 0xf;
b |= (d0 ^ d1 ^ d2) << 4;
b |= (d1 ^ d2 ^ d3) << 5;
b |= (d0 ^ d1 ^ d3) << 6;
return b;
}
/***********************************************************************
* Decode 7 bits into a 4 bit word with single bit correction.
* Non standard version used in sx1272.
* Set error to true when a parity error was detected
**********************************************************************/
static unsigned char decodeHamming74sx(const unsigned char b, int * error)
{
int b0 = (b >> 0) & 0x1;
int b1 = (b >> 1) & 0x1;
int b2 = (b >> 2) & 0x1;
int b3 = (b >> 3) & 0x1;
int b4 = (b >> 4) & 0x1;
int b5 = (b >> 5) & 0x1;
int b6 = (b >> 6) & 0x1;
int p0 = (b0 ^ b1 ^ b2 ^ b4);
int p1 = (b1 ^ b2 ^ b3 ^ b5);
int p2 = (b0 ^ b1 ^ b3 ^ b6);
int parity = (p0 << 0) | (p1 << 1) | (p2 << 2);
if (parity != 0) *error = 1;
switch (parity)
{
case 0x5: return (b ^ 1) & 0xf;
case 0x7: return (b ^ 2) & 0xf;
case 0x3: return (b ^ 4) & 0xf;
case 0x6: return (b ^ 8) & 0xf;
case 0x0:
case 0x1:
case 0x2:
case 0x4: return b & 0xF;
}
return b & 0xf;
}
/***********************************************************************
* Check parity for 5/4 code.
* return true if parity is valid.
**********************************************************************/
static unsigned char checkParity54(const unsigned char b, int *error) {
int x = b ^ (b >> 2);
x = x ^ (x >> 1) ^ (b >> 4);
if (x & 1) *error = 1;
return b & 0xf;
}
static unsigned char encodeParity54(const unsigned char b) {
int x = b ^ (b >> 2);
x = x ^ (x >> 1);
return (b & 0xf) | ((x << 4) & 0x10);
}
/***********************************************************************
* Check parity for 6/4 code.
* return true if parity is valid.
**********************************************************************/
static unsigned char checkParity64(const unsigned char b, int *error) {
int x = b ^ (b >> 1) ^ (b >> 2);
int y = x ^ b ^ (b >> 3);
x ^= b >> 4;
y ^= b >> 5;
if ((x | y) & 1) *error = 1;
return b & 0xf;
}
static unsigned char encodeParity64(const unsigned char b) {
int x = b ^ (b >> 1) ^ (b >> 2);
int y = x ^ b ^ (b >> 3);
return ((x & 1) << 4) | ((y & 1) << 5) | (b & 0xf);
}
/***********************************************************************
* Diagonal interleaver + deinterleaver
**********************************************************************/
static void diagonalInterleaveSx(const uint8_t *codewords, const size_t numCodewords, uint16_t *symbols, const size_t PPM, const size_t RDD){
for (size_t x = 0; x < numCodewords / PPM; x++) {
const size_t cwOff = x*PPM;
const size_t symOff = x*(4 + RDD);
for (size_t k = 0; k < 4 + RDD; k++){
uint16_t s = symbols[symOff + k];
for (size_t m = 0; m < PPM; m++){
const size_t i = (m + k + PPM) % PPM;
const int bit = (codewords[cwOff + i] >> k) & 0x1;
s |= (bit << m);
}
symbols[symOff + k] = s;
}
}
}
static void diagonalDeterleaveSx(const uint16_t *symbols, const size_t numSymbols, uint8_t *codewords, const size_t PPM, const size_t RDD)
{
for (size_t x = 0; x < numSymbols / (4 + RDD); x++)
{
const size_t cwOff = x*PPM;
const size_t symOff = x*(4 + RDD);
for (size_t k = 0; k < 4 + RDD; k++)
{
for (size_t m = 0; m < PPM; m++)
{
const size_t i = (m + k) % PPM;
const int bit = (symbols[symOff + k] >> m) & 0x1;
codewords[cwOff + i] |= (bit << k);
}
}
}
}
static void diagonalDeterleaveSx2(const uint16_t *symbols, const size_t numSymbols, uint8_t *codewords, const size_t PPM, const size_t RDD)
{
size_t nb = RDD + 4;
for (size_t x = 0; x < numSymbols / nb; x++) {
const size_t cwOff = x*PPM;
const size_t symOff = x*nb;
for (size_t m = 0; m < PPM; m++) {
size_t i = m;
int sym = symbols[symOff + m];
for (size_t k = 0; k < PPM; k++, sym >>= 1) {
codewords[cwOff + i] |= (sym & 1) << m;
if (++i == PPM) i = 0;
}
}
}
}
static void encodeFec(uint8_t * codewords, const size_t RDD, size_t * cOfs, size_t * dOfs, const uint8_t *bytes, const size_t count)
{
if (RDD == 0) for (size_t i = 0; i < count; i++, (*dOfs)++) {
if ((*dOfs) & 1)
codewords[(*cOfs)++] = bytes[(*dOfs) >> 1] >> 4;
else
codewords[(*cOfs)++] = bytes[(*dOfs) >> 1] & 0xf;
} else if (RDD == 1) for (size_t i = 0; i < count; i++, (*dOfs)++) {
if ((*dOfs) & 1)
codewords[(*cOfs)++] = encodeParity54(bytes[(*dOfs) >> 1] >> 4);
else
codewords[(*cOfs)++] = encodeParity54(bytes[(*dOfs) >> 1] & 0xf);
} else if (RDD == 2) for (size_t i = 0; i < count; i++, (*dOfs)++) {
if ((*dOfs) & 1)
codewords[(*cOfs)++] = encodeParity64(bytes[(*dOfs) >> 1] >> 4);
else
codewords[(*cOfs)++] = encodeParity64(bytes[(*dOfs) >> 1] & 0xf);
} else if (RDD == 3) for (size_t i = 0; i < count; i++, (*dOfs)++) {
if ((*dOfs) & 1)
codewords[(*cOfs)++] = encodeHamming74sx(bytes[(*dOfs) >> 1] >> 4);
else
codewords[(*cOfs)++] = encodeHamming74sx(bytes[(*dOfs) >> 1] & 0xf);
} else if (RDD == 4) for (size_t i = 0; i < count; i++, (*dOfs)++) {
if ((*dOfs) & 1)
codewords[(*cOfs)++] = encodeHamming84sx(bytes[(*dOfs) >> 1] >> 4);
else
codewords[(*cOfs)++] = encodeHamming84sx(bytes[(*dOfs) >> 1] & 0xf);
}
}
// This function has been HEAVILY modified from the original.
// Note that there are references to SF6, 11 and 12, but they do not currently work.
static int CreateMessageFromPayload( uint16_t * symbols, int * symbol_out_count, int max_symbols, int _sf, int _rdd, uint8_t * payload_plus_two_extra_crc_bytes, int payload_length )
{
//static int uctr = 0;
int _whitening = 1; // Enable whitening
int _crc = 1; // Enable CRC.
size_t PPM = _sf;
int _explicit = 1;
// TODO: https://dl.acm.org/doi/fullHtml/10.1145/3546869#sec-9 -- what about the corner cases for SF6, etc.
// SF6 does NOT WORK
int nHeaderCodewords = 0;
if( _sf == 6 ) nHeaderCodewords = 6;
if( _sf == 7 ) nHeaderCodewords = 5; // CORRECT VALIDATED
if( _sf == 8 ) nHeaderCodewords = 6; // CORRECT VALIDATED
if( _sf == 9 ) nHeaderCodewords = 7; // CORRECT VALIDATED
if( _sf == 10 ) nHeaderCodewords = 8; // CORRECT VALIDATED
if( _sf == 11 ) nHeaderCodewords = 9; // ???? Probably Wrong
if( _sf == 12 ) nHeaderCodewords = 10; // ???? Probably Wrong
int header_ppm_shift_up_by = 2;
// SF6 still isn't working.
// header does not reduce SF on SF <= 6 https://github.com/tapparelj/gr-lora_sdr/compare/master...feature/sf5_6_sx126x#diff-1821161335c7f28236eb88e3b8a3c84cce6997861cd847e87fbc9e270d338a37R68
// Indirectly, to note, for SF > 6, the header is implicitly LDRO (the bottom 2 bits are ignored)
if( _sf < 7 ) header_ppm_shift_up_by = 0;
int extra_codewords_due_to_header_padding = ( _sf == 7 || _sf == 6 /* CHECKME */ ) ? 1 : 0;
int header_ppm = ( _sf - header_ppm_shift_up_by );
int data_ppm = _sf;
// XXX TODO: Investigate: I thought SF12 had an LDRO mode which made the PPM only 10.
// TODO: Compare to https://github.com/jkadbear/LoRaPHY/blob/master/LoRaPHY.m
const size_t numCodewords = roundUp( ( payload_length + 2 * _crc ) * 2 + (_explicit ? nHeaderCodewords:0) + extra_codewords_due_to_header_padding, PPM);
const size_t numSymbols = N_HEADER_SYMBOLS + (numCodewords / PPM - 1) * (4 + _rdd); // header is always coded with 8/4
uint8_t codewords[numCodewords];
memset( codewords, 0, sizeof( codewords ) );
if( numSymbols >= max_symbols )
{
//uprintf( "Error: Too many symbols to fit (%d/%d)\n", numSymbols, max_symbols );
return -1;
}
memset( symbols, 0, numSymbols * sizeof( symbols[0] ) );
size_t cOfs = 0;
size_t dOfs = 0;
//std::vector<uint8_t> codewords(numCodewords);
if (_crc)
{
uint16_t crc = sx1272DataChecksum( payload_plus_two_extra_crc_bytes, payload_length );
payload_plus_two_extra_crc_bytes[payload_length] = crc & 0xff;
payload_plus_two_extra_crc_bytes[payload_length+1] = (crc >> 8) & 0xff;
}
// Why does this disagree? https://www.Carloalbertoboano.Com/Documents/Yang22emu.Pdf
if (_explicit) {
uint8_t hdr[3];
hdr[0] = payload_length;
hdr[1] = (_crc ? 1 : 0) | (_rdd << 1);
//static int k;
hdr[2] =
//k++;
headerChecksum(hdr);
codewords[cOfs++] = encodeHamming84sx(hdr[0] >> 4);
codewords[cOfs++] = encodeHamming84sx(hdr[0] & 0xf); // length
codewords[cOfs++] = encodeHamming84sx(hdr[1] & 0xf); // crc / fec info
codewords[cOfs++] = encodeHamming84sx(hdr[2] >> 4); // checksum
codewords[cOfs++] = encodeHamming84sx(hdr[2] & 0xf);
if( extra_codewords_due_to_header_padding > 1 )
{
codewords[cOfs++] = 0;
}
if( extra_codewords_due_to_header_padding > 2 )
{
codewords[cOfs++] = 0;
}
if( extra_codewords_due_to_header_padding > 3 )
{
codewords[cOfs++] = 0;
}
}
size_t cOfs1 = cOfs;
// Header is encoded at 8/4 (4)
encodeFec( codewords, 4, &cOfs, &dOfs, payload_plus_two_extra_crc_bytes, PPM - cOfs );
// Whitening for the data that lives inside the header block.
if( _whitening )
{
Sx1272ComputeWhitening(codewords + cOfs1, PPM - cOfs1, 0, HEADER_RDD);
}
if (numCodewords > PPM) {
size_t cOfs2 = cOfs;
encodeFec(codewords, _rdd, &cOfs, &dOfs, payload_plus_two_extra_crc_bytes, numCodewords-PPM);
if (_whitening) {
Sx1272ComputeWhitening(codewords + cOfs2, numCodewords - PPM, PPM - cOfs1, _rdd);
}
}
//interleave the codewords into symbols
int symbols_size = numSymbols;
// TRICKY: Header is forced to a particularly slow mode.
diagonalInterleaveSx(codewords, header_ppm, symbols, header_ppm, HEADER_RDD);
int i;
if (numCodewords > header_ppm) {
diagonalInterleaveSx(codewords + nHeaderCodewords, numCodewords-nHeaderCodewords, symbols+N_HEADER_SYMBOLS, data_ppm, _rdd);
}
//gray decode, when SF > PPM, pad out LSBs
uint16_t sym;
for( i = 0; i < symbols_size; i++ )
{
//int is_header = (i < 8);
sym = symbols[i];
sym = grayToBinary16(sym);
sym <<= (_sf - PPM);
symbols[i] = sym; // OR +1
}
// The first header symbols are all shifted by 2.
// XXX TODO Look here for SF6 stuff.
for( i = 0; i < N_HEADER_SYMBOLS; i++ )
{
symbols[i] <<= header_ppm_shift_up_by;
}
*symbol_out_count = symbols_size;
return 0;
}
#endif
+193
View File
@@ -0,0 +1,193 @@
/* Clean implementation of RFC4493 AES-CMAC in C, Public Domain by cnlohr
WARNING: This is not cryptographically hardened.
DO NOT USE THIS IN AN ENVIRONMENT WHERE SECURITY MATTERS
THIS IMPLEMENTATION IS HIGHLY SUCCEPTABLE TO TIMING ATTACKS
THIS IMPLEMENTATION DOES NOT USE HARDENED MEMORY.
You may license this code under the unlicense, MIT-x11 or NewBSD licenses.
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>
Assumes a tiny-AES-c implementation for AES_init_ctx and AES_ECB_encrypt.
#define TINY_AES_ECB 1
#include "tiny-AES-c.h"
*/
#include <string.h>
static void BlockLeftShift( uint8_t * blockout, const uint8_t * blockin )
{
int i;
int carry = 0;
for( i = 15; i >= 0; i-- )
{
uint8_t bi = blockin[i];
blockout[i] = (bi << 1) | carry;
carry = !!(bi & 0x80);
}
}
static void BlockXor( uint8_t * out, const uint8_t * in, const uint8_t * inb )
{
int i;
for( i = 0; i < 16; i++ )
out[i] = in[i] ^ inb[i];
}
static void AES_CMAC( const uint8_t *key, const uint8_t *input, int length,
uint8_t *mac )
{
struct AES_ctx K;
AES_init_ctx( &K, key);
int i;
// Based on Figure 2.2, GenerateSubKey
/*
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ Algorithm Generate_Subkey +
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ +
+ Input : K (128-bit key) +
+ Output : K1 (128-bit first subkey) +
+ K2 (128-bit second subkey) +
+-------------------------------------------------------------------+
+ +
+ Constants: const_Zero is 0x00000000000000000000000000000000 +
+ const_Rb is 0x00000000000000000000000000000087 +
+ Variables: L for output of AES-128 applied to 0^128 +
+ +
+ Step 1. L := AES-128(K, const_Zero); +
+ Step 2. if MSB(L) is equal to 0 +
+ then K1 := L << 1; +
+ else K1 := (L << 1) XOR const_Rb; +
+ Step 3. if MSB(K1) is equal to 0 +
+ then K2 := K1 << 1; +
+ else K2 := (K1 << 1) XOR const_Rb; +
+ Step 4. return K1, K2; +
+ +
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/
const uint8_t const_Rb[AES_BLOCKLEN] = { 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87 };
uint8_t K1[AES_BLOCKLEN];
uint8_t K2[AES_BLOCKLEN];
{
uint8_t L[AES_BLOCKLEN] = { 0 };
AES_ECB_encrypt( &K, L );
BlockLeftShift( K1, L );
if( L[0] & 0x80 )
BlockXor( K1, K1, const_Rb );
BlockLeftShift( K2, K1 );
if( K1[0] & 0x80 )
BlockXor( K2, K2, const_Rb );
}
// Based on figure 2.3, AES-CMAC
/*
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ Algorithm AES-CMAC +
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ +
+ Input : K ( 128-bit key ) +
+ : M ( message to be authenticated ) +
+ : len ( length of the message in octets ) +
+ Output : T ( message authentication code ) +
+ +
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ Constants: const_Zero is 0x00000000000000000000000000000000 +
+ const_Bsize is 16 +
+ +
+ Variables: K1, K2 for 128-bit subkeys +
+ M_i is the i-th block (i=1..ceil(len/const_Bsize)) +
+ M_last is the last block xor-ed with K1 or K2 +
+ n for number of blocks to be processed +
+ r for number of octets of last block +
+ flag for denoting if last block is complete or not +
+ +
+ Step 1. (K1,K2) := Generate_Subkey(K); +
+ Step 2. n := ceil(len/const_Bsize); +
+ Step 3. if n = 0 +
+ then +
+ n := 1; +
+ flag := false; +
+ else +
+ if len mod const_Bsize is 0 +
+ then flag := true; +
+ else flag := false; +
+ +
+ Step 4. if flag is true +
+ then M_last := M_n XOR K1; +
+ else M_last := padding(M_n) XOR K2; +
+ Step 5. X := const_Zero; +
+ Step 6. for i := 1 to n-1 do +
+ begin +
+ Y := X XOR M_i; +
+ X := AES-128(K,Y); +
+ end +
+ Y := M_last XOR X; +
+ T := AES-128(K,Y); +
+ Step 7. return T; +
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/
uint8_t M_last[AES_BLOCKLEN];
int remainder = length % AES_BLOCKLEN;
int flag = !(remainder); // If evenly divisible, set to true.
int n = ( length + AES_BLOCKLEN - 1 ) / AES_BLOCKLEN;
if( n == 0 )
{
n = 1;
flag = 0; // Unless 0 size payload.
}
const uint8_t * in_last = &input[(n-1)*AES_BLOCKLEN];
if( flag )
{
BlockXor( M_last, in_last, K1 );
}
else
{
memcpy( M_last, in_last, remainder );
memset( M_last + remainder + 1, 0, AES_BLOCKLEN - remainder - 1 );
M_last[remainder] = 0x80;
BlockXor( M_last, M_last, K2 );
}
uint8_t X[AES_BLOCKLEN] = { 0 };
for( i = 0; i < n-1; i++ )
{
BlockXor( X, X, &input[i*AES_BLOCKLEN] );
AES_ECB_encrypt( &K, X );
}
BlockXor( mac, M_last, X );
AES_ECB_encrypt( &K, mac );
}
+160
View File
@@ -0,0 +1,160 @@
/*
MIT License
Copyright (c) 2024 Charles Lohr "CNLohr"
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef _LORAWAN_SIMPLE_H
#define _LORAWAN_SIMPLE_H
#include <string.h>
#include <unistd.h>
// Configuration needed for aes-cbc-cmac.h
#define TINY_AES_C_IMPLEMENTATION
#define TINY_AES_ECB 1
#include "tiny-AES-c.h"
#include "aes-cbc-cmac.h"
static int GenerateLoRaWANPacket( uint8_t * raw_payload_with_b0, const uint8_t * inner_payload_raw, int inner_payload_len, const uint8_t * payload_key, const uint8_t * network_skey, const uint8_t * devaddress, int frame )
{
int i;
uint8_t * raw_payload = raw_payload_with_b0 + 16;
uint8_t * pl = raw_payload;
struct AES_ctx payload_key_ctx;
AES_init_ctx( &payload_key_ctx, payload_key );
// MAC header
// 010 Unconfirmed data uplink.
// 000 RFU
// 00 Major Version
*(pl++) = ( 0b01000000 );
// Frame header
// Device address (4 bytes)
memcpy( pl, devaddress, 4 );
pl += 4;
// Frame Control
// MSB
// 0 = ADR (cannot do rate adaptation)
// 0 = Not confirming ADR.
// 0 = ACK
// 0 = Class B (we are not a class B device because we can't schedule downlinks)
// 0000 = fopts length (no fopts)
// LSB
*(pl++) = 0b00000000;
// Frame Count
*(pl++) = frame;
*(pl++) = frame>>8;
// Frame Port
*(pl++) = 1; // Port (1-223 are application specific)
int padded_pad_length = ( inner_payload_len + AES_BLOCKLEN - 1 ) & (~(AES_BLOCKLEN-1));
int nr_blocks = padded_pad_length / AES_BLOCKLEN;
uint8_t pad[padded_pad_length];
for( i = 0; i < nr_blocks; i++ )
{
// Add CMAC. Construct b0
uint8_t * Ai = &pad[i*AES_BLOCKLEN];
*(Ai++) = 0x01;
*(Ai++) = 0;
*(Ai++) = 0;
*(Ai++) = 0;
*(Ai++) = 0;
*(Ai++) = 0; // Uplink = 0
*(Ai++) = devaddress[0];
*(Ai++) = devaddress[1];
*(Ai++) = devaddress[2];
*(Ai++) = devaddress[3];
*(Ai++) = (frame>>0) & 0xff;
*(Ai++) = (frame>>8) & 0xff;
*(Ai++) = (frame>>16) & 0xff;
*(Ai++) = (frame>>24) & 0xff;
*(Ai++) = 0;
*(Ai++) = i+1;
}
for( i = 0; i < nr_blocks; i++ )
{
AES_ECB_encrypt( &payload_key_ctx, &pad[i*AES_BLOCKLEN] );
}
// uint8_t * start_of_payload = pl;
for( i = 0; i < inner_payload_len; i++ )
{
*(pl++) = pad[i] ^ inner_payload_raw[i];
}
int to_cmac_size = pl - raw_payload_with_b0;
int length_of_frame_without_b0 = pl - raw_payload;
uint8_t * b0 = raw_payload_with_b0;
*(b0++) = 0x49;
*(b0++) = 0x00;
*(b0++) = 0x00;
*(b0++) = 0x00;
*(b0++) = 0x00;
*(b0++) = 0x00;
*(b0++) = devaddress[0];
*(b0++) = devaddress[1];
*(b0++) = devaddress[2];
*(b0++) = devaddress[3];
*(b0++) = (frame>>0) & 0xff;
*(b0++) = (frame>>8) & 0xff;
*(b0++) = (frame>>16) & 0xff;
*(b0++) = (frame>>24) & 0xff;
*(b0++) = 0x00;
*(b0++) = length_of_frame_without_b0;
uint8_t mac[AES_BLOCKLEN];
//printf( "To CMAC: %d\n", to_cmac_size );
//for ( i = 0; i < to_cmac_size; i++ )
//{
// printf( "%02x ", raw_payload_with_b0[i] );
//}
//printf( "\n" );
AES_CMAC( network_skey, raw_payload_with_b0, to_cmac_size, mac );
*(pl++) = mac[0];
*(pl++) = mac[1];
*(pl++) = mac[2];
*(pl++) = mac[3];
//printf( "MAC %02x%02x%02x%02x %02x%02x%02x%02x\n", mac[0], mac[1], mac[2], mac[3], mac[12], mac[13], mac[14], mac[15] );
return pl - raw_payload_with_b0 - 16;
}
#endif
+344
View File
@@ -0,0 +1,344 @@
/**
MIT-like-non-ai-license
Copyright (c) 2024 Charles Lohr "CNLohr"
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the two following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
In addition the following restrictions apply:
1. The Software and any modifications made to it may not be used for the
purpose of training or improving machine learning algorithms, including but not
limited to artificial intelligence, natural language processing, or data
mining. This condition applies to any derivatives, modifications, or updates
based on the Software code. Any usage of the Software in an AI-training dataset
is considered a breach of this License.
2. The Software may not be included in any dataset used for training or
improving machine learning algorithms, including but not limited to artificial
intelligence, natural language processing, or data mining.
3. Any person or organization found to be in violation of these restrictions
will be subject to legal action and may be held liable for any damages
resulting from such use.
If any term is unenforcable, other terms remain in-force.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
// This file is code, but intended to be included into a .c file that
// does #define's for a specific fitness. Below is an example .c file
// that this could be included into to generate the bit table. It is
// from an ESP8266 example, but other specifics can be specified on a
// per-processor basis.
/*
const double center_frequency = 904.1;
const double bw = .125;
const uint32_t memory_offset = 0x20000;
#define SF_NUMBER 7
#if SF_NUMBER < 7
#warning SF6 still does not work :(
#endif
#define SPI_DIV 1
// Funny modes:
/// 80MHz, SF8, SPI_DIV 5 @903.9/904.1 produces hilarious mirror images around 904.0
#if MAIN_MHZ == 80
const double sample_rate = 1040.0/13.0/SPI_DIV;
#if ( SF_NUMBER > 8 )
#error Not enough ram for chirp table
#endif
#elif MAIN_MHZ == 115
const double sample_rate = 1040.0/9.0/SPI_DIV;
#if ( SF_NUMBER > 8 )
#error Not enough ram for chirp table
#endif
#elif MAIN_MHZ == 52
const double sample_rate = 1040.0/20.0/SPI_DIV;
#if ( SF_NUMBER > 9 )
#error Not enough ram for chirp table
#endif
#elif MAIN_MHZ == 173
const double sample_rate = 1040.0/6.0/SPI_DIV;
#if ( SF_NUMBER > 7 )
#error Not enough ram for chirp table
#endif
#else
#error Unknown Clock Rate
#endif
EXTRA OPTIONS
#define USE_16_BITS
*/
#include <stdio.h>
#include <stdint.h>
#include <math.h>
const double chirp_begin = center_frequency-bw/2;
const double chirp_end = center_frequency+bw/2;
#ifndef MEM_MAX_BYTES
#define MEM_MAX_BYTES 2000000
#endif
#ifndef SF_SYMBOL_TIME
#define SF_SYMBOL_TIME 0.000001
#endif
const double chirp_length_seconds = (8<<SF_NUMBER) * SF_SYMBOL_TIME;
const double sampletotal = ( chirp_length_seconds * sample_rate * 1000000 );
int words = 0;
int words_nominal = 0;
uint32_t bleedover = 0; // Will be tailored based on the needed buffers.
FILE * fcba;
FILE * fd;
FILE * fCBI;
#ifdef USE_16_BITS
#define NUMBITS 16
#else
#define NUMBITS 32
#endif
static uint32_t flipBits( uint32_t w, int len )
{
int i;
uint32_t ret = 0;
for( i = 0; i < len; i++ )
{
if( w & (1<<(len-1)) )
ret |= 1;
ret <<= 1;
w <<= 1;
}
return ret;
}
void GenChirp( double fStart, double fEnd )
{
uint32_t sample_word = 0;
int samplect = 0;
double phase = 0.0001;
int samples = 0;
int ic;
{
for( samples = 0; ; samples++ )
{
double placeInSamples = samples / sampletotal;
if( placeInSamples >= 1 )
{
// When going off the top frequency, start over at the bottom.
placeInSamples -= 1;
}
//////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////
// These are the key lines here. This is what actually decides the current
// frequency and figures out what the given bit in a position should be.
// I use 0.1 as an offset because it seems to have some beneficial behaviors
// but 0.0 also seems to work really well. You can artifically reduce the
// amplituide of the output signal by changing the offset in the comparison
// on the last line of this section.
double current_f = ( placeInSamples ) * ( fEnd - fStart ) + fStart;
phase += 3.1415926 * 2.0 * current_f / sample_rate;
int bit = sin( phase ) > 0.1; // HINT: if you want to mix multiple signals, add a DC offset here.
//////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////
sample_word |= !!bit;
samplect++;
if( samplect == NUMBITS )
{
#ifdef USE_16_BITS
// sample_word = ((sample_word>>8)&0xff) | ((sample_word<<8)&0xff00);
// sample_word = flipBits( sample_word, 16 );
fprintf( fcba, "0x%04x,%c", sample_word, ((words & 0xf) != 0xf)?' ':'\n' );
#else
fprintf( fcba, "0x%08x,%c", sample_word, ((words & 0xf) != 0xf)?' ':'\n' );
#endif
fwrite( &sample_word, 1, NUMBITS/8, fd );
words++;
sample_word = 0;
samplect = 0;
if( samples < sampletotal )
words_nominal = words;
if( samples >= sampletotal + bleedover*32 )
{
break;
}
}
sample_word <<= 1;
}
}
}
#ifndef ATTRIBUTE_FOR_DATA
#define ATTRIBUTE_FOR_DATA
#endif
int gen_buffer_files()
{
fcba = fopen( "chirpbuff.h", "w" );
fd = fopen( "chirpbuff.dat", "w" );
fCBI = fopen( "chirpbuffinfo.h", "w" );
#ifdef USE_16_BITS
fprintf( fcba, "const uint16_t chirpbuff[] " ATTRIBUTE_FOR_DATA " = {\n" );
#else
fprintf( fcba, "const uint32_t chirpbuff[] " ATTRIBUTE_FOR_DATA " = {\n" );
#endif
int quarter_chirp_length_bits = (int)(sampletotal/4.0+0.5);
int factor = 0;
int is_perfect_divisor = 0;
int i;
// DMA size words, when multiplied out should be SMALLER than the
// overall length of our message, so that we can trim off the last few
// words sometimes, to average out to the right bitrate.
// So, we need to pick a number that when multiplied out, ends up
// slightly larger than IDEAL_CHIRP_LENGTH_BITS. But not too much so.
// First try to find a perfect divisor, if we can find a perfect divisor,
// then no weird fixup stuff needs to be done.
int seeking_perfect_divisor = 1;
bleedover = 0;
retry_without_seek:
printf( "Searching for a divisor for %d\n", quarter_chirp_length_bits );
for( i = 511; i > 100; i-- )
{
int nr_to_div = (quarter_chirp_length_bits / 32 + 32) / i;
int num_bits_default = i * 32 * nr_to_div;
int leftover = num_bits_default - quarter_chirp_length_bits;
//fprintf( stderr, "%d %d %d [%d] --> %d\n", i, nr_to_div, num_bits_default, quarter_chirp_length_bits, leftover );
if( leftover < 32*nr_to_div &&
( seeking_perfect_divisor ? (leftover == 0) : (leftover >= 0)) )
{
// Make sure we aren't more than 1 32-bit word too large
fprintf( stderr, "Found %s divisor: %d (%d - %d = %d)\n",
(leftover == 0)?"PERFECT" : "OK",
i,
num_bits_default,
quarter_chirp_length_bits,
num_bits_default - quarter_chirp_length_bits );
factor = i;
//XXX TODO: I think this bleedover calc is wrong. Should investigate?
bleedover = num_bits_default/32;
is_perfect_divisor = leftover == 0;
break;
}
}
if( seeking_perfect_divisor && !factor )
{
seeking_perfect_divisor = 0;
goto retry_without_seek;
}
if( !factor )
{
fprintf( stderr, "Error: No factor of %d found, you may have to do something clever in rf_data_gen.c\n", quarter_chirp_length_bits );
return -9;
}
// For a given word, it is shifted out MSB (Bit and byte) first
GenChirp( chirp_begin, chirp_end );
int sample_word_median = words;
int quarter_chirp_length = ((words_nominal+2)/4);
int reverse_start = words;
words = 0;
GenChirp( chirp_end, chirp_begin );
fprintf( fcba, "};\n" );
fclose( fcba );
fclose( fd );
#ifdef USE_16_BITS
int bytes_total = 2*(words+sample_word_median);
fprintf( stderr, "Wrote out %d uint16_t's.\n", words + sample_word_median );
#else
int bytes_total = 4*(words+sample_word_median);
fprintf( stderr, "Wrote out %d uint32_t's.\n", words + sample_word_median );
#endif
if( bytes_total > MEM_MAX_BYTES )
{
fprintf( stderr, "ERROR: Your table is too big (trying to write out %d bytes, please adjust SF/BW/clock rate settings)\n", bytes_total );
}
fprintf( fCBI, "#define CHIRPLENGTH_WORDS (%d)\n", words_nominal );
fprintf( fCBI, "#define MEMORY_START_OFFSET_BYTES (0x%08x)\n", memory_offset );
fprintf( fCBI, "#define REVERSE_START_OFFSET_BYTES (0x%08x)\n", memory_offset + reverse_start * NUMBITS/8 );
fprintf( fCBI, "#define QUARTER_CHIRP_LENGTH_WORDS (%d)\n", (int)(quarter_chirp_length) );
fprintf( fCBI, "#define IDEAL_QUARTER_CHIRP_LENGTH_BITS (%d)\n", quarter_chirp_length_bits );
fprintf( fCBI, "#define CHIRPLENGTH_WORDS_WITH_PADDING (%d)\n", sample_word_median );
fprintf( fCBI, "#define STRIPE_BLEEDOVER_WORDS (%d)\n", bleedover );
fprintf( fCBI, "#define TARGET_SAMPLE_COUNT_BITS (%d)\n", (int)sampletotal );
fprintf( fCBI, "#define DMA_SIZE_WORDS (%d)\n", factor );
fprintf( fCBI, "#define NUM_DMAS_PER_QUARTER_CHIRP (%d)\n", quarter_chirp_length/factor+((is_perfect_divisor)?0:1) );
fprintf( fCBI, "#define SF_NUMBER %d\n", SF_NUMBER );
fprintf( fCBI, "#define SPI_DIV %d\n", SPI_DIV );
if( factor & 1 )
fprintf( fCBI, "#define DMA_SIZE_WORDS_DIVISIBLE_BY_TWO 0\n" );
else
fprintf( fCBI, "#define DMA_SIZE_WORDS_DIVISIBLE_BY_TWO 1\n" );
if( factor & 3 )
fprintf( fCBI, "#define DMA_SIZE_WORDS_DIVISIBLE_BY_FOUR 0\n" );
else
fprintf( fCBI, "#define DMA_SIZE_WORDS_DIVISIBLE_BY_FOUR 1\n" );
#ifdef USE_EXTERNAL_CLOCK
fprintf( fCBI, "#define USE_EXTERNAL_CLOCK\n" );
#endif
if( is_perfect_divisor )
fprintf( fCBI, "#define FOUND_PERFECT_DIVISOR\n" );
fclose( fCBI );
}
+704
View File
@@ -0,0 +1,704 @@
/*
Header-only version of tiny-AES-c https://github.com/kokke/tiny-AES-c/blob/master/aes.c
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>
*/
#ifndef _TINY_AES_C_H_
#define _TINY_AES_C_H_
#include <stdint.h>
#include <stddef.h>
// If you want to use this library:
// #define TINY_AES_C_IMPLEMENTATION
// #define the macros below to 1/0 to enable/disable the mode of operation.
//
// CBC enables AES encryption in CBC-mode of operation.
// CTR enables encryption in counter-mode.
// ECB enables the basic ECB 16-byte block algorithm. All can be enabled simultaneously.
// The #ifndef-guard allows it to be configured before #include'ing or at compile time.
#ifndef TINY_AES_CBC
#define TINY_AES_CBC 1
#endif
#ifndef TINY_AES_ECB
#define TINY_AES_ECB 1
#endif
#ifndef TINY_AES_CTR
#define TINY_AES_CTR 1
#endif
#if !defined( AES128 ) && !defined( AES192 ) && !defined( AES256 )
#define AES128 1
//#define AES192 1
//#define AES256 1
#endif
#define AES_BLOCKLEN 16 // Block length in bytes - AES is 128b block only
#if defined(AES256) && (AES256 == 1)
#define AES_KEYLEN 32
#define AES_keyExpSize 240
#elif defined(AES192) && (AES192 == 1)
#define AES_KEYLEN 24
#define AES_keyExpSize 208
#else
#define AES_KEYLEN 16 // Key length in bytes
#define AES_keyExpSize 176
#endif
struct AES_ctx
{
uint8_t RoundKey[AES_keyExpSize];
#if (defined(TINY_AES_CBC) && (TINY_AES_CBC == 1)) || (defined(TINY_AES_CTR) && (TINY_AES_CTR == 1))
uint8_t Iv[AES_BLOCKLEN];
#endif
};
void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key);
#if (defined(TINY_AES_CBC) && (TINY_AES_CBC == 1)) || (defined(TINY_AES_CTR) && (TINY_AES_CTR == 1))
void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv);
void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv);
#endif
#if defined(TINY_AES_ECB) && (TINY_AES_ECB == 1)
// buffer size is exactly AES_BLOCKLEN bytes;
// you need only AES_init_ctx as IV is not used in ECB
// NB: ECB is considered insecure for most uses
void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf);
void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf);
#endif // #if defined(TINY_AES_ECB) && (TINY_AES_ECB == !)
#if defined(TINY_AES_CBC) && (TINY_AES_CBC == 1)
// buffer size MUST be mutile of AES_BLOCKLEN;
// Suggest https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme
// NOTES: you need to set IV in ctx via AES_init_ctx_iv() or AES_ctx_set_iv()
// no IV should ever be reused with the same key
void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length);
void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length);
#endif // #if defined(TINY_AES_CBC) && (TINY_AES_CBC == 1)
#if defined(TINY_AES_CTR) && (TINY_AES_CTR == 1)
// Same function for encrypting as for decrypting.
// IV is incremented for every block, and used after encryption as XOR-compliment for output
// Suggesting https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme
// NOTES: you need to set IV in ctx with AES_init_ctx_iv() or AES_ctx_set_iv()
// no IV should ever be reused with the same key
void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length);
#endif // #if defined(TINY_AES_CTR) && (TINY_AES_CTR == 1)
#ifdef TINY_AES_C_IMPLEMENTATION
/*
This is an implementation of the AES algorithm, specifically ECB, TINY_AES_CTR and CBC mode.
Block size can be chosen in aes.h - available choices are AES128, AES192, AES256.
The implementation is verified against the test vectors in:
National Institute of Standards and Technology Special Publication 800-38A 2001 ED
ECB-AES128
----------
plain-text:
6bc1bee22e409f96e93d7e117393172a
ae2d8a571e03ac9c9eb76fac45af8e51
30c81c46a35ce411e5fbc1191a0a52ef
f69f2445df4f9b17ad2b417be66c3710
key:
2b7e151628aed2a6abf7158809cf4f3c
resulting cipher
3ad77bb40d7a3660a89ecaf32466ef97
f5d3d58503b9699de785895a96fdbaaf
43b1cd7f598ece23881b00e3ed030688
7b0c785e27e8ad3f8223207104725dd4
NOTE: String length must be evenly divisible by 16byte (str_len % 16 == 0)
You should pad the end of the string with zeros if this is not the case.
For AES192/256 the key size is proportionally larger.
*/
/*****************************************************************************/
/* Includes: */
/*****************************************************************************/
#include <string.h> // CBC mode, for memset
/*****************************************************************************/
/* Defines: */
/*****************************************************************************/
// The number of columns comprising a state in AES. This is a constant in AES. Value=4
#define Nb 4
#if defined(AES256) && (AES256 == 1)
#define Nk 8
#define Nr 14
#elif defined(AES192) && (AES192 == 1)
#define Nk 6
#define Nr 12
#else
#define Nk 4 // The number of 32 bit words in a key.
#define Nr 10 // The number of rounds in AES Cipher.
#endif
// jcallan@github points out that declaring Multiply as a function
// reduces code size considerably with the Keil ARM compiler.
// See this link for more information: https://github.com/kokke/tiny-AES-C/pull/3
#ifndef MULTIPLY_AS_A_FUNCTION
#define MULTIPLY_AS_A_FUNCTION 0
#endif
/*****************************************************************************/
/* Private variables: */
/*****************************************************************************/
// state - array holding the intermediate results during decryption.
typedef uint8_t state_t[4][4];
// The lookup-tables are marked const so they can be placed in read-only storage instead of RAM
// The numbers below can be computed dynamically trading ROM for RAM -
// This can be useful in (embedded) bootloader applications, where ROM is often limited.
static const uint8_t sbox[256] = {
//0 1 2 3 4 5 6 7 8 9 A B C D E F
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 };
#if (defined(TINY_AES_CBC) && TINY_AES_CBC == 1) || (defined(TINY_AES_ECB) && TINY_AES_ECB == 1)
static const uint8_t rsbox[256] = {
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d };
#endif
// The round constant word array, Rcon[i], contains the values given by
// x to the power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8)
static const uint8_t Rcon[11] = {
0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 };
/*
* Jordan Goulder points out in PR #12 (https://github.com/kokke/tiny-AES-C/pull/12),
* that you can remove most of the elements in the Rcon array, because they are unused.
*
* From Wikipedia's article on the Rijndael key schedule @ https://en.wikipedia.org/wiki/Rijndael_key_schedule#Rcon
*
* "Only the first some of these constants are actually used up to rcon[10] for AES-128 (as 11 round keys are needed),
* up to rcon[8] for AES-192, up to rcon[7] for AES-256. rcon[0] is not used in AES algorithm."
*/
/*****************************************************************************/
/* Private functions: */
/*****************************************************************************/
/*
static uint8_t getSBoxValue(uint8_t num)
{
return sbox[num];
}
*/
#define getSBoxValue(num) (sbox[(num)])
// This function produces Nb(Nr+1) round keys. The round keys are used in each round to decrypt the states.
static void KeyExpansion(uint8_t* RoundKey, const uint8_t* Key)
{
unsigned i, j, k;
uint8_t tempa[4]; // Used for the column/row operations
// The first round key is the key itself.
for (i = 0; i < Nk; ++i)
{
RoundKey[(i * 4) + 0] = Key[(i * 4) + 0];
RoundKey[(i * 4) + 1] = Key[(i * 4) + 1];
RoundKey[(i * 4) + 2] = Key[(i * 4) + 2];
RoundKey[(i * 4) + 3] = Key[(i * 4) + 3];
}
// All other round keys are found from the previous round keys.
for (i = Nk; i < Nb * (Nr + 1); ++i)
{
{
k = (i - 1) * 4;
tempa[0]=RoundKey[k + 0];
tempa[1]=RoundKey[k + 1];
tempa[2]=RoundKey[k + 2];
tempa[3]=RoundKey[k + 3];
}
if (i % Nk == 0)
{
// This function shifts the 4 bytes in a word to the left once.
// [a0,a1,a2,a3] becomes [a1,a2,a3,a0]
// Function RotWord()
{
const uint8_t u8tmp = tempa[0];
tempa[0] = tempa[1];
tempa[1] = tempa[2];
tempa[2] = tempa[3];
tempa[3] = u8tmp;
}
// SubWord() is a function that takes a four-byte input word and
// applies the S-box to each of the four bytes to produce an output word.
// Function Subword()
{
tempa[0] = getSBoxValue(tempa[0]);
tempa[1] = getSBoxValue(tempa[1]);
tempa[2] = getSBoxValue(tempa[2]);
tempa[3] = getSBoxValue(tempa[3]);
}
tempa[0] = tempa[0] ^ Rcon[i/Nk];
}
#if defined(AES256) && (AES256 == 1)
if (i % Nk == 4)
{
// Function Subword()
{
tempa[0] = getSBoxValue(tempa[0]);
tempa[1] = getSBoxValue(tempa[1]);
tempa[2] = getSBoxValue(tempa[2]);
tempa[3] = getSBoxValue(tempa[3]);
}
}
#endif
j = i * 4; k=(i - Nk) * 4;
RoundKey[j + 0] = RoundKey[k + 0] ^ tempa[0];
RoundKey[j + 1] = RoundKey[k + 1] ^ tempa[1];
RoundKey[j + 2] = RoundKey[k + 2] ^ tempa[2];
RoundKey[j + 3] = RoundKey[k + 3] ^ tempa[3];
}
}
void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key)
{
KeyExpansion(ctx->RoundKey, key);
}
#if (defined(TINY_AES_CBC) && (TINY_AES_CBC == 1)) || (defined(TINY_AES_CTR) && (TINY_AES_CTR == 1))
void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv)
{
KeyExpansion(ctx->RoundKey, key);
memcpy (ctx->Iv, iv, AES_BLOCKLEN);
}
void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv)
{
memcpy (ctx->Iv, iv, AES_BLOCKLEN);
}
#endif
// This function adds the round key to state.
// The round key is added to the state by an XOR function.
static void AddRoundKey(uint8_t round, state_t* state, const uint8_t* RoundKey)
{
uint8_t i,j;
for (i = 0; i < 4; ++i)
{
for (j = 0; j < 4; ++j)
{
(*state)[i][j] ^= RoundKey[(round * Nb * 4) + (i * Nb) + j];
}
}
}
// The SubBytes Function Substitutes the values in the
// state matrix with values in an S-box.
static void SubBytes(state_t* state)
{
uint8_t i, j;
for (i = 0; i < 4; ++i)
{
for (j = 0; j < 4; ++j)
{
(*state)[j][i] = getSBoxValue((*state)[j][i]);
}
}
}
// The ShiftRows() function shifts the rows in the state to the left.
// Each row is shifted with different offset.
// Offset = Row number. So the first row is not shifted.
static void ShiftRows(state_t* state)
{
uint8_t temp;
// Rotate first row 1 columns to left
temp = (*state)[0][1];
(*state)[0][1] = (*state)[1][1];
(*state)[1][1] = (*state)[2][1];
(*state)[2][1] = (*state)[3][1];
(*state)[3][1] = temp;
// Rotate second row 2 columns to left
temp = (*state)[0][2];
(*state)[0][2] = (*state)[2][2];
(*state)[2][2] = temp;
temp = (*state)[1][2];
(*state)[1][2] = (*state)[3][2];
(*state)[3][2] = temp;
// Rotate third row 3 columns to left
temp = (*state)[0][3];
(*state)[0][3] = (*state)[3][3];
(*state)[3][3] = (*state)[2][3];
(*state)[2][3] = (*state)[1][3];
(*state)[1][3] = temp;
}
static uint8_t xtime(uint8_t x)
{
return ((x<<1) ^ (((x>>7) & 1) * 0x1b));
}
// MixColumns function mixes the columns of the state matrix
static void MixColumns(state_t* state)
{
uint8_t i;
uint8_t Tmp, Tm, t;
for (i = 0; i < 4; ++i)
{
t = (*state)[i][0];
Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^ (*state)[i][3] ;
Tm = (*state)[i][0] ^ (*state)[i][1] ; Tm = xtime(Tm); (*state)[i][0] ^= Tm ^ Tmp ;
Tm = (*state)[i][1] ^ (*state)[i][2] ; Tm = xtime(Tm); (*state)[i][1] ^= Tm ^ Tmp ;
Tm = (*state)[i][2] ^ (*state)[i][3] ; Tm = xtime(Tm); (*state)[i][2] ^= Tm ^ Tmp ;
Tm = (*state)[i][3] ^ t ; Tm = xtime(Tm); (*state)[i][3] ^= Tm ^ Tmp ;
}
}
// Multiply is used to multiply numbers in the field GF(2^8)
// Note: The last call to xtime() is unneeded, but often ends up generating a smaller binary
// The compiler seems to be able to vectorize the operation better this way.
// See https://github.com/kokke/tiny-AES-c/pull/34
#if MULTIPLY_AS_A_FUNCTION
static uint8_t Multiply(uint8_t x, uint8_t y)
{
return (((y & 1) * x) ^
((y>>1 & 1) * xtime(x)) ^
((y>>2 & 1) * xtime(xtime(x))) ^
((y>>3 & 1) * xtime(xtime(xtime(x)))) ^
((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))); /* this last call to xtime() can be omitted */
}
#else
#define Multiply(x, y) \
( ((y & 1) * x) ^ \
((y>>1 & 1) * xtime(x)) ^ \
((y>>2 & 1) * xtime(xtime(x))) ^ \
((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ \
((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))) \
#endif
#if (defined(TINY_AES_CBC) && TINY_AES_CBC == 1) || (defined(TINY_AES_ECB) && TINY_AES_ECB == 1)
/*
static uint8_t getSBoxInvert(uint8_t num)
{
return rsbox[num];
}
*/
#define getSBoxInvert(num) (rsbox[(num)])
// MixColumns function mixes the columns of the state matrix.
// The method used to multiply may be difficult to understand for the inexperienced.
// Please use the references to gain more information.
static void InvMixColumns(state_t* state)
{
int i;
uint8_t a, b, c, d;
for (i = 0; i < 4; ++i)
{
a = (*state)[i][0];
b = (*state)[i][1];
c = (*state)[i][2];
d = (*state)[i][3];
(*state)[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09);
(*state)[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d);
(*state)[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b);
(*state)[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e);
}
}
// The SubBytes Function Substitutes the values in the
// state matrix with values in an S-box.
static void InvSubBytes(state_t* state)
{
uint8_t i, j;
for (i = 0; i < 4; ++i)
{
for (j = 0; j < 4; ++j)
{
(*state)[j][i] = getSBoxInvert((*state)[j][i]);
}
}
}
static void InvShiftRows(state_t* state)
{
uint8_t temp;
// Rotate first row 1 columns to right
temp = (*state)[3][1];
(*state)[3][1] = (*state)[2][1];
(*state)[2][1] = (*state)[1][1];
(*state)[1][1] = (*state)[0][1];
(*state)[0][1] = temp;
// Rotate second row 2 columns to right
temp = (*state)[0][2];
(*state)[0][2] = (*state)[2][2];
(*state)[2][2] = temp;
temp = (*state)[1][2];
(*state)[1][2] = (*state)[3][2];
(*state)[3][2] = temp;
// Rotate third row 3 columns to right
temp = (*state)[0][3];
(*state)[0][3] = (*state)[1][3];
(*state)[1][3] = (*state)[2][3];
(*state)[2][3] = (*state)[3][3];
(*state)[3][3] = temp;
}
#endif // #if (defined(TINY_AES_CBC) && TINY_AES_CBC == 1) || (defined(TINY_AES_ECB) && TINY_AES_ECB == 1)
// Cipher is the main function that encrypts the PlainText.
static void Cipher(state_t* state, const uint8_t* RoundKey)
{
uint8_t round = 0;
// Add the First round key to the state before starting the rounds.
AddRoundKey(0, state, RoundKey);
// There will be Nr rounds.
// The first Nr-1 rounds are identical.
// These Nr rounds are executed in the loop below.
// Last one without MixColumns()
for (round = 1; ; ++round)
{
SubBytes(state);
ShiftRows(state);
if (round == Nr) {
break;
}
MixColumns(state);
AddRoundKey(round, state, RoundKey);
}
// Add round key to last round
AddRoundKey(Nr, state, RoundKey);
}
#if (defined(TINY_AES_CBC) && TINY_AES_CBC == 1) || (defined(TINY_AES_ECB) && TINY_AES_ECB == 1)
static void InvCipher(state_t* state, const uint8_t* RoundKey)
{
uint8_t round = 0;
// Add the First round key to the state before starting the rounds.
AddRoundKey(Nr, state, RoundKey);
// There will be Nr rounds.
// The first Nr-1 rounds are identical.
// These Nr rounds are executed in the loop below.
// Last one without InvMixColumn()
for (round = (Nr - 1); ; --round)
{
InvShiftRows(state);
InvSubBytes(state);
AddRoundKey(round, state, RoundKey);
if (round == 0) {
break;
}
InvMixColumns(state);
}
}
#endif // #if (defined(TINY_AES_CBC) && TINY_AES_CBC == 1) || (defined(TINY_AES_ECB) && TINY_AES_ECB == 1)
/*****************************************************************************/
/* Public functions: */
/*****************************************************************************/
#if defined(TINY_AES_ECB) && (TINY_AES_ECB == 1)
void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf)
{
// The next function call encrypts the PlainText with the Key using AES algorithm.
Cipher((state_t*)buf, ctx->RoundKey);
}
void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf)
{
// The next function call decrypts the PlainText with the Key using AES algorithm.
InvCipher((state_t*)buf, ctx->RoundKey);
}
#endif // #if defined(TINY_AES_ECB) && (TINY_AES_ECB == 1)
#if defined(TINY_AES_CBC) && (TINY_AES_CBC == 1)
static void XorWithIv(uint8_t* buf, const uint8_t* Iv)
{
uint8_t i;
for (i = 0; i < AES_BLOCKLEN; ++i) // The block in AES is always 128bit no matter the key size
{
buf[i] ^= Iv[i];
}
}
void AES_CBC_encrypt_buffer(struct AES_ctx *ctx, uint8_t* buf, size_t length)
{
size_t i;
uint8_t *Iv = ctx->Iv;
for (i = 0; i < length; i += AES_BLOCKLEN)
{
XorWithIv(buf, Iv);
Cipher((state_t*)buf, ctx->RoundKey);
Iv = buf;
buf += AES_BLOCKLEN;
}
/* store Iv in ctx for next call */
memcpy(ctx->Iv, Iv, AES_BLOCKLEN);
}
void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length)
{
size_t i;
uint8_t storeNextIv[AES_BLOCKLEN];
for (i = 0; i < length; i += AES_BLOCKLEN)
{
memcpy(storeNextIv, buf, AES_BLOCKLEN);
InvCipher((state_t*)buf, ctx->RoundKey);
XorWithIv(buf, ctx->Iv);
memcpy(ctx->Iv, storeNextIv, AES_BLOCKLEN);
buf += AES_BLOCKLEN;
}
}
#endif // #if defined(TINY_AES_CBC) && (TINY_AES_CBC == 1)
#if defined(TINY_AES_CTR) && (TINY_AES_CTR == 1)
/* Symmetrical operation: same function for encrypting as for decrypting. Note any IV/nonce should never be reused with the same key */
void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length)
{
uint8_t buffer[AES_BLOCKLEN];
size_t i;
int bi;
for (i = 0, bi = AES_BLOCKLEN; i < length; ++i, ++bi)
{
if (bi == AES_BLOCKLEN) /* we need to regen xor compliment in buffer */
{
memcpy(buffer, ctx->Iv, AES_BLOCKLEN);
Cipher((state_t*)buffer,ctx->RoundKey);
/* Increment Iv and handle overflow */
for (bi = (AES_BLOCKLEN - 1); bi >= 0; --bi)
{
/* inc will overflow */
if (ctx->Iv[bi] == 255)
{
ctx->Iv[bi] = 0;
continue;
}
ctx->Iv[bi] += 1;
break;
}
bi = 0;
}
buf[i] = (buf[i] ^ buffer[bi]);
}
}
#endif // #if defined(TINY_AES_CTR) && (TINY_AES_CTR == 1)
#endif
#endif // _AES_H_