Revision control

Copy as Markdown

Other Tools

"""
(C) 2026 Jack Lloyd
(C) 2026 René Meusel, Rohde & Schwarz Cybersecurity
Botan is released under the Simplified BSD License (see license.txt)
"""
import binascii
import unittest
import botan3 as botan
from .wycheproof import WycheproofTests
def _from_hex(value: str) -> bytes:
return binascii.unhexlify(value)
def _map_curve_name(wycheproof_curve: str) -> str:
"""Map Wycheproof curve name to Botan curve name."""
mapping = {
"brainpoolP224r1": "brainpool224r1",
"brainpoolP256r1": "brainpool256r1",
"brainpoolP384r1": "brainpool384r1",
"brainpoolP512r1": "brainpool512r1",
}
return mapping.get(wycheproof_curve, wycheproof_curve)
class TestECDH(WycheproofTests, unittest.TestCase):
def input_files(self) -> list[str]:
return [
"ecdh_secp224r1_test.json",
"ecdh_secp256r1_test.json",
"ecdh_secp256k1_test.json",
"ecdh_secp384r1_test.json",
"ecdh_secp521r1_test.json",
"ecdh_brainpoolP224r1_test.json",
"ecdh_brainpoolP256r1_test.json",
"ecdh_brainpoolP384r1_test.json",
"ecdh_brainpoolP512r1_test.json",
]
def run_test(self, data: dict, group: dict, test: dict) -> None:
curve = _map_curve_name(group["curve"])
if not botan.ECGroup.supports_named_group(curve):
self.skipTest(f"Curve {curve} not supported in this build")
try:
priv_value = botan.MPI(test["private"], radix=16)
priv_key = botan.PrivateKey.load_ecdh(curve, priv_value)
except botan.BotanException:
if test["result"] != "valid":
return
raise
try:
pub_key = botan.PublicKey.load(_from_hex(test["public"]))
except botan.BotanException:
if test["result"] != "valid":
return
raise
explicit_encoding = pub_key.used_explicit_encoding()
if explicit_encoding:
# Wycheproof encodes ECDH public keys as DER encoded EC points that
# inherently allow an explicit encoding of the EC group. The Python
# API can load such keys generically (PublicKey.load()), but the
# PKKeyAgreement class expects the public value as SEC1 encoding
# which does not allow the explicit encoding of the EC group.
#
# Therefore, PKKeyAgreement.agree() cannot detect the discrepancy in
# the underlying EC groups and we need to handle this case manually.
self.assertIn(test["result"], ("invalid", "acceptable"))
return
try:
ka = botan.PKKeyAgreement(priv_key, "Raw")
shared_secret = ka.agree(pub_key.to_raw(), 0, b"")
except botan.BotanException:
if test["result"] != "valid":
return
raise
self.assertIn(
test["result"],
("valid", "acceptable"),
"Invalid test case produced a shared secret",
)
self.assertEqual(
shared_secret,
_from_hex(test["shared"]),
"Shared secret does not match expected value",
)