Skip to content

Commit

Permalink
payment protocol encryption/decryption with tests
Browse files Browse the repository at this point in the history
  • Loading branch information
voisine committed Aug 22, 2016
1 parent a56b66e commit 2dc0f45
Show file tree
Hide file tree
Showing 13 changed files with 723 additions and 150 deletions.
2 changes: 1 addition & 1 deletion BRAddress.c
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ size_t BRAddressFromScriptSig(char *addr, size_t addrLen, const uint8_t *script,
}

// writes the scriptPubKey for addr to script
// returns the number of bytes written or scripLen needed if script is NULL
// returns the number of bytes written or scriptLen needed if script is NULL
size_t BRAddressScriptPubKey(uint8_t *script, size_t scriptLen, const char *addr)
{
static uint8_t pubkeyAddress = BITCOIN_PUBKEY_ADDRESS, scriptAddress = BITCOIN_SCRIPT_ADDRESS;
Expand Down
4 changes: 3 additions & 1 deletion BRAddress.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@

#include "BRCrypto.h"
#include <string.h>
#include <stddef.h>
#include <inttypes.h>

#ifdef __cplusplus
extern "C" {
Expand Down Expand Up @@ -89,7 +91,7 @@ size_t BRAddressFromScriptPubKey(char *addr, size_t addrLen, const uint8_t *scri
size_t BRAddressFromScriptSig(char *addr, size_t addrLen, const uint8_t *script, size_t scriptLen);

// writes the scriptPubKey for addr to script
// returns the number of bytes written or scripLen needed if script is NULL
// returns the number of bytes written or scriptLen needed if script is NULL
size_t BRAddressScriptPubKey(uint8_t *script, size_t scriptLen, const char *addr);

// returns true if addr is a valid bitcoin address
Expand Down
5 changes: 2 additions & 3 deletions BRArray.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,8 @@ extern "C" {
// array_clear(myArray); // myArray is now empty
// array_free(myArray); // free memory allocated for myArray
//
// note:
// when new items are added to an array past its current capacity, its memory location may change, so other references
// to it or its members must be updated
// NOTE: when new items are added to an array past its current capacity, its memory location may change, so other
// references to it or its members must be updated

#define array_new(array, capacity) do {\
size_t _array_cap = (capacity);\
Expand Down
48 changes: 46 additions & 2 deletions BRBIP38Key.c
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ int BRBIP38KeyIsValid(const char *bip38Key)
uint8_t data[39];

assert(bip38Key != NULL);

if (BRBase58CheckDecode(data, sizeof(data), bip38Key) != 39) return 0; // invalid length

uint16_t prefix = UInt16GetBE(data);
Expand All @@ -232,6 +233,7 @@ int BRKeySetBIP38Key(BRKey *key, const char *bip38Key, const char *passphrase)
assert(key != NULL);
assert(bip38Key != NULL);
assert(passphrase != NULL);

if (BRBase58CheckDecode(data, sizeof(data), bip38Key) != 39) return 0; // invalid length

uint16_t prefix = UInt16GetBE(data);
Expand Down Expand Up @@ -327,6 +329,48 @@ void BRKeySetBIP38ItermediateCode(BRKey *key, const char *code, const uint8_t *s
// returns number of bytes written to bip38Key including NULL terminator or total bip38KeyLen needed if bip38Key is NULL
size_t BRKeyBIP38Key(BRKey *key, char *bip38Key, size_t bip38KeyLen, const char *passphrase)
{
// TODO: XXX implement
return 0;
uint16_t prefix = BIP38_NOEC_PREFIX;
uint8_t buf[39], flag = BIP38_NOEC_FLAG;
uint32_t salt;
size_t off = 0;
BRAddress address;
UInt512 derived;
UInt256 hash, derived1, derived2;
UInt128 encrypted1, encrypted2;

if (! bip38Key) return 43*138/100 + 1; // 43bytes*log(256)/log(58), rounded up

assert(key != NULL);
assert(passphrase != NULL);

if (key->compressed) flag |= BIP38_COMPRESSED_FLAG;
BRKeyAddress(key, address.s, sizeof(address));
BRSHA256_2(&hash, address.s, strlen(address.s));
salt = hash.u32[0];

BRScrypt(&derived, sizeof(derived), passphrase, strlen(passphrase), &salt, sizeof(salt),
BIP38_SCRYPT_N, BIP38_SCRYPT_R, BIP38_SCRYPT_P);
derived1 = *(UInt256 *)&derived, derived2 = *(UInt256 *)&derived.u64[4];

// enctryped1 = AES256Encrypt(privkey[0...15] xor derived1[0...15], derived2)
encrypted1.u64[0] = key->secret.u64[0] ^ derived1.u64[0];
encrypted1.u64[1] = key->secret.u64[1] ^ derived1.u64[1];
_BRAES256ECBEncrypt(&derived2, &encrypted1);

// encrypted2 = AES256Encrypt(privkey[16...31] xor derived1[16...31], derived2)
encrypted2.u64[0] = key->secret.u64[2] ^ derived1.u64[2];
encrypted2.u64[1] = key->secret.u64[3] ^ derived1.u64[3];
_BRAES256ECBEncrypt(&derived2, &encrypted2);

UInt16SetBE(&buf[off], prefix);
off += sizeof(prefix);
buf[off] = flag;
off += sizeof(flag);
UInt32SetBE(&buf[off], UInt32GetBE(&salt));
off += sizeof(salt);
UInt128Set(&buf[off], encrypted1);
off += sizeof(encrypted1);
UInt128Set(&buf[off], encrypted2);
off += sizeof(encrypted2);
return BRBase58CheckEncode(bip38Key, bip38KeyLen, buf, off);
}
53 changes: 51 additions & 2 deletions BRCrypto.c
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,7 @@ void BRHash160(void *md20, const void *data, size_t len)

assert(md20 != NULL);
assert(data != NULL || len == 0);

BRSHA256(t, data, len);
BRRMD160(md20, t, sizeof(t));
}
Expand Down Expand Up @@ -507,6 +508,7 @@ void BRHMAC(void *mac, void (*hash)(void *, const void *, size_t), size_t hashLe
assert(hashLen > 0 && (hashLen % 4) == 0);
assert(key != NULL || keyLen == 0);
assert(data != NULL || dataLen == 0);

if (keyLen > blockLen) hash(k, key, keyLen), key = k, keyLen = sizeof(k);
memset(kipad, 0, blockLen);
memcpy(kipad, key, keyLen);
Expand All @@ -523,6 +525,51 @@ void BRHMAC(void *mac, void (*hash)(void *, const void *, size_t), size_t hashLe
memset(kopad, 0, blockLen);
}

// hmac-drbg with no prediction resistance or additional input
// K and V must point to buffers of size hashLen, and ps (personalization string) may be NULL
// to generate additional drbg output, use K and V from the previous call, and set seed, nonce and ps to NULL
void BRHMACDRBG(void *out, size_t outLen, void *K, void *V, void (*hash)(void *, const void *, size_t), size_t hashLen,
const void *seed, size_t seedLen, const void *nonce, size_t nonceLen, const void *ps, size_t psLen)
{
size_t i, bufLen = hashLen + 1 + seedLen + nonceLen + psLen;
uint8_t buf[bufLen];

assert(out != NULL || outLen == 0);
assert(K != NULL);
assert(V != NULL);
assert(hash != NULL);
assert(hashLen > 0 && (hashLen % 4) == 0);
assert(seed != NULL || seedLen == 0);
assert(nonce != NULL || nonceLen == 0);
assert(ps != NULL || psLen == 0);

if (seed || nonce || ps) { // K = [0x00, 0x00, ... 0x00], V = [0x01, 0x01, ... 0x01]
for (i = 0; i < hashLen; i++) ((uint8_t *)K)[i] = 0x00, ((uint8_t *)V)[i] = 0x01;
}

memcpy(buf, V, hashLen);
buf[hashLen] = 0x00;
memcpy(&buf[hashLen + 1], seed, seedLen);
memcpy(&buf[hashLen + 1 + seedLen], nonce, nonceLen);
memcpy(&buf[hashLen + 1 + seedLen + nonceLen], ps, psLen);
BRHMAC(K, hash, hashLen, K, hashLen, buf, bufLen); // K = HMAC(K, V || 0x00 || entropy || nonce || ps)
BRHMAC(V, hash, hashLen, K, hashLen, V, hashLen); // V = HMAC(K, V)

if (seed || nonce || ps) {
memcpy(buf, V, hashLen);
buf[hashLen] = 0x01;
BRHMAC(K, hash, hashLen, K, hashLen, buf, bufLen); // K = HMAC(K, V || 0x01 || entropy || nonce || ps)
BRHMAC(V, hash, hashLen, K, hashLen, V, hashLen); // V = HMAC(K, V)
}

memset(buf, 0, bufLen);

for (i = 0; i*hashLen < outLen; i++) {
BRHMAC(V, hash, hashLen, K, hashLen, V, hashLen); // V = HMAC(K, V)
memcpy((uint8_t *)out + i*hashLen, V, (i*hashLen + hashLen <= outLen) ? hashLen : outLen % hashLen);
}
}

static void _BRPoly1305Compress(uint32_t h[5], const void *key32, const void *data, size_t len, int final)
{
uint32_t x[4], b, t0, t1, t2, t3, t4, r0, r1, r2, r3, r4;
Expand Down Expand Up @@ -590,7 +637,7 @@ static void _BRPoly1305Compress(uint32_t h[5], const void *key32, const void *da
}

// poly1305 authenticator: https://tools.ietf.org/html/rfc7539
// must use constant time mem comparison when verifying mac to defend against timing attacks
// NOTE: must use constant time mem comparison when verifying mac to defend against timing attacks
void BRPoly1305(void *mac16, const void *key32, const void *data, size_t len)
{
uint32_t h[5] = { 0, 0, 0, 0, 0 };
Expand Down Expand Up @@ -697,7 +744,7 @@ size_t BRChacha20Poly1305AEADDecrypt(void *out, size_t outLen, const void *key32
uint32_t h[5] = { 0, 0, 0, 0, 0 }, mac[4];

if (! out) return (dataLen < 16) ? 0 : dataLen - 16;
if (dataLen < 16 || (dataLen - 16)/64 >= UINT32_MAX || outLen < dataLen - 16) return 0;
if (dataLen < 16 || (dataLen - 16)/64 >= UINT32_MAX || outLen + 16 < dataLen) return 0;

assert(key32 != NULL);
assert(nonce12 != NULL);
Expand Down Expand Up @@ -743,6 +790,7 @@ void BRPBKDF2(void *dk, size_t dkLen, void (*hash)(void *, const void *, size_t)
assert(pw != NULL || pwLen == 0);
assert(salt != NULL || saltLen == 0);
assert(rounds > 0);

memcpy(s, salt, saltLen);

for (i = 0; i < (dkLen + hashLen - 1)/hashLen; i++) {
Expand Down Expand Up @@ -817,6 +865,7 @@ void BRScrypt(void *dk, size_t dkLen, const void *pw, size_t pwLen, const void *
assert(n > 0);
assert(r > 0);
assert(p > 0);

BRPBKDF2(b, sizeof(b), BRSHA256, 32, pw, pwLen, salt, saltLen, 1);

for (int i = 0; i < p; i++) {
Expand Down
8 changes: 7 additions & 1 deletion BRCrypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,14 @@ uint32_t BRMurmur3_32(const void *data, size_t len, uint32_t seed);
void BRHMAC(void *mac, void (*hash)(void *, const void *, size_t), size_t hashLen, const void *key, size_t keyLen,
const void *data, size_t dataLen);

// hmac-drbg with no prediction resistance or additional input
// K and V must point to buffers of size hashLen, and ps (personalization string) may be NULL
// to generate additional drbg output, use K and V from the previous call, and set seed, nonce and ps to NULL
void BRHMACDRBG(void *out, size_t outLen, void *K, void *V, void (*hash)(void *, const void *, size_t), size_t hashLen,
const void *seed, size_t seedLen, const void *nonce, size_t nonceLen, const void *ps, size_t psLen);

// poly1305 authenticator: https://tools.ietf.org/html/rfc7539
// must use constant time mem comparison when verifying mac to defend against timing attacks
// NOTE: must use constant time mem comparison when verifying mac to defend against timing attacks
void BRPoly1305(void *mac16, const void *key32, const void *data, size_t len);

// chacha20 stream cypher: https://cr.yp.to/chacha.html
Expand Down
10 changes: 8 additions & 2 deletions BRKey.c
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ int BRPrivKeyIsValid(const char *privKey)
int r = 0;

assert(privKey != NULL);

dataLen = BRBase58CheckDecode(data, sizeof(data), privKey);
strLen = strlen(privKey);

Expand Down Expand Up @@ -155,6 +156,7 @@ int BRKeySetSecret(BRKey *key, const UInt256 *secret, int compressed)
{
assert(key != NULL);
assert(secret != NULL);

pthread_once(&_ctx_once, _ctx_init);
BRKeyClean(key);
key->secret = UInt256Get(secret);
Expand All @@ -172,6 +174,7 @@ int BRKeySetPrivKey(BRKey *key, const char *privKey)
#if BITCOIN_TESTNET
version = BITCOIN_PRIVKEY_TEST;
#endif

assert(key != NULL);
assert(privKey != NULL);

Expand Down Expand Up @@ -211,6 +214,7 @@ int BRKeySetPubKey(BRKey *key, const uint8_t *pubKey, size_t pkLen)
assert(key != NULL);
assert(pubKey != NULL);
assert(pkLen == 33 || pkLen == 65);

pthread_once(&_ctx_once, _ctx_init);
BRKeyClean(key);
memcpy(key->pubKey, pubKey, pkLen);
Expand All @@ -219,12 +223,12 @@ int BRKeySetPubKey(BRKey *key, const uint8_t *pubKey, size_t pkLen)
}

// writes the private key to privKey and returns the number of bytes writen, or pkLen needed if privKey is NULL
// returns 0 on failure
size_t BRKeyPrivKey(const BRKey *key, char *privKey, size_t pkLen)
{
uint8_t data[34];

assert(key != NULL);
assert(privKey != NULL || pkLen == 0);

if (secp256k1_ec_seckey_verify(_ctx, key->secret.u8)) {
data[0] = BITCOIN_PRIVKEY;
Expand All @@ -250,7 +254,6 @@ size_t BRKeyPubKey(BRKey *key, void *pubKey, size_t pkLen)
secp256k1_pubkey pk;

assert(key != NULL);
assert(pubKey != NULL || pkLen == 0);

if (memcmp(key->pubKey, empty, size) == 0 && secp256k1_ec_pubkey_create(_ctx, &pk, key->secret.u8)) {
secp256k1_ec_pubkey_serialize(_ctx, key->pubKey, &size, &pk,
Expand Down Expand Up @@ -281,6 +284,7 @@ size_t BRKeyAddress(BRKey *key, char *addr, size_t addrLen)
uint8_t data[21];

assert(key != NULL);

hash = BRKeyHash160(key);
data[0] = BITCOIN_PUBKEY_ADDRESS;
#if BITCOIN_TESTNET
Expand All @@ -297,6 +301,7 @@ size_t BRKeyAddress(BRKey *key, char *addr, size_t addrLen)
}

// signs md with key and writes signature to sig and returns the number of bytes written or sigLen needed if sig is NULL
// returns 0 on failure
size_t BRKeySign(const BRKey *key, void *sig, size_t sigLen, UInt256 md)
{
secp256k1_ecdsa_signature s;
Expand All @@ -322,6 +327,7 @@ int BRKeyVerify(BRKey *key, UInt256 md, const void *sig, size_t sigLen)
assert(key != NULL);
assert(sig != NULL || sigLen == 0);
assert(sigLen > 0);

len = BRKeyPubKey(key, NULL, 0);

if (len > 0 && secp256k1_ec_pubkey_parse(_ctx, &pk, key->pubKey, len) &&
Expand Down
2 changes: 2 additions & 0 deletions BRKey.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ int BRKeySetPrivKey(BRKey *key, const char *privKey);
int BRKeySetPubKey(BRKey *key, const uint8_t *pubKey, size_t pkLen);

// writes the private key to privKey and returns the number of bytes writen, or pkLen needed if privKey is NULL
// returns 0 on failure
size_t BRKeyPrivKey(const BRKey *key, char *privKey, size_t pkLen);

// writes the public key to pubKey and returns the number of bytes written, or pkLen needed if pubKey is NULL
Expand All @@ -89,6 +90,7 @@ UInt160 BRKeyHash160(BRKey *key);
size_t BRKeyAddress(BRKey *key, char *addr, size_t addrLen);

// signs md with key and writes signature to sig and returns the number of bytes written or sigLen needed if sig is NULL
// returns 0 on failure
size_t BRKeySign(const BRKey *key, void *sig, size_t sigLen, UInt256 md);

// returns true if the signature for md is verified to have been made by key
Expand Down
Loading

0 comments on commit 2dc0f45

Please sign in to comment.