Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions scripts/build_ffi.py
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,9 @@ def get_features(local_wolfssl, features):
features["ML_DSA"] = 1 if '#define HAVE_DILITHIUM' in defines else 0
features["ML_KEM"] = 1 if '#define WOLFSSL_HAVE_MLKEM' in defines else 0
features["HKDF"] = 1 if "#define HAVE_HKDF" in defines else 0
# Unlike the other fatures, HASHDRBG is enabled by default in random.h, unless WC_NO_HASHDRBG or
# CUSTOM_RAND_GENERATE_BLOCK is defined.
features["HASHDRBG"] = 0 if ("#define WC_NO_HASHDRBG" in defines or "#define CUSTOM_RAND_GENERATE_BLOCK" in defines) else 1

if '#define HAVE_FIPS' in defines:
if not fips:
Expand Down Expand Up @@ -497,6 +500,7 @@ def build_ffi(local_wolfssl, features):
int ML_KEM_ENABLED = {features["ML_KEM"]};
int ML_DSA_ENABLED = {features["ML_DSA"]};
int HKDF_ENABLED = {features["HKDF"]};
int HASHDRBG_ENABLED = {features["HASHDRBG"]};
"""

ffibuilder.set_source( "wolfcrypt._ffi", init_source_string,
Expand Down Expand Up @@ -537,6 +541,7 @@ def build_ffi(local_wolfssl, features):
extern int ML_KEM_ENABLED;
extern int ML_DSA_ENABLED;
extern int HKDF_ENABLED;
extern int HASHDRBG_ENABLED;

typedef unsigned char byte;
typedef unsigned int word32;
Expand All @@ -551,6 +556,10 @@ def build_ffi(local_wolfssl, features):
int wc_RNG_GenerateByte(WC_RNG*, byte*);
int wc_FreeRng(WC_RNG*);
"""
if features["HASHDRBG"]:
cdef += """
int wc_RNG_DRBG_Reseed(WC_RNG*, const byte*, word32);
"""

if features["ERROR_STRINGS"]:
cdef += """
Expand Down Expand Up @@ -1369,6 +1378,7 @@ def main(ffibuilder):
"ML_KEM": 1,
"ML_DSA": 1,
"HKDF": 1,
"HASHDRBG": 1,
}

# Ed448 requires SHAKE256, which isn't part of the Windows build, yet.
Expand Down
32 changes: 32 additions & 0 deletions tests/test_random.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
# pylint: disable=redefined-outer-name

import pytest
from wolfcrypt._ffi import lib as _lib
from wolfcrypt.random import Random


Expand All @@ -38,13 +39,44 @@ def test_bytes(rng):
assert len(rng.bytes(8)) == 8
assert len(rng.bytes(128)) == 128


@pytest.fixture
def rng_nonce():
return Random(b"abcdefghijklmnopqrstuv")


def test_nonce_byte(rng_nonce):
assert len(rng_nonce.byte()) == 1


@pytest.mark.parametrize("length", (1, 8, 128))
def test_nonce_bytes(rng_nonce, length):
assert len(rng_nonce.bytes(length)) == length


@pytest.mark.skipif(not _lib.HASHDRBG_ENABLED, reason="Reseeding only available with hash-DRBG")
Comment thread
dgarske marked this conversation as resolved.
@pytest.mark.parametrize("seed_size", [0, 1, 32, 1000])
def test_reseed_sizes(rng, seed_size):
"""
Test that reseeding the random number generator works, for various seed sizes.
"""
# Create seed of required length.
seed = bytes(x % 256 for x in range(seed_size))
assert len(seed) == seed_size
rng.reseed(seed)
# Pull some bytes from the random number generator to test that it still works.
rng.bytes(32)


@pytest.mark.skipif(not _lib.HASHDRBG_ENABLED, reason="Reseeding only available with hash-DRBG")
Comment thread
dgarske marked this conversation as resolved.
def test_reseed_multiple(rng):
Comment thread
dgarske marked this conversation as resolved.
"""
Test that consecutive reseeding of the random number generator works.
"""
for _ in range(10):
# Create seed of typical size. Testing with various seed sizes done in `test_reseed_sizes`.
seed = bytes(x % 256 for x in range(32))
rng.reseed(seed)

# Pull some bytes from the random number generator to test that it still works.
rng.bytes(100)
1 change: 1 addition & 0 deletions windows/non_fips/user_settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#endif

#define WOLFCRYPT_ONLY
#define HAVE_HASHDRBG
#define WOLFSSL_AESGCM_STREAM
#define HAVE_AESGCM
#define GCM_TABLE_4BIT
Expand Down
2 changes: 2 additions & 0 deletions wolfcrypt/_ffi/lib.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ SHA256_ENABLED: int
SHA384_ENABLED: int
SHA512_ENABLED: int
WC_RNG_SEED_CB_ENABLED: int
HASHDRBG_ENABLED: int

FIPS_VERSION: int

Expand Down Expand Up @@ -73,4 +74,5 @@ RNG: TypeAlias = FFI.CData
def wc_InitRngNonce_ex(rng: RNG, nonce: bytes, nonce_size: int, heap: FFI.CData, device_id: int) -> int: ...
def wc_RNG_GenerateByte(rng: RNG, buffer: FFI.CData) -> int: ...
def wc_RNG_GenerateBlock(rng: RNG, buffer: FFI.CData, len: int) -> int: ...
def wc_RNG_DRBG_Reseed(rng: RNG, seed: bytes, seed_size: int) -> int: ...
def wc_FreeRng(rng: RNG) -> None: ...
10 changes: 10 additions & 0 deletions wolfcrypt/random.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,13 @@ def bytes(self, length: int) -> __builtins__.bytes:
raise WolfCryptApiError("RNG generate block error", ret)

return _ffi.buffer(result, length)[:]

if _lib.HASHDRBG_ENABLED:
Comment thread
dgarske marked this conversation as resolved.
def reseed(self, seed: __builtins__.bytes) -> None:
Comment thread
dgarske marked this conversation as resolved.
"""
Reseed the DRBG with the provided seed material.
"""
assert self.native_object is not None
ret = _lib.wc_RNG_DRBG_Reseed(self.native_object, seed, len(seed))
if ret < 0: # pragma: no cover
raise WolfCryptApiError("RNG reseed error", ret)
Loading