Migrated all asymmetric crypto operations to ECIES on Curve25519.
This commit is contained in:
parent
7f5625a526
commit
ce405b9252
|
@ -39,7 +39,7 @@ def program_setup(configpath, channel=None):
|
||||||
|
|
||||||
# We specify a callback that will get called every time
|
# We specify a callback that will get called every time
|
||||||
# the destination receives data.
|
# the destination receives data.
|
||||||
broadcast_destination.packet_callback(packet_callback)
|
broadcast_destination.set_packet_callback(packet_callback)
|
||||||
|
|
||||||
# Everything's ready!
|
# Everything's ready!
|
||||||
# Let's hand over control to the main loop
|
# Let's hand over control to the main loop
|
||||||
|
|
|
@ -52,7 +52,7 @@ def server(configpath):
|
||||||
# Tell the destination which function in our program to
|
# Tell the destination which function in our program to
|
||||||
# run when a packet is received. We do this so we can
|
# run when a packet is received. We do this so we can
|
||||||
# print a log message when the server receives a request
|
# print a log message when the server receives a request
|
||||||
echo_destination.packet_callback(server_callback)
|
echo_destination.set_packet_callback(server_callback)
|
||||||
|
|
||||||
# Everything's ready!
|
# Everything's ready!
|
||||||
# Let's Wait for client requests or user input
|
# Let's Wait for client requests or user input
|
||||||
|
@ -175,7 +175,7 @@ def client(destination_hexhash, configpath, timeout=None):
|
||||||
# We can then set a delivery callback on the receipt.
|
# We can then set a delivery callback on the receipt.
|
||||||
# This will get automatically called when a proof for
|
# This will get automatically called when a proof for
|
||||||
# this specific packet is received from the destination.
|
# this specific packet is received from the destination.
|
||||||
packet_receipt.delivery_callback(packet_delivered)
|
packet_receipt.set_delivery_callback(packet_delivered)
|
||||||
|
|
||||||
# Tell the user that the echo request was sent
|
# Tell the user that the echo request was sent
|
||||||
RNS.log("Sent echo request to "+RNS.prettyhexrep(request_destination.hash))
|
RNS.log("Sent echo request to "+RNS.prettyhexrep(request_destination.hash))
|
||||||
|
@ -189,7 +189,7 @@ def client(destination_hexhash, configpath, timeout=None):
|
||||||
# receives a proof packet.
|
# receives a proof packet.
|
||||||
def packet_delivered(receipt):
|
def packet_delivered(receipt):
|
||||||
if receipt.status == RNS.PacketReceipt.DELIVERED:
|
if receipt.status == RNS.PacketReceipt.DELIVERED:
|
||||||
rtt = receipt.rtt()
|
rtt = receipt.get_rtt()
|
||||||
if (rtt >= 1):
|
if (rtt >= 1):
|
||||||
rtt = round(rtt, 3)
|
rtt = round(rtt, 3)
|
||||||
rttstring = str(rtt)+" seconds"
|
rttstring = str(rtt)+" seconds"
|
||||||
|
|
|
@ -65,7 +65,7 @@ def server(configpath, path):
|
||||||
|
|
||||||
# We configure a function that will get called every time
|
# We configure a function that will get called every time
|
||||||
# a new client creates a link to this destination.
|
# a new client creates a link to this destination.
|
||||||
server_destination.link_established_callback(client_connected)
|
server_destination.set_link_established_callback(client_connected)
|
||||||
|
|
||||||
# Everything's ready!
|
# Everything's ready!
|
||||||
# Let's Wait for client requests or user input
|
# Let's Wait for client requests or user input
|
||||||
|
@ -102,7 +102,7 @@ def client_connected(link):
|
||||||
if os.path.isdir(serve_path):
|
if os.path.isdir(serve_path):
|
||||||
RNS.log("Client connected, sending file list...")
|
RNS.log("Client connected, sending file list...")
|
||||||
|
|
||||||
link.link_closed_callback(client_disconnected)
|
link.set_link_closed_callback(client_disconnected)
|
||||||
|
|
||||||
# We pack a list of files for sending in a packet
|
# We pack a list of files for sending in a packet
|
||||||
data = umsgpack.packb(list_files())
|
data = umsgpack.packb(list_files())
|
||||||
|
@ -114,7 +114,7 @@ def client_connected(link):
|
||||||
list_packet = RNS.Packet(link, data)
|
list_packet = RNS.Packet(link, data)
|
||||||
list_receipt = list_packet.send()
|
list_receipt = list_packet.send()
|
||||||
list_receipt.set_timeout(APP_TIMEOUT)
|
list_receipt.set_timeout(APP_TIMEOUT)
|
||||||
list_receipt.delivery_callback(list_delivered)
|
list_receipt.set_delivery_callback(list_delivered)
|
||||||
list_receipt.timeout_callback(list_timeout)
|
list_receipt.timeout_callback(list_timeout)
|
||||||
else:
|
else:
|
||||||
RNS.log("Too many files in served directory!", RNS.LOG_ERROR)
|
RNS.log("Too many files in served directory!", RNS.LOG_ERROR)
|
||||||
|
@ -125,7 +125,7 @@ def client_connected(link):
|
||||||
# open until the client requests a file. We'll
|
# open until the client requests a file. We'll
|
||||||
# configure a function that get's called when
|
# configure a function that get's called when
|
||||||
# the client sends a packet with a file request.
|
# the client sends a packet with a file request.
|
||||||
link.packet_callback(client_request)
|
link.set_packet_callback(client_request)
|
||||||
else:
|
else:
|
||||||
RNS.log("Client connected, but served path no longer exists!", RNS.LOG_ERROR)
|
RNS.log("Client connected, but served path no longer exists!", RNS.LOG_ERROR)
|
||||||
link.teardown()
|
link.teardown()
|
||||||
|
@ -254,18 +254,18 @@ def client(destination_hexhash, configpath):
|
||||||
# We expect any normal data packets on the link
|
# We expect any normal data packets on the link
|
||||||
# to contain a list of served files, so we set
|
# to contain a list of served files, so we set
|
||||||
# a callback accordingly
|
# a callback accordingly
|
||||||
link.packet_callback(filelist_received)
|
link.set_packet_callback(filelist_received)
|
||||||
|
|
||||||
# We'll also set up functions to inform the
|
# We'll also set up functions to inform the
|
||||||
# user when the link is established or closed
|
# user when the link is established or closed
|
||||||
link.link_established_callback(link_established)
|
link.set_link_established_callback(link_established)
|
||||||
link.link_closed_callback(link_closed)
|
link.set_link_closed_callback(link_closed)
|
||||||
|
|
||||||
# And set the link to automatically begin
|
# And set the link to automatically begin
|
||||||
# downloading advertised resources
|
# downloading advertised resources
|
||||||
link.set_resource_strategy(RNS.Link.ACCEPT_ALL)
|
link.set_resource_strategy(RNS.Link.ACCEPT_ALL)
|
||||||
link.resource_started_callback(download_began)
|
link.set_resource_started_callback(download_began)
|
||||||
link.resource_concluded_callback(download_concluded)
|
link.set_resource_concluded_callback(download_concluded)
|
||||||
|
|
||||||
menu()
|
menu()
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ def server(configpath):
|
||||||
|
|
||||||
# We configure a function that will get called every time
|
# We configure a function that will get called every time
|
||||||
# a new client creates a link to this destination.
|
# a new client creates a link to this destination.
|
||||||
server_destination.link_established_callback(client_connected)
|
server_destination.set_link_established_callback(client_connected)
|
||||||
|
|
||||||
# Everything's ready!
|
# Everything's ready!
|
||||||
# Let's Wait for client requests or user input
|
# Let's Wait for client requests or user input
|
||||||
|
@ -76,8 +76,8 @@ def client_connected(link):
|
||||||
global latest_client_link
|
global latest_client_link
|
||||||
|
|
||||||
RNS.log("Client connected")
|
RNS.log("Client connected")
|
||||||
link.link_closed_callback(client_disconnected)
|
link.set_link_closed_callback(client_disconnected)
|
||||||
link.packet_callback(server_packet_received)
|
link.set_packet_callback(server_packet_received)
|
||||||
latest_client_link = link
|
latest_client_link = link
|
||||||
|
|
||||||
def client_disconnected(link):
|
def client_disconnected(link):
|
||||||
|
@ -149,12 +149,12 @@ def client(destination_hexhash, configpath):
|
||||||
# We set a callback that will get executed
|
# We set a callback that will get executed
|
||||||
# every time a packet is received over the
|
# every time a packet is received over the
|
||||||
# link
|
# link
|
||||||
link.packet_callback(client_packet_received)
|
link.set_packet_callback(client_packet_received)
|
||||||
|
|
||||||
# We'll also set up functions to inform the
|
# We'll also set up functions to inform the
|
||||||
# user when the link is established or closed
|
# user when the link is established or closed
|
||||||
link.link_established_callback(link_established)
|
link.set_link_established_callback(link_established)
|
||||||
link.link_closed_callback(link_closed)
|
link.set_link_closed_callback(link_closed)
|
||||||
|
|
||||||
# Everything is set up, so let's enter a loop
|
# Everything is set up, so let's enter a loop
|
||||||
# for the user to interact with the example
|
# for the user to interact with the example
|
||||||
|
|
|
@ -133,8 +133,8 @@ class Destination:
|
||||||
|
|
||||||
def announce(self, app_data=None, path_response=False):
|
def announce(self, app_data=None, path_response=False):
|
||||||
"""
|
"""
|
||||||
Creates an announce packet for this destination and broadcasts it on
|
Creates an announce packet for this destination and broadcasts it on all
|
||||||
all interfaces. Application specific data can be added to the announce.
|
relevant interfaces. Application specific data can be added to the announce.
|
||||||
|
|
||||||
:param app_data: *bytes* containing the app_data.
|
:param app_data: *bytes* containing the app_data.
|
||||||
:param path_response: Internal flag used by :ref:`RNS.Transport<api-transport>`. Ignore.
|
:param path_response: Internal flag used by :ref:`RNS.Transport<api-transport>`. Ignore.
|
||||||
|
@ -172,7 +172,7 @@ class Destination:
|
||||||
RNS.Packet(self, announce_data, RNS.Packet.ANNOUNCE, context = announce_context).send()
|
RNS.Packet(self, announce_data, RNS.Packet.ANNOUNCE, context = announce_context).send()
|
||||||
|
|
||||||
|
|
||||||
def link_established_callback(self, callback):
|
def set_link_established_callback(self, callback):
|
||||||
"""
|
"""
|
||||||
Registers a function to be called when a link has been established to
|
Registers a function to be called when a link has been established to
|
||||||
this destination.
|
this destination.
|
||||||
|
@ -181,7 +181,7 @@ class Destination:
|
||||||
"""
|
"""
|
||||||
self.callbacks.link_established = callback
|
self.callbacks.link_established = callback
|
||||||
|
|
||||||
def packet_callback(self, callback):
|
def set_packet_callback(self, callback):
|
||||||
"""
|
"""
|
||||||
Registers a function to be called when a packet has been received by
|
Registers a function to be called when a packet has been received by
|
||||||
this destination.
|
this destination.
|
||||||
|
@ -190,7 +190,7 @@ class Destination:
|
||||||
"""
|
"""
|
||||||
self.callbacks.packet = callback
|
self.callbacks.packet = callback
|
||||||
|
|
||||||
def proof_requested_callback(self, callback):
|
def set_proof_requested_callback(self, callback):
|
||||||
"""
|
"""
|
||||||
Registers a function to be called when a proof has been requested for
|
Registers a function to be called when a proof has been requested for
|
||||||
a packet sent to this destination. Allows control over when and if
|
a packet sent to this destination. Allows control over when and if
|
||||||
|
|
289
RNS/Identity.py
289
RNS/Identity.py
|
@ -4,14 +4,15 @@ import os
|
||||||
import RNS
|
import RNS
|
||||||
import time
|
import time
|
||||||
import atexit
|
import atexit
|
||||||
|
import base64
|
||||||
from .vendor import umsgpack as umsgpack
|
from .vendor import umsgpack as umsgpack
|
||||||
from cryptography.hazmat.primitives import hashes
|
|
||||||
from cryptography.hazmat.backends import default_backend
|
from cryptography.hazmat.backends import default_backend
|
||||||
|
from cryptography.hazmat.primitives import hashes
|
||||||
from cryptography.hazmat.primitives import serialization
|
from cryptography.hazmat.primitives import serialization
|
||||||
from cryptography.hazmat.primitives.serialization import load_der_public_key
|
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey, Ed25519PublicKey
|
||||||
from cryptography.hazmat.primitives.serialization import load_der_private_key
|
from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey, X25519PublicKey
|
||||||
from cryptography.hazmat.primitives.asymmetric import rsa
|
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
|
||||||
from cryptography.hazmat.primitives.asymmetric import padding
|
from cryptography.fernet import Fernet
|
||||||
|
|
||||||
class Identity:
|
class Identity:
|
||||||
"""
|
"""
|
||||||
|
@ -19,26 +20,29 @@ class Identity:
|
||||||
for encryption, decryption, signatures and verification, and is the basis
|
for encryption, decryption, signatures and verification, and is the basis
|
||||||
for all encrypted communication over Reticulum networks.
|
for all encrypted communication over Reticulum networks.
|
||||||
|
|
||||||
:param public_only: Specifies whether this destination only holds a public key.
|
:param create_keys: Specifies whether new encryption and signing keys should be generated.
|
||||||
"""
|
"""
|
||||||
KEYSIZE = 1024
|
|
||||||
|
CURVE = "Curve25519"
|
||||||
"""
|
"""
|
||||||
RSA key size in bits.
|
The curve used for Elliptic Curve DH key exchanges
|
||||||
|
"""
|
||||||
|
|
||||||
|
KEYSIZE = 256*2
|
||||||
|
"""
|
||||||
|
X25519 key size in bits. A complete key is the concatenation of a 256 bit encryption key, and a 256 bit signing key.
|
||||||
"""
|
"""
|
||||||
DERKEYSIZE = KEYSIZE+272
|
|
||||||
|
|
||||||
# Non-configurable constants
|
# Non-configurable constants
|
||||||
PADDINGSIZE = 336 # In bits
|
AES_HMAC_OVERHEAD = 58 # In bytes
|
||||||
HASHLENGTH = 256 # In bits
|
AES128_BLOCKSIZE = 16 # In bytes
|
||||||
SIGLENGTH = KEYSIZE
|
HASHLENGTH = 256 # In bits
|
||||||
|
SIGLENGTH = KEYSIZE # In bits
|
||||||
ENCRYPT_CHUNKSIZE = (KEYSIZE-PADDINGSIZE)//8
|
|
||||||
DECRYPT_CHUNKSIZE = KEYSIZE//8
|
|
||||||
|
|
||||||
TRUNCATED_HASHLENGTH = 80 # In bits
|
TRUNCATED_HASHLENGTH = 80 # In bits
|
||||||
"""
|
"""
|
||||||
Constant specifying the truncated hash length (in bits) used by Reticulum
|
Constant specifying the truncated hash length (in bits) used by Reticulum
|
||||||
for addressable hashes. Non-configurable.
|
for addressable hashes and other purposes. Non-configurable.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Storage
|
# Storage
|
||||||
|
@ -60,7 +64,7 @@ class Identity:
|
||||||
RNS.log("Searching for "+RNS.prettyhexrep(destination_hash)+"...", RNS.LOG_EXTREME)
|
RNS.log("Searching for "+RNS.prettyhexrep(destination_hash)+"...", RNS.LOG_EXTREME)
|
||||||
if destination_hash in Identity.known_destinations:
|
if destination_hash in Identity.known_destinations:
|
||||||
identity_data = Identity.known_destinations[destination_hash]
|
identity_data = Identity.known_destinations[destination_hash]
|
||||||
identity = Identity(public_only=True)
|
identity = Identity(create_keys=False)
|
||||||
identity.load_public_key(identity_data[2])
|
identity.load_public_key(identity_data[2])
|
||||||
identity.app_data = identity_data[3]
|
identity.app_data = identity_data[3]
|
||||||
RNS.log("Found "+RNS.prettyhexrep(destination_hash)+" in known destinations", RNS.LOG_EXTREME)
|
RNS.log("Found "+RNS.prettyhexrep(destination_hash)+" in known destinations", RNS.LOG_EXTREME)
|
||||||
|
@ -145,19 +149,19 @@ class Identity:
|
||||||
if packet.packet_type == RNS.Packet.ANNOUNCE:
|
if packet.packet_type == RNS.Packet.ANNOUNCE:
|
||||||
RNS.log("Validating announce from "+RNS.prettyhexrep(packet.destination_hash), RNS.LOG_DEBUG)
|
RNS.log("Validating announce from "+RNS.prettyhexrep(packet.destination_hash), RNS.LOG_DEBUG)
|
||||||
destination_hash = packet.destination_hash
|
destination_hash = packet.destination_hash
|
||||||
public_key = packet.data[10:Identity.DERKEYSIZE//8+10]
|
public_key = packet.data[10:Identity.KEYSIZE//8+10]
|
||||||
random_hash = packet.data[Identity.DERKEYSIZE//8+10:Identity.DERKEYSIZE//8+20]
|
random_hash = packet.data[Identity.KEYSIZE//8+10:Identity.KEYSIZE//8+20]
|
||||||
signature = packet.data[Identity.DERKEYSIZE//8+20:Identity.DERKEYSIZE//8+20+Identity.KEYSIZE//8]
|
signature = packet.data[Identity.KEYSIZE//8+20:Identity.KEYSIZE//8+20+Identity.KEYSIZE//8]
|
||||||
app_data = b""
|
app_data = b""
|
||||||
if len(packet.data) > Identity.DERKEYSIZE//8+20+Identity.KEYSIZE//8:
|
if len(packet.data) > Identity.KEYSIZE//8+20+Identity.KEYSIZE//8:
|
||||||
app_data = packet.data[Identity.DERKEYSIZE//8+20+Identity.KEYSIZE//8:]
|
app_data = packet.data[Identity.KEYSIZE//8+20+Identity.KEYSIZE//8:]
|
||||||
|
|
||||||
signed_data = destination_hash+public_key+random_hash+app_data
|
signed_data = destination_hash+public_key+random_hash+app_data
|
||||||
|
|
||||||
if not len(packet.data) > Identity.DERKEYSIZE//8+20+Identity.KEYSIZE//8:
|
if not len(packet.data) > Identity.KEYSIZE//8+20+Identity.KEYSIZE//8:
|
||||||
app_data = None
|
app_data = None
|
||||||
|
|
||||||
announced_identity = Identity(public_only=True)
|
announced_identity = Identity(create_keys=False)
|
||||||
announced_identity.load_public_key(public_key)
|
announced_identity.load_public_key(public_key)
|
||||||
|
|
||||||
if announced_identity.pub != None and announced_identity.validate(signature, signed_data):
|
if announced_identity.pub != None and announced_identity.validate(signature, signed_data):
|
||||||
|
@ -184,40 +188,71 @@ class Identity:
|
||||||
:param path: The full path to the saved :ref:`RNS.Identity<api-identity>` data
|
:param path: The full path to the saved :ref:`RNS.Identity<api-identity>` data
|
||||||
:returns: A :ref:`RNS.Identity<api-identity>` instance, or *None* if the loaded data was invalid.
|
:returns: A :ref:`RNS.Identity<api-identity>` instance, or *None* if the loaded data was invalid.
|
||||||
"""
|
"""
|
||||||
identity = Identity(public_only=True)
|
identity = Identity(create_keys=False)
|
||||||
if identity.load(path):
|
if identity.load(path):
|
||||||
return identity
|
return identity
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_bytes(prv_bytes):
|
||||||
|
"""
|
||||||
|
Create a new :ref:`RNS.Identity<api-identity>` instance from *bytes* of private key.
|
||||||
|
Can be used to load previously created and saved identities into Reticulum.
|
||||||
|
|
||||||
def __init__(self,public_only=False):
|
:param prv_bytes: The *bytes* of private a saved private key. **HAZARD!** Never not use this to generate a new key by feeding random data in prv_bytes.
|
||||||
|
:returns: A :ref:`RNS.Identity<api-identity>` instance, or *None* if the *bytes* data was invalid.
|
||||||
|
"""
|
||||||
|
identity = Identity(create_keys=False)
|
||||||
|
if identity.load_private_key(prv_bytes):
|
||||||
|
return identity
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self,create_keys=True):
|
||||||
# Initialize keys to none
|
# Initialize keys to none
|
||||||
self.prv = None
|
self.prv = None
|
||||||
self.pub = None
|
self.prv_bytes = None
|
||||||
self.prv_bytes = None
|
self.sig_prv = None
|
||||||
self.pub_bytes = None
|
self.sig_prv_bytes = None
|
||||||
self.hash = None
|
|
||||||
self.hexhash = None
|
|
||||||
|
|
||||||
if not public_only:
|
self.pub = None
|
||||||
|
self.pub_bytes = None
|
||||||
|
self.sig_pub = None
|
||||||
|
self.sig_pub_bytes = None
|
||||||
|
|
||||||
|
self.hash = None
|
||||||
|
self.hexhash = None
|
||||||
|
|
||||||
|
if create_keys:
|
||||||
self.create_keys()
|
self.create_keys()
|
||||||
|
|
||||||
def create_keys(self):
|
def create_keys(self):
|
||||||
self.prv = rsa.generate_private_key(
|
self.prv = X25519PrivateKey.generate()
|
||||||
public_exponent=65537,
|
self.prv_bytes = self.prv.private_bytes(
|
||||||
key_size=Identity.KEYSIZE,
|
encoding=serialization.Encoding.Raw,
|
||||||
backend=default_backend()
|
format=serialization.PrivateFormat.Raw,
|
||||||
)
|
|
||||||
self.prv_bytes = self.prv.private_bytes(
|
|
||||||
encoding=serialization.Encoding.DER,
|
|
||||||
format=serialization.PrivateFormat.PKCS8,
|
|
||||||
encryption_algorithm=serialization.NoEncryption()
|
encryption_algorithm=serialization.NoEncryption()
|
||||||
)
|
)
|
||||||
self.pub = self.prv.public_key()
|
|
||||||
self.pub_bytes = self.pub.public_bytes(
|
self.sig_prv = Ed25519PrivateKey.generate()
|
||||||
encoding=serialization.Encoding.DER,
|
self.sig_prv_bytes = self.sig_prv.private_bytes(
|
||||||
format=serialization.PublicFormat.SubjectPublicKeyInfo
|
encoding=serialization.Encoding.Raw,
|
||||||
|
format=serialization.PrivateFormat.Raw,
|
||||||
|
encryption_algorithm=serialization.NoEncryption()
|
||||||
|
)
|
||||||
|
|
||||||
|
self.pub = self.prv.public_key()
|
||||||
|
self.pub_bytes = self.pub.public_bytes(
|
||||||
|
encoding=serialization.Encoding.Raw,
|
||||||
|
format=serialization.PublicFormat.Raw
|
||||||
|
)
|
||||||
|
|
||||||
|
self.sig_pub = self.sig_prv.public_key()
|
||||||
|
self.sig_pub_bytes = self.sig_pub.public_bytes(
|
||||||
|
encoding=serialization.Encoding.Raw,
|
||||||
|
format=serialization.PublicFormat.Raw
|
||||||
)
|
)
|
||||||
|
|
||||||
self.update_hashes()
|
self.update_hashes()
|
||||||
|
@ -228,13 +263,13 @@ class Identity:
|
||||||
"""
|
"""
|
||||||
:returns: The private key as *bytes*
|
:returns: The private key as *bytes*
|
||||||
"""
|
"""
|
||||||
return self.prv_bytes
|
return self.prv_bytes+self.sig_prv_bytes
|
||||||
|
|
||||||
def get_public_key(self):
|
def get_public_key(self):
|
||||||
"""
|
"""
|
||||||
:returns: The public key as *bytes*
|
:returns: The public key as *bytes*
|
||||||
"""
|
"""
|
||||||
return self.pub_bytes
|
return self.pub_bytes+self.sig_pub_bytes
|
||||||
|
|
||||||
def load_private_key(self, prv_bytes):
|
def load_private_key(self, prv_bytes):
|
||||||
"""
|
"""
|
||||||
|
@ -244,42 +279,53 @@ class Identity:
|
||||||
:returns: True if the key was loaded, otherwise False.
|
:returns: True if the key was loaded, otherwise False.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
self.prv_bytes = prv_bytes
|
self.prv_bytes = prv_bytes[:Identity.KEYSIZE//8//2]
|
||||||
self.prv = serialization.load_der_private_key(
|
self.prv = X25519PrivateKey.from_private_bytes(self.prv_bytes)
|
||||||
self.prv_bytes,
|
self.sig_prv_bytes = prv_bytes[Identity.KEYSIZE//8//2:]
|
||||||
password=None,
|
self.sig_prv = Ed25519PrivateKey.from_private_bytes(self.sig_prv_bytes)
|
||||||
backend=default_backend()
|
|
||||||
|
self.pub = self.prv.public_key()
|
||||||
|
self.pub_bytes = self.pub.public_bytes(
|
||||||
|
encoding=serialization.Encoding.Raw,
|
||||||
|
format=serialization.PublicFormat.Raw
|
||||||
)
|
)
|
||||||
self.pub = self.prv.public_key()
|
|
||||||
self.pub_bytes = self.pub.public_bytes(
|
self.sig_pub = self.sig_prv.public_key()
|
||||||
encoding=serialization.Encoding.DER,
|
self.sig_pub_bytes = self.sig_pub.public_bytes(
|
||||||
format=serialization.PublicFormat.SubjectPublicKeyInfo
|
encoding=serialization.Encoding.Raw,
|
||||||
|
format=serialization.PublicFormat.Raw
|
||||||
)
|
)
|
||||||
|
|
||||||
self.update_hashes()
|
self.update_hashes()
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
raise e
|
||||||
RNS.log("Failed to load identity key", RNS.LOG_ERROR)
|
RNS.log("Failed to load identity key", RNS.LOG_ERROR)
|
||||||
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
|
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def load_public_key(self, key):
|
def load_public_key(self, pub_bytes):
|
||||||
"""
|
"""
|
||||||
Load a public key into the instance.
|
Load a public key into the instance.
|
||||||
|
|
||||||
:param prv_bytes: The public key as *bytes*.
|
:param pub_bytes: The public key as *bytes*.
|
||||||
:returns: True if the key was loaded, otherwise False.
|
:returns: True if the key was loaded, otherwise False.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
self.pub_bytes = key
|
self.pub_bytes = pub_bytes[:Identity.KEYSIZE//8//2]
|
||||||
self.pub = load_der_public_key(self.pub_bytes, backend=default_backend())
|
self.sig_pub_bytes = pub_bytes[Identity.KEYSIZE//8//2:]
|
||||||
|
|
||||||
|
self.pub = X25519PublicKey.from_public_bytes(self.pub_bytes)
|
||||||
|
self.sig_pub = Ed25519PublicKey.from_public_bytes(self.sig_pub_bytes)
|
||||||
|
|
||||||
self.update_hashes()
|
self.update_hashes()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
RNS.log("Error while loading public key, the contained exception was: "+str(e), RNS.LOG_ERROR)
|
RNS.log("Error while loading public key, the contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||||
|
|
||||||
def update_hashes(self):
|
def update_hashes(self):
|
||||||
self.hash = Identity.truncated_hash(self.pub_bytes)
|
self.hash = Identity.truncated_hash(self.get_public_key())
|
||||||
self.hexhash = self.hash.hex()
|
self.hexhash = self.hash.hex()
|
||||||
|
|
||||||
def to_file(self, path):
|
def to_file(self, path):
|
||||||
|
@ -310,71 +356,78 @@ class Identity:
|
||||||
RNS.log("Error while loading identity from "+str(path), RNS.LOG_ERROR)
|
RNS.log("Error while loading identity from "+str(path), RNS.LOG_ERROR)
|
||||||
RNS.log("The contained exception was: "+str(e))
|
RNS.log("The contained exception was: "+str(e))
|
||||||
|
|
||||||
|
def get_salt(self):
|
||||||
|
return self.hash
|
||||||
|
|
||||||
|
def get_context(self):
|
||||||
|
return None
|
||||||
|
|
||||||
def encrypt(self, plaintext):
|
def encrypt(self, plaintext):
|
||||||
"""
|
"""
|
||||||
Encrypts information for the identity.
|
Encrypts information for the identity.
|
||||||
|
|
||||||
:param plaintext: The plaintext to be encrypted as *bytes*.
|
:param plaintext: The plaintext to be encrypted as *bytes*.
|
||||||
:returns: Ciphertext as *bytes*.
|
:returns: Ciphertext token as *bytes*.
|
||||||
:raises: *KeyError* if the instance does not hold a public key
|
:raises: *KeyError* if the instance does not hold a public key.
|
||||||
"""
|
"""
|
||||||
if self.pub != None:
|
if self.pub != None:
|
||||||
chunksize = Identity.ENCRYPT_CHUNKSIZE
|
ephemeral_key = X25519PrivateKey.generate()
|
||||||
chunks = int(math.ceil(len(plaintext)/(float(chunksize))))
|
ephemeral_pub_bytes = ephemeral_key.public_key().public_bytes(
|
||||||
|
encoding=serialization.Encoding.Raw,
|
||||||
|
format=serialization.PublicFormat.Raw
|
||||||
|
)
|
||||||
|
|
||||||
ciphertext = b"";
|
shared_key = ephemeral_key.exchange(self.pub)
|
||||||
for chunk in range(chunks):
|
derived_key = derived_key = HKDF(
|
||||||
start = chunk*chunksize
|
algorithm=hashes.SHA256(),
|
||||||
end = (chunk+1)*chunksize
|
length=32,
|
||||||
if (chunk+1)*chunksize > len(plaintext):
|
salt=self.get_salt(),
|
||||||
end = len(plaintext)
|
info=self.get_context(),
|
||||||
|
).derive(shared_key)
|
||||||
|
|
||||||
ciphertext += self.pub.encrypt(
|
fernet = Fernet(base64.urlsafe_b64encode(derived_key))
|
||||||
plaintext[start:end],
|
ciphertext = base64.urlsafe_b64decode(fernet.encrypt(plaintext))
|
||||||
padding.OAEP(
|
token = ephemeral_pub_bytes+ciphertext
|
||||||
mgf=padding.MGF1(algorithm=hashes.SHA1()),
|
|
||||||
algorithm=hashes.SHA1(),
|
return token
|
||||||
label=None
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return ciphertext
|
|
||||||
else:
|
else:
|
||||||
raise KeyError("Encryption failed because identity does not hold a public key")
|
raise KeyError("Encryption failed because identity does not hold a public key")
|
||||||
|
|
||||||
|
|
||||||
def decrypt(self, ciphertext):
|
def decrypt(self, ciphertext_token):
|
||||||
"""
|
"""
|
||||||
Decrypts information for the identity.
|
Decrypts information for the identity.
|
||||||
|
|
||||||
:param ciphertext: The ciphertext to be decrypted as *bytes*.
|
:param ciphertext: The ciphertext to be decrypted as *bytes*.
|
||||||
:returns: Plaintext as *bytes*, or *None* if decryption fails.
|
:returns: Plaintext as *bytes*, or *None* if decryption fails.
|
||||||
:raises: *KeyError* if the instance does not hold a private key
|
:raises: *KeyError* if the instance does not hold a private key.
|
||||||
"""
|
"""
|
||||||
if self.prv != None:
|
if self.prv != None:
|
||||||
plaintext = None
|
if len(ciphertext_token) > Identity.KEYSIZE//8//2:
|
||||||
try:
|
plaintext = None
|
||||||
chunksize = Identity.DECRYPT_CHUNKSIZE
|
try:
|
||||||
chunks = int(math.ceil(len(ciphertext)/(float(chunksize))))
|
peer_pub_bytes = ciphertext_token[:Identity.KEYSIZE//8//2]
|
||||||
|
peer_pub = X25519PublicKey.from_public_bytes(peer_pub_bytes)
|
||||||
|
|
||||||
plaintext = b"";
|
shared_key = self.prv.exchange(peer_pub)
|
||||||
for chunk in range(chunks):
|
derived_key = derived_key = HKDF(
|
||||||
start = chunk*chunksize
|
algorithm=hashes.SHA256(),
|
||||||
end = (chunk+1)*chunksize
|
length=32,
|
||||||
if (chunk+1)*chunksize > len(ciphertext):
|
salt=self.get_salt(),
|
||||||
end = len(ciphertext)
|
info=self.get_context(),
|
||||||
|
).derive(shared_key)
|
||||||
|
|
||||||
plaintext += self.prv.decrypt(
|
fernet = Fernet(base64.urlsafe_b64encode(derived_key))
|
||||||
ciphertext[start:end],
|
ciphertext = ciphertext_token[Identity.KEYSIZE//8//2:]
|
||||||
padding.OAEP(
|
plaintext = fernet.decrypt(base64.urlsafe_b64encode(ciphertext))
|
||||||
mgf=padding.MGF1(algorithm=hashes.SHA1()),
|
|
||||||
algorithm=hashes.SHA1(),
|
|
||||||
label=None
|
|
||||||
)
|
|
||||||
)
|
|
||||||
except:
|
|
||||||
RNS.log("Decryption by "+RNS.prettyhexrep(self.hash)+" failed", RNS.LOG_VERBOSE)
|
|
||||||
|
|
||||||
return plaintext;
|
except Exception as e:
|
||||||
|
RNS.log("Decryption by "+RNS.prettyhexrep(self.hash)+" failed: "+str(e), RNS.LOG_DEBUG)
|
||||||
|
|
||||||
|
return plaintext;
|
||||||
|
else:
|
||||||
|
RNS.log("Decryption failed because the token size was invalid.", RNS.LOG_DEBUG)
|
||||||
|
return None
|
||||||
else:
|
else:
|
||||||
raise KeyError("Decryption failed because identity does not hold a private key")
|
raise KeyError("Decryption failed because identity does not hold a private key")
|
||||||
|
|
||||||
|
@ -385,18 +438,14 @@ class Identity:
|
||||||
|
|
||||||
:param message: The message to be signed as *bytes*.
|
:param message: The message to be signed as *bytes*.
|
||||||
:returns: Signature as *bytes*.
|
:returns: Signature as *bytes*.
|
||||||
:raises: *KeyError* if the instance does not hold a private key
|
:raises: *KeyError* if the instance does not hold a private key.
|
||||||
"""
|
"""
|
||||||
if self.prv != None:
|
if self.sig_prv != None:
|
||||||
signature = self.prv.sign(
|
try:
|
||||||
message,
|
return self.sig_prv.sign(message)
|
||||||
padding.PSS(
|
except Exception as e:
|
||||||
mgf=padding.MGF1(hashes.SHA256()),
|
RNS.log("The identity "+str(self)+" could not sign the requested message. The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||||
salt_length=padding.PSS.MAX_LENGTH
|
raise e
|
||||||
),
|
|
||||||
hashes.SHA256()
|
|
||||||
)
|
|
||||||
return signature
|
|
||||||
else:
|
else:
|
||||||
raise KeyError("Signing failed because identity does not hold a private key")
|
raise KeyError("Signing failed because identity does not hold a private key")
|
||||||
|
|
||||||
|
@ -407,19 +456,11 @@ class Identity:
|
||||||
:param signature: The signature to be validated as *bytes*.
|
:param signature: The signature to be validated as *bytes*.
|
||||||
:param message: The message to be validated as *bytes*.
|
:param message: The message to be validated as *bytes*.
|
||||||
:returns: True if the signature is valid, otherwise False.
|
:returns: True if the signature is valid, otherwise False.
|
||||||
:raises: *KeyError* if the instance does not hold a public key
|
:raises: *KeyError* if the instance does not hold a public key.
|
||||||
"""
|
"""
|
||||||
if self.pub != None:
|
if self.pub != None:
|
||||||
try:
|
try:
|
||||||
self.pub.verify(
|
self.sig_pub.verify(signature, message)
|
||||||
signature,
|
|
||||||
message,
|
|
||||||
padding.PSS(
|
|
||||||
mgf=padding.MGF1(hashes.SHA256()),
|
|
||||||
salt_length=padding.PSS.MAX_LENGTH
|
|
||||||
),
|
|
||||||
hashes.SHA256()
|
|
||||||
)
|
|
||||||
return True
|
return True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return False
|
return False
|
||||||
|
|
27
RNS/Link.py
27
RNS/Link.py
|
@ -33,17 +33,15 @@ class Link:
|
||||||
:param peer_pub_bytes: Internal use, ignore this argument.
|
:param peer_pub_bytes: Internal use, ignore this argument.
|
||||||
:param peer_sig_pub_bytes: Internal use, ignore this argument.
|
:param peer_sig_pub_bytes: Internal use, ignore this argument.
|
||||||
"""
|
"""
|
||||||
CURVE = "Curve25519"
|
CURVE = RNS.Identity.CURVE
|
||||||
"""
|
"""
|
||||||
The curve used for Elliptic Curve DH key exchanges
|
The curve used for Elliptic Curve DH key exchanges
|
||||||
"""
|
"""
|
||||||
|
|
||||||
ECPUBSIZE = 32+32
|
ECPUBSIZE = 32+32
|
||||||
BLOCKSIZE = 16
|
KEYSIZE = 32
|
||||||
KEYSIZE = 32
|
|
||||||
|
|
||||||
AES_HMAC_OVERHEAD = 58
|
MDU = math.floor((RNS.Reticulum.MDU-RNS.Identity.AES_HMAC_OVERHEAD)/RNS.Identity.AES128_BLOCKSIZE)*RNS.Identity.AES128_BLOCKSIZE - 1
|
||||||
MDU = math.floor((RNS.Reticulum.MDU-AES_HMAC_OVERHEAD)/BLOCKSIZE)*BLOCKSIZE - 1
|
|
||||||
|
|
||||||
# TODO: This should not be hardcoded,
|
# TODO: This should not be hardcoded,
|
||||||
# but calculated from something like
|
# but calculated from something like
|
||||||
|
@ -90,11 +88,6 @@ class Link:
|
||||||
link.last_inbound = time.time()
|
link.last_inbound = time.time()
|
||||||
link.start_watchdog()
|
link.start_watchdog()
|
||||||
|
|
||||||
# TODO: Why was link_established callback here? Seems weird
|
|
||||||
# to call this before RTT packet has been received
|
|
||||||
#if self.owner.callbacks.link_established != None:
|
|
||||||
# self.owner.callbacks.link_established(link)
|
|
||||||
|
|
||||||
RNS.log("Incoming link request "+str(link)+" accepted", RNS.LOG_VERBOSE)
|
RNS.log("Incoming link request "+str(link)+" accepted", RNS.LOG_VERBOSE)
|
||||||
return link
|
return link
|
||||||
|
|
||||||
|
@ -537,13 +530,13 @@ class Link:
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def link_established_callback(self, callback):
|
def set_link_established_callback(self, callback):
|
||||||
self.callbacks.link_established = callback
|
self.callbacks.link_established = callback
|
||||||
|
|
||||||
def link_closed_callback(self, callback):
|
def set_link_closed_callback(self, callback):
|
||||||
self.callbacks.link_closed = callback
|
self.callbacks.link_closed = callback
|
||||||
|
|
||||||
def packet_callback(self, callback):
|
def set_packet_callback(self, callback):
|
||||||
"""
|
"""
|
||||||
Registers a function to be called when a packet has been
|
Registers a function to be called when a packet has been
|
||||||
received over this link.
|
received over this link.
|
||||||
|
@ -552,7 +545,7 @@ class Link:
|
||||||
"""
|
"""
|
||||||
self.callbacks.packet = callback
|
self.callbacks.packet = callback
|
||||||
|
|
||||||
def resource_callback(self, callback):
|
def set_resource_callback(self, callback):
|
||||||
"""
|
"""
|
||||||
Registers a function to be called when a resource has been
|
Registers a function to be called when a resource has been
|
||||||
advertised over this link. If the function returns *True*
|
advertised over this link. If the function returns *True*
|
||||||
|
@ -563,7 +556,7 @@ class Link:
|
||||||
"""
|
"""
|
||||||
self.callbacks.resource = callback
|
self.callbacks.resource = callback
|
||||||
|
|
||||||
def resource_started_callback(self, callback):
|
def set_resource_started_callback(self, callback):
|
||||||
"""
|
"""
|
||||||
Registers a function to be called when a resource has begun
|
Registers a function to be called when a resource has begun
|
||||||
transferring over this link.
|
transferring over this link.
|
||||||
|
@ -572,7 +565,7 @@ class Link:
|
||||||
"""
|
"""
|
||||||
self.callbacks.resource_started = callback
|
self.callbacks.resource_started = callback
|
||||||
|
|
||||||
def resource_concluded_callback(self, callback):
|
def set_resource_concluded_callback(self, callback):
|
||||||
"""
|
"""
|
||||||
Registers a function to be called when a resource has concluded
|
Registers a function to be called when a resource has concluded
|
||||||
transferring over this link.
|
transferring over this link.
|
||||||
|
|
|
@ -6,8 +6,16 @@ import RNS
|
||||||
|
|
||||||
class Packet:
|
class Packet:
|
||||||
"""
|
"""
|
||||||
The Packet class is used to create packet instances that can be
|
The Packet class is used to create packet instances that can be sent
|
||||||
sent over a Reticulum network.
|
over a Reticulum network. Packets to will automatically be encrypted if
|
||||||
|
they are adressed to a ``RNS.Destination.SINGLE`` destination,
|
||||||
|
``RNS.Destination.GROUP`` destination or a :ref:`RNS.Link<api-link>`.
|
||||||
|
|
||||||
|
For ``RNS.Destination.GROUP`` destinations, Reticulum will use the
|
||||||
|
pre-shared key configured for the destination.
|
||||||
|
|
||||||
|
For ``RNS.Destination.SINGLE`` destinations and :ref:`RNS.Link<api-link>`
|
||||||
|
destinations, reticulum will use ephemeral keys, and offers **Forward Secrecy**.
|
||||||
|
|
||||||
:param destination: A :ref:`RNS.Destination<api-destination>` instance to which the packet will be sent.
|
:param destination: A :ref:`RNS.Destination<api-destination>` instance to which the packet will be sent.
|
||||||
:param data: The data payload to be included in the packet as *bytes*.
|
:param data: The data payload to be included in the packet as *bytes*.
|
||||||
|
@ -56,14 +64,21 @@ class Packet:
|
||||||
|
|
||||||
# This is used to calculate allowable
|
# This is used to calculate allowable
|
||||||
# payload sizes
|
# payload sizes
|
||||||
HEADER_MAXSIZE = 23
|
HEADER_MAXSIZE = RNS.Reticulum.HEADER_MAXSIZE
|
||||||
MDU = RNS.Reticulum.MDU
|
MDU = RNS.Reticulum.MDU
|
||||||
|
|
||||||
# With an MTU of 500, the maximum RSA-encrypted
|
# TODO: Update this
|
||||||
# amount of data we can send in a single packet
|
# With an MTU of 500, the maximum of data we can
|
||||||
# is given by the below calculation; 258 bytes.
|
# send in a single encrypted packet is given by
|
||||||
RSA_MDU = math.floor(MDU/RNS.Identity.DECRYPT_CHUNKSIZE)*RNS.Identity.ENCRYPT_CHUNKSIZE
|
# the below calculation; 383 bytes.
|
||||||
PLAIN_MDU = MDU
|
ENCRYPTED_MDU = math.floor((RNS.Reticulum.MDU-RNS.Identity.AES_HMAC_OVERHEAD-RNS.Identity.KEYSIZE//16)/RNS.Identity.AES128_BLOCKSIZE)*RNS.Identity.AES128_BLOCKSIZE - 1
|
||||||
|
"""
|
||||||
|
The maximum size of the payload data in a single encrypted packet
|
||||||
|
"""
|
||||||
|
PLAIN_MDU = MDU
|
||||||
|
"""
|
||||||
|
The maximum size of the payload data in a single unencrypted packet
|
||||||
|
"""
|
||||||
|
|
||||||
# TODO: This should be calculated
|
# TODO: This should be calculated
|
||||||
# more intelligently
|
# more intelligently
|
||||||
|
@ -406,7 +421,7 @@ class PacketReceipt:
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def rtt(self):
|
def get_rtt(self):
|
||||||
"""
|
"""
|
||||||
:returns: The round-trip-time in seconds
|
:returns: The round-trip-time in seconds
|
||||||
"""
|
"""
|
||||||
|
@ -439,7 +454,7 @@ class PacketReceipt:
|
||||||
"""
|
"""
|
||||||
self.timeout = float(timeout)
|
self.timeout = float(timeout)
|
||||||
|
|
||||||
def delivery_callback(self, callback):
|
def set_delivery_callback(self, callback):
|
||||||
"""
|
"""
|
||||||
Sets a function that gets called if a successfull delivery has been proven.
|
Sets a function that gets called if a successfull delivery has been proven.
|
||||||
|
|
||||||
|
@ -449,7 +464,7 @@ class PacketReceipt:
|
||||||
|
|
||||||
# Set a function that gets called if the
|
# Set a function that gets called if the
|
||||||
# delivery times out
|
# delivery times out
|
||||||
def timeout_callback(self, callback):
|
def set_timeout_callback(self, callback):
|
||||||
"""
|
"""
|
||||||
Sets a function that gets called if the delivery times out.
|
Sets a function that gets called if the delivery times out.
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,6 @@ class Resource:
|
||||||
:param link: The :ref:`RNS.Link<api-link>` instance on which to transfer the data.
|
:param link: The :ref:`RNS.Link<api-link>` instance on which to transfer the data.
|
||||||
:param advertise: Whether to automatically advertise the resource. Can be *True* or *False*.
|
:param advertise: Whether to automatically advertise the resource. Can be *True* or *False*.
|
||||||
:param auto_compress: Whether to auto-compress the resource. Can be *True* or *False*.
|
:param auto_compress: Whether to auto-compress the resource. Can be *True* or *False*.
|
||||||
:param auto_compress: Whether the resource must be compressed. Can be *True* or *False*. Used for debugging, will disappear in the future.
|
|
||||||
:param callback: A *callable* with the signature *callback(resource)*. Will be called when the resource transfer concludes.
|
:param callback: A *callable* with the signature *callback(resource)*. Will be called when the resource transfer concludes.
|
||||||
:param progress_callback: A *callable* with the signature *callback(resource)*. Will be called whenever the resource transfer progress is updated.
|
:param progress_callback: A *callable* with the signature *callback(resource)*. Will be called whenever the resource transfer progress is updated.
|
||||||
:param segment_index: Internal use, ignore.
|
:param segment_index: Internal use, ignore.
|
||||||
|
@ -134,7 +133,7 @@ class Resource:
|
||||||
# Create a resource for transmission to a remote destination
|
# Create a resource for transmission to a remote destination
|
||||||
# The data passed can be either a bytes-array or a file opened
|
# The data passed can be either a bytes-array or a file opened
|
||||||
# in binary read mode.
|
# in binary read mode.
|
||||||
def __init__(self, data, link, advertise=True, auto_compress=True, must_compress=False, callback=None, progress_callback=None, segment_index = 1, original_hash = None):
|
def __init__(self, data, link, advertise=True, auto_compress=True, callback=None, progress_callback=None, segment_index = 1, original_hash = None):
|
||||||
data_size = None
|
data_size = None
|
||||||
resource_data = None
|
resource_data = None
|
||||||
if hasattr(data, "read"):
|
if hasattr(data, "read"):
|
||||||
|
@ -198,7 +197,7 @@ class Resource:
|
||||||
self.uncompressed_data = data
|
self.uncompressed_data = data
|
||||||
|
|
||||||
compression_began = time.time()
|
compression_began = time.time()
|
||||||
if must_compress or (auto_compress and len(self.uncompressed_data) < Resource.AUTO_COMPRESS_MAX_SIZE):
|
if (auto_compress and len(self.uncompressed_data) < Resource.AUTO_COMPRESS_MAX_SIZE):
|
||||||
RNS.log("Compressing resource data...", RNS.LOG_DEBUG)
|
RNS.log("Compressing resource data...", RNS.LOG_DEBUG)
|
||||||
self.compressed_data = bz2.compress(self.uncompressed_data)
|
self.compressed_data = bz2.compress(self.uncompressed_data)
|
||||||
RNS.log("Compression completed in "+str(round(time.time()-compression_began, 3))+" seconds", RNS.LOG_DEBUG)
|
RNS.log("Compression completed in "+str(round(time.time()-compression_began, 3))+" seconds", RNS.LOG_DEBUG)
|
||||||
|
@ -748,8 +747,6 @@ class Resource:
|
||||||
:returns: The current progress of the resource transfer as a *float* between 0.0 and 1.0.
|
:returns: The current progress of the resource transfer as a *float* between 0.0 and 1.0.
|
||||||
"""
|
"""
|
||||||
if self.initiator:
|
if self.initiator:
|
||||||
# TODO: Remove
|
|
||||||
# progress = self.sent_parts / len(self.parts)
|
|
||||||
self.processed_parts = (self.segment_index-1)*math.ceil(Resource.MAX_EFFICIENT_SIZE/Resource.SDU)
|
self.processed_parts = (self.segment_index-1)*math.ceil(Resource.MAX_EFFICIENT_SIZE/Resource.SDU)
|
||||||
self.processed_parts += self.sent_parts
|
self.processed_parts += self.sent_parts
|
||||||
self.progress_total_parts = float(self.grand_total_parts)
|
self.progress_total_parts = float(self.grand_total_parts)
|
||||||
|
|
|
@ -108,7 +108,7 @@ class Transport:
|
||||||
|
|
||||||
# Create transport-specific destinations
|
# Create transport-specific destinations
|
||||||
Transport.path_request_destination = RNS.Destination(None, RNS.Destination.IN, RNS.Destination.PLAIN, Transport.APP_NAME, "path", "request")
|
Transport.path_request_destination = RNS.Destination(None, RNS.Destination.IN, RNS.Destination.PLAIN, Transport.APP_NAME, "path", "request")
|
||||||
Transport.path_request_destination.packet_callback(Transport.path_request_handler)
|
Transport.path_request_destination.set_packet_callback(Transport.path_request_handler)
|
||||||
Transport.control_destinations.append(Transport.path_request_destination)
|
Transport.control_destinations.append(Transport.path_request_destination)
|
||||||
Transport.control_hashes.append(Transport.path_request_destination.hash)
|
Transport.control_hashes.append(Transport.path_request_destination.hash)
|
||||||
|
|
||||||
|
@ -652,7 +652,7 @@ class Transport:
|
||||||
# First, check that the announce is not for a destination
|
# First, check that the announce is not for a destination
|
||||||
# local to this system, and that hops are less than the max
|
# local to this system, and that hops are less than the max
|
||||||
if (not any(packet.destination_hash == d.hash for d in Transport.destinations) and packet.hops < Transport.PATHFINDER_M+1):
|
if (not any(packet.destination_hash == d.hash for d in Transport.destinations) and packet.hops < Transport.PATHFINDER_M+1):
|
||||||
random_blob = packet.data[RNS.Identity.DERKEYSIZE//8+10:RNS.Identity.DERKEYSIZE//8+20]
|
random_blob = packet.data[RNS.Identity.KEYSIZE//8+10:RNS.Identity.KEYSIZE//8+20]
|
||||||
random_blobs = []
|
random_blobs = []
|
||||||
if packet.destination_hash in Transport.destination_table:
|
if packet.destination_hash in Transport.destination_table:
|
||||||
random_blobs = Transport.destination_table[packet.destination_hash][4]
|
random_blobs = Transport.destination_table[packet.destination_hash][4]
|
||||||
|
|
Loading…
Reference in New Issue