# SECP256K1 vector creation

This page documents the code that was used to generate the SECP256K1 elliptic curve test vectors as well as code used to verify them against another implementation.

## Creation

The vectors are generated using a pure Python ecdsa implementation. The test messages and combinations of algorithms are derived from the NIST vector data.

    import hashlib
    import os
    from binascii import hexlify
    from collections import defaultdict

    from ecdsa import SECP256k1, SigningKey
    from ecdsa.util import sigdecode_der, sigencode_der

    from cryptography_vectors import open_vector_file

    from tests.utils import load_fips_ecdsa_signing_vectors, load_vectors_from_file

    HASHLIB_HASH_TYPES = {
        "SHA-1": hashlib.sha1,
        "SHA-224": hashlib.sha224,
        "SHA-256": hashlib.sha256,
        "SHA-384": hashlib.sha384,
        "SHA-512": hashlib.sha512,
    }

    class TruncatedHash(object):
        def __init__(self, hasher):
            self.hasher = hasher

        def __call__(self, data):
            self.hasher.update(data)
            return self

        def digest(self):
            return self.hasher.digest()[: 256 // 8]

    def build_vectors(fips_vectors):
        vectors = defaultdict(list)
        for vector in fips_vectors:
            vectors[vector["digest_algorithm"]].append(vector["message"])

        for digest_algorithm, messages in vectors.items():
            if digest_algorithm not in HASHLIB_HASH_TYPES:
                continue

            yield ""
            yield "[K-256,{0}]".format(digest_algorithm)
            yield ""

            for message in messages:
                # Make a hash context
                hash_func = TruncatedHash(HASHLIB_HASH_TYPES[digest_algorithm]())

                # Sign the message using warner/ecdsa
                secret_key = SigningKey.generate(curve=SECP256k1)
                public_key = secret_key.get_verifying_key()
                signature = secret_key.sign(
                    message, hashfunc=hash_func, sigencode=sigencode_der
                )

                r, s = sigdecode_der(signature, None)

                yield "Msg = {0}".format(hexlify(message))
                yield "d = {0:x}".format(secret_key.privkey.secret_multiplier)
                yield "Qx = {0:x}".format(public_key.pubkey.point.x())
                yield "Qy = {0:x}".format(public_key.pubkey.point.y())
                yield "R = {0:x}".format(r)
                yield "S = {0:x}".format(s)
                yield ""

    def write_file(lines, dest):
        for line in lines:
            print(line)
            print(line, file=dest)

    source_path = os.path.join("asymmetric", "ECDSA", "FIPS_186-3", "SigGen.txt")
    dest_path = os.path.join("asymmetric", "ECDSA", "SECP256K1", "SigGen.txt")

    fips_vectors = load_vectors_from_file(
        source_path, load_fips_ecdsa_signing_vectors
    )

    with open_vector_file(dest_path, "w") as dest_file:
        write_file(build_vectors(fips_vectors), dest_file)

Download link: `generate_secp256k1.py`

## Verification

`cryptography` was modified to support the SECP256K1 curve. Then the following python script was run to generate the vector files.

    import os

    from cryptography.hazmat.backends import default_backend
    from cryptography.hazmat.primitives import hashes
    from cryptography.hazmat.primitives.asymmetric import ec
    from cryptography.hazmat.primitives.asymmetric.utils import (
        encode_dss_signature,
    )

    from tests.utils import load_fips_ecdsa_signing_vectors, load_vectors_from_file

    CRYPTOGRAPHY_HASH_TYPES = {
        "SHA-1": hashes.SHA1,
        "SHA-224": hashes.SHA224,
        "SHA-256": hashes.SHA256,
        "SHA-384": hashes.SHA384,
        "SHA-512": hashes.SHA512,
    }

    def verify_one_vector(vector):
        digest_algorithm = vector["digest_algorithm"]
        message = vector["message"]
        x = vector["x"]
        y = vector["y"]
        signature = encode_dss_signature(vector["r"], vector["s"])

        numbers = ec.EllipticCurvePublicNumbers(x, y, ec.SECP256K1())

        key = numbers.public_key(default_backend())

        verifier = key.verifier(
            signature, ec.ECDSA(CRYPTOGRAPHY_HASH_TYPES[digest_algorithm]())
        )
        verifier.update(message)
        verifier.verify()

    def verify_vectors(vectors):
        for vector in vectors:
            verify_one_vector(vector)

    vector_path = os.path.join("asymmetric", "ECDSA", "SECP256K1", "SigGen.txt")

    secp256k1_vectors = load_vectors_from_file(
        vector_path, load_fips_ecdsa_signing_vectors
    )

    verify_vectors(secp256k1_vectors)

Download link: `verify_secp256k1.py`