ECDH#

Module for test ECDH implementations.

The crypto_condor.primitives.ECDH module can test implementations of the ECDH key exchange with the test_exchange() function.

Test implementations#

crypto_condor.primitives.ECDH.test_exchange(ecdh, curve, *, compliance=True, resilience=False)#

Tests an implementation of ECDH.

Parameters:
  • ecdh (ECDH) – The implementation to test. It must conform to the ECDH protocol.

  • curve (Curve) – The elliptic curve to use. For available test vectors, see Curve.

Keyword Arguments:
  • compliance – Whether to use NIST vectors.

  • resilience – Whether to use Wycheproof vectors.

Returns:

The results of testing with NIST and Wycheproof vectors, depending on the options used. Dictionary keys are ECDH/test_exchange_nist/<curve name> and ECDH/test_exchange_wycheproof/<curve name>. The dictionary can be empty if there are no vectors from the sources selected.

Return type:

ResultsDict

Notes

Internally calls the test_exchange_nist() and test_exchange_wycheproof() functions.

Example

Let’s test PyCryptodome’s implementation with NIST vectors on the P-192 curve.

>>> from Crypto.Protocol import DH
>>> from Crypto.PublicKey import ECC
>>> # Build a class that conforms to the ECDH protocol.
>>> class MyEcdh:
...     def exchange_nist(
...         self,
...         secret: int,
...         pub_x: int,
...         pub_y: int,
...         pub_key: bytes
...     ) -> bytes:
...         # We can use the coordinates directly.
...         pk = ECC.construct(curve="P-192", point_x=pub_x, point_y=pub_y)
...         # And we only need the secret value to construct the private key.
...         sk = ECC.construct(curve="P-192", d=secret)
...         # We want the raw shared secret, so we use a KDF that does nothing.
...         shared = DH.key_agreement(
...             static_priv=sk,
...             static_pub=pk,
...             kdf=lambda x: x,
...         )
...         return shared
...     def exchange_wycheproof(
...         self,
...         secret,
...         public_key,
...     ) -> bytes:
...         # We define the Wycheproof way to ensure the test is skipped if the
...         # exception is raised.
...         raise NotImplementedError

We can now test this implementation:

>>> from crypto_condor.primitives import ECDH
>>> curve = ECDH.Curve("P-192")
>>> rdict = ECDH.test_exchange(MyEcdh(), curve)
[NIST] ...

We can check the result of testing with the NIST vectors.

>>> res = rdict["ECDH/test_exchange_nist/P-192"]
>>> assert res.check()

Parameters#

enum crypto_condor.primitives.ECDH.Curve(value)#

Elliptic curves supported for ECDH.

As shown below, not all curves have NIST and Wycheproof vectors. To determine whether a curve has a specific source programmatically, see nist and wycheproof.

Test vector sources#

Curve

NIST

Wycheproof

P-224, P-256, P-384, P-521

Y

Y

B-283, B-409, B-571

Y

Y

K-283, K-409, K-571

Y

Y

P-192

Y

N

B-163, B-233

Y

N

K-163, K-233

Y

N

secp256k1

N

Y

brainpool*

N

Y

Member Type:

str

Valid values are as follows:

P192 = <Curve.P192: 'P-192'>#
P224 = <Curve.P224: 'P-224'>#
P256 = <Curve.P256: 'P-256'>#
P384 = <Curve.P384: 'P-384'>#
P521 = <Curve.P521: 'P-521'>#
K163 = <Curve.K163: 'K-163'>#
K233 = <Curve.K233: 'K-233'>#
K283 = <Curve.K283: 'K-283'>#
K409 = <Curve.K409: 'K-409'>#
K571 = <Curve.K571: 'K-571'>#
B163 = <Curve.B163: 'B-163'>#
B233 = <Curve.B233: 'B-233'>#
B283 = <Curve.B283: 'B-283'>#
B409 = <Curve.B409: 'B-409'>#
B571 = <Curve.B571: 'B-571'>#
BRAINPOOLP224R1 = <Curve.BRAINPOOLP224R1: 'brainpoolP224r1'>#
BRAINPOOLP256R1 = <Curve.BRAINPOOLP256R1: 'brainpoolP256r1'>#
BRAINPOOLP320R1 = <Curve.BRAINPOOLP320R1: 'brainpoolP320r1'>#
BRAINPOOLP384R1 = <Curve.BRAINPOOLP384R1: 'brainpoolP384r1'>#
BRAINPOOLP512R1 = <Curve.BRAINPOOLP512R1: 'brainpoolP512r1'>#
SECP256K1 = <Curve.SECP256K1: 'secp256k1'>#

The Enum and its members also have the following methods:

property nist: bool#

True if there are NIST vectors for this curve.

property wycheproof: bool#

True if there are Wycheproof vectors for this curve.

property ec_curve: EllipticCurve | None#

Returns an instance of the elliptic curve from cryptography.

This property is intended for test_exchange_nist() to serialize the EC point to SEC1 format. As such, only curves for which there are NIST vectors are returned. Returns None otherwise.

Protocols#

protocol crypto_condor.primitives.ECDH.ECDH#

Class that implements ECDH.

Implementations use one party’s private value and the other’s public key to perform their half of the key exchange.

There are two methods to implement which depend on the test vectors used: exchange_nist() uses NIST vectors which provide the public key by its coordinates, while the Wycheproof vectors used by exchange_wycheproof() provide them encoded with X509.

For compliance, use exchange_nist().

Classes that implement this protocol must have the following methods / attributes:

exchange_nist(secret, pub_x, pub_y, pub_key)#

ECDH exchange with NIST vectors.

NIST vectors provide the public key as point coordinates. In case an implementation does not deal with coordinates, but at least can deal with an SEC1-encoded point (subset of X9.62), crypto-condor constructs and provided this encoded point. However, we recommend using the coordinates whenever possible.

Parameters:
  • secret (int) – Party A’s secret value.

  • pub_x (int) – The x-coordinate of party B’s public key.

  • pub_y (int) – The y-coordinate of party B’s public key.

  • pub_key (bytes) – The public key as a SEC1-encoded point. It does not provide information on the curve used. Constructed by crypto-condor from the coordinates.

Returns:

The shared key.

Return type:

bytes

exchange_wycheproof(secret, pub_key)#

ECDH exchange with Wycheproof vectors.

Wycheproof vectors provide the public key encoded with X509. This encoding includes information about the curve, along with the coordinates.

Parameters:
  • secret (int) – Party A’s secret value.

  • pub_key (bytes) – Party B’s public key, encoded with X509.

Returns:

The shared key.

Return type:

bytes

Vectors#

class crypto_condor.primitives.ECDH.EcdhVectors(curve, nist, wycheproof)#

Test vectors for ECDH.

Do not instantiate directly, use load().

Available vectors are defined by Curve.

Parameters:
  • curve (Curve) – The elliptic curve used for the test vectors.

  • nist (EcdhNistVectors | None) – NIST test vectors if they exist for the given curve, None otherwise.

  • wycheproof (EcdhWycheproofVectors | None) – Wycheproof test vectors if they exist for the given curve, None otherwise.

classmethod load(curve, *, compliance=True, resilience=True)#

Loads ECDH test vectors.

Parameters:

curve (Curve) – The elliptic curve to get vectors for.

Keyword Arguments:
  • compliance – Whether to load NIST vectors.

  • resilience – Whether to load Wycheproof vectors.

Wrappers#

enum crypto_condor.primitives.ECDH.Wrapper(value)#

Supported languages for wrappers.

Member Type:

str

Valid values are as follows:

PYTHON = <Wrapper.PYTHON: 'Python'>#
crypto_condor.primitives.ECDH.run_wrapper(wrapper, lang, curve, compliance=True, resilience=False)#

Runs a ECDH wrapper.

Parameters:
  • wrapper (Path) – The wrapper to test.

  • lang (Wrapper) – The language of the wrapper.

  • curve (Curve) – The elliptic curve to use.

  • compliance (bool) – Whether to use NIST vectors.

  • resilience (bool) – Whether to use Wycheproof vectors.

Returns:

The results of test_exchange().

Raises:
  • FileNotFoundError – If the wrapper could not be found.

  • ValueError – If lang or curve are not valid values.

Return type:

ResultsDict

Example

>>> from crypto_condor.primitives import ECDH
>>> from pathlib import Path
>>> my_wrapper = Path("my_wrapper.py")
>>> lang = ECDH.Wrapper.PYTHON
>>> curve = ECDH.Curve.P192
>>> rdict = ECDH.run_wrapper(my_wrapper, lang, curve)  

Internal tests#

crypto_condor.primitives.ECDH.test_exchange_nist(ecdh, curve)#

Tests ECDH exchange with NIST vectors.

Parameters:
  • ecdh (ECDH) – The implementation of the ECDH protocol to test.

  • curve (Curve) – The elliptic curve to use.

Returns:

The results of testing the implementation by computing the shared secret with the implementation and comparing it to the expected one. None if there are no NIST vectors for the given curve or the exchange_nist method is not implemented.

Return type:

Results | None

crypto_condor.primitives.ECDH.test_exchange_wycheproof(ecdh, curve)#

Tests ECDH.exchange with Wycheproof vectors.

Wycheproof vectors provide X509-encoded public keys.

Parameters:
  • ecdh (ECDH) – The implementation to test.

  • curve (Curve) – The elliptic curve to use.

Returns:

The results of testing the implementation by computing the shared secret and comparing it to the expected one, or None if there are no Wycheproof vectors for the given curve.

Return type:

Results | None

Internal runners#

crypto_condor.primitives.ECDH.run_wrapper_python(wrapper, curve, compliance, resilience)#

Runs a Python wrapper of ECDH.

Imports the wrapper script and searches for a class named CC_ECDH. If found, it is passed to test_exchange() with the corresponding options.

Parameters:
  • wrapper (Path) – The wrapper to test. The path must be valid.

  • curve (Curve) – The elliptic curve to use.

  • compliance (bool) – Whether to use NIST vectors.

  • resilience (bool) – Whether to use Wycheproof vectors.

Returns:

The results returned by test_exchange().

Raises:

ModuleNotFoundError – If the module could not be loaded.

Return type:

ResultsDict

Internal vectors#

The following section describes the internal test vectors classes, which are protobuf Python classes.

Hint

The autodoc extension can’t properly document these clases so we include the .proto file to show the different fields each class has. IDEs should be able to use the included .pyi files to provide auto-completion and type checking.

class crypto_condor.vectors._ECDH.ECDH_pb2.EcdhNistVectors#

Protobuf class that stores NIST test vectors. See the description below.

class crypto_condor.vectors._ECDH.ECDH_pb2.EcdhWycheproofVectors#

Protobuf class that stores Wycheproof test vectors. See the description below.

syntax = "proto3";

package crypto_condor;

// A single NIST test for ECDH. The coordinates and private value are in bytes to ensure
// that they fit - the values used for e.g B-571 are roughly 70 bits long.
message EcdhNistTest {
    // The test ID. It should be unique among tests for the same curve.
    int32 count = 1;
    // The peer's public x-coordinate.
    bytes peer_x = 2;
    // The peer's public y-coordinate.
    bytes peer_y = 3;
    // Our secret value.
    bytes own_d = 4;
    // Our public x-coordinate.
    bytes own_x = 5;
    // Our public y-coordinate.
    bytes own_y = 6;
    // The resulting shared secret.
    bytes z = 7;
}

// NIST test vectors for a given curve.
message EcdhNistVectors {
    // The name of the curve.
    string curve = 1;
    // A list of tests for this curve.
    repeated EcdhNistTest tests = 2;
}

// A single Wycheproof test vector for ECDH. It is common to both types of tests
// (encoded point and encoded public key).
message EcdhWycheproofTest {
    // Test ID. Should be unique in a file of vectors.
    int32 id = 1;
    // A comment describing what is being tested.
    string comment = 2;
    // The peer's public key - either an encoded point or an encoded public key. See the type
    // of EcdhWycheproofGroup.
    bytes public = 3;
    // The private value. Stored as bytes to ensure it fits.
    bytes private = 4;
    // The resulting shared secret.
    bytes shared = 5;
    // The TestType.
    string result = 6;
    // Flags annotating this test.
    repeated string flags = 7;
}

// A group of Wycheproof tests.
message EcdhWycheproofGroup {
    // The name of the curve.
    string curve = 1;
    // The type of encoding used for the public key.
    string encoding = 2;
    // The type of test: either EcdhTest where the public key is wholly encoded, or
    // EcdhEcpointTest where only the encoded coordinates are given.
    string type = 3;
    // A list of tests.
    repeated EcdhWycheproofTest tests = 4;
}

// A file of Wycheproof test vectors for ECDH.
message EcdhWycheproofVectors {
    // The algorithm - ECDH in this case.
    string algorithm = 1;
    // The version of the generator used - refer to Wycheproof.
    string generator_version = 2;
    // The total number of tests included in this file.
    int32 number_of_tests = 3;
    // Additional information about these tests.
    string header = 4;
    // Notes describing the flags that annotate the tests.
    map<string, string> notes = 5;
    // The JSON scheme of the original file.
    string schema = 6;
    // The groups of tests.
    repeated EcdhWycheproofGroup groups = 7;

    // The name of the source file.
    string filename = 15;
}