mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-29 10:58:21 +00:00
bip39 implementation with unit tests
This commit is contained in:
parent
a439d8674d
commit
42da580ce8
2
Makefile
2
Makefile
@ -1,6 +1,6 @@
|
||||
CC = gcc
|
||||
CFLAGS = -Wall -Os
|
||||
OBJS = bignum.o ecdsa.o secp256k1.o sha2.o rand.o hmac.o bip32.o ripemd160.o
|
||||
OBJS = bignum.o ecdsa.o secp256k1.o sha2.o rand.o hmac.o bip32.o ripemd160.o bip39.o
|
||||
OBJS += aescrypt.o aeskey.o aestab.o
|
||||
|
||||
all: tests test-openssl
|
||||
|
1
README
1
README
@ -7,6 +7,7 @@ These include:
|
||||
- AES/Rijndael encryption/decryption
|
||||
- Big Number (256 bit) Arithmetics
|
||||
- BIP32 Hierarchical Deterministic Wallets
|
||||
- BIP39 Mnemonic code
|
||||
- ECDSA signing/verifying (only hardcoded secp256k1 curve,
|
||||
uses RFC6979 for deterministic signatures)
|
||||
- ECDSA public key derivation + Base58 address representation
|
||||
|
155
bip39.c
Normal file
155
bip39.c
Normal file
@ -0,0 +1,155 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "bip39.h"
|
||||
#include "sha2.h"
|
||||
#include "aes.h"
|
||||
#include "bip39_english.h"
|
||||
|
||||
#define RIJNDAEL_ITERATIONS 10000
|
||||
|
||||
void mnemonic_rijndael(uint8_t *data, int len, char *passphrase, bool encrypt)
|
||||
{
|
||||
if (len != 16 && len != 24 && len != 32) return;
|
||||
|
||||
SHA256_CTX sha_ctx;
|
||||
aes_ctx ctx;
|
||||
uint8_t key[32];
|
||||
int i;
|
||||
|
||||
SHA256_Init(&sha_ctx);
|
||||
SHA256_Update(&sha_ctx, (uint8_t *)"mnemonic", 8);
|
||||
if (passphrase) {
|
||||
SHA256_Update(&sha_ctx, (uint8_t *)passphrase, strlen(passphrase));
|
||||
}
|
||||
SHA256_Final(key, &sha_ctx);
|
||||
|
||||
aes_blk_len(len, &ctx);
|
||||
|
||||
if (encrypt) {
|
||||
aes_enc_key(key, 32, &ctx);
|
||||
for (i = 0; i < RIJNDAEL_ITERATIONS; i++) {
|
||||
aes_enc_blk(data, data, &ctx);
|
||||
}
|
||||
} else {
|
||||
aes_dec_key(key, 32, &ctx);
|
||||
for (i = 0; i < RIJNDAEL_ITERATIONS; i++) {
|
||||
aes_dec_blk(data, data, &ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define mnemonic_stretch(D, L, P) mnemonic_rijndael((D), (L), (P), true)
|
||||
#define mnemonic_unstretch(D, L, P) mnemonic_rijndael((D), (L), (P), false)
|
||||
|
||||
static char mnemo[24 * 10];
|
||||
static char bits[256 + 8];
|
||||
|
||||
char mnemonic_checksum(uint8_t *data, int len)
|
||||
{
|
||||
char r = 0;
|
||||
int i;
|
||||
switch (len) {
|
||||
case 16: // checksum = 4 bits
|
||||
for (i = 0; i < 16; i++) {
|
||||
r ^= (data[i] & 0xF0) >> 4;
|
||||
r ^= (data[i] & 0x0F);
|
||||
}
|
||||
break;
|
||||
case 24: // checksum = 6 bits
|
||||
for (i = 0; i < 8; i++) {
|
||||
r ^= (data[3 * i] & 0xFC) >> 2; // xxxxxx__ ________ ________
|
||||
r ^= ((data[3 * i] & 0x03) << 4) | ((data[3 * i + 1] & 0xF0) >> 4); // ______xx xxxx____ ________
|
||||
r ^= ((data[3 * i + 1] & 0x0F) << 2) | ((data[3 * i + 2] & 0xC0) >> 6); // ________ ____xxxx xx______
|
||||
r ^= data[3 * i + 2] & 0x3F; // ________ ________ __xxxxxx
|
||||
}
|
||||
break;
|
||||
case 32: // checksum = 8 bits
|
||||
for (i = 0; i < 32; i++) {
|
||||
r ^= data[i];
|
||||
}
|
||||
break;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
const char *mnemonic_encode(uint8_t *data, int len, char *passphrase)
|
||||
{
|
||||
if (len != 16 && len != 24 && len != 32) return 0;
|
||||
|
||||
mnemonic_stretch(data, len, passphrase);
|
||||
|
||||
int i, j;
|
||||
for (i = 0; i < len; i++) {
|
||||
for (j = 0; j < 8; j++) {
|
||||
bits[8 * i + j] = (data[i] & (1 << (7 - j))) > 0;
|
||||
}
|
||||
}
|
||||
|
||||
char checksum = mnemonic_checksum(data, len);
|
||||
for (j = 0; j < (len/4); j++) {
|
||||
bits[8 * len + j] = (checksum & (1 << ((len / 4 - 1) - j))) > 0;
|
||||
}
|
||||
|
||||
len = len * 3 / 4;
|
||||
char *p = mnemo;
|
||||
for (i = 0; i < len; i++) {
|
||||
int idx = 0;
|
||||
for (j = 0; j < 11; j++) {
|
||||
idx += bits[i * 11 + j] << (10 - j);
|
||||
}
|
||||
strcpy(p, wordlist[idx]);
|
||||
p += strlen(wordlist[idx]);
|
||||
*p = (i < len - 1) ? ' ' : 0;
|
||||
p++;
|
||||
}
|
||||
|
||||
return mnemo;
|
||||
}
|
||||
|
||||
int wordlist_index(const char *word)
|
||||
{
|
||||
int i = 0;
|
||||
while (wordlist[i]) {
|
||||
if (strcmp(word, wordlist[i]) == 0) return i;
|
||||
i++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int mnemonic_decode(const char *mnemonic, uint8_t *data, char *passphrase)
|
||||
{
|
||||
strcpy(mnemo, mnemonic);
|
||||
|
||||
int i, j, b = 0, len;
|
||||
char *p = strtok(mnemo, " ");
|
||||
while (p) {
|
||||
int idx = wordlist_index(p);
|
||||
if (idx < 0 || idx > 2047) return 0;
|
||||
for (j = 0; j < 11; j++) {
|
||||
bits[b] = (idx & (1 << (10 - j))) > 0;
|
||||
b++;
|
||||
}
|
||||
p = strtok(NULL, " ");
|
||||
}
|
||||
if (b != 128 + 4 && b != 192 + 6 && b != 256 + 8) return 0;
|
||||
|
||||
len = b / 33 * 4;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
data[i] = 0;
|
||||
for (j = 0; j < 8; j++) {
|
||||
data[i] |= bits[8 * i + j] << (7 - j);
|
||||
}
|
||||
}
|
||||
char checksum = 0;
|
||||
for (j = 0; j < (len/4); j++) {
|
||||
checksum |= bits[8 * len + j] << ((len / 4 - 1) - j);
|
||||
}
|
||||
if (checksum != mnemonic_checksum(data, len)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
mnemonic_unstretch(data, len, passphrase);
|
||||
|
||||
return len;
|
||||
}
|
11
bip39.h
Normal file
11
bip39.h
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef __BIP39_H__
|
||||
#define __BIP39_H__
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
const char *mnemonic_encode(uint8_t *data, int len, char *passphrase);
|
||||
|
||||
int mnemonic_decode(const char *mnemonic, uint8_t *data, char *passphrase);
|
||||
|
||||
#endif
|
2051
bip39_english.h
Normal file
2051
bip39_english.h
Normal file
File diff suppressed because it is too large
Load Diff
86
tests.c
86
tests.c
@ -29,6 +29,7 @@
|
||||
#include "aes.h"
|
||||
#include "bignum.h"
|
||||
#include "bip32.h"
|
||||
#include "bip39.h"
|
||||
#include "ecdsa.h"
|
||||
#include "sha2.h"
|
||||
|
||||
@ -290,6 +291,87 @@ START_TEST(test_rijndael)
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_mnemonic)
|
||||
{
|
||||
static const char *vectors[] = {
|
||||
"00000000000000000000000000000000",
|
||||
"risk tiger venture dinner age assume float denial penalty hello game wing",
|
||||
"7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",
|
||||
"truth chase learn pretty right casual acoustic frozen betray main slogan method",
|
||||
"80808080808080808080808080808080",
|
||||
"olive garment twenty drill people finish hat own usual level milk usage",
|
||||
"ffffffffffffffffffffffffffffffff",
|
||||
"laundry faint system client frog vanish plug shell slot cable large embrace",
|
||||
"000000000000000000000000000000000000000000000000",
|
||||
"giant twelve seat embark ostrich jazz leader lunch budget hover much weapon vendor build truth garden year list",
|
||||
"7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",
|
||||
"awful faint gun mean fuel side slogan marine glad donkey velvet oyster movie real type digital dress federal",
|
||||
"808080808080808080808080808080808080808080808080",
|
||||
"bless carpet daughter animal hospital pave faculty escape fortune song sign twin unknown bread mobile normal agent use",
|
||||
"ffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"saddle curve flight drama client resemble venture arch will ordinary enrich clutch razor shallow trophy tumble dice outer",
|
||||
"0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"supreme army trim onion neglect coach squirrel spider device glass cabbage giant web digital floor able social magnet only fork fuel embrace salt fence",
|
||||
"7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",
|
||||
"cloth video uncle switch year captain artist country adjust edit inherit ocean tennis soda baby express hospital forest panel actual profit boy spice elite",
|
||||
"8080808080808080808080808080808080808080808080808080808080808080",
|
||||
"fence twin prize extra choose mask twist deny cereal quarter can power term ostrich leg staff nature nut swift sausage amateur aim script wisdom",
|
||||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"moon fiscal evidence exile rifle series neglect giant exclude banana glance frown kangaroo globe turtle hat fitness casual sudden select idle arctic best unlock",
|
||||
"449ea2d7249c6e0d8d295424fb8894cf",
|
||||
"choice barrel artefact cram increase sell veteran matrix mirror hollow walk pave",
|
||||
"75fc3f44a7ff8e2b8af05aa18bded3827a3796df406763dd",
|
||||
"crack outside teach chat praise client manual scorpion predict chalk decrease casino lunch garbage enable ball when bamboo",
|
||||
"1cce2f8c2c6a7f2d8473ebf1c32ce13b36737835d7a8768f44dcf96d64782c0e",
|
||||
"muffin evoke all fiber night guard black quote neck expire dial tenant leisure have dragon neck notable peace captain insane nice uphold shine angry",
|
||||
"3daa82dd08bd144ec9fb9f77c6ece3d2",
|
||||
"foil dawn net enroll turtle bird vault trumpet service fun immune unveil",
|
||||
"9720239c0039f8446d44334daec325f3c24b3a490315d6d9",
|
||||
"damp all desert dash insane pear debate easily soup enough goddess make friend plug violin pact wealth insect",
|
||||
"fe58c6644bc3fad95832d4400cea0cce208c8b19bb4734a26995440b7fae7600",
|
||||
"wet sniff asthma once gap enrich pumpkin define trust rude gesture keen grass fine emerge census immense smooth ritual spirit rescue problem beef choice",
|
||||
"99fe82c94edadffe75e1cc64cbd7ada7",
|
||||
"thing real emerge verify domain cloud lens teach travel radio effort glad",
|
||||
"4fd6e8d06d55b4700130f8f462f7f9bfc6188da83e3faadb",
|
||||
"diary opinion lobster code orange odor insane permit spirit evolve upset final antique grant friend dutch say enroll",
|
||||
"7a547fb59606e89ba88188013712946f6cb31c3e0ca606a7ee1ff23f57272c63",
|
||||
"layer owner legal stadium glance oyster element spell episode eager wagon stand pride old defense black print junior fade easy topic ready galaxy debris",
|
||||
"e5fc62d20e0e5d9b2756e8d4d91cbb80",
|
||||
"flat make unit discover rifle armed unit acquire group panel nerve want",
|
||||
"d29be791a9e4b6a48ff79003dbf31d6afabdc4290a273765",
|
||||
"absurd valve party disorder basket injury make blanket vintage ancient please random theory cart retire odor borrow belt",
|
||||
"c87c135433c16f1ecbf9919dc53dd9f30f85824dc7264d4e1bd644826c902be2",
|
||||
"upper will wisdom term once bean blur inquiry used bamboo frequent hamster amazing cake attack any author mimic leopard day token joy install company",
|
||||
0,
|
||||
0,
|
||||
};
|
||||
|
||||
const char **d, **s, *m;
|
||||
|
||||
// check encode
|
||||
d = vectors;
|
||||
s = vectors + 1;
|
||||
while (*d && *s) {
|
||||
m = mnemonic_encode(fromhex(*d), strlen(*d) / 2, 0);
|
||||
ck_assert_ptr_ne(m, 0);
|
||||
ck_assert_str_eq(m, *s);
|
||||
d += 2; s += 2;
|
||||
}
|
||||
|
||||
// check decode
|
||||
d = vectors;
|
||||
s = vectors + 1;
|
||||
uint8_t data[32];
|
||||
int len;
|
||||
while (*d && *s) {
|
||||
len = mnemonic_decode(*s, data, 0);
|
||||
ck_assert_int_eq(len, strlen(*d) / 2);
|
||||
ck_assert_mem_eq(fromhex(*d), data, len);
|
||||
d += 2; s += 2;
|
||||
}
|
||||
}
|
||||
END_TEST
|
||||
|
||||
// define test suite and cases
|
||||
Suite *test_suite(void)
|
||||
{
|
||||
@ -314,6 +396,10 @@ Suite *test_suite(void)
|
||||
tcase_add_test(tc, test_rijndael);
|
||||
suite_add_tcase(s, tc);
|
||||
|
||||
tc = tcase_create("bip39");
|
||||
tcase_add_test(tc, test_mnemonic);
|
||||
suite_add_tcase(s, tc);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user