ECDSA¶
How to use the crypto_condor.primitives.ECDSA module to test implementations of
ECDSA.
Supported parameters¶
To test ECDSA implementations you must choose an elliptic curve and a hash function. We
use enums to define the supported parameters: Curve and Hash.
Some functions require an indication of which key encoding to use: refer to
KeyEncoding and PubKeyEncoding.
- enum crypto_condor.primitives.ECDSA.Curve(value)¶
- Defines all supported curves. - Member Type:
- str
 - Valid values are as follows: - SECP192R1 = <Curve.SECP192R1: 'secp192r1'>¶
 - SECP224R1 = <Curve.SECP224R1: 'secp224r1'>¶
 - SECP256R1 = <Curve.SECP256R1: 'secp256r1'>¶
 - SECP384R1 = <Curve.SECP384R1: 'secp384r1'>¶
 - SECP521R1 = <Curve.SECP521R1: 'secp521r1'>¶
 - SECP256K1 = <Curve.SECP256K1: 'secp256k1'>¶
 - BRAINPOOLP224R1 = <Curve.BRAINPOOLP224R1: 'brainpoolP224r1'>¶
 - BRAINPOOLP256R1 = <Curve.BRAINPOOLP256R1: 'brainpoolP256r1'>¶
 - SECT283R1 = <Curve.SECT283R1: 'sect283r1'>¶
 - SECT409R1 = <Curve.SECT409R1: 'sect409r1'>¶
 - SECT571R1 = <Curve.SECT571R1: 'sect571r1'>¶
 
- enum crypto_condor.primitives.ECDSA.Hash(value)¶
- Defines all supported hash functions. - Member Type:
- str
 - Valid values are as follows: - SHA_256 = <Hash.SHA_256: 'SHA-256'>¶
 - SHA_384 = <Hash.SHA_384: 'SHA-384'>¶
 - SHA_512 = <Hash.SHA_512: 'SHA-512'>¶
 - SHA3_256 = <Hash.SHA3_256: 'SHA3-256'>¶
 - SHA3_384 = <Hash.SHA3_384: 'SHA3-384'>¶
 - SHA3_512 = <Hash.SHA3_512: 'SHA3-512'>¶
 
Test a signature verification function¶
The following table describes whether a given curve is recommended by the ANSSI or the NIST, and whether the combination of a curve and a hash function has NIST (N) and/or Wycheproof (W) test vectors available.
| Curve | ANSSI | NIST | SHA-256 | SHA-384 | SHA-512 | SHA-3 256 | SHA-3 384 | SHA-3 512 | 
|---|---|---|---|---|---|---|---|---|
| P-224 | No | Yes | N+W | N | N+W | W | x | W | 
| P-256 | Yes | Yes | N+W | N | N+W | W | x | W | 
| P-384 | Yes | Yes | N | N+W | N+W | x | W | W | 
| P-521 | Yes | Yes | N | N | N+W | x | x | W | 
| B-283 | Yes | No | N | N | N | x | x | x | 
| B-409 | Yes | No | N | N | N | x | x | x | 
| B-571 | Yes | No | N | N | N | x | x | x | 
| FRP256v1 | Yes | No | x | x | x | x | x | x | 
- crypto_condor.primitives.ECDSA.test_verify(
- verify_function,
- curve,
- hash_function,
- pubkey_encoding,
- *,
- pre_hashed=False,
- compliance=True,
- resilience=True,
- Tests a function that verifies ECDSA signatures. - It runs the function with a set of test vectors selected depending on the curve, hash function, and compliance and resilience options. - The function to test must conform to the - Verifyprotocol.- The documentation has a table describing the available sources of test vectors depending on the curve and hash function. - Parameters:
- verify_function (Verify) – The function to test, see - Verify.
- curve (Curve) – The elliptic curve to use. 
- hash_function (Hash) – The hash function to use. 
- pubkey_encoding (PubKeyEncoding) – A public key encoding accepted by the function. 
 
- Keyword Arguments:
- pre_hashed – Whether the message should be hashed before passing it to - verify_function.
- compliance – Whether to use compliance test vectors. 
- resilience – Whether to use resilience test vectors. 
 
- Returns:
- A dictionary of results. Can contain the results of testing NIST ( - nist) and/or Wycheproof (- wycheproof) test vectors, if there are any with the given parameters.
- Return type:
 - Example - Let’s test crypto-condor’s internal verifier. First import the ECDSA module. - >>> from crypto_condor.primitives import ECDSA - We define the parameters we want to test (curve, hash, encoding). - >>> curve = ECDSA.Curve.SECP256R1 >>> hash_function = ECDSA.Hash.SHA_256 >>> encoding = ECDSA.PubKeyEncoding.DER - Then wrap the internal verifier to match the expected signature, defined by - Verify.- >>> def my_verify(public_key: bytes, message: bytes, signature: bytes) -> bool: ... return ECDSA._verify(public_key, hash_function, message, signature) - And test this function. - >>> group = ECDSA.test_verify(my_verify, curve, hash_function, encoding) [NIST] Verifying signatures ... - >>> assert group["nist"].check() >>> assert group["wycheproof"].check() 
Test a signing function¶
The table below indicates whether a given curve is recommended by the ANSSI or the NIST, and whether the combination of a curve and a hash function has NIST (N) or Wycheproof (W) test vectors available.
| Curve | ANSSI | NIST | SHA-256 | SHA-384 | SHA-512 | SHA-3 256 | SHA-3 384 | SHA-3 512 | 
|---|---|---|---|---|---|---|---|---|
| P-224 | No | Yes | N | N | N | x | x | x | 
| P-256 | Yes | Yes | N | N | N | x | x | x | 
| P-384 | Yes | Yes | N | N | N | x | x | x | 
| P-521 | Yes | Yes | N | N | N | x | x | x | 
| B-283 | Yes | No | N | N | N | x | x | x | 
| B-409 | Yes | No | N | N | N | x | x | x | 
| B-571 | Yes | No | N | N | N | x | x | x | 
| FRP256v1 | Yes | No | x | x | x | x | x | x | 
- crypto_condor.primitives.ECDSA.test_sign(
- sign_function,
- curve,
- hash_function,
- key_encoding,
- *,
- pre_hashed=False,
- compliance=True,
- Tests a function that signs with ECDSA. - It runs the function with a set of test vectors selected depending on the curve, hash function, and compliance option. - The function to test must conform to the - Signprotocol.- The documentation has a table describing the available sources of test vectors depending on the curve and hash function. - Parameters:
- curve (Curve) – The elliptic curve to use. 
- hash_function (Hash) – The hash function to use. 
- key_encoding (KeyEncoding) – The key encoding accepted by the signing function. 
 
- Keyword Arguments:
- pre_hashed – If True, the messages are hashed before passing them to the signing function. 
- compliance – Whether to use compliance test vectors. 
 
- Returns:
- The results of testing with NIST test vectors if there are test vectors for the given curve and hash function. 
- Return type:
- Results | None 
 - Example - Let’s test crypto-condor’s internal signing function. First, import the ECDSA module. - >>> from crypto_condor.primitives import ECDSA - We define the parameters we want to test (curve, hash, encoding). - >>> curve = ECDSA.Curve.SECP256R1 >>> hash_function = ECDSA.Hash.SHA_256 >>> encoding = ECDSA.KeyEncoding.DER - Then wrap the function to match the expected signature, defined by - Sign.- >>> def my_sign(private_key: bytes, message: bytes) -> bytes: ... return ECDSA._sign(private_key, hash_function, message) - And test the function. - >>> results = ECDSA.test_sign(my_sign, curve, hash_function, encoding) [NIST] Signing ... >>> assert results.check() 
Test signing then verifying¶
- crypto_condor.primitives.ECDSA.test_sign_then_verify(
- sign,
- verify,
- curve,
- key_encoding,
- pubkey_encoding,
- hash_function=None,
- Tests both functions. - A single random key is generated and encoded. Random messages are generated, signed with the - signfunction, and the signatures verifies with the- verifyfunction. A test is passed is the signing function correctly generated a signature and the verifying function considers this signature valid.- Parameters:
- sign (Sign) – The signing function to test. 
- verify (Verify) – The verifying function to test. 
- curve (Curve) – The elliptic curve to use. 
- key_encoding (KeyEncoding) – The private key encoding used by the signing function. 
- pubkey_encoding (PubKeyEncoding) – The public key encoding used by the verifying function. 
- hash_function (Hash | None) – Optional. The given hash function is used to hash the message before passing it to the functions. If None, the message is passed as is. 
 
- Returns:
- The results. A test is considered a pass if the produced signature is valid for the corresponding message according to the verifying function. 
- Return type:
 - Example - Let’s test crypto-condor’s internal functions. First import the ECDSA module. - >>> from crypto_condor.primitives import ECDSA - Define the test parameters. - >>> curve = ECDSA.Curve.SECP256R1 >>> key_encoding = ECDSA.KeyEncoding.DER >>> pubkey_encoding = ECDSA.PubKeyEncoding.DER - Wrap both functions to match the corresponding protocols ( - Verifyand- Sign).- >>> def my_verify(public_key: bytes, message: bytes, signature: bytes) -> bool: ... return ECDSA._verify(public_key, hash_function, message, signature) >>> def my_sign(private_key: bytes, message: bytes) -> bytes: ... return ECDSA._sign(private_key, hash_function, message) - Then test both functions. - >>> results = ECDSA.test_sign_then_verify( ... my_sign, my_verify, curve, key_encoding, pubkey_encoding ... ) Signing and verifying ... >>> assert results.check() 
Test a function generating key pairs¶
- crypto_condor.primitives.ECDSA.test_key_pair_gen(keygen, curve)¶
- Tests a function that generates ECDSA key pairs. - It uses the given function to generate 5000 keys pairs, in the format defined by - KeyPair. The private value is used to derive a private key. If the coordinates of the public value are included it checks that these represent the correct public key. A test passes if the private value could be used to derive the private key and if the public value matches, if applicable.- A second test is performed, which consists in concatenating the private values in a single stream, and testing it with - TestU01.- Parameters:
- Returns:
- A dictionary of results containing the results of generating the key pairs ( - keygen) and the result of testing the private values with TestU01 (- testu01).
- Return type:
 - Notes - 5000 keys gives us at least 1 million bits on - secp224r1.- Example - Let’s test PyCryptodome’s key generation. We import the ECDSA module. - >>> from crypto_condor.primitives import ECDSA - We pick the curve secp224r1. - >>> curve = ECDSA.Curve.SECP224R1 - Then wrap the implementation to match the signature defined by - KeyGen.- >>> from Crypto.PublicKey import ECC >>> def my_key_gen() -> tuple[int, int | None, int | None]: ... key = ECC.generate(curve=str(curve)) ... return (int(key.d), key.pointQ.x, key.pointQ.y) - And test it. - >>> results_dict = ECDSA.test_key_pair_gen(my_key_gen, curve) Generating keys ... >>> assert results_dict["keygen"].check() >>> assert results_dict["testu01"].check() 
Verify a file of signatures¶
- crypto_condor.primitives.ECDSA.verify_file(filename, pubkey_encoding, hash_function)¶
- Verifies signatures contained in a file. - To test ECDSA signatures, the file must follow the format described below. - Format:
- One set of arguments per line. 
- Lines are separated by newlines ( - \n).
- Lines that start with ‘#’ are counted as comments and ignored. 
- Arguments are written in hexadecimal and separated by slashes. 
- The keys may be different for each line but they must be encoded in the same format. 
- The order of the arguments is: 
 - key/message/signature 
 - Parameters:
- filename (str) – The name of the path containing the signatures to verify. 
- pubkey_encoding (PubKeyEncoding) – The encoding of the public keys used. Only DER- and PEM-encoded keys are supported. 
- hash_function (Hash) – The hash function used to generate the signatures. 
 
- Returns:
- The results of verifying each signature with an internal implementation. Errors, including parsing ones, are counted as failures and do not raise exceptions, except for the IOError indicated below. - For parsing errors, the line numbering starts at 1. 
- Raises:
- IOError – If the file could not be read. 
- Return type:
 - Example - We start by importing the ECDSA module. - >>> from crypto_condor.primitives import ECDSA - For this example we already have a correctly formatted: let’s print the first line to show the format. The output is a bit long but we can see the three expected arguments: key, message, and signature. - >>> filename = "/tmp/ecdsa-p256-sha256-signatures.txt" >>> with open(filename, "r") as fd: ... print(fd.readline()) 3059301306072a8648ce3d020106082a8648ce3d030107034200042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e/313233343030/304402202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e1802204cd60b855d442f5b3c7b11eb6c4e0ae7525fe710fab9aa7c77a67f79e6fadd76 - We use - verify_file()to test this file. In this case the keys are DER-encoded and we used SHA-256 to hash the messages. Since we know that it can raise- IOError, we wrap it in a try/except statement to print the error without crashing.- >>> try: ... result = ECDSA.verify_file( ... filename, ECDSA.PubKeyEncoding.DER, ECDSA.Hash("SHA-256") ... ) ... except IOError as error: ... print(error) Testing ... 
Run a wrapper¶
Note
Available wrappers are defined by Wrapper.
- crypto_condor.primitives.ECDSA.run_wrapper(
- language,
- curve,
- hash_function,
- pre_hashed,
- test_sign,
- key_encoding,
- test_verify,
- pubkey_encoding,
- test_sign_then_verify,
- compliance,
- resilience,
- Runs a wrapper. - Parameters:
- language (Wrapper) – The language of the wrapper. 
- curve (Curve) – The elliptic curve to use. 
- hash_function (Hash) – The hash function to use. 
- pre_hashed (bool) – Whether the message passed should be hashed beforehand. 
- test_sign (bool) – Whether to test the signing function. 
- key_encoding (KeyEncoding | None) – The private key encoding used by the signing function. Set to None only when not testing the signing function. 
- test_verify (bool) – Whether to test the signature verification function. 
- pubkey_encoding (PubKeyEncoding | None) – The public key encoding used by the verifying function. Set to None only when not testing the verifying function. 
- test_sign_then_verify (bool) – If True, both functions are tested by generating a random key and random messages, signing them, and verifying the signature. 
- compliance (bool) – Whether to use compliance test vectors. 
- resilience (bool) – Whether to use resilience test vectors. 
 
- Returns:
- A dictionary of results. Depending on the options used and test vectors available, it contains results of testing the signing function ( - sign), testing the verifying function (- nist/verifyand- wycheproof/verify), and testing both (- sign-then-verify).
- Return type:
 
Protocols¶
- protocol crypto_condor.primitives.ECDSA.Verify¶
- Represents a function that verifies ECDSA signatures. - Classes that implement this protocol must have the following methods / attributes: - __call__(public_key, message, signature)¶
- Verifies an ECDSA signature. - Parameters:
- public_key (bytes) – The public elliptic curve key. Either PEM-encoded, DER-encoded, or as serialized int. 
- message (bytes) – The signed message. 
- signature (bytes) – The resulting signature. 
 
- Returns:
- True if the signature is valid, False otherwise. 
- Return type:
- bool 
 
 
- protocol crypto_condor.primitives.ECDSA.Sign¶
- Represents a function that signs a message with ECDSA. - Classes that implement this protocol must have the following methods / attributes: - __call__(private_key, message)¶
- Signs a message with ECDSA. - Parameters:
- private_key (bytes) – The private elliptic curve key. Either PEM-encoded, DER-encoded, or as serialized int. 
- message (bytes) – The message to sign. 
 
- Returns:
- The signed message. 
- Return type:
- bytes 
 
 
- protocol crypto_condor.primitives.ECDSA.KeyGen¶
- Represents a function that generates ECDSA key pairs. - Classes that implement this protocol must have the following methods / attributes: - __call__()¶
- Generates an ECDSA key pair. - Returns:
- A tuple (d, Qx, Qy) containing the private value - dand the coordinates- Qxand- Qyof the public value, or a tuple (d, None, None) containing only the private value- d.
- Return type:
- tuple[int, int | None, int | None]