Add check-commit-sig.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2015-06-02 13:40:00 +09:30
parent 316f29cb69
commit c291d19af6
6 changed files with 247 additions and 17 deletions

View File

@ -3,7 +3,7 @@
# Needs to have oneof support: Ubuntu vivid's is too old :(
PROTOCC:=protoc-c
PROGRAMS := open-channel open-anchor-sig leak-anchor-sigs open-commit-sig
PROGRAMS := open-channel open-anchor-sig leak-anchor-sigs open-commit-sig check-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 pubkey.o
@ -37,12 +37,15 @@ $(LEAK_ANCHOR_SIGS_OBJS): $(HEADERS)
open-commit-sig: $(OPEN_COMMIT_SIG_OBJS) $(HELPER_OBJS) $(CCAN_OBJS)
$(OPEN_COMMIT_SIG_OBJS): $(HEADERS)
check-commit-sig: $(CHECK_COMMIT_SIG_OBJS) $(HELPER_OBJS) $(CCAN_OBJS)
$(CHECK_COMMIT_SIG_OBJS): $(HEADERS)
distclean: clean
$(RM) lightning.pb-c.c lightning.pb-c.h
clean:
$(RM) $(PROGRAMS)
$(RM) $(OPEN_CHANNEL_OBJS) $(OPEN_ANCHOR_SIG_OBJS) $(LEAK_ANCHOR_SIGS_OBJS) $(OPEN_COMMIT_SIG_OBJS) $(HELPER_OBJS) $(CCAN_OBJS)
$(RM) *.o $(CCAN_OBJS)
ccan-tal.o: $(CCANDIR)/ccan/tal/tal.c
$(CC) $(CFLAGS) -c -o $@ $<

View File

@ -165,6 +165,23 @@ u8 *scriptsig_pay_to_pubkeyhash(const tal_t *ctx,
return script;
}
u8 *scriptsig_p2sh_2of2(const tal_t *ctx,
const struct signature *sig1,
const struct signature *sig2,
const struct pubkey *key1,
const struct pubkey *key2)
{
u8 *script = tal_arr(ctx, u8, 0);
u8 *redeemscript;
add_push_sig(&script, sig1);
add_push_sig(&script, sig2);
redeemscript = bitcoin_redeem_2of2(script, key1, key2);
add_push_bytes(&script, redeemscript, tal_count(redeemscript));
return script;
}
/* Is this a normal pay to pubkey hash? */
bool is_pay_to_pubkey_hash(const u8 *script, size_t script_len)
{
@ -183,6 +200,19 @@ bool is_pay_to_pubkey_hash(const u8 *script, size_t script_len)
return true;
}
bool is_p2sh(const u8 *script, size_t script_len)
{
if (script_len != 23)
return false;
if (script[0] != OP_HASH160)
return false;
if (script[1] != OP_PUSHBYTES(20))
return false;
if (script[22] != OP_EQUAL)
return false;
return true;
}
/* One of:
* mysig and theirsig, OR
* mysig and relative locktime passed, OR

View File

@ -38,7 +38,17 @@ u8 *scriptsig_pay_to_pubkeyhash(const tal_t *ctx,
const struct bitcoin_address *addr,
const struct signature *sig);
/* Create an input script to accept pay to pubkey */
u8 *scriptsig_p2sh_2of2(const tal_t *ctx,
const struct signature *sig1,
const struct signature *sig2,
const struct pubkey *key1,
const struct pubkey *key2);
/* Is this a normal pay to pubkey hash? */
bool is_pay_to_pubkey_hash(const u8 *script, size_t script_len);
/* Is this a pay to script hash? */
bool is_p2sh(const u8 *script, size_t script_len);
#endif /* LIGHTNING_BITCOIN_SCRIPT_H */

102
check-commit-sig.c Normal file
View File

@ -0,0 +1,102 @@
/* My example:
* ./check-commit-sig A-open.pb B-open.pb A-commit-sig.pb B-commit-sig.pb cUBCjrdJu8tfvM7FT8So6aqs6G6bZS1Cax6Rc9rFzYL6nYG4XNEC A-leak-anchor-sigs.pb B-leak-anchor-sigs.pb > A-commit.tx
* ./check-commit-sig B-open.pb A-open.pb B-commit-sig.pb A-commit-sig.pb cQXhbUnNRsFcdzTQwjbCrud5yVskHTEas7tZPUWoJYNk5htGQrpi B-leak-anchor-sigs.pb A-leak-anchor-sigs.pb > B-commit.tx
*/
#include <ccan/crypto/shachain/shachain.h>
#include <ccan/short_types/short_types.h>
#include <ccan/tal/tal.h>
#include <ccan/opt/opt.h>
#include <ccan/str/hex/hex.h>
#include <ccan/err/err.h>
#include <ccan/read_write_all/read_write_all.h>
#include "lightning.pb-c.h"
#include "anchor.h"
#include "base58.h"
#include "pkt.h"
#include "bitcoin_script.h"
#include "permute_tx.h"
#include "signature.h"
#include "commit_tx.h"
#include "pubkey.h"
#include <openssl/ec.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
const tal_t *ctx = tal_arr(NULL, char, 0);
OpenChannel *o1, *o2;
OpenCommitSig *cs1, *cs2;
struct bitcoin_tx *anchor, *commit;
struct sha256_double txid;
u8 *tx_arr;
size_t *inmap, *outmap;
struct pubkey pubkey1, pubkey2;
struct signature sig1, sig2;
char *tx_hex;
err_set_progname(argv[0]);
opt_register_noarg("--help|-h", opt_usage_and_exit,
"<open-channel-file1> <open-channel-file2> <commit-sig-1> <commit-sig-2> <commit-key1> <leak-anchor-sigs1> <leak-anchor-sigs2>\n"
"Output the commitment transaction if both signatures are valid",
"Print this message.");
opt_parse(&argc, argv, opt_log_stderr_exit);
if (argc != 8)
opt_usage_and_exit(NULL);
o1 = pkt_from_file(argv[1], PKT__PKT_OPEN)->open;
o2 = pkt_from_file(argv[2], PKT__PKT_OPEN)->open;
cs1 = pkt_from_file(argv[3], PKT__PKT_OPEN_COMMIT_SIG)->open_commit_sig;
cs2 = pkt_from_file(argv[4], PKT__PKT_OPEN_COMMIT_SIG)->open_commit_sig;
/* Get the transaction ID of the anchor. */
anchor = anchor_tx_create(ctx, o1, o2, &inmap, &outmap);
if (!anchor)
errx(1, "Failed transaction merge");
anchor_txid(anchor, argv[6], argv[7], inmap, &txid);
/* Now create THEIR commitment tx. */
commit = create_commit_tx(ctx, o2, o1, &txid, outmap[0]);
/* If contributions don't exceed fees, this fails. */
if (!commit)
errx(1, "Contributions %llu & %llu vs fees %llu & %llu",
(long long)o1->anchor->total,
(long long)o2->anchor->total,
(long long)o1->commitment_fee,
(long long)o2->commitment_fee);
/* Signatures and pubkeys well-formed? */
if (!proto_to_signature(cs1->sig, &sig1))
errx(1, "Invalid commit-sig-1");
if (!proto_to_signature(cs2->sig, &sig2))
errx(1, "Invalid commit-sig-2");
if (!proto_to_pubkey(o1->anchor->pubkey, &pubkey1))
errx(1, "Invalid anchor-1 key");
if (!proto_to_pubkey(o2->anchor->pubkey, &pubkey2))
errx(1, "Invalid anchor-2 key");
/* Their signature must validate correctly. */
if (!check_2of2_sig(commit, 0, &anchor->output[outmap[0]],
&pubkey1, &pubkey2, &sig1, &sig2))
errx(1, "Signature failed");
/* Create p2sh input for commit */
commit->input[0].script = scriptsig_p2sh_2of2(commit, &sig1, &sig2,
&pubkey1, &pubkey2);
commit->input[0].script_length = tal_count(commit->input[0].script);
/* Print it out in hex. */
tx_arr = linearize_tx(ctx, commit);
tx_hex = tal_arr(tx_arr, char, hex_str_size(tal_count(tx_arr)));
hex_encode(tx_arr, tal_count(tx_arr), tx_hex, tal_count(tx_hex));
if (!write_all(STDOUT_FILENO, tx_hex, strlen(tx_hex)))
err(1, "Writing out transaction");
tal_free(ctx);
return 0;
}

View File

@ -1,6 +1,8 @@
#include "signature.h"
#include "shadouble.h"
#include "bitcoin_tx.h"
#include "pubkey.h"
#include "bitcoin_script.h"
#include <openssl/bn.h>
#include <openssl/obj_mac.h>
#include <assert.h>
@ -47,31 +49,107 @@ struct signature *sign_hash(const tal_t *ctx, EC_KEY *private_key,
return s;
}
/* Only does SIGHASH_ALL */
static void sha256_tx_one_input(struct bitcoin_tx *tx,
size_t input_num,
const u8 *script, size_t script_len,
struct sha256_double *hash)
{
struct sha256_ctx ctx = SHA256_INIT;
size_t i;
assert(input_num < tx->input_count);
/* You must have all inputs zeroed to start. */
for (i = 0; i < tx->input_count; i++)
assert(tx->input[i].script_length == 0);
tx->input[input_num].script_length = script_len;
tx->input[input_num].script = cast_const(u8 *, script);
sha256_init(&ctx);
sha256_tx(&ctx, tx);
sha256_le32(&ctx, SIGHASH_ALL);
sha256_double_done(&ctx, hash);
/* Reset it for next time. */
tx->input[input_num].script_length = 0;
tx->input[input_num].script = NULL;
}
struct signature *sign_tx_input(const tal_t *ctx, struct bitcoin_tx *tx,
unsigned int in,
const u8 *subscript, size_t subscript_len,
EC_KEY *privkey)
{
struct sha256_double hash;
struct sha256_ctx shactx;
/* Transaction gets signed as if the output subscript is the
* only input script. */
tx->input[in].script_length = subscript_len;
tx->input[in].script = cast_const(u8 *, subscript);
sha256_init(&shactx);
sha256_tx(&shactx, tx);
sha256_le32(&shactx, SIGHASH_ALL);
sha256_double_done(&shactx, &hash);
/* Reset it for next time. */
tx->input[in].script_length = 0;
tx->input[in].script = NULL;
sha256_tx_one_input(tx, in, subscript, subscript_len, &hash);
return sign_hash(ctx, privkey, &hash);
}
static bool check_signed_hash(const struct sha256_double *hash,
const struct signature *signature,
const struct pubkey *key)
{
bool ok = false;
BIGNUM r, s;
ECDSA_SIG sig = { &r, &s };
EC_KEY *eckey = EC_KEY_new_by_curve_name(NID_secp256k1);
const unsigned char *k = key->key;
/* S must be even: https://github.com/sipa/bitcoin/commit/a81cd9680 */
assert((signature->s[31] & 1) == 0);
/* Unpack public key. */
if (!o2i_ECPublicKey(&eckey, &k, pubkey_len(key)))
goto out;
/* Unpack signature. */
BN_init(&r);
BN_init(&s);
if (!BN_bin2bn(signature->r, sizeof(signature->r), &r)
|| !BN_bin2bn(signature->s, sizeof(signature->s), &s))
goto free_bns;
/* Now verify hash with public key and signature. */
switch (ECDSA_do_verify(hash->sha.u.u8, sizeof(hash->sha.u), &sig,
eckey)) {
case 0:
/* Invalid signature */
goto free_bns;
case -1:
/* Malformed or other error. */
goto free_bns;
}
ok = true;
free_bns:
BN_free(&r);
BN_free(&s);
out:
EC_KEY_free(eckey);
return ok;
}
bool check_2of2_sig(struct bitcoin_tx *tx, size_t input_num,
const struct bitcoin_tx_output *output,
const struct pubkey *key1, const struct pubkey *key2,
const struct signature *sig1, const struct signature *sig2)
{
struct sha256_double hash;
assert(input_num < tx->input_count);
assert(is_p2sh(output->script, output->script_length));
sha256_tx_one_input(tx, input_num,
output->script, output->script_length, &hash);
return check_signed_hash(&hash, sig1, key1)
&& check_signed_hash(&hash, sig2, key2);
}
Signature *signature_to_proto(const tal_t *ctx, const struct signature *sig)
{
Signature *pb = tal(ctx, Signature);

View File

@ -20,6 +20,8 @@ struct signature {
struct sha256_double;
struct bitcoin_tx;
struct pubkey;
struct bitcoin_tx_output;
struct signature *sign_hash(const tal_t *ctx, EC_KEY *private_key,
const struct sha256_double *h);
@ -30,6 +32,11 @@ struct signature *sign_tx_input(const tal_t *ctx,
const u8 *subscript, size_t subscript_len,
EC_KEY *privkey);
bool check_2of2_sig(struct bitcoin_tx *tx, size_t input_num,
const struct bitcoin_tx_output *spending,
const struct pubkey *key1, const struct pubkey *key2,
const struct signature *sig1, const struct signature *sig2);
/* Convert to-from protobuf to internal representation. */
Signature *signature_to_proto(const tal_t *ctx, const struct signature *sig);
bool proto_to_signature(const Signature *pb, struct signature *sig);