From aabb763cea5f3bf3cb4a339c41d90768f62048d8 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Fri, 22 Nov 2024 15:19:12 +0100 Subject: [PATCH] Refactored fernet to token --- README.md | 4 ++-- RNS/Cryptography/{Fernet.py => Token.py} | 4 ++-- RNS/Cryptography/__init__.py | 2 +- RNS/Destination.py | 8 ++++---- RNS/Identity.py | 16 ++++++++-------- RNS/Link.py | 20 ++++++++++---------- RNS/Packet.py | 2 +- docs/source/understanding.rst | 16 ++++++++++------ 8 files changed, 38 insertions(+), 34 deletions(-) rename RNS/Cryptography/{Fernet.py => Token.py} (97%) diff --git a/README.md b/README.md index 4c8a7be..cb05884 100755 --- a/README.md +++ b/README.md @@ -325,12 +325,12 @@ In the default installation configuration, the `X25519`, `Ed25519` and (via the [PyCA/cryptography](https://github.com/pyca/cryptography) package). The hashing functions `SHA-256` and `SHA-512` are provided by the standard Python [hashlib](https://docs.python.org/3/library/hashlib.html). The `HKDF`, -`HMAC`, `Fernet` primitives, and the `PKCS7` padding function are always +`HMAC`, `Token` primitives, and the `PKCS7` padding function are always provided by the following internal implementations: - [HKDF.py](RNS/Cryptography/HKDF.py) - [HMAC.py](RNS/Cryptography/HMAC.py) -- [Fernet.py](RNS/Cryptography/Fernet.py) +- [Token.py](RNS/Cryptography/Token.py) - [PKCS7.py](RNS/Cryptography/PKCS7.py) diff --git a/RNS/Cryptography/Fernet.py b/RNS/Cryptography/Token.py similarity index 97% rename from RNS/Cryptography/Fernet.py rename to RNS/Cryptography/Token.py index 245d86c..61e2b9a 100644 --- a/RNS/Cryptography/Fernet.py +++ b/RNS/Cryptography/Token.py @@ -27,7 +27,7 @@ from RNS.Cryptography import HMAC from RNS.Cryptography import PKCS7 from RNS.Cryptography.AES import AES_128_CBC -class Fernet(): +class Token(): """ This class provides a slightly modified implementation of the Fernet spec found at: https://github.com/fernet/spec/blob/master/Spec.md @@ -37,7 +37,7 @@ class Fernet(): not relevant to Reticulum. They are therefore stripped from this implementation, since they incur overhead and leak initiator metadata. """ - FERNET_OVERHEAD = 48 # Bytes + TOKEN_OVERHEAD = 48 # Bytes @staticmethod def generate_key(): diff --git a/RNS/Cryptography/__init__.py b/RNS/Cryptography/__init__.py index 78c04df..eb2934e 100644 --- a/RNS/Cryptography/__init__.py +++ b/RNS/Cryptography/__init__.py @@ -5,7 +5,7 @@ from .Hashes import sha256 from .Hashes import sha512 from .HKDF import hkdf from .PKCS7 import PKCS7 -from .Fernet import Fernet +from .Token import Token from .Provider import backend import RNS.Cryptography.Provider as cp diff --git a/RNS/Destination.py b/RNS/Destination.py index bca8f35..6b28b9b 100755 --- a/RNS/Destination.py +++ b/RNS/Destination.py @@ -26,7 +26,7 @@ import time import threading import RNS -from RNS.Cryptography import Fernet +from RNS.Cryptography import Token from .vendor import umsgpack as umsgpack class Callbacks: @@ -525,8 +525,8 @@ class Destination: raise TypeError("A single destination holds keys through an Identity instance") if self.type == Destination.GROUP: - self.prv_bytes = Fernet.generate_key() - self.prv = Fernet(self.prv_bytes) + self.prv_bytes = Token.generate_key() + self.prv = Token(self.prv_bytes) def get_private_key(self): """ @@ -556,7 +556,7 @@ class Destination: if self.type == Destination.GROUP: self.prv_bytes = key - self.prv = Fernet(self.prv_bytes) + self.prv = Token(self.prv_bytes) def load_public_key(self, key): if self.type != Destination.SINGLE: diff --git a/RNS/Identity.py b/RNS/Identity.py index 1232486..5631fb3 100644 --- a/RNS/Identity.py +++ b/RNS/Identity.py @@ -31,7 +31,7 @@ import threading from .vendor import umsgpack as umsgpack from RNS.Cryptography import X25519PrivateKey, X25519PublicKey, Ed25519PrivateKey, Ed25519PublicKey -from RNS.Cryptography import Fernet +from RNS.Cryptography import Token class Identity: @@ -66,7 +66,7 @@ class Identity: """ # Non-configurable constants - FERNET_OVERHEAD = RNS.Cryptography.Fernet.FERNET_OVERHEAD + TOKEN_OVERHEAD = RNS.Cryptography.Token.TOKEN_OVERHEAD AES128_BLOCKSIZE = 16 # In bytes HASHLENGTH = 256 # In bits SIGLENGTH = KEYSIZE # In bits @@ -646,8 +646,8 @@ class Identity: context=self.get_context(), ) - fernet = Fernet(derived_key) - ciphertext = fernet.encrypt(plaintext) + token = Token(derived_key) + ciphertext = token.encrypt(plaintext) token = ephemeral_pub_bytes+ciphertext return token @@ -684,8 +684,8 @@ class Identity: context=self.get_context(), ) - fernet = Fernet(derived_key) - plaintext = fernet.decrypt(ciphertext) + token = Token(derived_key) + plaintext = token.decrypt(ciphertext) if ratchet_id_receiver: ratchet_id_receiver.latest_ratchet_id = ratchet_id @@ -709,8 +709,8 @@ class Identity: context=self.get_context(), ) - fernet = Fernet(derived_key) - plaintext = fernet.decrypt(ciphertext) + token = Token(derived_key) + plaintext = token.decrypt(ciphertext) if ratchet_id_receiver: ratchet_id_receiver.latest_ratchet_id = None diff --git a/RNS/Link.py b/RNS/Link.py index 9ab8fbf..c53fb95 100644 --- a/RNS/Link.py +++ b/RNS/Link.py @@ -21,7 +21,7 @@ # SOFTWARE. from RNS.Cryptography import X25519PrivateKey, X25519PublicKey, Ed25519PrivateKey, Ed25519PublicKey -from RNS.Cryptography import Fernet +from RNS.Cryptography import Token from RNS.Channel import Channel, LinkChannelOutlet from time import sleep @@ -61,7 +61,7 @@ class Link: ECPUBSIZE = 32+32 KEYSIZE = 32 - MDU = math.floor((RNS.Reticulum.MTU-RNS.Reticulum.IFAC_MIN_SIZE-RNS.Reticulum.HEADER_MINSIZE-RNS.Identity.FERNET_OVERHEAD)/RNS.Identity.AES128_BLOCKSIZE)*RNS.Identity.AES128_BLOCKSIZE - 1 + MDU = math.floor((RNS.Reticulum.MTU-RNS.Reticulum.IFAC_MIN_SIZE-RNS.Reticulum.HEADER_MINSIZE-RNS.Identity.TOKEN_OVERHEAD)/RNS.Identity.AES128_BLOCKSIZE)*RNS.Identity.AES128_BLOCKSIZE - 1 ESTABLISHMENT_TIMEOUT_PER_HOP = RNS.Reticulum.DEFAULT_PER_HOP_TIMEOUT """ @@ -188,7 +188,7 @@ class Link: self.prv = X25519PrivateKey.generate() self.sig_prv = Ed25519PrivateKey.generate() - self.fernet = None + self.token = None self.pub = self.prv.public_key() self.pub_bytes = self.pub.public_bytes() @@ -979,14 +979,14 @@ class Link: def encrypt(self, plaintext): try: - if not self.fernet: + if not self.token: try: - self.fernet = Fernet(self.derived_key) + self.token = Token(self.derived_key) except Exception as e: - RNS.log("Could not instantiate Fernet while performin encryption on link "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR) + RNS.log("Could not instantiate token while performing encryption on link "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR) raise e - return self.fernet.encrypt(plaintext) + return self.token.encrypt(plaintext) except Exception as e: RNS.log("Encryption on link "+str(self)+" failed. The contained exception was: "+str(e), RNS.LOG_ERROR) @@ -995,10 +995,10 @@ class Link: def decrypt(self, ciphertext): try: - if not self.fernet: - self.fernet = Fernet(self.derived_key) + if not self.token: + self.token = Token(self.derived_key) - return self.fernet.decrypt(ciphertext) + return self.token.decrypt(ciphertext) except Exception as e: RNS.log("Decryption failed on link "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR) diff --git a/RNS/Packet.py b/RNS/Packet.py index 8911c77..9569810 100755 --- a/RNS/Packet.py +++ b/RNS/Packet.py @@ -95,7 +95,7 @@ class Packet: # With an MTU of 500, the maximum of data we can # send in a single encrypted packet is given by # the below calculation; 383 bytes. - ENCRYPTED_MDU = math.floor((RNS.Reticulum.MDU-RNS.Identity.FERNET_OVERHEAD-RNS.Identity.KEYSIZE//16)/RNS.Identity.AES128_BLOCKSIZE)*RNS.Identity.AES128_BLOCKSIZE - 1 + ENCRYPTED_MDU = math.floor((RNS.Reticulum.MDU-RNS.Identity.TOKEN_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 """ diff --git a/docs/source/understanding.rst b/docs/source/understanding.rst index e458ebf..842b59f 100644 --- a/docs/source/understanding.rst +++ b/docs/source/understanding.rst @@ -868,13 +868,17 @@ both on general-purpose CPUs and on microcontrollers. The necessary primitives a * HKDF for key derivation -* Modified Fernet for encrypted tokens +* Encrypted tokens are based on the Fernet spec - * AES-128 in CBC mode + * Ephemeral keys derived from an ECDH key exchange on Curve25519 - * HMAC for message authentication + * AES-128 in CBC mode with PKCS7 padding - * No Version and Timestamp metadata included + * HMAC using SHA256 for message authentication + + * IVs are generated through os.urandom() + + * No Fernet version and timestamp metadata fields * SHA-256 @@ -884,12 +888,12 @@ In the default installation configuration, the ``X25519``, ``Ed25519`` and ``AES primitives are provided by `OpenSSL `_ (via the `PyCA/cryptography `_ package). The hashing functions ``SHA-256`` and ``SHA-512`` are provided by the standard Python `hashlib `_. The ``HKDF``, ``HMAC``, -``Fernet`` primitives, and the ``PKCS7`` padding function are always provided by the +``Token`` primitives, and the ``PKCS7`` padding function are always provided by the following internal implementations: - ``RNS/Cryptography/HKDF.py`` - ``RNS/Cryptography/HMAC.py`` -- ``RNS/Cryptography/Fernet.py`` +- ``RNS/Cryptography/Token.py`` - ``RNS/Cryptography/PKCS7.py``