mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-02-22 14:42:40 +01:00
key_derive: key derivation from basepoints as specified in BOLT 3
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
e899c47260
commit
dc3b27e1d3
5 changed files with 348 additions and 2 deletions
|
@ -27,7 +27,8 @@ LIGHTNINGD_OLD_OBJS := $(LIGHTNINGD_OLD_SRC:.c=.o)
|
|||
LIGHTNINGD_OLD_HEADERS := $(LIGHTNINGD_OLD_SRC:.c=.h)
|
||||
|
||||
LIGHTNINGD_LIB_SRC := \
|
||||
lightningd/cryptomsg.c
|
||||
lightningd/cryptomsg.c \
|
||||
lightningd/key_derive.c
|
||||
|
||||
LIGHTNINGD_LIB_OBJS := $(LIGHTNINGD_LIB_SRC:.c=.o)
|
||||
LIGHTNINGD_LIB_HEADERS := $(LIGHTNINGD_LIB_SRC:.c=.h)
|
||||
|
|
238
lightningd/key_derive.c
Normal file
238
lightningd/key_derive.c
Normal file
|
@ -0,0 +1,238 @@
|
|||
#include <bitcoin/privkey.h>
|
||||
#include <bitcoin/pubkey.h>
|
||||
#include <ccan/crypto/sha256/sha256.h>
|
||||
#include <lightningd/key_derive.h>
|
||||
#include <utils.h>
|
||||
|
||||
/* BOLT #3:
|
||||
*
|
||||
* ### `localkey`, `remotekey`, `local-delayedkey` and `remote-delayedkey` Derivation
|
||||
*
|
||||
* These keys are simply generated by addition from their base points:
|
||||
*
|
||||
* pubkey = basepoint + SHA256(per-commitment-point || basepoint)*G
|
||||
*
|
||||
* The `localkey` uses the local node's `payment-basepoint`, `remotekey`
|
||||
* uses the remote node's `payment-basepoint`, the `local-delayedkey`
|
||||
* uses the local node's `delayed-payment-basepoint`, and the
|
||||
* `remote-delayedkey` uses the remote node's
|
||||
* `delayed-payment-basepoint`.
|
||||
*/
|
||||
bool derive_simple_key(const struct pubkey *basepoint,
|
||||
const struct pubkey *per_commitment_point,
|
||||
struct pubkey *key)
|
||||
{
|
||||
struct sha256 sha;
|
||||
unsigned char der_keys[PUBKEY_DER_LEN * 2];
|
||||
|
||||
pubkey_to_der(der_keys, per_commitment_point);
|
||||
pubkey_to_der(der_keys + PUBKEY_DER_LEN, basepoint);
|
||||
sha256(&sha, der_keys, sizeof(der_keys));
|
||||
#ifdef SUPERVERBOSE
|
||||
printf("# SHA256(per-commitment-point || basepoint)\n");
|
||||
printf("# => SHA256(0x%s || 0x%s)\n",
|
||||
tal_hexstr(tmpctx, der_keys, PUBKEY_DER_LEN),
|
||||
tal_hexstr(tmpctx, der_keys + PUBKEY_DER_LEN, PUBKEY_DER_LEN));
|
||||
printf("# = 0x%s\n",
|
||||
tal_hexstr(tmpctx, &sha, sizeof(sha)));
|
||||
#endif
|
||||
|
||||
*key = *basepoint;
|
||||
if (secp256k1_ec_pubkey_tweak_add(secp256k1_ctx,
|
||||
&key->pubkey, sha.u.u8) != 1)
|
||||
return false;
|
||||
#ifdef SUPERVERBOSE
|
||||
printf("# + basepoint (0x%s)\n",
|
||||
type_to_string(tmpctx, struct pubkey, basepoint));
|
||||
printf("# = 0x%s\n",
|
||||
type_to_string(tmpctx, struct pubkey, key));
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
/* BOLT #3:
|
||||
*
|
||||
* The corresponding private keys can be derived similarly if the basepoint
|
||||
* secrets are known (i.e., `localkey` and `local-delayedkey` only):
|
||||
*
|
||||
* secretkey = basepoint-secret + SHA256(per-commitment-point || basepoint)
|
||||
*/
|
||||
bool derive_simple_privkey(const struct privkey *base_secret,
|
||||
const struct pubkey *basepoint,
|
||||
const struct pubkey *per_commitment_point,
|
||||
struct privkey *key)
|
||||
{
|
||||
struct sha256 sha;
|
||||
unsigned char der_keys[PUBKEY_DER_LEN * 2];
|
||||
|
||||
pubkey_to_der(der_keys, per_commitment_point);
|
||||
pubkey_to_der(der_keys + PUBKEY_DER_LEN, basepoint);
|
||||
sha256(&sha, der_keys, sizeof(der_keys));
|
||||
#ifdef SUPERVERBOSE
|
||||
printf("# SHA256(per-commitment-point || basepoint)\n");
|
||||
printf("# => SHA256(0x%s || 0x%s)\n",
|
||||
tal_hexstr(tmpctx, der_keys, PUBKEY_DER_LEN),
|
||||
tal_hexstr(tmpctx, der_keys + PUBKEY_DER_LEN, PUBKEY_DER_LEN));
|
||||
printf("# = 0x%s\n", tal_hexstr(tmpctx, &sha, sizeof(sha)));
|
||||
#endif
|
||||
|
||||
*key = *base_secret;
|
||||
if (secp256k1_ec_privkey_tweak_add(secp256k1_ctx, key->secret,
|
||||
sha.u.u8) != 1)
|
||||
return false;
|
||||
#ifdef SUPERVERBOSE
|
||||
printf("# + basepoint_secret (0x%s)\n",
|
||||
tal_hexstr(tmpctx, base_secret, sizeof(*base_secret)));
|
||||
printf("# = 0x%s\n",
|
||||
tal_hexstr(tmpctx, key, sizeof(*key)));
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
/* BOLT #3:
|
||||
*
|
||||
* The revocationkey is a blinded key: the remote node provides the base,
|
||||
* and the local node provides the blinding factor which it later
|
||||
* reveals, so the remote node can use the secret revocationkey for a
|
||||
* penalty transaction.
|
||||
*
|
||||
* The `per-commitment-point` is generated using EC multiplication:
|
||||
*
|
||||
* per-commitment-point = per-commitment-secret * G
|
||||
*
|
||||
* And this is used to derive the revocation key from the remote node's
|
||||
* `revocation-basepoint`:
|
||||
*
|
||||
* revocationkey = revocation-basepoint * SHA256(revocation-basepoint || per-commitment-point) + per-commitment-point*SHA256(per-commitment-point || revocation-basepoint)
|
||||
*/
|
||||
bool derive_revocation_key(const struct pubkey *basepoint,
|
||||
const struct pubkey *per_commitment_point,
|
||||
struct pubkey *key)
|
||||
{
|
||||
struct sha256 sha;
|
||||
unsigned char der_keys[PUBKEY_DER_LEN * 2];
|
||||
secp256k1_pubkey add[2];
|
||||
const secp256k1_pubkey *args[2];
|
||||
|
||||
pubkey_to_der(der_keys, basepoint);
|
||||
pubkey_to_der(der_keys + PUBKEY_DER_LEN, per_commitment_point);
|
||||
sha256(&sha, der_keys, sizeof(der_keys));
|
||||
#ifdef SUPERVERBOSE
|
||||
printf("# SHA256(revocation-basepoint || per-commitment-point)\n");
|
||||
printf("# => SHA256(0x%s || 0x%s)\n",
|
||||
tal_hexstr(tmpctx, der_keys, PUBKEY_DER_LEN),
|
||||
tal_hexstr(tmpctx, der_keys + PUBKEY_DER_LEN, PUBKEY_DER_LEN));
|
||||
printf("# = 0x%s\n", tal_hexstr(tmpctx, sha.u.u8, sizeof(sha.u.u8))),
|
||||
#endif
|
||||
|
||||
add[0] = basepoint->pubkey;
|
||||
if (secp256k1_ec_pubkey_tweak_mul(secp256k1_ctx, &add[0], sha.u.u8) != 1)
|
||||
return false;
|
||||
#ifdef SUPERVERBOSE
|
||||
printf("# x revocation-basepoint = 0x%s\n",
|
||||
type_to_string(tmpctx, secp256k1_pubkey, &add[0]));
|
||||
#endif
|
||||
|
||||
pubkey_to_der(der_keys, per_commitment_point);
|
||||
pubkey_to_der(der_keys + PUBKEY_DER_LEN, basepoint);
|
||||
sha256(&sha, der_keys, sizeof(der_keys));
|
||||
#ifdef SUPERVERBOSE
|
||||
printf("# SHA256(per-commitment-point || revocation-basepoint)\n");
|
||||
printf("# => SHA256(0x%s || 0x%s)\n",
|
||||
tal_hexstr(tmpctx, der_keys, PUBKEY_DER_LEN),
|
||||
tal_hexstr(tmpctx, der_keys + PUBKEY_DER_LEN, PUBKEY_DER_LEN));
|
||||
printf("# = 0x%s\n", tal_hexstr(tmpctx, sha.u.u8, sizeof(sha.u.u8))),
|
||||
#endif
|
||||
|
||||
add[1] = per_commitment_point->pubkey;
|
||||
if (secp256k1_ec_pubkey_tweak_mul(secp256k1_ctx, &add[1], sha.u.u8) != 1)
|
||||
return false;
|
||||
#ifdef SUPERVERBOSE
|
||||
printf("# x per-commitment-point = 0x%s\n",
|
||||
type_to_string(tmpctx, secp256k1_pubkey, &add[1]));
|
||||
#endif
|
||||
|
||||
args[0] = &add[0];
|
||||
args[1] = &add[1];
|
||||
if (secp256k1_ec_pubkey_combine(secp256k1_ctx, &key->pubkey, args, 2)
|
||||
!= 1)
|
||||
return false;
|
||||
|
||||
#ifdef SUPERVERBOSE
|
||||
printf("# 0x%s + 0x%s => 0x%s\n",
|
||||
type_to_string(tmpctx, secp256k1_pubkey, args[0]),
|
||||
type_to_string(tmpctx, secp256k1_pubkey, args[1]),
|
||||
type_to_string(tmpctx, struct pubkey, key));
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
/* BOLT #3:
|
||||
*
|
||||
* The corresponding private key can be derived once the `per-commitment-secret`
|
||||
* is known:
|
||||
*
|
||||
* revocationsecretkey = revocation-basepoint-secret * SHA256(revocation-basepoint || per-commitment-point) + per-commitment-secret*SHA256(per-commitment-point || revocation-basepoint)
|
||||
*/
|
||||
bool derive_revocation_privkey(const struct privkey *base_secret,
|
||||
const struct privkey *per_commitment_secret,
|
||||
const struct pubkey *basepoint,
|
||||
const struct pubkey *per_commitment_point,
|
||||
struct privkey *key)
|
||||
{
|
||||
struct sha256 sha;
|
||||
unsigned char der_keys[PUBKEY_DER_LEN * 2];
|
||||
struct privkey part2;
|
||||
|
||||
pubkey_to_der(der_keys, basepoint);
|
||||
pubkey_to_der(der_keys + PUBKEY_DER_LEN, per_commitment_point);
|
||||
sha256(&sha, der_keys, sizeof(der_keys));
|
||||
#ifdef SUPERVERBOSE
|
||||
printf("# SHA256(revocation-basepoint || per-commitment-point)\n");
|
||||
printf("# => SHA256(0x%s || 0x%s)\n",
|
||||
tal_hexstr(tmpctx, der_keys, PUBKEY_DER_LEN),
|
||||
tal_hexstr(tmpctx, der_keys + PUBKEY_DER_LEN, PUBKEY_DER_LEN));
|
||||
printf("# = 0x%s\n", tal_hexstr(tmpctx, sha.u.u8, sizeof(sha.u.u8))),
|
||||
#endif
|
||||
|
||||
*key = *base_secret;
|
||||
if (secp256k1_ec_privkey_tweak_mul(secp256k1_ctx, key->secret, sha.u.u8)
|
||||
!= 1)
|
||||
return false;
|
||||
#ifdef SUPERVERBOSE
|
||||
printf("# * revocation-basepoint-secret (0x%s)",
|
||||
tal_hexstr(tmpctx, base_secret, sizeof(*base_secret))),
|
||||
printf("# = 0x%s\n", tal_hexstr(tmpctx, key, sizeof(*key))),
|
||||
#endif
|
||||
|
||||
pubkey_to_der(der_keys, per_commitment_point);
|
||||
pubkey_to_der(der_keys + PUBKEY_DER_LEN, basepoint);
|
||||
sha256(&sha, der_keys, sizeof(der_keys));
|
||||
#ifdef SUPERVERBOSE
|
||||
printf("# SHA256(per-commitment-point || revocation-basepoint)\n");
|
||||
printf("# => SHA256(0x%s || 0x%s)\n",
|
||||
tal_hexstr(tmpctx, der_keys, PUBKEY_DER_LEN),
|
||||
tal_hexstr(tmpctx, der_keys + PUBKEY_DER_LEN, PUBKEY_DER_LEN));
|
||||
printf("# = 0x%s\n", tal_hexstr(tmpctx, sha.u.u8, sizeof(sha.u.u8))),
|
||||
#endif
|
||||
|
||||
part2 = *per_commitment_secret;
|
||||
if (secp256k1_ec_privkey_tweak_mul(secp256k1_ctx, part2.secret,
|
||||
sha.u.u8) != 1)
|
||||
return false;
|
||||
#ifdef SUPERVERBOSE
|
||||
printf("# * per-commitment-secret (0x%s)",
|
||||
tal_hexstr(tmpctx, per_commitment_secret,
|
||||
sizeof(*per_commitment_secret))),
|
||||
printf("# = 0x%s\n", tal_hexstr(tmpctx, &part2, sizeof(part2)));
|
||||
#endif
|
||||
|
||||
if (secp256k1_ec_privkey_tweak_add(secp256k1_ctx, key->secret,
|
||||
part2.secret) != 1)
|
||||
return false;
|
||||
|
||||
#ifdef SUPERVERBOSE
|
||||
printf("# => 0x%s\n", tal_hexstr(tmpctx, key, sizeof(*key)));
|
||||
#endif
|
||||
return true;
|
||||
}
|
28
lightningd/key_derive.h
Normal file
28
lightningd/key_derive.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
#ifndef LIGHTNING_LIGHTNINGD_KEY_DERIVE_H
|
||||
#define LIGHTNING_LIGHTNINGD_KEY_DERIVE_H
|
||||
#include "config.h"
|
||||
|
||||
struct pubkey;
|
||||
|
||||
/* For `localkey`, `remotekey`, `local-delayedkey` and `remote-delayedkey` */
|
||||
bool derive_simple_key(const struct pubkey *basepoint,
|
||||
const struct pubkey *per_commitment_point,
|
||||
struct pubkey *key);
|
||||
|
||||
bool derive_simple_privkey(const struct privkey *base_secret,
|
||||
const struct pubkey *basepoint,
|
||||
const struct pubkey *per_commitment_point,
|
||||
struct privkey *key);
|
||||
|
||||
/* For `revocationkey` */
|
||||
bool derive_revocation_key(const struct pubkey *basepoint,
|
||||
const struct pubkey *per_commitment_point,
|
||||
struct pubkey *key);
|
||||
|
||||
bool derive_revocation_privkey(const struct privkey *base_secret,
|
||||
const struct privkey *per_commitment_secret,
|
||||
const struct pubkey *basepoint,
|
||||
const struct pubkey *per_commitment_point,
|
||||
struct privkey *key);
|
||||
|
||||
#endif /* LIGHTNING_LIGHTNINGD_KEY_DERIVE_H */
|
|
@ -8,7 +8,7 @@ LIGHTNINGD_TEST_PROGRAMS := $(LIGHTNINGD_TEST_OBJS:.o=)
|
|||
|
||||
update-mocks: $(LIGHTNINGD_TEST_SRC:%=update-mocks/%)
|
||||
|
||||
$(LIGHTNINGD_TEST_PROGRAMS): $(CCAN_OBJS) $(BITCOIN_OBJS) $(WIRE_OBJS) $(LIBBASE58_OBJS) $(LIGHTNINGD_HANDSHAKE_GEN_SRC:.c=.o) utils.o libsecp256k1.a libsodium.a
|
||||
$(LIGHTNINGD_TEST_PROGRAMS): $(CCAN_OBJS) $(CORE_OBJS) $(BITCOIN_OBJS) $(WIRE_OBJS) $(LIBBASE58_OBJS) utils.o libsecp256k1.a libsodium.a
|
||||
|
||||
$(LIGHTNINGD_TEST_OBJS): $(LIGHTNINGD_HEADERS)
|
||||
|
||||
|
|
79
lightningd/test/run-key_derive.c
Normal file
79
lightningd/test/run-key_derive.c
Normal file
|
@ -0,0 +1,79 @@
|
|||
#define SUPERVERBOSE
|
||||
static void *tmpctx;
|
||||
|
||||
#include <stdio.h>
|
||||
#include <utils.h>
|
||||
#include <type_to_string.h>
|
||||
#include "../key_derive.c"
|
||||
#include <assert.h>
|
||||
#include <ccan/str/hex/hex.h>
|
||||
#include <stdio.h>
|
||||
#include <type_to_string.h>
|
||||
|
||||
static struct privkey privkey_from_hex(const char *hex)
|
||||
{
|
||||
struct privkey privkey;
|
||||
hex += 2;
|
||||
if (!hex_decode(hex, strlen(hex), &privkey, sizeof(privkey)))
|
||||
abort();
|
||||
return privkey;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
struct privkey base_secret, per_commitment_secret, privkey;
|
||||
struct pubkey base_point, per_commitment_point, pubkey, pubkey2;
|
||||
|
||||
tmpctx = tal_tmpctx(NULL);
|
||||
secp256k1_ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY
|
||||
| SECP256K1_CONTEXT_SIGN);
|
||||
|
||||
base_secret = privkey_from_hex("0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f");
|
||||
per_commitment_secret = privkey_from_hex("0x1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100");
|
||||
|
||||
printf("base_secret: 0x%s\n",
|
||||
tal_hexstr(tmpctx, &base_secret, sizeof(base_secret)));
|
||||
printf("per_commitment_secret: 0x%s\n",
|
||||
tal_hexstr(tmpctx, &per_commitment_secret,
|
||||
sizeof(per_commitment_secret)));
|
||||
if (!pubkey_from_privkey(&per_commitment_secret, &per_commitment_point))
|
||||
abort();
|
||||
if (!pubkey_from_privkey(&base_secret, &base_point))
|
||||
abort();
|
||||
printf("base_point: 0x%s\n",
|
||||
type_to_string(tmpctx, struct pubkey, &base_point));
|
||||
printf("per_commitment_point: 0x%s\n",
|
||||
type_to_string(tmpctx, struct pubkey, &per_commitment_point));
|
||||
|
||||
/* FIXME: Annotate internal steps. */
|
||||
if (!derive_simple_key(&base_point, &per_commitment_point, &pubkey))
|
||||
abort();
|
||||
printf("localkey: 0x%s\n",
|
||||
type_to_string(tmpctx, struct pubkey, &pubkey));
|
||||
if (!derive_simple_privkey(&base_secret, &base_point,
|
||||
&per_commitment_point, &privkey))
|
||||
abort();
|
||||
printf("localprivkey: 0x%s\n",
|
||||
tal_hexstr(tmpctx, &privkey, sizeof(privkey)));
|
||||
pubkey_from_privkey(&privkey, &pubkey2);
|
||||
assert(pubkey_eq(&pubkey, &pubkey2));
|
||||
|
||||
/* FIXME: Annotate internal steps. */
|
||||
if (!derive_revocation_key(&base_point, &per_commitment_point, &pubkey))
|
||||
abort();
|
||||
printf("revocationkey: 0x%s\n",
|
||||
type_to_string(tmpctx, struct pubkey, &pubkey));
|
||||
if (!derive_revocation_privkey(&base_secret, &per_commitment_secret,
|
||||
&base_point, &per_commitment_point,
|
||||
&privkey))
|
||||
abort();
|
||||
printf("revocationprivkey: 0x%s\n",
|
||||
tal_hexstr(tmpctx, &privkey, sizeof(privkey)));
|
||||
pubkey_from_privkey(&privkey, &pubkey2);
|
||||
assert(pubkey_eq(&pubkey, &pubkey2));
|
||||
|
||||
/* No memory leaks please */
|
||||
secp256k1_context_destroy(secp256k1_ctx);
|
||||
tal_free(tmpctx);
|
||||
return 0;
|
||||
}
|
Loading…
Add table
Reference in a new issue