mirror of
https://github.com/aljazceru/lightning.git
synced 2025-12-20 15:44:21 +01:00
Add check-commit-sig.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
7
Makefile
7
Makefile
@@ -3,7 +3,7 @@
|
|||||||
# Needs to have oneof support: Ubuntu vivid's is too old :(
|
# Needs to have oneof support: Ubuntu vivid's is too old :(
|
||||||
PROTOCC:=protoc-c
|
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
|
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: $(OPEN_COMMIT_SIG_OBJS) $(HELPER_OBJS) $(CCAN_OBJS)
|
||||||
$(OPEN_COMMIT_SIG_OBJS): $(HEADERS)
|
$(OPEN_COMMIT_SIG_OBJS): $(HEADERS)
|
||||||
|
|
||||||
|
check-commit-sig: $(CHECK_COMMIT_SIG_OBJS) $(HELPER_OBJS) $(CCAN_OBJS)
|
||||||
|
$(CHECK_COMMIT_SIG_OBJS): $(HEADERS)
|
||||||
|
|
||||||
distclean: clean
|
distclean: clean
|
||||||
$(RM) lightning.pb-c.c lightning.pb-c.h
|
$(RM) lightning.pb-c.c lightning.pb-c.h
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
$(RM) $(PROGRAMS)
|
$(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
|
ccan-tal.o: $(CCANDIR)/ccan/tal/tal.c
|
||||||
$(CC) $(CFLAGS) -c -o $@ $<
|
$(CC) $(CFLAGS) -c -o $@ $<
|
||||||
|
|||||||
@@ -165,6 +165,23 @@ u8 *scriptsig_pay_to_pubkeyhash(const tal_t *ctx,
|
|||||||
return script;
|
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? */
|
/* Is this a normal pay to pubkey hash? */
|
||||||
bool is_pay_to_pubkey_hash(const u8 *script, size_t script_len)
|
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;
|
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:
|
/* One of:
|
||||||
* mysig and theirsig, OR
|
* mysig and theirsig, OR
|
||||||
* mysig and relative locktime passed, OR
|
* mysig and relative locktime passed, OR
|
||||||
|
|||||||
@@ -38,7 +38,17 @@ u8 *scriptsig_pay_to_pubkeyhash(const tal_t *ctx,
|
|||||||
const struct bitcoin_address *addr,
|
const struct bitcoin_address *addr,
|
||||||
const struct signature *sig);
|
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? */
|
/* Is this a normal pay to pubkey hash? */
|
||||||
bool is_pay_to_pubkey_hash(const u8 *script, size_t script_len);
|
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 */
|
#endif /* LIGHTNING_BITCOIN_SCRIPT_H */
|
||||||
|
|||||||
102
check-commit-sig.c
Normal file
102
check-commit-sig.c
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
108
signature.c
108
signature.c
@@ -1,6 +1,8 @@
|
|||||||
#include "signature.h"
|
#include "signature.h"
|
||||||
#include "shadouble.h"
|
#include "shadouble.h"
|
||||||
#include "bitcoin_tx.h"
|
#include "bitcoin_tx.h"
|
||||||
|
#include "pubkey.h"
|
||||||
|
#include "bitcoin_script.h"
|
||||||
#include <openssl/bn.h>
|
#include <openssl/bn.h>
|
||||||
#include <openssl/obj_mac.h>
|
#include <openssl/obj_mac.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
@@ -47,31 +49,107 @@ struct signature *sign_hash(const tal_t *ctx, EC_KEY *private_key,
|
|||||||
return s;
|
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,
|
struct signature *sign_tx_input(const tal_t *ctx, struct bitcoin_tx *tx,
|
||||||
unsigned int in,
|
unsigned int in,
|
||||||
const u8 *subscript, size_t subscript_len,
|
const u8 *subscript, size_t subscript_len,
|
||||||
EC_KEY *privkey)
|
EC_KEY *privkey)
|
||||||
{
|
{
|
||||||
struct sha256_double hash;
|
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);
|
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 *signature_to_proto(const tal_t *ctx, const struct signature *sig)
|
||||||
{
|
{
|
||||||
Signature *pb = tal(ctx, Signature);
|
Signature *pb = tal(ctx, Signature);
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ struct signature {
|
|||||||
|
|
||||||
struct sha256_double;
|
struct sha256_double;
|
||||||
struct bitcoin_tx;
|
struct bitcoin_tx;
|
||||||
|
struct pubkey;
|
||||||
|
struct bitcoin_tx_output;
|
||||||
|
|
||||||
struct signature *sign_hash(const tal_t *ctx, EC_KEY *private_key,
|
struct signature *sign_hash(const tal_t *ctx, EC_KEY *private_key,
|
||||||
const struct sha256_double *h);
|
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,
|
const u8 *subscript, size_t subscript_len,
|
||||||
EC_KEY *privkey);
|
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. */
|
/* Convert to-from protobuf to internal representation. */
|
||||||
Signature *signature_to_proto(const tal_t *ctx, const struct signature *sig);
|
Signature *signature_to_proto(const tal_t *ctx, const struct signature *sig);
|
||||||
bool proto_to_signature(const Signature *pb, struct signature *sig);
|
bool proto_to_signature(const Signature *pb, struct signature *sig);
|
||||||
|
|||||||
Reference in New Issue
Block a user