v0.0.29 - Feather firmware: auto-approve sign_event and always return explicit JSON-RPC errors for unhandled/oversized requests
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "esp_random.h"
|
||||
|
||||
@@ -18,6 +19,10 @@
|
||||
#define CHECKSUM_12_BITS 4
|
||||
#define TOTAL_12_BITS (ENTROPY_12_BYTES * 8 + CHECKSUM_12_BITS)
|
||||
|
||||
static bool s_letter_index_built = false;
|
||||
static size_t s_letter_start[26] = {0};
|
||||
static size_t s_letter_count[26] = {0};
|
||||
|
||||
static bool append_word(char *out, size_t out_len, size_t *cursor, const char *word)
|
||||
{
|
||||
const size_t word_len = strlen(word);
|
||||
@@ -59,6 +64,74 @@ static uint8_t get_bit_from_entropy_checksum(const uint8_t entropy[ENTROPY_12_BY
|
||||
return (uint8_t)((checksum[0] >> bit_in_byte) & 0x01);
|
||||
}
|
||||
|
||||
static int bip39_word_index(const char *word)
|
||||
{
|
||||
if (word == NULL || word[0] == '\0') {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 2048; ++i) {
|
||||
if (strcmp(word, BIP39_WORDLIST[i]) == 0) {
|
||||
return (int)i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static bool next_token(const char **cursor, char *out_word, size_t out_len)
|
||||
{
|
||||
size_t n = 0;
|
||||
const char *p = *cursor;
|
||||
|
||||
while (*p != '\0' && isspace((unsigned char)*p)) {
|
||||
++p;
|
||||
}
|
||||
|
||||
if (*p == '\0') {
|
||||
*cursor = p;
|
||||
return false;
|
||||
}
|
||||
|
||||
while (*p != '\0' && !isspace((unsigned char)*p)) {
|
||||
if (n + 1 >= out_len) {
|
||||
return false;
|
||||
}
|
||||
out_word[n++] = *p++;
|
||||
}
|
||||
|
||||
out_word[n] = '\0';
|
||||
*cursor = p;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void build_letter_index(void)
|
||||
{
|
||||
if (s_letter_index_built) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 26; ++i) {
|
||||
s_letter_start[i] = 0;
|
||||
s_letter_count[i] = 0;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 2048; ++i) {
|
||||
char c = BIP39_WORDLIST[i][0];
|
||||
if (c < 'a' || c > 'z') {
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t li = (size_t)(c - 'a');
|
||||
if (s_letter_count[li] == 0) {
|
||||
s_letter_start[li] = i;
|
||||
}
|
||||
s_letter_count[li]++;
|
||||
}
|
||||
|
||||
s_letter_index_built = true;
|
||||
}
|
||||
|
||||
int generate_mnemonic_12(char *out, size_t out_len)
|
||||
{
|
||||
uint8_t entropy[ENTROPY_12_BYTES] = {0};
|
||||
@@ -109,6 +182,109 @@ int generate_mnemonic_12(char *out, size_t out_len)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mnemonic_validate(const char *mnemonic)
|
||||
{
|
||||
uint16_t word_indices[MNEMONIC_12_WORDS] = {0};
|
||||
uint8_t entropy[ENTROPY_12_BYTES] = {0};
|
||||
uint8_t hash[32] = {0};
|
||||
const char *cursor = mnemonic;
|
||||
char token[16];
|
||||
size_t words = 0;
|
||||
size_t bit_pos = 0;
|
||||
|
||||
if (mnemonic == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (next_token(&cursor, token, sizeof(token))) {
|
||||
int idx;
|
||||
|
||||
if (words >= MNEMONIC_12_WORDS) {
|
||||
memset(entropy, 0, sizeof(entropy));
|
||||
memset(hash, 0, sizeof(hash));
|
||||
return -1;
|
||||
}
|
||||
|
||||
idx = bip39_word_index(token);
|
||||
if (idx < 0) {
|
||||
memset(entropy, 0, sizeof(entropy));
|
||||
memset(hash, 0, sizeof(hash));
|
||||
return -1;
|
||||
}
|
||||
|
||||
word_indices[words++] = (uint16_t)idx;
|
||||
}
|
||||
|
||||
if (words != MNEMONIC_12_WORDS) {
|
||||
memset(entropy, 0, sizeof(entropy));
|
||||
memset(hash, 0, sizeof(hash));
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (size_t w = 0; w < MNEMONIC_12_WORDS; ++w) {
|
||||
for (int b = 10; b >= 0; --b) {
|
||||
uint8_t bit = (uint8_t)((word_indices[w] >> b) & 0x01);
|
||||
if (bit_pos < ENTROPY_12_BYTES * 8) {
|
||||
size_t byte_index = bit_pos / 8;
|
||||
size_t bit_in_byte = 7 - (bit_pos % 8);
|
||||
entropy[byte_index] |= (uint8_t)(bit << bit_in_byte);
|
||||
}
|
||||
bit_pos++;
|
||||
}
|
||||
}
|
||||
|
||||
mbedtls_sha256(entropy, sizeof(entropy), hash, 0);
|
||||
|
||||
for (size_t i = 0; i < CHECKSUM_12_BITS; ++i) {
|
||||
size_t checksum_bit_pos = ENTROPY_12_BYTES * 8 + i;
|
||||
size_t word_idx = checksum_bit_pos / 11;
|
||||
size_t bit_in_word = 10 - (checksum_bit_pos % 11);
|
||||
uint8_t expected = (uint8_t)((hash[0] >> (7 - i)) & 0x01);
|
||||
uint8_t actual = (uint8_t)((word_indices[word_idx] >> bit_in_word) & 0x01);
|
||||
if (actual != expected) {
|
||||
memset(entropy, 0, sizeof(entropy));
|
||||
memset(hash, 0, sizeof(hash));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
memset(entropy, 0, sizeof(entropy));
|
||||
memset(hash, 0, sizeof(hash));
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t mnemonic_words_for_letter(char letter, size_t *out_start)
|
||||
{
|
||||
int idx;
|
||||
|
||||
if (out_start == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (letter >= 'A' && letter <= 'Z') {
|
||||
letter = (char)(letter - 'A' + 'a');
|
||||
}
|
||||
|
||||
if (letter < 'a' || letter > 'z') {
|
||||
*out_start = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
build_letter_index();
|
||||
|
||||
idx = letter - 'a';
|
||||
*out_start = s_letter_start[idx];
|
||||
return s_letter_count[idx];
|
||||
}
|
||||
|
||||
const char *mnemonic_word_at(size_t index)
|
||||
{
|
||||
if (index >= 2048) {
|
||||
return NULL;
|
||||
}
|
||||
return BIP39_WORDLIST[index];
|
||||
}
|
||||
|
||||
int mnemonic_to_seed(const char *mnemonic, uint8_t seed[64])
|
||||
{
|
||||
const mbedtls_md_info_t *md_info;
|
||||
|
||||
Reference in New Issue
Block a user