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
12- from cryptography .hazmat .primitives .kdf .argon2 import Argon2id
11+ from nacl import pwhash , bindings
1312from core .constants import (
1413 OTP_PAD_SIZE ,
15- AES_GCM_NONCE_LEN ,
14+ XCHACHA20POLY1305_NONCE_LEN ,
1615 ARGON2_ITERS ,
1716 ARGON2_MEMORY ,
1817 ARGON2_LANES ,
@@ -39,7 +38,7 @@ def sha3_512(data: bytes) -> bytes:
3938 return h .digest ()
4039
4140
42- def derive_key_argon2id (password : bytes , salt : bytes = None , salt_length : int = ARGON2_SALT_LEN , output_length : int = ARGON2_OUTPUT_LEN ) -> tuple [bytes , bytes ]:
41+ def derive_key_argon2id (password : bytes , salt : bytes = None , output_length : int = ARGON2_OUTPUT_LEN ) -> tuple [bytes , bytes ]:
4342 """
4443 Derive a symmetric key from a password using Argon2id.
4544
@@ -57,55 +56,60 @@ def derive_key_argon2id(password: bytes, salt: bytes = None, salt_length: int =
5756 - salt: The salt used for derivation.
5857 """
5958 if salt is None :
60- salt = secrets .token_bytes (salt_length )
59+ salt = secrets .token_bytes (ARGON2_SALT_LEN )
6160
62- kdf = Argon2id (
63- salt = salt ,
64- iterations = ARGON2_ITERS ,
65- memory_cost = ARGON2_MEMORY ,
66- length = output_length ,
67- lanes = ARGON2_LANES
68- )
69- derived_key = kdf .derive (password )
70- return derived_key , salt
61+ return pwhash .argon2id .kdf (
62+ output_length ,
63+ password ,
64+ salt ,
65+ opslimit = ARGON2_ITERS ,
66+ memlimit = ARGON2_MEMORY
67+ ), salt
7168
7269
73- def encrypt_aes_gcm (key : bytes , plaintext : bytes ) -> tuple [bytes , bytes ]:
70+ def encrypt_xchacha20poly1305 (key : bytes , plaintext : bytes , counter : int = None , counter_safety : int = 2 ** 32 ) -> tuple [bytes , bytes ]:
7471 """
75- Encrypt plaintext using AES-256 in GCM mode .
72+ Encrypt plaintext using ChaCha20Poly1305 .
7673
7774 A random nonce is generated for each encryption.
7875
7976 Args:
80- key: A 32-byte AES key.
77+ key: A 32-byte ChaCha20Poly1305 key.
8178 plaintext: Data to encrypt.
79+ counter: an (optional) number to add to nonce
8280
8381 Returns:
8482 A tuple (nonce, ciphertext) where:
8583 - nonce: The randomly generated AES-GCM nonce.
8684 - ciphertext: The encrypted data including the authentication tag.
8785 """
88- nonce = secrets .token_bytes (AES_GCM_NONCE_LEN )
89- aes_gcm = AESGCM (key )
90- ciphertext = aes_gcm .encrypt (nonce , plaintext , None )
86+ nonce = secrets .token_bytes (XCHACHA20POLY1305_NONCE_LEN )
87+ if counter is not None :
88+ if counter > counter_safety :
89+ raise ValueError ("ChaCha counter has overflowen" )
90+
91+ nonce = nonce [:XCHACHA20POLY1305_NONCE_LEN - 4 ] + counter .to_bytes (4 , "big" )
92+
93+ ciphertext = bindings .crypto_aead_xchacha20poly1305_ietf_encrypt (plaintext , None , nonce , key )
94+
9195 return nonce , ciphertext
9296
9397
94- def decrypt_aes_gcm (key : bytes , nonce : bytes , ciphertext : bytes ) -> bytes :
98+ def decrypt_xchacha20poly1305 (key : bytes , nonce : bytes , ciphertext : bytes ) -> bytes :
9599 """
96- Decrypt ciphertext using AES-256 in GCM mode .
100+ Decrypt ciphertext using ChaCha20Poly1305 .
97101
98102 Raises an exception if authentication fails.
99103
100104 Args:
101- key: The 32-byte AES key used for encryption.
105+ key: The 32-byte ChaCha20Poly1305 key used for encryption.
102106 nonce: The nonce used during encryption.
103107 ciphertext: The encrypted data including the authentication tag.
104108
105109 Returns:
106110 The decrypted plaintext bytes.
107111 """
108- aes_gcm = AESGCM ( key )
109- return aes_gcm . decrypt ( nonce , ciphertext , None )
112+
113+ return bindings . crypto_aead_xchacha20poly1305_ietf_decrypt ( ciphertext , None , nonce , key )
110114
111115
0 commit comments