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:
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.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()
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