lnonion: factor out next_blinding_from_shared_secret
What changed, and why it matters
This commit is a simple code cleanup: it extracts a small, repeated calculation into a shared helper function. The math used to derive the next blinding key is unchanged, and there is no indication of a security fix or behavior change.
No security action required; treat as routine refactoring.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change factors out the BOLT 4-style next ephemeral/blinding key derivation (E_i+1 = SHA256(E_i || ss_i) * E_i) from two call sites in lnonion.py and onion_message.py into a new shared function next_blinding_from_shared_secret(). The implementation is identical: sha256 of the public key concatenated with the shared secret, interpreted as a big-endian integer, multiplied against the ECPubkey. No constants, control flow, or cryptographic semantics changed.
Changed components
electrum/lnonion.pyelectrum/onion_message.pyInspect captured patch +12 / −10
diff --git a/electrum/lnonion.py b/electrum/lnonion.py
index 25d2686..e3deb45 100644
--- a/electrum/lnonion.py
+++ b/electrum/lnonion.py
@@ -204,6 +204,14 @@ def blinding_privkey(privkey: bytes, blinding: bytes) -> bytes:
return our_privkey
+def next_blinding_from_shared_secret(pubkey: bytes, shared_secret: bytes) -> bytes:
+ # E_i+1=SHA256(E_i||ss_i) * E_i
+ blinding_factor = sha256(pubkey + shared_secret)
+ blinding_factor_int = int.from_bytes(blinding_factor, byteorder="big")
+ next_public_key_int = ecc.ECPubkey(pubkey) * blinding_factor_int
+ return next_public_key_int.get_public_key_bytes()
+
+
def new_onion_packet(
payment_path_pubkeys: Sequence[bytes],
session_key: bytes,
@@ -454,10 +462,7 @@ def process_onion_packet(
trampoline_onion_packet = trampoline_onion_packet['trampoline_onion_packet']
trampoline_onion_packet = OnionPacket.from_bytes(trampoline_onion_packet)
# calc next ephemeral key
- blinding_factor = sha256(onion_packet.public_key + shared_secret)
- blinding_factor_int = int.from_bytes(blinding_factor, byteorder="big")
- next_public_key_int = ecc.ECPubkey(onion_packet.public_key) * blinding_factor_int
- next_public_key = next_public_key_int.get_public_key_bytes()
+ next_public_key = next_blinding_from_shared_secret(onion_packet.public_key, shared_secret)
next_onion_packet = OnionPacket(
public_key=next_public_key,
hops_data=next_hops_data_fd.read(data_size),
diff --git a/electrum/onion_message.py b/electrum/onion_message.py
index 85b69f2..c291f99 100644
--- a/electrum/onion_message.py
+++ b/electrum/onion_message.py
@@ -39,7 +39,8 @@ from electrum.crypto import sha256, get_ecdh
from electrum.lnmsg import OnionWireSerializer
from electrum.lnonion import (get_bolt04_onion_key, OnionPacket, process_onion_packet, blinding_privkey,
OnionHopsDataSingle, decrypt_onionmsg_data_tlv, encrypt_onionmsg_data_tlv,
- get_shared_secrets_along_route, new_onion_packet, encrypt_hops_recipient_data)
+ get_shared_secrets_along_route, new_onion_packet, encrypt_hops_recipient_data,
+ next_blinding_from_shared_secret)
from electrum.lnutil import LnFeatures, MIN_FINAL_CLTV_DELTA_ACCEPTED, MAXIMUM_REMOTE_TO_SELF_DELAY_ACCEPTED
from electrum.util import OldTaskGroup, log_exceptions, random_shuffled_copy
@@ -297,11 +298,7 @@ def send_onion_message_to(
if next_path_key_override:
next_path_key = next_path_key_override.get('path_key')
else:
- # E_i+1=SHA256(E_i||ss_i) * E_i
- blinding_factor = sha256(our_blinding + shared_secret)
- blinding_factor_int = int.from_bytes(blinding_factor, byteorder="big")
- next_public_key_int = ecc.ECPubkey(our_blinding) * blinding_factor_int
- next_path_key = next_public_key_int.get_public_key_bytes()
+ next_path_key = next_blinding_from_shared_secret(our_blinding, shared_secret)
path_key = next_path_key
Why this scored 15/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.