mirror of
https://github.com/LBRYFoundation/LBRY-Vault.git
synced 2025-09-12 13:39:52 +00:00
bolt-04: decryption of errors
This commit is contained in:
parent
5da3820a28
commit
5b1da26041
2 changed files with 76 additions and 5 deletions
|
@ -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:
|
||||||
|
|
|
@ -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…
Add table
Reference in a new issue