Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions lib/ed25519/ge.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ B is the Ed25519 base point (x,4/5) with x positive.
*/

void ge_double_scalarmult_vartime(ge_p2 *r, const unsigned char *a, const ge_p3 *A, const unsigned char *b) {
signed char aslide[256];
signed char bslide[256];
ge_cached Ai[8]; /* A,3A,5A,7A,9A,11A,13A,15A */
static signed char aslide[256];
static signed char bslide[256];
static ge_cached Ai[8]; /* A,3A,5A,7A,9A,11A,13A,15A */
Comment on lines +68 to +70
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making these static saves ~1.8KB of stack which fixes the overflow, but it also makes ge_double_scalarmult_vartime() non-reentrant. If ed25519_verify() is ever called concurrently from multiple FreeRTOS tasks, these shared buffers will be silently corrupted.

Currently this looks safe — Identity::verify() is only called from the packet receive path in Mesh.cpp — but it's worth documenting so future maintainers don't get bitten.

Suggested change
static signed char aslide[256];
static signed char bslide[256];
static ge_cached Ai[8]; /* A,3A,5A,7A,9A,11A,13A,15A */
static signed char aslide[256]; /* static: saves ~1.8KB stack; NOT reentrant */
static signed char bslide[256];
static ge_cached Ai[8]; /* A,3A,5A,7A,9A,11A,13A,15A */

Copy link
Contributor Author

@ViezeVingertjes ViezeVingertjes Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you thnk about an Assert? just in case its not read... (guilty of it sometimes)

48019bf

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, the assert makes the constraint very visible. Two minor notes:

  1. assert() is typically compiled out in release builds (NDEBUG), so this is really a development-time guard. That's fine for this purpose — the comment does the heavy lifting for future readers.
  2. The static volatile bool check + set isn't atomic, so there's a theoretical TOCTOU race. In practice this doesn't matter — ESP32 FreeRTOS tasks on a single core won't preempt mid-statement, and even on dual-core the window is vanishingly small. Just mentioning it for completeness.

Overall this looks good to me.

ge_p1p1 t;
ge_p3 u;
ge_p3 A2;
Expand Down
269 changes: 269 additions & 0 deletions lib/rweather_crypto/AES.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
/*
* Copyright (C) 2015,2018 Southern Storm Software, Pty Ltd.
*
* 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 CRYPTO_AES_h
#define CRYPTO_AES_h

#include "BlockCipher.h"

// Determine which AES implementation to export to applications.
#if defined(ESP32)
#define CRYPTO_AES_ESP32 1
#else
#define CRYPTO_AES_DEFAULT 1
#endif

#if defined(CRYPTO_AES_DEFAULT) || defined(CRYPTO_DOC)

class AESTiny128;
class AESTiny256;
class AESSmall128;
class AESSmall256;

class AESCommon : public BlockCipher
{
public:
virtual ~AESCommon();

size_t blockSize() const;

void encryptBlock(uint8_t *output, const uint8_t *input);
void decryptBlock(uint8_t *output, const uint8_t *input);

void clear();

protected:
AESCommon();

/** @cond aes_internal */
uint8_t rounds;
uint8_t *schedule;

static void subBytesAndShiftRows(uint8_t *output, const uint8_t *input);
static void inverseShiftRowsAndSubBytes(uint8_t *output, const uint8_t *input);
static void mixColumn(uint8_t *output, uint8_t *input);
static void inverseMixColumn(uint8_t *output, const uint8_t *input);
static void keyScheduleCore(uint8_t *output, const uint8_t *input, uint8_t iteration);
static void applySbox(uint8_t *output, const uint8_t *input);
/** @endcond */

friend class AESTiny128;
friend class AESTiny256;
friend class AESSmall128;
friend class AESSmall256;
};

class AES128 : public AESCommon
{
public:
AES128();
virtual ~AES128();

size_t keySize() const;

bool setKey(const uint8_t *key, size_t len);

private:
uint8_t sched[176];
};

class AES192 : public AESCommon
{
public:
AES192();
virtual ~AES192();

size_t keySize() const;

bool setKey(const uint8_t *key, size_t len);

private:
uint8_t sched[208];
};

class AES256 : public AESCommon
{
public:
AES256();
virtual ~AES256();

size_t keySize() const;

bool setKey(const uint8_t *key, size_t len);

private:
uint8_t sched[240];
};

class AESTiny256 : public BlockCipher
{
public:
AESTiny256();
virtual ~AESTiny256();

size_t blockSize() const;
size_t keySize() const;

bool setKey(const uint8_t *key, size_t len);

void encryptBlock(uint8_t *output, const uint8_t *input);
void decryptBlock(uint8_t *output, const uint8_t *input);

void clear();

private:
uint8_t schedule[32];
};

class AESSmall256 : public AESTiny256
{
public:
AESSmall256();
virtual ~AESSmall256();

bool setKey(const uint8_t *key, size_t len);

void decryptBlock(uint8_t *output, const uint8_t *input);

void clear();

private:
uint8_t reverse[32];
};

class AESTiny128 : public BlockCipher
{
public:
AESTiny128();
virtual ~AESTiny128();

size_t blockSize() const;
size_t keySize() const;

bool setKey(const uint8_t *key, size_t len);

void encryptBlock(uint8_t *output, const uint8_t *input);
void decryptBlock(uint8_t *output, const uint8_t *input);

void clear();

private:
uint8_t schedule[16];
};

class AESSmall128 : public AESTiny128
{
public:
AESSmall128();
virtual ~AESSmall128();

bool setKey(const uint8_t *key, size_t len);

void decryptBlock(uint8_t *output, const uint8_t *input);

void clear();

private:
uint8_t reverse[16];
};

#endif // CRYPTO_AES_DEFAULT

#if defined(CRYPTO_AES_ESP32)

/** @cond aes_esp_rename */

// The esp32 SDK keeps moving where aes.h is located, so we have to
// declare the API functions ourselves and make the context opaque.
//
// About the only thing the various SDK versions agree on is that the
// first byte is the length of the key in bytes.
//
// Some versions of esp-idf have a 33 byte AES context, and others 34.
// Allocate up to 40 to make space for future expansion.
#define CRYPTO_ESP32_CONTEXT_SIZE 40

// Some of the esp-idf system headers define enumerations for AES128,
// AES192, and AES256 to identify the hardware-accelerated algorithms.
// These can cause conflicts with the names we use in our library.
// Define our class names to something else to work around esp-idf.
#undef AES128
#undef AES192
#undef AES256
#define AES128 AES128_ESP
#define AES192 AES192_ESP
#define AES256 AES256_ESP

/** @endcond */

class AESCommon : public BlockCipher
{
public:
virtual ~AESCommon();

size_t blockSize() const;
size_t keySize() const;

bool setKey(const uint8_t *key, size_t len);

void encryptBlock(uint8_t *output, const uint8_t *input);
void decryptBlock(uint8_t *output, const uint8_t *input);

void clear();

protected:
AESCommon(uint8_t keySize);

private:
uint8_t ctx[CRYPTO_ESP32_CONTEXT_SIZE];
};

class AES128 : public AESCommon
{
public:
AES128() : AESCommon(16) {}
virtual ~AES128();
};

class AES192 : public AESCommon
{
public:
AES192() : AESCommon(24) {}
virtual ~AES192();
};

class AES256 : public AESCommon
{
public:
AES256() : AESCommon(32) {}
virtual ~AES256();
};

// The ESP32 AES context is so small that it already qualifies as "tiny".
typedef AES128 AESTiny128;
typedef AES256 AESTiny256;
typedef AES128 AESSmall128;
typedef AES256 AESSmall256;

#endif // CRYPTO_AES_ESP32

#endif
Loading