88These functions rely on the cryptography library and are intended for use within Coldwire's higher-level protocol logic.
99"""
1010
11- from cryptography .hazmat .primitives .ciphers .aead import AESGCM
11+ from cryptography .hazmat .primitives .ciphers .aead import ChaCha20Poly1305
1212from cryptography .hazmat .primitives .kdf .argon2 import Argon2id
13+ from cryptography .hazmat .primitives import hashes
14+ from cryptography .hazmat .primitives .kdf .hkdf import HKDF
1315from core .constants import (
1416 OTP_PAD_SIZE ,
15- AES_GCM_NONCE_LEN ,
17+ CHACHA20POLY1305_NONCE_LEN ,
1618 ARGON2_ITERS ,
1719 ARGON2_MEMORY ,
1820 ARGON2_LANES ,
@@ -39,6 +41,14 @@ def sha3_512(data: bytes) -> bytes:
3941 return h .digest ()
4042
4143
44+ def hkdf (key : bytes , length : int = 32 , salt : bytes = None , info : bytes = None ) -> bytes :
45+ return HKDF (
46+ algorithm = hashes .SHA3_256 (),
47+ length = length ,
48+ salt = salt ,
49+ info = info ,
50+ ).derive (key )
51+
4252def derive_key_argon2id (password : bytes , salt : bytes = None , salt_length : int = ARGON2_SALT_LEN , output_length : int = ARGON2_OUTPUT_LEN ) -> tuple [bytes , bytes ]:
4353 """
4454 Derive a symmetric key from a password using Argon2id.
@@ -70,42 +80,49 @@ def derive_key_argon2id(password: bytes, salt: bytes = None, salt_length: int =
7080 return derived_key , salt
7181
7282
73- def encrypt_aes_gcm (key : bytes , plaintext : bytes ) -> tuple [bytes , bytes ]:
83+ def encrypt_chacha20poly1305 (key : bytes , plaintext : bytes , counter : int = None , counter_safety : int = 2 ** 32 ) -> tuple [bytes , bytes ]:
7484 """
75- Encrypt plaintext using AES-256 in GCM mode .
85+ Encrypt plaintext using ChaCha20Poly1305 .
7686
7787 A random nonce is generated for each encryption.
7888
7989 Args:
80- key: A 32-byte AES key.
90+ key: A 32-byte ChaCha20Poly1305 key.
8191 plaintext: Data to encrypt.
92+ counter: an (optional) number to add to nonce
8293
8394 Returns:
8495 A tuple (nonce, ciphertext) where:
8596 - nonce: The randomly generated AES-GCM nonce.
8697 - ciphertext: The encrypted data including the authentication tag.
8798 """
88- nonce = secrets .token_bytes (AES_GCM_NONCE_LEN )
89- aes_gcm = AESGCM (key )
90- ciphertext = aes_gcm .encrypt (nonce , plaintext , None )
99+ nonce = secrets .token_bytes (CHACHA20POLY1305_NONCE_LEN )
100+ if counter is not None :
101+ if counter > counter_safety :
102+ raise ValueError ("ChaCha counter has overflowen" )
103+
104+ nonce = nonce [:CHACHA20POLY1305_NONCE_LEN - 4 ] + counter .to_bytes (4 , "big" )
105+
106+ chacha = ChaCha20Poly1305 (key )
107+ ciphertext = chacha .encrypt (nonce , plaintext , None )
91108 return nonce , ciphertext
92109
93110
94- def decrypt_aes_gcm (key : bytes , nonce : bytes , ciphertext : bytes ) -> bytes :
111+ def decrypt_chacha20poly1305 (key : bytes , nonce : bytes , ciphertext : bytes ) -> bytes :
95112 """
96- Decrypt ciphertext using AES-256 in GCM mode .
113+ Decrypt ciphertext using ChaCha20Poly1305 .
97114
98115 Raises an exception if authentication fails.
99116
100117 Args:
101- key: The 32-byte AES key used for encryption.
118+ key: The 32-byte ChaCha20Poly1305 key used for encryption.
102119 nonce: The nonce used during encryption.
103120 ciphertext: The encrypted data including the authentication tag.
104121
105122 Returns:
106123 The decrypted plaintext bytes.
107124 """
108- aes_gcm = AESGCM (key )
109- return aes_gcm .decrypt (nonce , ciphertext , None )
125+ chacha = ChaCha20Poly1305 (key )
126+ return chacha .decrypt (nonce , ciphertext , None )
110127
111128
0 commit comments