Skip to content

Commit 929b7ab

Browse files
committed
feat: Convertendo cripto2 para classe
1 parent 2b37dc6 commit 929b7ab

2 files changed

Lines changed: 222 additions & 0 deletions

File tree

src/brseclabcripto/cripto3.py

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
"""
2+
Functions for AES256-GCM encryption/decryption (pyca/cryptography and gpg) and Argon2 hashing.
3+
This module provides functions to generate an AES key, encrypt and decrypt messages,
4+
and hash and verify passwords using Argon2 and SHA3-256.
5+
It uses the criptography library for AES encryption and Argon2 for password hashing.
6+
It is important to note that the AES key should be kept secret and secure.
7+
The Argon2 hash should also be stored securely, as it is used to verify passwords.
8+
This module is intended for educational purposes and should not be used in
9+
production without proper security measures.
10+
11+
Author: RAFAEL PERAZZO B MOTA
12+
Date: 2025-03-30
13+
Version: 1.1
14+
"""
15+
16+
import base64
17+
import secrets
18+
import cryptography
19+
from cryptography.hazmat.primitives import hashes, hmac
20+
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
21+
import argon2
22+
from argon2 import PasswordHasher
23+
24+
class SecCripto:
25+
"""
26+
Class for AES256-GCM encryption/decryption and Argon2 hashing.
27+
"""
28+
29+
def __init__(self,key):
30+
"""
31+
Initializes the SecCripto class with a given AES key.
32+
:param key: AES key (must be 16, 24, or 32 bytes long) - bytes or str
33+
"""
34+
if isinstance(key, str):
35+
# Convert hexadecimal string key to bytes
36+
try:
37+
self.key = bytes.fromhex(key)
38+
except ValueError as exc:
39+
# If the key is not a valid hexadecimal string, raise an error
40+
raise ValueError(
41+
"Invalid key format. Key must be a hexadecimal string."
42+
) from exc
43+
elif isinstance(key, bytes):
44+
# If the key is already in bytes, just assign it
45+
self.key = key
46+
else:
47+
# If the key is neither a string nor bytes, raise an error
48+
raise TypeError("Key must be a hexadecimal string or bytes.")
49+
# Check if the key length is valid (16, 24, or 32 bytes)
50+
if len(self.key) not in (16, 24, 32):
51+
raise ValueError(
52+
"Invalid key length. Key must be 16, 24, or 32 bytes long."
53+
)
54+
55+
def aes_gcm_encrypt(self,plaintext):
56+
"""
57+
Encrypts the plaintext using AES GCM encryption with a random nonce.
58+
:param plaintext: plaintext to be encrypted - bytes
59+
:return: ciphertext (nonce + ciphertext + tag) - base64 string
60+
"""
61+
if isinstance(plaintext, str):
62+
# Convert string plaintext to bytes
63+
plaintext = plaintext.encode()
64+
cipher = AESGCM(self.key)
65+
nonce = secrets.token_bytes(12) # Generate a random nonce
66+
ciphertext = cipher.encrypt(nonce, plaintext, None)
67+
return base64.b64encode(nonce + ciphertext).decode("utf-8")
68+
69+
def aes_gcm_decrypt(self,ciphertext):
70+
"""
71+
Decrypts the ciphertext using AES GCM decryption.
72+
:param ciphertext: ciphertext to be decrypted (nonce + ciphertext + tag)
73+
- bytes or base64 string
74+
:return: decrypted plaintext - string
75+
"""
76+
if isinstance(ciphertext, str):
77+
# Convert base64 string ciphertext to bytes
78+
ciphertext = base64.b64decode(ciphertext)
79+
nonce = ciphertext[:12]
80+
ciphertext = ciphertext[12:]
81+
cipher = AESGCM(self.key)
82+
plaintext = cipher.decrypt(nonce, ciphertext, None)
83+
return plaintext.decode("utf-8")
84+
85+
def hash_hmac(self,message):
86+
"""
87+
Computes the HMAC of the given message using the provided key.
88+
:param message: message to be hashed - bytes
89+
:return: HMAC - hexadecimal string
90+
"""
91+
if isinstance(message, str):
92+
# Convert string message to bytes
93+
message = message.encode()
94+
h = hmac.HMAC(self.key, hashes.SHA3_256())
95+
h.update(message)
96+
return h.finalize().hex()
97+
98+
def hash_hmac_verify(self, message, hmac_value):
99+
"""
100+
Verifies the HMAC of the given message using the provided key.
101+
:param message: message to be hashed - bytes or string
102+
:param hmac_value: HMAC to be verified - bytes or hexadecimal string
103+
:return: True if the HMAC is valid, False otherwise
104+
"""
105+
if isinstance(message, str):
106+
# Convert string message to bytes
107+
message = message.encode()
108+
if isinstance(hmac_value, str):
109+
# Convert hexadecimal string HMAC to bytes
110+
try:
111+
hmac_value = bytes.fromhex(hmac_value)
112+
except ValueError:
113+
# If the HMAC is not a valid hexadecimal string, return False
114+
return False
115+
h = hmac.HMAC(self.key, hashes.SHA3_256())
116+
h.update(message)
117+
try:
118+
h.verify(hmac_value)
119+
return True
120+
except cryptography.exceptions.InvalidSignature:
121+
return False
122+
123+
def hash_argon2id(self, password):
124+
"""
125+
Applies Argon2 hashing to the password using a HMAC.
126+
:param password: password to be hashed - string
127+
:return: Argon2 hash - string
128+
"""
129+
if isinstance(password, str):
130+
# Convert string password to bytes
131+
password = password.encode()
132+
# Create the HMAC
133+
signature = self.hash_hmac(password)
134+
# Apply Argon2 hashing
135+
ph = PasswordHasher(
136+
time_cost=3,
137+
memory_cost=65536,
138+
parallelism=4,
139+
hash_len=32,
140+
salt_len=16,
141+
encoding="utf-8",
142+
)
143+
hash_argon = ph.hash(signature)
144+
return hash_argon
145+
146+
def hash_argon2id_verify(self,hash_argon, password):
147+
"""
148+
Verifies if the Argon2 hash matches the password.
149+
:param hash_argon: stored Argon2 hash - string
150+
:param password: password to be verified - string
151+
:return: True if the password is correct, False otherwise
152+
"""
153+
if isinstance(password, str):
154+
# Convert string password to bytes
155+
password = password.encode()
156+
# Create the HMAC
157+
signature = self.hash_hmac(password)
158+
# Apply Argon2 hashing
159+
ph = PasswordHasher(
160+
time_cost=3,
161+
memory_cost=65536,
162+
parallelism=4,
163+
hash_len=32,
164+
salt_len=16,
165+
encoding="utf-8",
166+
)
167+
try:
168+
ph.verify(hash_argon, signature)
169+
return True
170+
except argon2.exceptions.VerifyMismatchError:
171+
return False
172+
173+
def sha256(self,message):
174+
"""
175+
Computes the SHA3-256 hash of the given message.
176+
:param message: message to be hashed - bytes or string
177+
:return: SHA3-256 hash - hexadecimal string
178+
"""
179+
if isinstance(message, str):
180+
# Convert string message to bytes
181+
message = message.encode()
182+
digest = hashes.Hash(hashes.SHA3_256())
183+
digest.update(message)
184+
return digest.finalize().hex()
185+
186+
def sha256_verify(self,message, hash_value):
187+
"""
188+
Verifies if the SHA3-256 hash matches the message.
189+
:param message: message to be verified - bytes or string
190+
:param hash_value: SHA3-256 hash to be verified - bytes or hexadecimal string
191+
:return: True if the hash is valid, False otherwise
192+
"""
193+
if isinstance(message, str):
194+
# Convert string message to bytes
195+
message = message.encode()
196+
if isinstance(hash_value, str):
197+
# Convert hexadecimal string hash to bytes
198+
try:
199+
hash_value = bytes.fromhex(hash_value)
200+
except ValueError:
201+
# If the hash is not a valid hexadecimal string, return False
202+
return False
203+
digest = hashes.Hash(hashes.SHA3_256())
204+
digest.update(message)
205+
return digest.finalize() == hash_value
206+
207+
def generate_aes_key(self,length):
208+
"""
209+
Generates a random AES key.
210+
length: length of the key in bytes (must be 16, 24, or 32)
211+
:return: AES key - hexadecimal string
212+
"""
213+
if length not in (16, 24, 32):
214+
raise ValueError("Key length must be 16, 24, or 32 bytes.")
215+
return secrets.token_bytes(length).hex()

tests/test_seccripto.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
'''
2+
Automated tests for the SecCripto module.
3+
'''
4+
import secrets
5+
from brseclabcripto.cripto3 import SecCripto
6+
7+

0 commit comments

Comments
 (0)