Browse Source

bolt-04: decryption of errors

regtest_lnd
SomberNight 7 years ago
parent
commit
9de5ead368
No known key found for this signature in database GPG Key ID: B33B5F232C6271E9
  1. 65
      lib/lnbase.py
  2. 16
      lib/tests/test_lnbase.py

65
lib/lnbase.py

@ -18,7 +18,7 @@ import time
import binascii import binascii
import hashlib import hashlib
import hmac import hmac
from typing import Sequence from typing import Sequence, Union
import cryptography.hazmat.primitives.ciphers.aead as AEAD import cryptography.hazmat.primitives.ciphers.aead as AEAD
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms from cryptography.hazmat.primitives.ciphers import Cipher, algorithms
from cryptography.hazmat.backends import default_backend from cryptography.hazmat.backends import default_backend
@ -1617,18 +1617,17 @@ class OnionPacket:
def get_bolt04_onion_key(key_type: bytes, secret: bytes) -> bytes: def get_bolt04_onion_key(key_type: bytes, secret: bytes) -> bytes:
if key_type not in (b'rho', b'mu', b'um'): if key_type not in (b'rho', b'mu', b'um', b'ammag'):
raise Exception('invalid key_type {}'.format(key_type)) raise Exception('invalid key_type {}'.format(key_type))
key = hmac.new(key_type, msg=secret, digestmod=hashlib.sha256).digest() key = hmac.new(key_type, msg=secret, digestmod=hashlib.sha256).digest()
return key return key
def new_onion_packet(payment_path_pubkeys: Sequence[bytes], session_key: bytes, def get_shared_secrets_along_route(payment_path_pubkeys: Sequence[bytes],
hops_data: Sequence[OnionHopsDataSingle], associated_data: bytes) -> OnionPacket: session_key: bytes) -> Sequence[bytes]:
num_hops = len(payment_path_pubkeys) num_hops = len(payment_path_pubkeys)
hop_shared_secrets = num_hops * [b''] hop_shared_secrets = num_hops * [b'']
ephemeral_key = session_key ephemeral_key = session_key
# compute shared key for each hop # compute shared key for each hop
for i in range(0, num_hops): for i in range(0, num_hops):
hop_shared_secrets[i] = get_ecdh(ephemeral_key, payment_path_pubkeys[i]) hop_shared_secrets[i] = get_ecdh(ephemeral_key, payment_path_pubkeys[i])
@ -1638,6 +1637,13 @@ def new_onion_packet(payment_path_pubkeys: Sequence[bytes], session_key: bytes,
ephemeral_key_int = int.from_bytes(ephemeral_key, byteorder="big") ephemeral_key_int = int.from_bytes(ephemeral_key, byteorder="big")
ephemeral_key_int = ephemeral_key_int * blinding_factor_int % SECP256k1.order ephemeral_key_int = ephemeral_key_int * blinding_factor_int % SECP256k1.order
ephemeral_key = ephemeral_key_int.to_bytes(32, byteorder="big") ephemeral_key = ephemeral_key_int.to_bytes(32, byteorder="big")
return hop_shared_secrets
def new_onion_packet(payment_path_pubkeys: Sequence[bytes], session_key: bytes,
hops_data: Sequence[OnionHopsDataSingle], associated_data: bytes) -> OnionPacket:
num_hops = len(payment_path_pubkeys)
hop_shared_secrets = get_shared_secrets_along_route(payment_path_pubkeys, session_key)
filler = generate_filler(b'rho', num_hops, PER_HOP_FULL_SIZE, hop_shared_secrets) filler = generate_filler(b'rho', num_hops, PER_HOP_FULL_SIZE, hop_shared_secrets)
mix_header = bytes(HOPS_DATA_SIZE) mix_header = bytes(HOPS_DATA_SIZE)
@ -1726,6 +1732,55 @@ def process_onion_packet(onion_packet: OnionPacket, associated_data: bytes,
are_we_final = False are_we_final = False
return ProcessedOnionPacket(are_we_final, hop_data, next_onion_packet) return ProcessedOnionPacket(are_we_final, hop_data, next_onion_packet)
class FailedToDecodeOnionError(Exception): pass
class OnionRoutingFailureMessage:
def __init__(self, code: int, data: bytes):
self.code = code
self.data = data
def _decode_onion_error(error_packet: bytes, payment_path_pubkeys: Sequence[bytes],
session_key: bytes) -> (bytes, int):
"""Returns the decoded error bytes, and the index of the sender of the error."""
num_hops = len(payment_path_pubkeys)
hop_shared_secrets = get_shared_secrets_along_route(payment_path_pubkeys, session_key)
for i in range(num_hops):
ammag_key = get_bolt04_onion_key(b'ammag', hop_shared_secrets[i])
um_key = get_bolt04_onion_key(b'um', hop_shared_secrets[i])
stream_bytes = generate_cipher_stream(ammag_key, len(error_packet))
error_packet = xor_bytes(error_packet, stream_bytes)
hmac_computed = hmac.new(um_key, msg=error_packet[32:], digestmod=hashlib.sha256).digest()
hmac_found = error_packet[:32]
if hmac_computed == hmac_found:
return error_packet, i
raise FailedToDecodeOnionError()
def decode_onion_error(error_packet: bytes, payment_path_pubkeys: Sequence[bytes],
session_key: bytes) -> (OnionRoutingFailureMessage, int):
"""Returns the failure message, and the index of the sender of the error."""
decrypted_error, sender_index = _decode_onion_error(error_packet, payment_path_pubkeys, session_key)
failure_msg = get_failure_msg_from_onion_error(decrypted_error)
return failure_msg, sender_index
def get_failure_msg_from_onion_error(decrypted_error_packet: bytes) -> OnionRoutingFailureMessage:
# get failure_msg bytes from error packet
failure_len = int.from_bytes(decrypted_error_packet[32:34], byteorder='big')
failure_msg = decrypted_error_packet[34:34+failure_len]
# create failure message object
failure_code = int.from_bytes(failure_msg[:2], byteorder='big')
failure_data = failure_msg[2:]
return OnionRoutingFailureMessage(failure_code, failure_data)
# <----- bolt 04, "onion"
def count_trailing_zeros(index): def count_trailing_zeros(index):
""" BOLT-03 (where_to_put_secret) """ """ BOLT-03 (where_to_put_secret) """
try: try:

16
lib/tests/test_lnbase.py

@ -385,6 +385,22 @@ class Test_LNBase(unittest.TestCase):
self.assertEqual(hops_data[i].per_hop.to_bytes(), processed_packet.hop_data.per_hop.to_bytes()) self.assertEqual(hops_data[i].per_hop.to_bytes(), processed_packet.hop_data.per_hop.to_bytes())
packet = processed_packet.next_packet packet = processed_packet.next_packet
def test_decode_onion_error(self):
# test vector from bolt-04
payment_path_pubkeys = [
bfh('02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619'),
bfh('0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c'),
bfh('027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007'),
bfh('032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991'),
bfh('02edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145'),
]
session_key = bfh('4141414141414141414141414141414141414141414141414141414141414141')
error_packet_for_node_0 = bfh('9c5add3963fc7f6ed7f148623c84134b5647e1306419dbe2174e523fa9e2fbed3a06a19f899145610741c83ad40b7712aefaddec8c6baf7325d92ea4ca4d1df8bce517f7e54554608bf2bd8071a4f52a7a2f7ffbb1413edad81eeea5785aa9d990f2865dc23b4bc3c301a94eec4eabebca66be5cf638f693ec256aec514620cc28ee4a94bd9565bc4d4962b9d3641d4278fb319ed2b84de5b665f307a2db0f7fbb757366067d88c50f7e829138fde4f78d39b5b5802f1b92a8a820865af5cc79f9f30bc3f461c66af95d13e5e1f0381c184572a91dee1c849048a647a1158cf884064deddbf1b0b88dfe2f791428d0ba0f6fb2f04e14081f69165ae66d9297c118f0907705c9c4954a199bae0bb96fad763d690e7daa6cfda59ba7f2c8d11448b604d12d')
decoded_error, index_of_sender = lnbase._decode_onion_error(error_packet_for_node_0, payment_path_pubkeys, session_key)
self.assertEqual(bfh('4c2fc8bc08510334b6833ad9c3e79cd1b52ae59dfe5c2a4b23ead50f09f7ee0b0002200200fe0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'),
decoded_error)
self.assertEqual(4, index_of_sender)
def test_shachain_store(self): def test_shachain_store(self):
tests = [ tests = [
{ {

Loading…
Cancel
Save