From 07c7214edb2cfecae0c851d75d0a150e263ba8ee Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 2 Jun 2015 11:53:59 +0930 Subject: [PATCH] Explicit pubkey structure. This checks that the protobuf is the right form, also handles uncompressed keys (though you shouldn't be using those any more, should you?) Signed-off-by: Rusty Russell --- Makefile | 2 +- base58.c | 25 ++++++++++++---------- base58.h | 6 ++++-- bitcoin_address.c | 6 +++--- bitcoin_address.h | 9 +++----- open-anchor-sig.c | 6 +++--- open-channel.c | 4 +++- open-commit-sig.c | 3 ++- pkt.c | 11 ---------- pkt.h | 4 ---- pubkey.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++ pubkey.h | 18 ++++++++++++++++ 12 files changed, 105 insertions(+), 43 deletions(-) create mode 100644 pubkey.c create mode 100644 pubkey.h diff --git a/Makefile b/Makefile index 8bee0683f..68d49c614 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ PROTOCC:=protoc-c PROGRAMS := open-channel open-anchor-sig leak-anchor-sigs open-commit-sig -HELPER_OBJS := base58.o lightning.pb-c.o shadouble.o pkt.o bitcoin_script.o permute_tx.o signature.o bitcoin_tx.o bitcoin_address.o anchor.o commit_tx.o +HELPER_OBJS := base58.o lightning.pb-c.o shadouble.o pkt.o bitcoin_script.o permute_tx.o signature.o bitcoin_tx.o bitcoin_address.o anchor.o commit_tx.o pubkey.o CCAN_OBJS := ccan-crypto-sha256.o ccan-crypto-shachain.o ccan-err.o ccan-tal.o ccan-tal-str.o ccan-take.o ccan-list.o ccan-str.o ccan-opt-helpers.o ccan-opt.o ccan-opt-parse.o ccan-opt-usage.o ccan-read_write_all.o ccan-str-hex.o ccan-tal-grab_file.o ccan-noerr.o diff --git a/base58.c b/base58.c index c9918a604..42065edeb 100644 --- a/base58.c +++ b/base58.c @@ -5,6 +5,8 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "base58.h" #include "shadouble.h" +#include "bitcoin_address.h" +#include "pubkey.h" #include #include #include @@ -299,23 +301,25 @@ static bool EC_KEY_regenerate_key(EC_KEY *eckey, BIGNUM *priv_key) } EC_KEY *key_from_base58(const char *base58, size_t base58_len, - bool *test_net, struct bitcoin_compressed_pubkey *key) + bool *test_net, struct pubkey *key) { size_t keylen; - u8 keybuf[1 + 32 + 1 + 4], *pubkey; + u8 keybuf[1 + 32 + 1 + 4], *kptr; u8 csum[4]; EC_KEY *priv; BIGNUM bn; + point_conversion_form_t cform; BN_init(&bn); if (!raw_decode_base58(&bn, base58, base58_len)) return NULL; keylen = BN_num_bytes(&bn); - /* FIXME: Handle non-compressed keys! */ if (keylen == 1 + 32 + 4) - goto fail_free_bn; - if (keylen != 1 + 32 + 1 + 4) + cform = POINT_CONVERSION_UNCOMPRESSED; + else if (keylen == 1 + 32 + 1 + 4) + cform = POINT_CONVERSION_COMPRESSED; + else goto fail_free_bn; BN_bn2bin(&bn, keybuf); @@ -324,7 +328,7 @@ EC_KEY *key_from_base58(const char *base58, size_t base58_len, goto fail_free_bn; /* Byte after key should be 1 to represent a compressed key. */ - if (keybuf[1 + 32] != 1) + if (cform == POINT_CONVERSION_COMPRESSED && keybuf[1 + 32] != 1) goto fail_free_bn; if (keybuf[0] == 128) @@ -335,8 +339,7 @@ EC_KEY *key_from_base58(const char *base58, size_t base58_len, goto fail_free_bn; priv = EC_KEY_new_by_curve_name(NID_secp256k1); - /* We *always* used compressed form keys. */ - EC_KEY_set_conv_form(priv, POINT_CONVERSION_COMPRESSED); + EC_KEY_set_conv_form(priv, cform); BN_free(&bn); BN_init(&bn); @@ -346,9 +349,9 @@ EC_KEY *key_from_base58(const char *base58, size_t base58_len, goto fail_free_priv; /* Save public key */ - pubkey = key->key; - keylen = i2o_ECPublicKey(priv, &pubkey); - assert(keylen == sizeof(key->key)); + kptr = key->key; + keylen = i2o_ECPublicKey(priv, &kptr); + assert(keylen == pubkey_len(key)); BN_free(&bn); return priv; diff --git a/base58.h b/base58.h index 832b2c0d6..879db16e2 100644 --- a/base58.h +++ b/base58.h @@ -8,7 +8,9 @@ #include #include #include -#include "bitcoin_address.h" + +struct pubkey; +struct bitcoin_address; /* Encoding is version byte + ripemd160 + 4-byte checksum == 200 bits => 2^200. * @@ -36,7 +38,7 @@ char *base58_with_check(char dest[BASE58_ADDR_MAX_LEN], char *key_to_base58(const tal_t *ctx, bool test_net, EC_KEY *key); EC_KEY *key_from_base58(const char *base58, size_t base58_len, - bool *test_net, struct bitcoin_compressed_pubkey *key); + bool *test_net, struct pubkey *key); bool raw_decode_base_n(BIGNUM *bn, const char *src, size_t len, int base); bool raw_decode_base58(BIGNUM *bn, const char *src, size_t len); diff --git a/bitcoin_address.c b/bitcoin_address.c index 763003ced..edd3c4f53 100644 --- a/bitcoin_address.c +++ b/bitcoin_address.c @@ -1,11 +1,11 @@ #include "bitcoin_address.h" +#include "pubkey.h" #include -void bitcoin_address(const struct bitcoin_compressed_pubkey *key, - struct bitcoin_address *addr) +void bitcoin_address(const struct pubkey *key, struct bitcoin_address *addr) { struct sha256 h; - sha256(&h, key, sizeof(*key)); + sha256(&h, key->key, pubkey_len(key)); RIPEMD160(h.u.u8, sizeof(h), addr->addr); } diff --git a/bitcoin_address.h b/bitcoin_address.h index 3f4c0134f..c77aa6c03 100644 --- a/bitcoin_address.h +++ b/bitcoin_address.h @@ -3,16 +3,13 @@ #include #include +struct pubkey; + /* An address is the RIPEMD160 of the SHA of the public key. */ struct bitcoin_address { u8 addr[RIPEMD160_DIGEST_LENGTH]; /* 20 */ }; -/* An ECDSA compressed public key. 33 chars long, even on ARM. */ -struct bitcoin_compressed_pubkey { - u8 key[33]; -} __attribute__((aligned(1))); - -void bitcoin_address(const struct bitcoin_compressed_pubkey *key, +void bitcoin_address(const struct pubkey *key, struct bitcoin_address *addr); #endif /* LIGHTNING_BITCOIN_ADDRESS_H */ diff --git a/open-anchor-sig.c b/open-anchor-sig.c index c55008925..105685aa1 100644 --- a/open-anchor-sig.c +++ b/open-anchor-sig.c @@ -13,6 +13,7 @@ #include "bitcoin_address.h" #include "base58.h" #include "anchor.h" +#include "pubkey.h" #include #include @@ -23,7 +24,7 @@ static u8 *tx_scriptsig(const tal_t *ctx, unsigned int i, const BitcoinInput *input, EC_KEY *privkey, - const struct bitcoin_compressed_pubkey *pubkey) + const struct pubkey *pubkey) { struct signature *sig; struct bitcoin_address addr; @@ -76,8 +77,7 @@ int main(int argc, char *argv[]) sigs = tal_arr(ctx, u8 *, o1->anchor->n_inputs); for (i = 0; i < o1->anchor->n_inputs; i++) { - /* FIXME: Support non-compressed keys? */ - struct bitcoin_compressed_pubkey pubkey; + struct pubkey pubkey; EC_KEY *privkey; bool testnet; diff --git a/open-channel.c b/open-channel.c index b53f5f8d9..36e11abe6 100644 --- a/open-channel.c +++ b/open-channel.c @@ -13,6 +13,8 @@ #include "base58.h" #include "pkt.h" #include "bitcoin_script.h" +#include "bitcoin_address.h" +#include "pubkey.h" #include #include @@ -93,7 +95,7 @@ int main(int argc, char *argv[]) bool testnet; u8 *script_to_me; size_t i; - struct bitcoin_compressed_pubkey commitkey; + struct pubkey commitkey; EC_KEY *commitprivkey; err_set_progname(argv[0]); diff --git a/open-commit-sig.c b/open-commit-sig.c index 7349e6d47..442aba06f 100644 --- a/open-commit-sig.c +++ b/open-commit-sig.c @@ -17,6 +17,7 @@ #include "permute_tx.h" #include "signature.h" #include "commit_tx.h" +#include "pubkey.h" #include #include @@ -31,7 +32,7 @@ int main(int argc, char *argv[]) size_t *inmap, *outmap; EC_KEY *privkey; bool testnet; - struct bitcoin_compressed_pubkey pubkey; + struct pubkey pubkey; err_set_progname(argv[0]); diff --git a/pkt.c b/pkt.c index 0e992fdb3..c864f385d 100644 --- a/pkt.c +++ b/pkt.c @@ -47,17 +47,6 @@ void proto_to_sha256(const Sha256Hash *pb, struct sha256 *hash) memcpy(hash->u.u8 + 24, &pb->d, 8); } -BitcoinPubkey *pubkey_to_proto(const tal_t *ctx, - const struct bitcoin_compressed_pubkey *key) -{ - BitcoinPubkey *p = tal(ctx, BitcoinPubkey); - - bitcoin_pubkey__init(p); - p->key.data = tal_dup_arr(ctx, u8, key->key, sizeof(key->key), 0); - p->key.len = sizeof(key->key); - return p; -} - struct pkt *openchannel_pkt(const tal_t *ctx, u64 seed, const struct sha256 *revocation_hash, diff --git a/pkt.h b/pkt.h index 1be0fa25b..ea3961c2d 100644 --- a/pkt.h +++ b/pkt.h @@ -69,8 +69,4 @@ struct pkt *open_commit_sig_pkt(const tal_t *ctx, const struct signature *sig); /* Useful helper for allocating & populating a protobuf Sha256Hash */ Sha256Hash *sha256_to_proto(const tal_t *ctx, const struct sha256 *hash); void proto_to_sha256(const Sha256Hash *pb, struct sha256 *hash); - -BitcoinPubkey *pubkey_to_proto(const tal_t *ctx, - const struct bitcoin_compressed_pubkey *key); - #endif /* LIGHTNING_PKT_H */ diff --git a/pubkey.c b/pubkey.c new file mode 100644 index 000000000..064175e82 --- /dev/null +++ b/pubkey.c @@ -0,0 +1,54 @@ +#include "pubkey.h" +#include + +/* Must agree on key validity with bitcoin! Stolen from bitcoin/src/pubkey.h's + * GetLen: + * // Copyright (c) 2009-2010 Satoshi Nakamoto + * // Copyright (c) 2009-2014 The Bitcoin Core developers + * // Distributed under the MIT software license, see the accompanying + * // file COPYING or http://www.opensource.org/licenses/mit-license.php. + */ +static unsigned int GetLen(unsigned char chHeader) +{ + if (chHeader == 2 || chHeader == 3) + return 33; + if (chHeader == 4 || chHeader == 6 || chHeader == 7) + return 65; + return 0; +} + +static bool valid_pubkey(const BitcoinPubkey *key) +{ + if (key->key.len < 1) + return false; + return (key->key.len == GetLen(key->key.data[0])); +} + +size_t pubkey_len(const struct pubkey *key) +{ + size_t len = GetLen(key->key[0]); + + assert(len); + return len; +} + +BitcoinPubkey *pubkey_to_proto(const tal_t *ctx, const struct pubkey *key) +{ + BitcoinPubkey *p = tal(ctx, BitcoinPubkey); + + bitcoin_pubkey__init(p); + p->key.len = pubkey_len(key); + p->key.data = tal_dup_arr(p, u8, key->key, p->key.len, 0); + + assert(valid_pubkey(p)); + return p; +} + +bool proto_to_pubkey(const BitcoinPubkey *pb, struct pubkey *key) +{ + if (!valid_pubkey(pb)) + return false; + + memcpy(key->key, pb->key.data, pb->key.len); + return true; +} diff --git a/pubkey.h b/pubkey.h new file mode 100644 index 000000000..fd6c932fc --- /dev/null +++ b/pubkey.h @@ -0,0 +1,18 @@ +#ifndef LIGHTNING_PUBKEY_H +#define LIGHTNING_PUBKEY_H +#include +#include +#include "lightning.pb-c.h" + +struct pubkey { + u8 key[65]; +}; + +/* Convert to-from protobuf to internal representation. */ +BitcoinPubkey *pubkey_to_proto(const tal_t *ctx, const struct pubkey *key); +bool proto_to_pubkey(const BitcoinPubkey *pb, struct pubkey *key); + +/* 33 or 65 bytes? */ +size_t pubkey_len(const struct pubkey *key); + +#endif /* LIGHTNING_PUBKEY_H */