mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-03-15 20:09:18 +01:00
common/blindedpath: enctlv unwrapping primitives.
And we check them on our test vectors. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
b98e8a099d
commit
3a966191b8
3 changed files with 282 additions and 0 deletions
|
@ -204,6 +204,167 @@ static u8 *enctlv_from_encmsg(const tal_t *ctx,
|
|||
return ret;
|
||||
}
|
||||
|
||||
bool unblind_onion(const struct pubkey *blinding,
|
||||
void (*ecdh)(const struct pubkey *point, struct secret *ss),
|
||||
struct pubkey *onion_key,
|
||||
struct secret *ss)
|
||||
{
|
||||
struct secret hmac;
|
||||
|
||||
/* E(i) */
|
||||
ecdh(blinding, ss);
|
||||
|
||||
/* b(i) = HMAC256("blinded_node_id", ss(i)) * k(i) */
|
||||
subkey_from_hmac("blinded_node_id", ss, &hmac);
|
||||
|
||||
/* We instead tweak the *ephemeral* key from the onion and use
|
||||
* our normal privkey: since hsmd knows only how to ECDH with
|
||||
* our real key */
|
||||
return secp256k1_ec_pubkey_tweak_mul(secp256k1_ctx,
|
||||
&onion_key->pubkey,
|
||||
hmac.data) == 1;
|
||||
}
|
||||
|
||||
static struct tlv_encmsg_tlvs *decrypt_encmsg(const tal_t *ctx,
|
||||
const struct pubkey *blinding,
|
||||
const struct secret *ss,
|
||||
const u8 *enctlv)
|
||||
{
|
||||
struct secret rho;
|
||||
u8 *dec;
|
||||
const u8 *cursor;
|
||||
size_t maxlen;
|
||||
struct tlv_encmsg_tlvs *encmsg;
|
||||
/* All-zero npub */
|
||||
static const unsigned char npub[crypto_aead_chacha20poly1305_ietf_NPUBBYTES];
|
||||
|
||||
/* We need this to decrypt enctlv */
|
||||
subkey_from_hmac("rho", ss, &rho);
|
||||
|
||||
/* BOLT-onion-message #4:
|
||||
* - if `enctlv` is not present, or does not decrypt with the
|
||||
* shared secret from the given `blinding` parameter:
|
||||
* - MUST drop the message.
|
||||
*/
|
||||
/* Too short? */
|
||||
if (tal_bytelen(enctlv) < crypto_aead_chacha20poly1305_ietf_ABYTES)
|
||||
return NULL;
|
||||
|
||||
dec = tal_arr(tmpctx, u8, tal_bytelen(enctlv)
|
||||
- crypto_aead_chacha20poly1305_ietf_ABYTES);
|
||||
if (crypto_aead_chacha20poly1305_ietf_decrypt(dec, NULL,
|
||||
NULL,
|
||||
enctlv, tal_bytelen(enctlv),
|
||||
NULL, 0,
|
||||
npub,
|
||||
rho.data) != 0)
|
||||
return NULL;
|
||||
|
||||
cursor = dec;
|
||||
maxlen = tal_bytelen(dec);
|
||||
|
||||
/* BOLT-onion-message #4:
|
||||
*
|
||||
* - if the `enctlv` is not a valid TLV...
|
||||
* - MUST drop the message.
|
||||
*/
|
||||
encmsg = tlv_encmsg_tlvs_new(ctx);
|
||||
if (!fromwire_encmsg_tlvs(&cursor, &maxlen, encmsg)
|
||||
|| !tlv_fields_valid(encmsg->fields, NULL, NULL))
|
||||
return tal_free(encmsg);
|
||||
|
||||
return encmsg;
|
||||
}
|
||||
|
||||
bool decrypt_enctlv(const struct pubkey *blinding,
|
||||
const struct secret *ss,
|
||||
const u8 *enctlv,
|
||||
struct pubkey *next_node,
|
||||
struct pubkey *next_blinding)
|
||||
{
|
||||
struct tlv_encmsg_tlvs *encmsg;
|
||||
|
||||
encmsg = decrypt_encmsg(tmpctx, blinding, ss, enctlv);
|
||||
if (!encmsg)
|
||||
return false;
|
||||
|
||||
/* BOLT-onion-message #4:
|
||||
*
|
||||
* The reader:
|
||||
* - if it is not the final node according to the onion encryption:
|
||||
*...
|
||||
* - if the `enctlv` ... does not contain
|
||||
* `next_node_id`:
|
||||
* - MUST drop the message.
|
||||
*/
|
||||
if (!encmsg->next_node_id)
|
||||
return false;
|
||||
|
||||
/* BOLT-onion-message #4:
|
||||
* The reader:
|
||||
* - if it is not the final node according to the onion encryption:
|
||||
*...
|
||||
* - if the `enctlv` contains `self_id`:
|
||||
* - MUST drop the message.
|
||||
*/
|
||||
if (encmsg->self_id)
|
||||
return false;
|
||||
|
||||
/* BOLT-onion-message #4:
|
||||
* The reader:
|
||||
* - if it is not the final node according to the onion encryption:
|
||||
*...
|
||||
* - if `blinding` is specified in the `enctlv`:
|
||||
* - MUST pass that as `blinding` in the `onion_message`
|
||||
* - otherwise:
|
||||
* - MUST pass `blinding` derived as in
|
||||
* [Route Blinding][route-blinding] (i.e.
|
||||
* `E(i+1) = H(E(i) || ss(i)) * E(i)`).
|
||||
*/
|
||||
*next_node = *encmsg->next_node_id;
|
||||
if (encmsg->next_blinding)
|
||||
*next_blinding = *encmsg->next_blinding;
|
||||
else {
|
||||
/* E(i-1) = H(E(i) || ss(i)) * E(i) */
|
||||
struct sha256 h;
|
||||
blinding_hash_e_and_ss(blinding, ss, &h);
|
||||
blinding_next_pubkey(blinding, &h, next_blinding);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool decrypt_final_enctlv(const tal_t *ctx,
|
||||
const struct pubkey *blinding,
|
||||
const struct secret *ss,
|
||||
const u8 *enctlv,
|
||||
const struct pubkey *my_id,
|
||||
struct pubkey *alias,
|
||||
struct secret **self_id)
|
||||
{
|
||||
struct tlv_encmsg_tlvs *encmsg;
|
||||
struct secret node_id_blinding;
|
||||
|
||||
/* Repeat the tweak to get the alias it was using for us */
|
||||
subkey_from_hmac("blinded_node_id", ss, &node_id_blinding);
|
||||
*alias = *my_id;
|
||||
if (secp256k1_ec_pubkey_tweak_mul(secp256k1_ctx,
|
||||
&alias->pubkey,
|
||||
node_id_blinding.data) != 1)
|
||||
return false;
|
||||
|
||||
encmsg = decrypt_encmsg(tmpctx, blinding, ss, enctlv);
|
||||
if (!encmsg)
|
||||
return false;
|
||||
|
||||
if (tal_bytelen(encmsg->self_id) == sizeof(**self_id)) {
|
||||
*self_id = tal(ctx, struct secret);
|
||||
memcpy(*self_id, encmsg->self_id, sizeof(**self_id));
|
||||
} else
|
||||
*self_id = NULL;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
u8 *create_enctlv(const tal_t *ctx,
|
||||
const struct privkey *blinding,
|
||||
const struct pubkey *node,
|
||||
|
|
|
@ -59,4 +59,57 @@ u8 *create_final_enctlv(const tal_t *ctx,
|
|||
struct pubkey *node_alias)
|
||||
NON_NULL_ARGS(2, 3, 6);
|
||||
|
||||
/**
|
||||
* unblind_onion - tweak onion epheremeral key so we can decode it with ours.
|
||||
* @blinding: E(i), the blinding pubkey the previous peer gave us.
|
||||
* @ecdh: the ecdh routine (usually ecdh from common/ecdh_hsmd).
|
||||
* @onion_key: (in, out) the onionpacket->ephemeralkey to tweak.
|
||||
* @ss: (out) the shared secret we gained from blinding pubkey.
|
||||
*
|
||||
* The shared secret is needed to decrypt the enctlv we expect to find, too.
|
||||
*/
|
||||
bool unblind_onion(const struct pubkey *blinding,
|
||||
void (*ecdh)(const struct pubkey *point, struct secret *ss),
|
||||
struct pubkey *onion_key,
|
||||
struct secret *ss)
|
||||
NO_NULL_ARGS;
|
||||
|
||||
/**
|
||||
* decrypt_enctlv - Decrypt an encmsg to form an enctlv.
|
||||
* @blinding: E(i), the blinding pubkey the previous peer gave us.
|
||||
* @ss: the blinding secret from unblind_onion().
|
||||
* @enctlv: the enctlv from the onion (tal, may be NULL).
|
||||
* @next_node: (out) the next node_id.
|
||||
* @next_blinding: (out) the next blinding E(i+1).
|
||||
*
|
||||
* Returns false if decryption failed or encmsg was malformed.
|
||||
*/
|
||||
bool decrypt_enctlv(const struct pubkey *blinding,
|
||||
const struct secret *ss,
|
||||
const u8 *enctlv,
|
||||
struct pubkey *next_node,
|
||||
struct pubkey *next_blinding)
|
||||
NON_NULL_ARGS(1, 2, 4, 5);
|
||||
|
||||
/**
|
||||
* decrypt_final_enctlv - Decrypt an encmsg to form an enctlv.
|
||||
* @ctx: tal context for @self_id
|
||||
* @blinding: E(i), the blinding pubkey the previous peer gave us.
|
||||
* @ss: the blinding secret from unblind_onion().
|
||||
* @enctlv: the enctlv from the onion (tal, may be NULL).
|
||||
* @my_id: the pubkey of this node.
|
||||
* @alias: (out) the node_id this was addressed to.
|
||||
* @self_id: (out) the secret contained in the enctlv, if any.
|
||||
*
|
||||
* Returns false if decryption failed or encmsg was malformed.
|
||||
*/
|
||||
bool decrypt_final_enctlv(const tal_t *ctx,
|
||||
const struct pubkey *blinding,
|
||||
const struct secret *ss,
|
||||
const u8 *enctlv,
|
||||
const struct pubkey *my_id,
|
||||
struct pubkey *alias,
|
||||
struct secret **self_id)
|
||||
NON_NULL_ARGS(1, 2, 4, 5);
|
||||
|
||||
#endif /* LIGHTNING_COMMON_BLINDEDPATH_H */
|
||||
|
|
|
@ -60,10 +60,64 @@ static void json_strfield(const char *name, const char *val)
|
|||
printf("\t\"%s\": \"%s\",\n", name, val);
|
||||
}
|
||||
|
||||
/* Updated each time, as we pretend to be Alice, Bob, Carol */
|
||||
static const struct privkey *mykey;
|
||||
|
||||
static void test_ecdh(const struct pubkey *point, struct secret *ss)
|
||||
{
|
||||
if (secp256k1_ecdh(secp256k1_ctx, ss->data, &point->pubkey,
|
||||
mykey->secret.data, NULL, NULL) != 1)
|
||||
abort();
|
||||
}
|
||||
|
||||
static void test_decrypt(const struct pubkey *blinding,
|
||||
const u8 *enctlv,
|
||||
const struct privkey *me,
|
||||
const struct pubkey *expected_next_node,
|
||||
const struct privkey *expected_next_blinding_priv)
|
||||
{
|
||||
struct pubkey expected_next_blinding, dummy, next_node, next_blinding;
|
||||
struct secret ss;
|
||||
|
||||
/* We don't actually have an onion, so we put some dummy */
|
||||
pubkey_from_privkey(me, &dummy);
|
||||
|
||||
mykey = me;
|
||||
assert(unblind_onion(blinding, test_ecdh, &dummy, &ss));
|
||||
assert(decrypt_enctlv(blinding, &ss, enctlv, &next_node, &next_blinding));
|
||||
|
||||
pubkey_from_privkey(expected_next_blinding_priv, &expected_next_blinding);
|
||||
assert(pubkey_eq(&next_blinding, &expected_next_blinding));
|
||||
assert(pubkey_eq(&next_node, expected_next_node));
|
||||
}
|
||||
|
||||
static void test_final_decrypt(const struct pubkey *blinding,
|
||||
const u8 *enctlv,
|
||||
const struct privkey *me,
|
||||
const struct pubkey *expected_alias,
|
||||
const struct secret *expected_self_id)
|
||||
{
|
||||
struct pubkey my_pubkey, dummy, alias;
|
||||
struct secret ss, *self_id;
|
||||
|
||||
/* We don't actually have an onion, so we put some dummy */
|
||||
pubkey_from_privkey(me, &dummy);
|
||||
|
||||
mykey = me;
|
||||
pubkey_from_privkey(me, &my_pubkey);
|
||||
assert(unblind_onion(blinding, test_ecdh, &dummy, &ss));
|
||||
assert(decrypt_final_enctlv(tmpctx, blinding, &ss, enctlv, &my_pubkey,
|
||||
&alias, &self_id));
|
||||
|
||||
assert(pubkey_eq(&alias, expected_alias));
|
||||
assert(secret_eq_consttime(self_id, expected_self_id));
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct privkey alice, bob, carol, dave, blinding, override_blinding;
|
||||
struct pubkey alice_id, bob_id, carol_id, dave_id, blinding_pub, override_blinding_pub, alias;
|
||||
struct secret self_id;
|
||||
u8 *enctlv;
|
||||
|
||||
common_setup(argv[0]);
|
||||
|
@ -102,6 +156,8 @@ int main(int argc, char *argv[])
|
|||
"},\n",
|
||||
tal_hex(tmpctx, enctlv));
|
||||
|
||||
test_decrypt(&blinding_pub, enctlv, &alice, &bob_id, &blinding);
|
||||
|
||||
pubkey_from_privkey(&blinding, &blinding_pub);
|
||||
memset(&override_blinding, 7, sizeof(override_blinding));
|
||||
pubkey_from_privkey(&override_blinding, &override_blinding_pub);
|
||||
|
@ -130,6 +186,8 @@ int main(int argc, char *argv[])
|
|||
"},\n",
|
||||
tal_hex(tmpctx, enctlv));
|
||||
|
||||
test_decrypt(&blinding_pub, enctlv, &bob, &carol_id, &override_blinding);
|
||||
|
||||
/* That replaced the blinding */
|
||||
blinding = override_blinding;
|
||||
blinding_pub = override_blinding_pub;
|
||||
|
@ -157,5 +215,15 @@ int main(int argc, char *argv[])
|
|||
"}]\n",
|
||||
tal_hex(tmpctx, enctlv));
|
||||
|
||||
test_decrypt(&blinding_pub, enctlv, &carol, &dave_id, &blinding);
|
||||
|
||||
/* FIXME: Add this to the test vectors! */
|
||||
fclose(stdout);
|
||||
memset(&self_id, 0x77, sizeof(self_id));
|
||||
enctlv = create_final_enctlv(tmpctx, &blinding, &dave_id,
|
||||
0, &self_id, &alias);
|
||||
|
||||
pubkey_from_privkey(&blinding, &blinding_pub);
|
||||
test_final_decrypt(&blinding_pub, enctlv, &dave, &alias, &self_id);
|
||||
common_shutdown();
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue