common/sphinx: don't create unused keys, fill in BOLT quotes.
What changed, and why it matters
This is a small code cleanup in the onion routing (sphinx) code. It removes an unused internal key container and adds explanatory quotes from the BOLT specification. There is no functional change to how packets are encrypted or verified, and no security bug is fixed.
No security action required; treat as normal maintenance/refactoring.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit refactors common/sphinx.c: it deletes the struct keyset and generate_key_set() helper, and instead derives only the rho and mu subkeys inline where they are actually used. Previously pi and gamma were derived but never consumed. The diff also inserts BOLT #4 specification comments around the create_onionpacket and process_onionpacket loops. The cryptographic operations and their order remain identical.
Changed components
common/sphinx.cInspect captured patch +49 / −23
diff --git a/common/sphinx.c b/common/sphinx.c
index 66074296..b8497b63 100644
--- a/common/sphinx.c
+++ b/common/sphinx.c
@@ -24,10 +24,6 @@ struct hop_params {
struct pubkey ephemeralkey;
};
-struct keyset {
- struct secret pi, mu, rho, gamma;
-};
-
/* Encapsulates the information about a given payment path for the the onion
* routing algorithm.
*/
@@ -420,15 +416,6 @@ bool onion_shared_secret(
&privkey->secret);
}
-static void generate_key_set(const struct secret *secret,
- struct keyset *keys)
-{
- subkey_from_hmac("rho", secret, &keys->rho);
- subkey_from_hmac("pi", secret, &keys->pi);
- subkey_from_hmac("mu", secret, &keys->mu);
- subkey_from_hmac("gamma", secret, &keys->gamma);
-}
-
static struct hop_params *generate_hop_params(
const tal_t *ctx,
const u8 *sessionkey,
@@ -548,7 +535,6 @@ struct onionpacket *create_onionpacket(
size_t fillerSize = sphinx_path_payloads_size(sp) -
sphinx_hop_size(&sp->hops[num_hops - 1]);
u8 *filler;
- struct keyset keys;
struct secret padkey;
struct hmac nexthmac;
struct hop_params *params;
@@ -596,22 +582,48 @@ struct onionpacket *create_onionpacket(
sphinx_prefill(packet->routinginfo, sp, max_prefill, params,
fixed_size);
+ /* BOLT #4:
+ * For each hop in the route, in reverse order, the sender applies the
+ * following operations:
+ * - The _rho_-key and _mu_-key are generated using the hop's shared secret.
+ * - `shift_size` is defined as the length of the `hop_payload` plus the
+ * bigsize encoding of the length and the length of that HMAC. Thus
+ * if the payload length is `l` then the `shift_size` is `1 + l + 32`
+ * for `l < 253`, otherwise `3 + l + 32` due to the bigsize encoding of `l`.
+ * - The `hop_payload` field is right-shifted by `shift_size` bytes,
+ * discarding the last `shift_size` bytes that exceed its 1300-byte size.
+ * - The bigsize-serialized length, serialized `hop_payload` and `hmac`
+ * are copied into the following `shift_size` bytes.
+ * - The _rho_-key is used to generate 1300 bytes of pseudo-random byte stream
+ * which is then applied, with `XOR`, to the `hop_payloads` field.
+ */
for (i = num_hops - 1; i >= 0; i--) {
- generate_key_set(¶ms[i].secret, &keys);
-
+ struct secret rho, mu;
/* Rightshift mix-header by FRAME_SIZE */
size_t shiftSize = sphinx_hop_size(&sp->hops[i]);
memmove(packet->routinginfo + shiftSize, packet->routinginfo,
fixed_size - shiftSize);
sphinx_write_frame(packet->routinginfo, &sp->hops[i], &nexthmac);
- xor_cipher_stream(packet->routinginfo, &keys.rho,
- fixed_size);
+ subkey_from_hmac("rho", ¶ms[i].secret, &rho);
+ xor_cipher_stream(packet->routinginfo, &rho, fixed_size);
+
+ /* BOLT #4:
+ *...
+ * - If this is the last hop, i.e. the first iteration, then the tail of the
+ * `hop_payloads` field is overwritten with the routing information `filler`.
+ */
if (i == num_hops - 1) {
memcpy(packet->routinginfo + fixed_size - fillerSize, filler, fillerSize);
}
- compute_packet_hmac(packet, sp->associated_data, tal_bytelen(sp->associated_data), &keys.mu,
+ /* BOLT #4:
+ *...
+ * - The next HMAC is computed (with the _mu_-key as HMAC-key) over the
+ * concatenated `hop_payloads` and associated data.
+ */
+ subkey_from_hmac("mu", ¶ms[i].secret, &mu);
+ compute_packet_hmac(packet, sp->associated_data, tal_bytelen(sp->associated_data), &mu,
&nexthmac);
}
packet->hmac = nexthmac;
@@ -641,7 +653,7 @@ struct route_step *process_onionpacket(
{
struct route_step *step = talz(ctx, struct route_step);
struct hmac hmac;
- struct keyset keys;
+ struct secret mu, rho;
u8 blind[BLINDING_FACTOR_SIZE];
u8 *paddedheader;
size_t payload_size;
@@ -651,19 +663,33 @@ struct route_step *process_onionpacket(
step->next = talz(step, struct onionpacket);
step->next->version = msg->version;
- generate_key_set(shared_secret, &keys);
- compute_packet_hmac(msg, assocdata, assocdatalen, &keys.mu, &hmac);
+ /* BOLT #4:
+ * - Derive `mu` as $`HMAC256(\text{"mu"}, ss)`$
+ * (see [Key Generation](#key-generation)).
+ * - Derive the HMAC as $`HMAC256(mu, hop\_payloads || associated\_data)`$.
+ */
+ subkey_from_hmac("mu", shared_secret, &mu);
+ compute_packet_hmac(msg, assocdata, assocdatalen, &mu, &hmac);
if (!hmac_eq(&msg->hmac, &hmac) || dev_fail_process_onionpacket) {
/* Computed MAC does not match expected MAC, the message was modified. */
return tal_free(step);
}
+ /* BOLT #4:
+ * - Derive `rho` as $`HMAC256(\text{"rho"}, ss)`$
+ * (see [Key Generation](#key-generation)).
+ * - Derive `bytestream` of twice the length of `hop_payloads` using `rho`
+ * (see [Pseudo Random Byte Stream](pseudo-random-byte-stream)).
+ * - Set `unwrapped_payloads` to the XOR of `hop_payloads` and `bytestream`.
+ */
+ subkey_from_hmac("rho", shared_secret, &rho);
+
//FIXME:store seen secrets to avoid replay attacks
paddedheader = tal_arrz(step, u8, tal_bytelen(msg->routinginfo)*2);
memcpy(paddedheader, msg->routinginfo, tal_bytelen(msg->routinginfo));
- xor_cipher_stream(paddedheader, &keys.rho, tal_bytelen(paddedheader));
+ xor_cipher_stream(paddedheader, &rho, tal_bytelen(paddedheader));
compute_blinding_factor(&msg->ephemeralkey, shared_secret, blind);
if (!blind_group_element(&step->next->ephemeralkey, &msg->ephemeralkey, blind))
Why this scored 13/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.