ESP32: Add Sodium module (#2550)
* Add Sodium module * Split sodium API into subtables; updated docs * Fixed refactored names of crypto_box fns
This commit is contained in:
parent
d2345f2beb
commit
1cb1aff4cd
|
@ -166,6 +166,12 @@ config LUA_MODULE_SIGMA_DELTA
|
|||
Includes the sigma_delta module. This module provides access to the
|
||||
sigma-delta hardware.
|
||||
|
||||
config LUA_MODULE_SODIUM
|
||||
bool "Sodium module"
|
||||
default "n"
|
||||
help
|
||||
Includes the libsodium crypto module.
|
||||
|
||||
config LUA_MODULE_SPI
|
||||
bool "SPI module"
|
||||
default "n"
|
||||
|
|
|
@ -0,0 +1,152 @@
|
|||
#include "module.h"
|
||||
#include "lauxlib.h"
|
||||
#include "lmem.h"
|
||||
|
||||
#include "sodium.h"
|
||||
|
||||
static void check_init(lua_State *L)
|
||||
{
|
||||
if (sodium_init() == -1) {
|
||||
luaL_error(L, "sodium_init returned an error");
|
||||
}
|
||||
}
|
||||
|
||||
// https://download.libsodium.org/doc/generating_random_data
|
||||
|
||||
static int l_randombytes_random(lua_State *L)
|
||||
{
|
||||
check_init(L);
|
||||
uint32_t ret = randombytes_random();
|
||||
lua_pushnumber(L, (lua_Number)ret);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_randombytes_uniform(lua_State *L)
|
||||
{
|
||||
check_init(L);
|
||||
uint32_t upper_bound = (uint32_t)luaL_checkinteger(L, 1);
|
||||
uint32_t ret = randombytes_uniform(upper_bound);
|
||||
lua_pushnumber(L, (lua_Number)ret);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_randombytes_buf(lua_State *L)
|
||||
{
|
||||
check_init(L);
|
||||
size_t count = (size_t)luaL_checkinteger(L, 1);
|
||||
if (count <= LUAL_BUFFERSIZE) {
|
||||
luaL_Buffer b;
|
||||
luaL_buffinit(L, &b);
|
||||
randombytes_buf(luaL_prepbuffer(&b), count);
|
||||
luaL_addsize(&b, count);
|
||||
luaL_pushresult(&b);
|
||||
} else {
|
||||
char *buf = (char *)luaM_malloc(L, count);
|
||||
randombytes_buf(buf, count);
|
||||
lua_pushlstring(L, buf, count);
|
||||
luaM_freemem(L, buf, count);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// See https://download.libsodium.org/doc/public-key_cryptography/sealed_boxes
|
||||
|
||||
static int l_crypto_box_keypair(lua_State *L)
|
||||
{
|
||||
check_init(L);
|
||||
unsigned char pk[crypto_box_PUBLICKEYBYTES];
|
||||
unsigned char sk[crypto_box_SECRETKEYBYTES];
|
||||
|
||||
int err = crypto_box_keypair(pk, sk);
|
||||
if (err) {
|
||||
return luaL_error(L, "crypto_box_keypair returned %d", err);
|
||||
}
|
||||
lua_pushlstring(L, (char *)pk, sizeof(pk));
|
||||
lua_pushlstring(L, (char *)sk, sizeof(sk));
|
||||
return 2;
|
||||
}
|
||||
|
||||
static const uint8_t * get_pk(lua_State *L, int idx)
|
||||
{
|
||||
check_init(L);
|
||||
size_t pk_len;
|
||||
const char *pk = luaL_checklstring(L, 2, &pk_len);
|
||||
if (pk_len != crypto_box_PUBLICKEYBYTES) {
|
||||
luaL_error(L, "Bad public key size!");
|
||||
}
|
||||
return (const uint8_t *)pk;
|
||||
}
|
||||
|
||||
static const uint8_t * get_sk(lua_State *L, int idx)
|
||||
{
|
||||
check_init(L);
|
||||
size_t sk_len;
|
||||
const char *sk = luaL_checklstring(L, idx, &sk_len);
|
||||
if (sk_len != crypto_box_SECRETKEYBYTES) {
|
||||
luaL_error(L, "Bad secret key size!");
|
||||
}
|
||||
return (const uint8_t *)sk;
|
||||
}
|
||||
|
||||
static int l_crypto_box_seal(lua_State *L)
|
||||
{
|
||||
check_init(L);
|
||||
size_t msg_len;
|
||||
const uint8_t *msg = (const uint8_t *)luaL_checklstring(L, 1, &msg_len);
|
||||
const uint8_t *pk = get_pk(L, 2);
|
||||
|
||||
const size_t ciphertext_len = crypto_box_SEALBYTES + msg_len;
|
||||
uint8_t *ciphertext = (uint8_t *)luaM_malloc(L, ciphertext_len);
|
||||
|
||||
int err = crypto_box_seal(ciphertext, msg, msg_len, pk);
|
||||
if (err) {
|
||||
luaM_freemem(L, ciphertext, ciphertext_len);
|
||||
return luaL_error(L, "crypto_box_seal returned %d", err);
|
||||
}
|
||||
lua_pushlstring(L, (char *)ciphertext, ciphertext_len);
|
||||
luaM_freemem(L, ciphertext, ciphertext_len);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_crypto_box_seal_open(lua_State *L)
|
||||
{
|
||||
check_init(L);
|
||||
size_t ciphertext_len;
|
||||
const uint8_t *ciphertext = (const uint8_t *)luaL_checklstring(L, 1, &ciphertext_len);
|
||||
const uint8_t *pk = get_pk(L, 2);
|
||||
const uint8_t *sk = get_sk(L, 3);
|
||||
|
||||
const size_t decrypted_len = ciphertext_len - crypto_box_SEALBYTES;
|
||||
uint8_t *decrypted = (uint8_t *)luaM_malloc(L, decrypted_len);
|
||||
|
||||
int err = crypto_box_seal_open(decrypted, ciphertext, ciphertext_len, pk, sk);
|
||||
if (err) {
|
||||
lua_pushnil(L);
|
||||
} else {
|
||||
lua_pushlstring(L, (char *)decrypted, decrypted_len);
|
||||
}
|
||||
luaM_freemem(L, decrypted, decrypted_len);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const LUA_REG_TYPE random_map[] = {
|
||||
{ LSTRKEY("random"), LFUNCVAL(l_randombytes_random) },
|
||||
{ LSTRKEY("uniform"), LFUNCVAL(l_randombytes_uniform) },
|
||||
{ LSTRKEY("buf"), LFUNCVAL(l_randombytes_buf) },
|
||||
{ LNILKEY, LNILVAL }
|
||||
};
|
||||
|
||||
static const LUA_REG_TYPE crypto_box_map[] = {
|
||||
{ LSTRKEY("keypair"), LFUNCVAL(l_crypto_box_keypair) },
|
||||
{ LSTRKEY("seal"), LFUNCVAL(l_crypto_box_seal) },
|
||||
{ LSTRKEY("seal_open"), LFUNCVAL(l_crypto_box_seal_open) },
|
||||
{ LNILKEY, LNILVAL }
|
||||
};
|
||||
|
||||
static const LUA_REG_TYPE sodium_map[] = {
|
||||
{ LSTRKEY("random"), LROVAL(random_map) },
|
||||
{ LSTRKEY("crypto_box"), LROVAL(crypto_box_map) },
|
||||
{ LNILKEY, LNILVAL }
|
||||
};
|
||||
|
||||
NODEMCU_MODULE(SODIUM, "sodium", sodium_map, NULL);
|
|
@ -0,0 +1,108 @@
|
|||
# Sodium module
|
||||
| Since | Origin / Contributor | Maintainer | Source |
|
||||
| :----- | :-------------------- | :---------- | :------ |
|
||||
| 2018-10-27 | [Tom Sutcliffe](https://github.com/tomsci) | [Tom Sutcliffe](https://github.com/tomsci) | [sodium.c](../../../components/modules/sodium.c)|
|
||||
|
||||
This module wraps the [LibSodium](https://libsodium.org/) C library. LibSodium is a library for performing Elliptic Curve Cryptography.
|
||||
|
||||
In addition to the flag for enabling this module during ROM build, `Component config -> NodeMCU Modules -> Sodium module`, there are additional settings for libsodium under `Component config -> libsodium`.
|
||||
|
||||
!!! note
|
||||
|
||||
Almost all functions in this module require a working random number generator. On the ESP32 this means that *WiFi must be started* otherwise ALL OF THE CRYPTOGRAPHY WILL SILENTLY BE COMPROMISED. Make sure to call `wifi.start()` before any of the functions in this module. See the [Espressif documentation](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/system/system.html#random-number-generation) for more information. The only exception is `sodium.crypto_box.seal_open()` which does not require a random number source to operate.
|
||||
|
||||
# Random number generation
|
||||
See also [https://download.libsodium.org/doc/generating_random_data](https://download.libsodium.org/doc/generating_random_data)
|
||||
|
||||
## sodium.random.random()
|
||||
Returns a random integer between `0` and `0xFFFFFFFF` inclusive. Note that on a build using `LUA_NUMBER_INTEGRAL`, results may appear negative due to integer overflow. Wifi must be started, by calling `wifi.start()`, before calling this function.
|
||||
|
||||
#### Syntax
|
||||
`sodium.random.random()`
|
||||
|
||||
#### Parameters
|
||||
None
|
||||
|
||||
#### Returns
|
||||
A uniformly-distributed random integer between `0` and `0xFFFFFFFF` inclusive.
|
||||
|
||||
## sodium.random.uniform()
|
||||
Returns a random integer `0 <= result < upper_bound`. Unlike `sodium.random.random() % upper_bound`, it guarantees a uniform distribution of the possible output values even when `upper_bound` is not a power of 2. Note that on a build using `LUA_NUMBER_INTEGRAL`, if `upper_bound >= 0x80000000` the result may appear negative due to integer overflow. Wifi must be started, by calling `wifi.start()`, before calling this function.
|
||||
|
||||
#### Syntax
|
||||
`sodium.random.uniform(upper_bound)`
|
||||
|
||||
#### Parameters
|
||||
- `upper_bound` must be an integer `<= 0xFFFFFFFF`.
|
||||
|
||||
#### Returns
|
||||
An integer `>= 0` and `< upper_bound`
|
||||
|
||||
## sodium.random.buf()
|
||||
Generates `n` bytes of random data. Wifi must be started, by calling `wifi.start()`, before calling this function.
|
||||
|
||||
#### Syntax
|
||||
`sodium.random.buf(n)`
|
||||
|
||||
#### Parameters
|
||||
- `n` number of bytes to return.
|
||||
|
||||
#### Returns
|
||||
A string of `n` random bytes.
|
||||
|
||||
# Generating public and secret keys
|
||||
The keys created by `crypto_box.keypair()` can be used the `crypto_box.seal*()` functions.
|
||||
|
||||
## sodium.crypto_box.keypair()
|
||||
Generates a new keypair. Wifi must be started, by calling `wifi.start()`, before calling this function.
|
||||
|
||||
#### Parameters
|
||||
None
|
||||
|
||||
#### Returns
|
||||
Two values, `public_key, secret_key`. Both are strings (although containing non-printable characters).
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
public_key, secret_key = sodium.crypto_box.keypair()
|
||||
```
|
||||
|
||||
# Sealed box public key cryptography
|
||||
See also [https://download.libsodium.org/doc/public-key_cryptography/sealed_boxes](https://download.libsodium.org/doc/public-key_cryptography/sealed_boxes).
|
||||
|
||||
## sodium.crypto_box.seal()
|
||||
Encrypts a message using a public key, such that only someone knowing the corresponding secret key can decrypt it using [`sodium.crypto_box.seal_open()`](#sodiumcryptoboxsealopen). This API does not store any information about who encrypted the message, therefore at the point of decryption there is is no proof the message hasn't been tampered with or sent by somone else. Wifi must be started, by calling `wifi.start()`, before calling this function.
|
||||
|
||||
#### Syntax
|
||||
`sodium.crypto_box.seal(message, public_key)`
|
||||
|
||||
#### Parameters
|
||||
- `message` - the string to encrypt.
|
||||
- `public_key` - the public key to encrypt with.
|
||||
|
||||
#### Returns
|
||||
The encrypted message, as a string. Errors if `public_key` is not a valid public key as returned by `sodium.crypto_box.keypair()` or if the message could not be encrypted.
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
ciphertext = sodium.crypto_box.seal(message, public_key)
|
||||
```
|
||||
|
||||
## sodium.crypto_box.seal_open
|
||||
Decrypts a message encrypted with [`crypto_box.seal()`](#sodiumcryptoboxseal).
|
||||
|
||||
#### Syntax
|
||||
`sodium.crypto_box.seal_open(ciphertext, public_key, secret_key)`
|
||||
|
||||
#### Parameters
|
||||
- `ciphertext` - the encrypted message.
|
||||
- `public_key` - the public key the message was encrypted with.
|
||||
- `secret_key` - the secret key corresponding to the specified public key.
|
||||
|
||||
#### Returns
|
||||
The decrypted plain text of the message. Returns `nil` if the `ciphertext` could not be decrypted.
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
message = sodium.crypto_box.seal_open(ciphertext, public_key, secret_key)
|
||||
```
|
|
@ -50,6 +50,7 @@ pages:
|
|||
- 'ow (1-Wire)': 'en/modules/ow.md'
|
||||
- 'sdmmc': 'en/modules/sdmmc.md'
|
||||
- 'sigma delta': 'en/modules/sigma-delta.md'
|
||||
- 'sodium': 'en/modules/sodium.md'
|
||||
- 'spi': 'en/modules/spi.md'
|
||||
- 'struct': 'en/modules/struct.md'
|
||||
- 'tmr': 'en/modules/tmr.md'
|
||||
|
|
Loading…
Reference in New Issue