Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix AES.test_lib result #14

Merged
merged 4 commits into from
Feb 7, 2025
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
46 changes: 18 additions & 28 deletions crypto_condor/primitives/AES.py
Original file line number Diff line number Diff line change
Expand Up @@ -1912,13 +1912,15 @@ def verify_file(filename: str, mode: Mode, operation: Operation) -> Results:


# --------------------------- Lib hook functions --------------------------------------


def _test_lib_enc(
ffi: cffi.FFI, lib, function: str, mode: Mode, key_length: KeyLength
) -> list[Results]:
) -> ResultsDict:
"""Tests CC_AES_encrypt.

Returns:
A list of results, from the ResultsDict returned by :func:`test`.
The dictionary of results returned by :func:`test`.
"""
logger.info("Testing harness function %s", function)

Expand All @@ -1936,17 +1938,16 @@ def _enc(key: bytes, plaintext: bytes, iv: bytes = b"") -> bytes:
enc(buf, len(plaintext), _key, len(key), _iv, len(iv))
return bytes(buf)

rd = test(_enc, None, mode, key_length) # type: ignore
return list(rd.values())
return test(_enc, None, mode, key_length) # type: ignore


def _test_lib_dec(
ffi: cffi.FFI, lib, function: str, mode: Mode, key_length: KeyLength
) -> list[Results]:
) -> ResultsDict:
"""Tests CC_AES_decrypt.

Returns:
A list of results, from the ResultsDict returned by :func:`test`.
The dictionary of results returned by :func:`test`.
"""
logger.info("Testing harness function %s", function)

Expand All @@ -1964,17 +1965,16 @@ def _dec(key: bytes, ciphertext: bytes, iv: bytes = b"") -> bytes:
dec(buf, len(ciphertext), _key, len(key), _iv, len(iv))
return bytes(buf)

rd = test(None, _dec, mode, key_length) # type: ignore
return list(rd.values())
return test(None, _dec, mode, key_length) # type: ignore


def _test_lib_enc_aead(
ffi: cffi.FFI, lib, function: str, mode: Mode, key_length: KeyLength
) -> list[Results]:
) -> ResultsDict:
"""Tests CC_AES_AEAD_encrypt.

Returns:
A list of results, from the ResultsDict returned by :func:`test`.
The dictionary of results returned by :func:`test`.
"""
logger.info("Testing harness function %s", function)

Expand Down Expand Up @@ -2017,17 +2017,16 @@ def _enc(
)
return (bytes(buf), bytes(mac_buf))

rd = test(_enc, None, mode, key_length) # type: ignore[arg-type]
return list(rd.values())
return test(_enc, None, mode, key_length) # type: ignore[arg-type]


def _test_lib_dec_aead(
ffi: cffi.FFI, lib, function: str, mode: Mode, key_length: KeyLength
) -> list[Results]:
) -> ResultsDict:
"""Tests CC_AES_AEAD_decrypt.

Returns:
A list of results, from the ResultsDict returned by :func:`test`.
The dictionary of results returned by :func:`test`.
"""
logger.info("Testing harness function %s", function)

Expand Down Expand Up @@ -2075,8 +2074,7 @@ def _dec(
else:
raise ValueError(f"Invalid returned value {rc} (expected 0 or -1)")

rd = test(None, _dec, mode, key_length) # type: ignore[arg-type]
return list(rd.values())
return test(None, _dec, mode, key_length) # type: ignore[arg-type]


def test_lib(ffi: cffi.FFI, lib, functions: list[str]) -> ResultsDict:
Expand Down Expand Up @@ -2145,21 +2143,13 @@ def test_lib(ffi: cffi.FFI, lib, functions: list[str]) -> ResultsDict:
# If the condition is false, it continues searching for a pattern.
match (mode, operation):
case (mode, Operation.ENCRYPT) if mode in Mode.classic_modes():
results[f"AES/test_lib_enc/{str(mode)}"] = _test_lib_enc(
ffi, lib, function, mode, key_size
)
results |= _test_lib_enc(ffi, lib, function, mode, key_size)
case (mode, Operation.ENCRYPT):
results[f"AES/test_lib_enc_aead/{str(mode)}"] = _test_lib_enc_aead(
ffi, lib, function, mode, key_size
)
results |= _test_lib_enc_aead(ffi, lib, function, mode, key_size)
case (mode, Operation.DECRYPT) if mode in Mode.classic_modes():
results[f"AES/test_lib_dec/{str(mode)}"] = _test_lib_dec(
ffi, lib, function, mode, key_size
)
results |= _test_lib_dec(ffi, lib, function, mode, key_size)
case (mode, Operation.DECRYPT):
results[f"AES/test_lib_dec_aead/{str(mode)}"] = _test_lib_dec_aead(
ffi, lib, function, mode, key_size
)
results |= _test_lib_dec_aead(ffi, lib, function, mode, key_size)

return results

Expand Down
2 changes: 1 addition & 1 deletion crypto_condor/primitives/RSASSA.py
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,7 @@ def _test_verify_pss_wycheproof(
"Tests a function that signs with RSASSA-PSS.",
{"hash_algorithm": hash_algorithm, "vectors file": filename},
)
results_dict["Wycheproof/verify/{filename}"] = results
results_dict[f"Wycheproof/verify/{filename}"] = results
logger.debug("Using vectors from: %s" % filename)
# Add Wycheproof notes to results.
results.add_notes(vectors_file.get("notes", {}))
Expand Down
51 changes: 38 additions & 13 deletions crypto_condor/primitives/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -577,21 +577,49 @@ def check(self, *, empty_as_fail: bool = False) -> bool:
return True


# Key duplication checks from: https://stackoverflow.com/a/30242574
class ResultsDict(dict):
"""A dictionary of Results.

This class extends the built-in dictionary to group :class:`Results` as values. The
keys are defined by the calling function. Currently key uniqueness is not enforced,
the caller is responsible for not overwriting previous results. See :meth:`add` for
a suggestion.
uniqueness of the keys is enforced: updating the dictionary or trying to set a key
already present will raise ValueError. To facilitate the creation of unique keys,
see the :meth:`add` method.

It provides the :meth:`check` method to check for failed results over all of its
values. It also defines a string representation with :meth:`__str__`, similar to
that of :class:`Results`.
It provides the :meth:`check` method to check if the results included have failed
tests. It also defines a string representation with :meth:`__str__`, similar to that
of :class:`Results`.
"""

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def __init__(self, other=None, **kwargs):
super().__init__()
self.update(other, **kwargs)

# TODO: override __ror__ too?
# TODO: how to get rid of the type ignore?
def __ior__(self, other): # type: ignore
"""Override method to raise ValueError on duplicate keys."""
if isinstance(other, ResultsDict):
self.update(other)
return self
else:
raise TypeError(f"Invalid type '{type(other)}' for operator |=")

def __setitem__(self, k, v):
"""Override method to raise ValueError on duplicate keys."""
if k in self:
raise ValueError(f"Duplicate key '{k}' in ResultsDict")
super().__setitem__(k, v)

def update(self, other=None, **kwargs):
"""Override method to raise ValueError on duplicate keys."""
if other is not None:
for k, v in (
other.items() if isinstance(other, collections.abc.Mapping) else other
):
self[k] = v
for k, v in kwargs.items():
self[k] = v

def __str__(self) -> str:
"""Returns a summary of the results contained."""
Expand Down Expand Up @@ -770,12 +798,9 @@ def process_results(
"valid inputs that the implementation should use correctly.\n"
)
description += (
"Invalid tests : "
"invalid inputs that the implementation should reject.\n"
)
description += (
"Acceptable tests: " "inputs for legacy cases or weak parameters."
"Invalid tests : invalid inputs that the implementation should reject.\n"
)
description += "Acceptable tests: inputs for legacy cases or weak parameters."
self.print(Panel(description, title="Types of tests"))
# Show results summary: give some general info like primitives tested and show
# the total count of tests. Include CC version as subtitle for reference.
Expand Down
File renamed without changes.
24 changes: 23 additions & 1 deletion tests/primitives/test_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
from hashlib import sha256
from pathlib import Path

import pytest

from crypto_condor.primitives import SHA
from crypto_condor.primitives.common import Console
from crypto_condor.primitives.common import Console, Results, ResultsDict


class TestConsole:
Expand All @@ -18,3 +20,23 @@ def test_filename_none(self, tmp_path: Path):
rd = SHA.test(lambda msg: sha256(msg).digest(), SHA.Algorithm.SHA_256)
console = Console()
assert console.process_results(rd, None)

def test_results_dict():
"""Tests that ResultsDict raises ValueError on duplicate keys."""
rd1 = ResultsDict()
rd2 = ResultsDict()

res1 = Results("AES", "test", "description", {"mode": "ECB"})
res2 = Results("AES", "test", "description", {"mode": "ECB"})

rd1.add(res1)
with pytest.raises(ValueError):
rd1.add(res2)

rd2.add(res1)
with pytest.raises(ValueError):
rd1.update(rd2)

with pytest.raises(ValueError):
rd1 |= rd2