bip39 implementation with unit tests

pull/25/head
Pavol Rusnak 11 years ago
parent a439d8674d
commit 42da580ce8

@ -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

@ -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

@ -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;
}

@ -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

File diff suppressed because it is too large Load Diff

@ -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…
Cancel
Save