codekingpro/portable-devtools
115k
1# SPDX-License-Identifier: MIT2 3"""4Low-level functions if you want to build your own higher level abstractions.5 6.. warning::7 This is a "Hazardous Materials" module. You should **ONLY** use it if8 you're 100% absolutely sure that you know what you're doing because this9 module is full of land mines, dragons, and dinosaurs with laser guns.10"""11 12from __future__ import annotations13 14from enum import Enum15from typing import Any, Literal16 17from _argon2_cffi_bindings import ffi, lib18 19from .exceptions import HashingError, VerificationError, VerifyMismatchError20 21 22__all__ = [23 "ARGON2_VERSION",24 "Type",25 "ffi",26 "hash_secret",27 "hash_secret_raw",28 "verify_secret",29]30 31ARGON2_VERSION = lib.ARGON2_VERSION_NUMBER32"""33The latest version of the Argon2 algorithm that is supported (and used by34default).35 36.. versionadded:: 16.1.037"""38 39 40class Type(Enum):41 """42 Enum of Argon2 variants.43 44 Please see :doc:`parameters` on how to pick one.45 """46 47 D = lib.Argon2_d48 I = lib.Argon2_i # noqa: E74149 ID = lib.Argon2_id50 51 52def hash_secret(53 secret: bytes,54 salt: bytes,55 time_cost: int,56 memory_cost: int,57 parallelism: int,58 hash_len: int,59 type: Type,60 version: int = ARGON2_VERSION,61) -> bytes:62 """63 Hash *secret* and return an **encoded** hash.64 65 An encoded hash can be directly passed into :func:`verify_secret` as it66 contains all parameters and the salt.67 68 Args:69 secret: Secret to hash.70 71 salt: A salt_. Should be random and different for each secret.72 73 type: Which Argon2 variant to use.74 75 version: Which Argon2 version to use.76 77 For an explanation of the Argon2 parameters see78 :class:`argon2.PasswordHasher`.79 80 Returns:81 An encoded Argon2 hash.82 83 Raises:84 argon2.exceptions.HashingError: If hashing fails.85 86 .. versionadded:: 16.0.087 88 .. _salt: https://en.wikipedia.org/wiki/Salt_(cryptography)89 """90 size = (91 lib.argon2_encodedlen(92 time_cost,93 memory_cost,94 parallelism,95 len(salt),96 hash_len,97 type.value,98 )99 + 1100 )101 buf = ffi.new("char[]", size)102 rv = lib.argon2_hash(103 time_cost,104 memory_cost,105 parallelism,106 ffi.new("uint8_t[]", secret),107 len(secret),108 ffi.new("uint8_t[]", salt),109 len(salt),110 ffi.NULL,111 hash_len,112 buf,113 size,114 type.value,115 version,116 )117 if rv != lib.ARGON2_OK:118 raise HashingError(error_to_str(rv))119 120 return ffi.string(buf) # type: ignore[no-any-return]121 122 123def hash_secret_raw(124 secret: bytes,125 salt: bytes,126 time_cost: int,127 memory_cost: int,128 parallelism: int,129 hash_len: int,130 type: Type,131 version: int = ARGON2_VERSION,132) -> bytes:133 """134 Hash *password* and return a **raw** hash.135 136 This function takes the same parameters as :func:`hash_secret`.137 138 .. versionadded:: 16.0.0139 """140 buf = ffi.new("uint8_t[]", hash_len)141 142 rv = lib.argon2_hash(143 time_cost,144 memory_cost,145 parallelism,146 ffi.new("uint8_t[]", secret),147 len(secret),148 ffi.new("uint8_t[]", salt),149 len(salt),150 buf,151 hash_len,152 ffi.NULL,153 0,154 type.value,155 version,156 )157 if rv != lib.ARGON2_OK:158 raise HashingError(error_to_str(rv))159 160 return bytes(ffi.buffer(buf, hash_len))161 162 163def verify_secret(hash: bytes, secret: bytes, type: Type) -> Literal[True]:164 """165 Verify whether *secret* is correct for *hash* of *type*.166 167 Args:168 hash:169 An encoded Argon2 hash as returned by :func:`hash_secret`.170 171 secret:172 The secret to verify whether it matches the one in *hash*.173 174 type: Type for *hash*.175 176 Raises:177 argon2.exceptions.VerifyMismatchError:178 If verification fails because *hash* is not valid for *secret* of179 *type*.180 181 argon2.exceptions.VerificationError:182 If verification fails for other reasons.183 184 Returns:185 ``True`` on success, raise :exc:`~argon2.exceptions.VerificationError`186 otherwise.187 188 .. versionadded:: 16.0.0189 .. versionchanged:: 16.1.0190 Raise :exc:`~argon2.exceptions.VerifyMismatchError` on mismatches191 instead of its more generic superclass.192 """193 rv = lib.argon2_verify(194 ffi.new("char[]", hash),195 ffi.new("uint8_t[]", secret),196 len(secret),197 type.value,198 )199 200 if rv == lib.ARGON2_OK:201 return True202 203 if rv == lib.ARGON2_VERIFY_MISMATCH:204 raise VerifyMismatchError(error_to_str(rv))205 206 raise VerificationError(error_to_str(rv))207 208 209def core(context: Any, type: int) -> int:210 """211 Direct binding to the ``argon2_ctx`` function.212 213 .. warning::214 This is a strictly advanced function working on raw C data structures.215 Both Argon2's and *argon2-cffi*'s higher-level bindings do a lot of216 sanity checks and housekeeping work that *you* are now responsible for217 (e.g. clearing buffers). The structure of the *context* object can,218 has, and will change with *any* release!219 220 Use at your own peril; *argon2-cffi* does *not* use this binding221 itself.222 223 Args:224 context:225 A CFFI Argon2 context object (i.e. an ``struct Argon2_Context`` /226 ``argon2_context``).227 228 type:229 Which Argon2 variant to use. You can use the ``value`` field of230 :class:`Type`'s fields.231 232 Returns:233 An Argon2 error code. Can be transformed into a string using234 :func:`error_to_str`.235 236 .. versionadded:: 16.0.0237 """238 return lib.argon2_ctx(context, type) # type: ignore[no-any-return]239 240 241def error_to_str(error: int) -> str:242 """243 Convert an Argon2 error code into a native string.244 245 Args:246 error: An Argon2 error code as returned by :func:`core`.247 248 Returns:249 A human-readable string describing the error.250 251 .. versionadded:: 16.0.0252 """253 return ffi.string(lib.argon2_error_message(error)).decode("ascii") # type: ignore[no-any-return]254 