diff --git a/common/derive_basepoints.c b/common/derive_basepoints.c index a2dccc6f1..eaeb1999a 100644 --- a/common/derive_basepoints.c +++ b/common/derive_basepoints.c @@ -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); diff --git a/common/derive_basepoints.h b/common/derive_basepoints.h index 433d56b6d..a5b69c02b 100644 --- a/common/derive_basepoints.h +++ b/common/derive_basepoints.h @@ -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 diff --git a/common/test/run-derive_basepoints.c b/common/test/run-derive_basepoints.c new file mode 100644 index 000000000..acf54db23 --- /dev/null +++ b/common/test/run-derive_basepoints.c @@ -0,0 +1,157 @@ +#include "../derive_basepoints.c" +#include +#include +#include +#include +#include +#include +#include + +/* 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; +}