94 lines
2.5 KiB
Python
94 lines
2.5 KiB
Python
import os
|
|
import bcrypt
|
|
import base64
|
|
import hashlib
|
|
|
|
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
|
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
|
|
from cryptography.hazmat.primitives import hashes as _crypto_hashes
|
|
|
|
|
|
def HashPassword(password, rounds=12):
|
|
if isinstance(password, str):
|
|
password = password.encode('utf-8')
|
|
return bcrypt.hashpw(password, bcrypt.gensalt(rounds=rounds))
|
|
|
|
|
|
def VerifyPassword(password, hashed):
|
|
if isinstance(password, str):
|
|
password = password.encode('utf-8')
|
|
if isinstance(hashed, str):
|
|
hashed = hashed.encode('utf-8')
|
|
return bcrypt.checkpw(password, hashed)
|
|
|
|
|
|
def _derive_key(master_secret):
|
|
"""使用 HKDF-SHA256 从任意长度的主密钥派生 256-bit AES 密钥"""
|
|
if isinstance(master_secret, str):
|
|
master_secret = master_secret.encode('utf-8')
|
|
hkdf = HKDF(
|
|
algorithm=_crypto_hashes.SHA256(),
|
|
length=32,
|
|
salt=b'gvsds-crypto-salt',
|
|
info=b'aes-gcm-256',
|
|
)
|
|
return hkdf.derive(master_secret)
|
|
|
|
|
|
def SymmetricEncrypt(plaintext, master_secret):
|
|
"""使用 AES-256-GCM 加密
|
|
|
|
输出格式: base64(nonce_12 + ciphertext + tag_16)
|
|
"""
|
|
if isinstance(plaintext, str):
|
|
plaintext = plaintext.encode('utf-8')
|
|
key = _derive_key(master_secret)
|
|
aesgcm = AESGCM(key)
|
|
nonce = os.urandom(12)
|
|
ciphertext_with_tag = aesgcm.encrypt(nonce, plaintext, None)
|
|
return base64.b64encode(nonce + ciphertext_with_tag).decode('ascii')
|
|
|
|
|
|
def SymmetricDecrypt(ciphertext_b64, master_secret):
|
|
"""使用 AES-256-GCM 解密
|
|
|
|
输入格式: base64(nonce_12 + ciphertext + tag_16)
|
|
"""
|
|
raw = base64.b64decode(ciphertext_b64)
|
|
nonce = raw[:12]
|
|
ciphertext_with_tag = raw[12:]
|
|
key = _derive_key(master_secret)
|
|
aesgcm = AESGCM(key)
|
|
plaintext = aesgcm.decrypt(nonce, ciphertext_with_tag, None)
|
|
return plaintext.decode('utf-8')
|
|
|
|
|
|
def FileSHA1(filepath):
|
|
sha1 = hashlib.sha1()
|
|
with open(filepath, 'rb') as f:
|
|
while True:
|
|
chunk = f.read(8192)
|
|
if not chunk:
|
|
break
|
|
sha1.update(chunk)
|
|
return sha1.hexdigest()
|
|
|
|
|
|
def FileSHA256(filepath):
|
|
sha256 = hashlib.sha256()
|
|
with open(filepath, 'rb') as f:
|
|
while True:
|
|
chunk = f.read(8192)
|
|
if not chunk:
|
|
break
|
|
sha256.update(chunk)
|
|
return sha256.hexdigest()
|
|
|
|
|
|
def BytesSHA1(data):
|
|
return hashlib.sha1(data).hexdigest()
|
|
|
|
|
|
def BytesSHA256(data):
|
|
return hashlib.sha256(data).hexdigest()
|