Skip to content

Commit

Permalink
common/derive_basepoints: add routines to get a specific secret.
Browse files Browse the repository at this point in the history
Often we only need a single secret, so it's clearer to have routines
to do just that.  When we change to the lnd key scheme, there will be
no benefit in calculating them all together.

This also adds a test!

Signed-off-by: Rusty Russell <[email protected]>
  • Loading branch information
rustyrussell authored and cdecker committed Jul 17, 2018
1 parent b2b8510 commit 64008e2
Show file tree
Hide file tree
Showing 3 changed files with 281 additions and 0 deletions.
79 changes: 79 additions & 0 deletions common/derive_basepoints.c
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,85 @@ bool per_commit_point(const struct sha256 *shaseed,
return true;
}

bool derive_payment_basepoint(const struct secret *seed,
struct pubkey *payment_basepoint,
struct secret *payment_secret)
{
struct keys {
struct privkey f, r, h, p, d;
struct sha256 shaseed;
} keys;

hkdf_sha256(&keys, sizeof(keys), NULL, 0, seed, sizeof(*seed),
"c-lightning", strlen("c-lightning"));

if (payment_basepoint) {
if (!pubkey_from_privkey(&keys.p, payment_basepoint))
return false;
}

if (payment_secret)
*payment_secret = keys.p.secret;

return true;
}

bool derive_delayed_payment_basepoint(const struct secret *seed,
struct pubkey *delayed_payment_basepoint,
struct secret *delayed_payment_secret)
{
struct keys {
struct privkey f, r, h, p, d;
struct sha256 shaseed;
} keys;

hkdf_sha256(&keys, sizeof(keys), NULL, 0, seed, sizeof(*seed),
"c-lightning", strlen("c-lightning"));

if (delayed_payment_basepoint) {
if (!pubkey_from_privkey(&keys.d, delayed_payment_basepoint))
return false;
}

if (delayed_payment_secret)
*delayed_payment_secret = keys.d.secret;

return true;
}

bool derive_shaseed(const struct secret *seed, struct sha256 *shaseed)
{
struct keys {
struct privkey f, r, h, p, d;
struct sha256 shaseed;
} keys;

hkdf_sha256(&keys, sizeof(keys), NULL, 0, seed, sizeof(*seed),
"c-lightning", strlen("c-lightning"));
*shaseed = keys.shaseed;
return true;
}

bool derive_funding_key(const struct secret *seed,
struct pubkey *funding_pubkey,
struct privkey *funding_privkey)
{
struct privkey f;

hkdf_sha256(&f, sizeof(f), NULL, 0, seed, sizeof(*seed),
"c-lightning", strlen("c-lightning"));

if (funding_pubkey) {
if (!pubkey_from_privkey(&f, funding_pubkey))
return false;
}

if (funding_privkey)
*funding_privkey = f;

return true;
}

void towire_basepoints(u8 **pptr, const struct basepoints *b)
{
towire_pubkey(pptr, &b->revocation);
Expand Down
45 changes: 45 additions & 0 deletions common/derive_basepoints.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,51 @@ bool derive_basepoints(const struct secret *seed,
struct secrets *secrets,
struct sha256 *shaseed);

/**
* derive_funding_key - give a (per-peer) seed, get just funding key
* @seed: (in) seed (derived by master daemon from counter and main seed)
* @funding_pubkey: (out) pubkey for funding tx output (if non-NULL)
* @funding_privkey: (out) privkey for funding tx output (if non-NULL)
*
* This is a cut-down version of derive_basepoints.
*/
bool derive_funding_key(const struct secret *seed,
struct pubkey *funding_pubkey,
struct privkey *funding_privkey);

/**
* derive_payment_basepoint - give a (per-channel) seed, get just payment basepoint
* @seed: (in) seed (derived by master daemon from counter and main seed)
* @payment_basepoint: (out) basepoint for payment output (if non-NULL)
* @payment_secret: (out) secret for payment basepoint (if non-NULL)
*
* This is a cut-down version of derive_basepoints.
*/
bool derive_payment_basepoint(const struct secret *seed,
struct pubkey *payment_basepoint,
struct secret *payment_secret);

/**
* derive_shaseed - give a (per-peer) seed, get just the shaseed
* @seed: (in) seed (derived by master daemon from counter and main seed)
* @shaseed: (out) seed for shachain
*
* This is a cut-down version of derive_basepoints.
*/
bool derive_shaseed(const struct secret *seed, struct sha256 *shaseed);

/**
* derive_delayed_payment_basepoint - give a (per-channel) seed, get just delayed payment basepoint
* @seed: (in) seed (derived by master daemon from counter and main seed)
* @delayed_payment_basepoint: (out) basepoint for payment output (if non-NULL)
* @delayed_payment_secret: (out) secret for payment basepoint (if non-NULL)
*
* This is a cut-down version of derive_basepoints.
*/
bool derive_delayed_payment_basepoint(const struct secret *seed,
struct pubkey *delayed_payment_basepoint,
struct secret *delayed_payment_secret);

/**
* per_commit_secret - get a secret for this index.
* @shaseed: the sha256 seed
Expand Down
157 changes: 157 additions & 0 deletions common/test/run-derive_basepoints.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
#include "../derive_basepoints.c"
#include <ccan/err/err.h>
#include <ccan/mem/mem.h>
#include <ccan/str/hex/hex.h>
#include <ccan/structeq/structeq.h>
#include <common/utils.h>
#include <stdio.h>
#include <wally_core.h>

/* AUTOGENERATED MOCKS START */
/* Generated stub for fromwire_pubkey */
void fromwire_pubkey(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct pubkey *pubkey UNNEEDED)
{ fprintf(stderr, "fromwire_pubkey called!\n"); abort(); }
/* Generated stub for towire_pubkey */
void towire_pubkey(u8 **pptr UNNEEDED, const struct pubkey *pubkey UNNEEDED)
{ fprintf(stderr, "towire_pubkey called!\n"); abort(); }
/* AUTOGENERATED MOCKS END */

STRUCTEQ_DEF(basepoints, 0,
revocation.pubkey,
payment.pubkey,
htlc.pubkey,
delayed_payment.pubkey);
STRUCTEQ_DEF(secrets, 0,
funding_privkey.secret.data,
revocation_basepoint_secret.data,
payment_basepoint_secret.data,
htlc_basepoint_secret.data,
delayed_payment_basepoint_secret.data);
STRUCTEQ_DEF(privkey, 0,
secret.data);

struct info {
struct secret seed;
struct pubkey funding_pubkey;
struct basepoints basepoints;
struct secrets secrets;
struct sha256 shaseed;
};

/* We get a fresh one each time, to catch uninitialized fields */
static struct info *new_info(const tal_t *ctx)
{
struct info *info = tal(ctx, struct info);
memset(&info->seed, 7, sizeof(info->seed));

return info;
}

int main(void)
{
setup_locale();

const tal_t *ctx = tal(NULL, char);
struct info *baseline, *info;

secp256k1_ctx = wally_get_secp_context();
baseline = new_info(ctx);
assert(derive_basepoints(&baseline->seed, &baseline->funding_pubkey,
&baseline->basepoints,
&baseline->secrets,
&baseline->shaseed));

/* Same seed, same result. */
info = new_info(ctx);
assert(derive_basepoints(&info->seed, &info->funding_pubkey,
&info->basepoints,
&info->secrets,
&info->shaseed));
assert(pubkey_eq(&baseline->funding_pubkey, &info->funding_pubkey));
assert(basepoints_eq(&baseline->basepoints, &info->basepoints));
assert(secrets_eq(&baseline->secrets, &info->secrets));
assert(sha256_eq(&baseline->shaseed, &info->shaseed));

/* Different seed, different result. */
for (size_t i = 0; i < sizeof(info->seed); i++) {
for (size_t b = 0; b < CHAR_BIT; b++) {
info = new_info(ctx);
info->seed.data[i] ^= (1 << b);

assert(derive_basepoints(&info->seed,
&info->funding_pubkey,
&info->basepoints,
&info->secrets,
&info->shaseed));
assert(!pubkey_eq(&baseline->funding_pubkey,
&info->funding_pubkey));
assert(!basepoints_eq(&baseline->basepoints,
&info->basepoints));
assert(!secrets_eq(&baseline->secrets, &info->secrets));
assert(!sha256_eq(&baseline->shaseed, &info->shaseed));
}
}

/* Any field can be NULL (except seed). */
info = new_info(ctx);
assert(derive_basepoints(&info->seed, NULL,
&info->basepoints,
&info->secrets,
&info->shaseed));
assert(basepoints_eq(&baseline->basepoints, &info->basepoints));
assert(secrets_eq(&baseline->secrets, &info->secrets));
assert(sha256_eq(&baseline->shaseed, &info->shaseed));

info = new_info(ctx);
assert(derive_basepoints(&info->seed, &info->funding_pubkey,
NULL,
&info->secrets,
&info->shaseed));
assert(pubkey_eq(&baseline->funding_pubkey, &info->funding_pubkey));
assert(secrets_eq(&baseline->secrets, &info->secrets));
assert(sha256_eq(&baseline->shaseed, &info->shaseed));

info = new_info(ctx);
assert(derive_basepoints(&info->seed, &info->funding_pubkey,
&info->basepoints,
NULL,
&info->shaseed));
assert(pubkey_eq(&baseline->funding_pubkey, &info->funding_pubkey));
assert(basepoints_eq(&baseline->basepoints, &info->basepoints));
assert(sha256_eq(&baseline->shaseed, &info->shaseed));

info = new_info(ctx);
assert(derive_basepoints(&info->seed, &info->funding_pubkey,
&info->basepoints,
&info->secrets,
NULL));
assert(pubkey_eq(&baseline->funding_pubkey, &info->funding_pubkey));
assert(basepoints_eq(&baseline->basepoints, &info->basepoints));
assert(secrets_eq(&baseline->secrets, &info->secrets));

/* derive_payment_basepoint should give same results. */
info = new_info(ctx);
assert(derive_payment_basepoint(&info->seed, &info->basepoints.payment,
&info->secrets.payment_basepoint_secret));
assert(pubkey_eq(&baseline->basepoints.payment,
&info->basepoints.payment));
assert(secret_eq(&baseline->secrets.payment_basepoint_secret,
&info->secrets.payment_basepoint_secret));

/* derive_funding_key should give same results. */
info = new_info(ctx);
assert(derive_funding_key(&info->seed, &info->funding_pubkey,
&info->secrets.funding_privkey));
assert(pubkey_eq(&baseline->funding_pubkey, &info->funding_pubkey));
assert(privkey_eq(&baseline->secrets.funding_privkey,
&info->secrets.funding_privkey));

/* derive_shaseed should give same results. */
info = new_info(ctx);
assert(derive_shaseed(&info->seed, &info->shaseed));
assert(sha256_eq(&baseline->shaseed, &info->shaseed));

tal_free(ctx);
wally_cleanup(0);
return 0;
}

0 comments on commit 64008e2

Please sign in to comment.