|
|
|
|
@ -17,6 +17,17 @@ _SCRYPT_R: Final = 8
@@ -17,6 +17,17 @@ _SCRYPT_R: Final = 8
|
|
|
|
|
_SCRYPT_P: Final = 1 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _scrypt_maxmem_bytes(n: int, r: int) -> int: |
|
|
|
|
"""Upper bound for ``hashlib.scrypt(..., maxmem=…)`` OpenSSL will accept. |
|
|
|
|
|
|
|
|
|
RFC 7914 block mixing uses about ``128 * N * r`` bytes; some OpenSSL 3.x |
|
|
|
|
builds still raise *memory limit exceeded* when ``maxmem`` is only that |
|
|
|
|
minimum, so we add a fixed and proportional allowance. |
|
|
|
|
""" |
|
|
|
|
romix = 128 * n * r |
|
|
|
|
return romix + max(64 * 1024 * 1024, romix // 8) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _normalize_password(password: str) -> bytes: |
|
|
|
|
return unicodedata.normalize("NFKC", password).encode("utf-8") |
|
|
|
|
|
|
|
|
|
@ -25,12 +36,17 @@ def _decode_ncryptsec(ncryptsec: str) -> bytes:
@@ -25,12 +36,17 @@ def _decode_ncryptsec(ncryptsec: str) -> bytes:
|
|
|
|
|
hrp, data = bech32.bech32_decode(ncryptsec) |
|
|
|
|
if hrp != "ncryptsec" or data is None: |
|
|
|
|
raise ValueError("invalid ncryptsec hrp or data") |
|
|
|
|
return bytes(bech32.convertbits(data, 5, 8, False)) |
|
|
|
|
conv = bech32.convertbits(data, 5, 8, False) |
|
|
|
|
if conv is None: |
|
|
|
|
raise ValueError("invalid ncryptsec payload") |
|
|
|
|
return bytes(conv) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _encode_ncryptsec(payload: bytes) -> str: |
|
|
|
|
data = bech32.convertbits(list(payload), 8, 5, True) |
|
|
|
|
return bech32.bech32_encode("ncryptsec", data) |
|
|
|
|
words = bech32.convertbits(list(payload), 8, 5, True) |
|
|
|
|
if words is None: |
|
|
|
|
raise ValueError("invalid ncryptsec encoding") |
|
|
|
|
return bech32.bech32_encode("ncryptsec", words) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def decrypt_ncryptsec(ncryptsec: str, password: str) -> bytes: |
|
|
|
|
@ -47,8 +63,7 @@ def decrypt_ncryptsec(ncryptsec: str, password: str) -> bytes:
@@ -47,8 +63,7 @@ def decrypt_ncryptsec(ncryptsec: str, password: str) -> bytes:
|
|
|
|
|
aad = raw[42:43] |
|
|
|
|
ciphertext = raw[43:] |
|
|
|
|
n = 1 << log_n |
|
|
|
|
# OpenSSL scrypt enforces a max memory bound; raise for large log_n (e.g. 20 ~= 1 GiB). |
|
|
|
|
maxmem = max(256 * 1024 * 1024, 128 * n * _SCRYPT_R) |
|
|
|
|
maxmem = _scrypt_maxmem_bytes(n, _SCRYPT_R) |
|
|
|
|
key = hashlib.scrypt( |
|
|
|
|
_normalize_password(password), |
|
|
|
|
salt=salt, |
|
|
|
|
@ -72,7 +87,7 @@ def encrypt_to_ncryptsec(secret_key: bytes, password: str, log_n: int = _DEFAULT
@@ -72,7 +87,7 @@ def encrypt_to_ncryptsec(secret_key: bytes, password: str, log_n: int = _DEFAULT
|
|
|
|
|
nonce = os.urandom(24) |
|
|
|
|
aad = bytes([KEY_SECURITY_MEDIUM]) |
|
|
|
|
n = 1 << log_n |
|
|
|
|
maxmem = max(256 * 1024 * 1024, 128 * n * _SCRYPT_R) |
|
|
|
|
maxmem = _scrypt_maxmem_bytes(n, _SCRYPT_R) |
|
|
|
|
sym = hashlib.scrypt( |
|
|
|
|
_normalize_password(password), |
|
|
|
|
salt=salt, |
|
|
|
|
|