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:
Rusty Russell 2021-09-22 06:47:24 +09:30
parent b98e8a099d
commit 3a966191b8
3 changed files with 282 additions and 0 deletions

View file

@ -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,

View file

@ -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 */

View file

@ -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();
}