Skip to content
4 changes: 4 additions & 0 deletions skycoin-api/bitcoin_crypto.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@

#include "bitcoin_constants.h"
#include "skycoin_signature.h"
#include "tools/bip39.h"
#include "tools/base58.h"
#include "tools/secp256k1.h"
#include "tools/curves.h"
#include "tools/ecdsa.h"
#include "tools/ripemd160.h"
#include "tools/sha2.h"
#include "tools/hasher.h"
#include "tools/memzero.h"
#include "stdint.h"

int bitcoin_address_from_pubkey(const uint8_t* pubkey, char* b58address, size_t* size_b58address){
const curve_info* curve = get_curve_by_name(SECP256K1_NAME);
Expand Down
18 changes: 18 additions & 0 deletions skycoin-api/skycoin_crypto.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
#include "tools/ripemd160.h"
#include "tools/sha2.h"

#define FROMHEX_MAXLEN 512

extern void bn_print(const bignum256* a);

bool verify_pub_key(const uint8_t* pub_key) {
Expand All @@ -47,6 +49,22 @@ void tohex(char* str, const uint8_t* buffer, int buffer_length)
}
}

const uint8_t* fromhex(const char* str)
{
static uint8_t buf[FROMHEX_MAXLEN];
size_t len = strlen(str) / 2;
if (len > FROMHEX_MAXLEN) len = FROMHEX_MAXLEN;
for (size_t i = 0; i < len; i++) {
uint8_t c = 0;
if (str[i * 2] >= '0' && str[i * 2] <= '9') c += (str[i * 2] - '0') << 4;
if ((str[i * 2] & ~0x20) >= 'A' && (str[i * 2] & ~0x20) <= 'F') c += (10 + (str[i * 2] & ~0x20) - 'A') << 4;
if (str[i * 2 + 1] >= '0' && str[i * 2 + 1] <= '9') c += (str[i * 2 + 1] - '0');
if ((str[i * 2 + 1] & ~0x20) >= 'A' && (str[i * 2 + 1] & ~0x20) <= 'F') c += (10 + (str[i * 2 + 1] & ~0x20) - 'A');
buf[i] = c;
}
return buf;
}

void tobuff(const char* str, uint8_t* buf, size_t buffer_length)
{
for (size_t i = 0; i < buffer_length; i++) {
Expand Down
1 change: 1 addition & 0 deletions skycoin-api/skycoin_crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ void skycoin_pubkey_from_seckey(const uint8_t* seckey, uint8_t* pubkey);
int skycoin_address_from_pubkey(const uint8_t* pubkey, char* address, size_t* size_address);
int skycoin_ecdsa_sign_digest(const uint8_t* priv_key, const uint8_t* digest, uint8_t* sig);
void tohex(char* str, const uint8_t* buffer, int buffer_length);
const uint8_t* fromhex(const char* str);
void tobuff(const char* str, uint8_t* buf, size_t buffer_length);
void writebuf_fromhexstr(const char* str, uint8_t* buf);

Expand Down
102 changes: 85 additions & 17 deletions skycoin-api/test_skycoin_crypto.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,6 @@
#include "tools/sha2.h" //SHA256_DIGEST_LENGTH
#include "tools/sha3.h"

#define FROMHEX_MAXLEN 512

const uint8_t* fromhex(const char* str)
{
static uint8_t buf[FROMHEX_MAXLEN];
size_t len = strlen(str) / 2;
if (len > FROMHEX_MAXLEN) len = FROMHEX_MAXLEN;
for (size_t i = 0; i < len; i++) {
uint8_t c = 0;
if (str[i * 2] >= '0' && str[i * 2] <= '9') c += (str[i * 2] - '0') << 4;
if ((str[i * 2] & ~0x20) >= 'A' && (str[i * 2] & ~0x20) <= 'F') c += (10 + (str[i * 2] & ~0x20) - 'A') << 4;
if (str[i * 2 + 1] >= '0' && str[i * 2 + 1] <= '9') c += (str[i * 2 + 1] - '0');
if ((str[i * 2 + 1] & ~0x20) >= 'A' && (str[i * 2 + 1] & ~0x20) <= 'F') c += (10 + (str[i * 2 + 1] & ~0x20) - 'A');
buf[i] = c;
}
return buf;
}

START_TEST(test_base58_decode)
{
Expand Down Expand Up @@ -685,6 +668,90 @@ START_TEST(test_deterministic_key_pair_iterator)
}
END_TEST

// test vector 1 from
// https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#test-vector-1
START_TEST(test_bip32_vector_1){
HDNode node;
uint32_t fingerprint;
char str[112] = {0};

//init m
hdnode_from_seed(fromhex("000102030405060708090a0b0c0d0e0f"), 16, SECP256K1_NAME, &node);


// Chain m
fingerprint = 0;
hdnode_fill_public_key(&node);

hdnode_serialize(&node, fingerprint, VERSION_PRIVATE, 0, str, sizeof(str));
ck_assert_str_eq(str, "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi");

hdnode_serialize(&node, fingerprint, VERSION_PUBLIC, 1, str, sizeof(str));
ck_assert_str_eq(str, "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8");


// Chain m/0'
fingerprint = hdnode_fingerprint(&node);
hdnode_private_ckd_prime(&node, 0);
hdnode_fill_public_key(&node);

hdnode_serialize(&node, fingerprint, VERSION_PRIVATE, 0, str, sizeof(str));
ck_assert_str_eq(str, "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7");

hdnode_serialize(&node, fingerprint, VERSION_PUBLIC, 1, str, sizeof(str));
ck_assert_str_eq(str, "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw");


//Chain m/0'/1
fingerprint = hdnode_fingerprint(&node);
hdnode_private_ckd(&node, 1);
hdnode_fill_public_key(&node);

hdnode_serialize(&node, fingerprint, VERSION_PRIVATE, 0, str, sizeof(str));
ck_assert_str_eq(str, "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs");

hdnode_serialize(&node, fingerprint, VERSION_PUBLIC, 1, str, sizeof(str));
ck_assert_str_eq(str, "xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ");


//Chain m/0'/1/2'
fingerprint = hdnode_fingerprint(&node);
hdnode_private_ckd_prime(&node, 2);
hdnode_fill_public_key(&node);

hdnode_serialize(&node, fingerprint, VERSION_PRIVATE, 0, str, sizeof(str));
ck_assert_str_eq(str, "xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM");

hdnode_serialize(&node, fingerprint, VERSION_PUBLIC, 1, str, sizeof(str));
ck_assert_str_eq(str, "xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5");


//Chain m/0'/1/2'/2
fingerprint = hdnode_fingerprint(&node);
hdnode_private_ckd(&node, 2);
hdnode_fill_public_key(&node);

hdnode_serialize(&node, fingerprint, VERSION_PRIVATE, 0, str, sizeof(str));
ck_assert_str_eq(str, "xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334");

hdnode_serialize(&node, fingerprint, VERSION_PUBLIC, 1, str, sizeof(str));
ck_assert_str_eq(str, "xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV");


//Chain m/0'/1/2'/2/1000000000
fingerprint = hdnode_fingerprint(&node);
hdnode_private_ckd(&node, 1000000000);
hdnode_fill_public_key(&node);

hdnode_serialize(&node, fingerprint, VERSION_PRIVATE, 0, str, sizeof(str));
ck_assert_str_eq(str, "xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76");

hdnode_serialize(&node, fingerprint, VERSION_PUBLIC, 1, str, sizeof(str));
ck_assert_str_eq(str, "xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy");

}
END_TEST

START_TEST(test_skycoin_address_from_pubkey)
{
uint8_t pubkey[33] = {0};
Expand Down Expand Up @@ -1448,6 +1515,7 @@ Suite* test_suite(void)
tcase_add_test(tc, test_skycoin_pubkey_from_seckey);
tcase_add_test(tc, test_secp256k1Hash);
tcase_add_test(tc, test_deterministic_key_pair_iterator);
tcase_add_test(tc, test_bip32_vector_1);
tcase_add_test(tc, test_skycoin_address_from_pubkey);
tcase_add_test(tc, test_bitcoin_address_from_pubkey);
tcase_add_test(tc, test_compute_sha256sum);
Expand Down
3 changes: 1 addition & 2 deletions skycoin-api/tools/base58.c
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ bool b58enc(char* b58, size_t* b58sz, const void* data, size_t binsz)
for (j = 0; j < (ssize_t)size && !buf[j]; ++j)
;

if (*b58sz <= zcount + size - j) {
if (*b58sz < zcount + size - j) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Are you sure, that it should be in that way ? It is part of original trezor code

*b58sz = zcount + size - j + 1;
return false;
}
Expand All @@ -310,7 +310,6 @@ int base58_encode_check(const uint8_t* data, int datalen, HasherType hasher_type
uint8_t* hash = buf + datalen;
memcpy(buf, data, datalen);
hasher_Raw(hasher_type, data, datalen, hash);
hasher_Raw(hasher_type, hash, 32, hash);
size_t res = strsize;
bool success = b58enc(str, &res, buf, datalen + 4);
memzero(buf, sizeof(buf));
Expand Down
99 changes: 99 additions & 0 deletions skycoin-api/tools/bip32.c
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,102 @@ void hdnode_fill_public_key(HDNode* node)
ecdsa_get_public_key33(node->curve->params, node->private_key, node->public_key);
}
}

int hdnode_serialize(const HDNode *node, uint32_t fingerprint,
uint32_t version, char use_public, char *str,
int strsize) {
uint8_t node_data[78];
write_be(node_data, version);
node_data[4] = node->depth;
write_be(node_data + 5, fingerprint);
write_be(node_data + 9, node->child_num);
memcpy(node_data + 13, node->chain_code, 32);
if (use_public) {
memcpy(node_data + 45, node->public_key, 33);
} else {
node_data[45] = 0;
memcpy(node_data + 46, node->private_key, 32);
}
int ret = base58_encode_check(node_data, sizeof(node_data),
node->curve->hasher_base58, str, strsize);
memzero(node_data, sizeof(node_data));
return ret;
}

uint32_t hdnode_fingerprint(HDNode *node) {
uint8_t digest[32];
uint32_t fingerprint;

hdnode_fill_public_key(node);
hasher_Raw(node->curve->hasher_pubkey, node->public_key, 33, digest);
fingerprint = ((uint32_t)digest[0] << 24) + (digest[1] << 16) +
(digest[2] << 8) + digest[3];
memzero(digest, sizeof(digest));
return fingerprint;
}

int hdnode_private_ckd(HDNode *inout, uint32_t i) {
static CONFIDENTIAL uint8_t data[1 + 32 + 4];
static CONFIDENTIAL uint8_t I[32 + 32];
static CONFIDENTIAL bignum256 a, b;

if (i & 0x80000000) { // private derivation
data[0] = 0;
memcpy(data + 1, inout->private_key, 32);
} else { // public derivation
if (!inout->curve->params) {
return 0;
}
hdnode_fill_public_key(inout);
memcpy(data, inout->public_key, 33);
}
write_be(data + 33, i);

bn_read_be(inout->private_key, &a);

static CONFIDENTIAL HMAC_SHA512_CTX ctx;
hmac_sha512_Init(&ctx, inout->chain_code, 32);
hmac_sha512_Update(&ctx, data, sizeof(data));
hmac_sha512_Final(&ctx, I);

if (inout->curve->params) {
while (true) {
bool failed = false;
bn_read_be(I, &b);
if (!bn_is_less(&b, &inout->curve->params->order)) { // >= order
failed = true;
} else {
bn_add(&b, &a);
bn_mod(&b, &inout->curve->params->order);
if (bn_is_zero(&b)) {
failed = true;
}
}

if (!failed) {
bn_write_be(&b, inout->private_key);
break;
}

data[0] = 1;
memcpy(data + 1, I + 32, 32);
hmac_sha512_Init(&ctx, inout->chain_code, 32);
hmac_sha512_Update(&ctx, data, sizeof(data));
hmac_sha512_Final(&ctx, I);
}
} else {
memcpy(inout->private_key, I, 32);
}

memcpy(inout->chain_code, I + 32, 32);
inout->depth++;
inout->child_num = i;
memzero(inout->public_key, sizeof(inout->public_key));

// making sure to wipe our memory
memzero(&a, sizeof(a));
memzero(&b, sizeof(b));
memzero(I, sizeof(I));
memzero(data, sizeof(data));
return 1;
}
15 changes: 15 additions & 0 deletions skycoin-api/tools/bip32.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,15 @@
// #include "ed25519-donna/ed25519.h"
#include "options.h"

#define VERSION_PRIVATE 0x0488ADE4
#define VERSION_PUBLIC 0x0488B21E

typedef struct {
const char* bip32_name; // string for generating BIP32 xprv from seed
const ecdsa_curve* params; // ecdsa curve parameters, null for ed25519
HasherType hasher_type; // hasher type for BIP32 and ECDSA
HasherType hasher_base58;
HasherType hasher_pubkey;
} curve_info;

typedef struct {
Expand All @@ -52,7 +57,17 @@ int hdnode_from_xprv(uint32_t depth, uint32_t child_num, const uint8_t* chain_co

int hdnode_from_seed(const uint8_t* seed, int seed_len, const char* curve, HDNode* out);

int hdnode_private_ckd(HDNode *inout, uint32_t i);

#define hdnode_private_ckd_prime(X, I) \
hdnode_private_ckd((X), ((I) | 0x80000000))

void hdnode_fill_public_key(HDNode* node);
int hdnode_serialize(const HDNode *node, uint32_t fingerprint,
uint32_t version, char use_public, char *str,
int strsize);

uint32_t hdnode_fingerprint(HDNode *node);

int hdnode_sign(HDNode* node, const uint8_t* msg, uint32_t msg_len, uint8_t* sig, uint8_t* pby, int (*is_canonical)(uint8_t by, uint8_t sig[64]));
int hdnode_sign_digest(HDNode* node, const uint8_t* digest, uint8_t* sig, uint8_t* pby, int (*is_canonical)(uint8_t by, uint8_t sig[64]));
Expand Down
7 changes: 7 additions & 0 deletions skycoin-api/tools/bip44_coins.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#ifndef __BIP44_COINS_H__
#define __BIP44_COINS_H__

#define BIP44_BITCOIN 0
#define BIP44_SKYCOIN 8000

#endif
13 changes: 13 additions & 0 deletions skycoin-api/tools/hasher.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,16 @@
*/

#include "hasher.h"
#include "ripemd160.h"

void hasher_Init(Hasher* hasher, HasherType type)
{
hasher->type = type;

switch (hasher->type) {
case HASHER_SHA2:
case HASHER_SHA2D:
case HASHER_SHA2_RIPEMD:
sha256_Init(&hasher->ctx.sha2);
break;
case HASHER_BLAKE:
Expand All @@ -45,6 +48,8 @@ void hasher_Update(Hasher* hasher, const uint8_t* data, size_t length)
{
switch (hasher->type) {
case HASHER_SHA2:
case HASHER_SHA2D:
case HASHER_SHA2_RIPEMD:
sha256_Update(&hasher->ctx.sha2, data, length);
break;
case HASHER_BLAKE:
Expand All @@ -59,6 +64,14 @@ void hasher_Final(Hasher* hasher, uint8_t hash[HASHER_DIGEST_LENGTH])
case HASHER_SHA2:
sha256_Final(&hasher->ctx.sha2, hash);
break;
case HASHER_SHA2D:
sha256_Final(&hasher->ctx.sha2, hash);
hasher_Raw(HASHER_SHA2, hash, HASHER_DIGEST_LENGTH, hash);
break;
case HASHER_SHA2_RIPEMD:
sha256_Final(&hasher->ctx.sha2, hash);
ripemd160(hash, HASHER_DIGEST_LENGTH, hash);
break;
case HASHER_BLAKE:
blake256_Final(&hasher->ctx.blake, hash);
break;
Expand Down
2 changes: 2 additions & 0 deletions skycoin-api/tools/hasher.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
typedef enum {
HASHER_SHA2,
HASHER_BLAKE,
HASHER_SHA2D,
HASHER_SHA2_RIPEMD,
} HasherType;

typedef struct {
Expand Down
Loading