SECP256K1 vector creation – Cryptography 42.0.0.dev1 documentation

Today we’re going to dive deep into the world of SECP256K1 vector creation using Cryptography 42.0.0.dev1 documentation. But before we get started, let’s take a moment to appreciate how cool it is that we can create our own vectors for testing and debugging purposes.

Now, if you’re like me, your eyes might be glazing over at the thought of reading through pages upon pages of technical jargon. Don’t Worry! We’ll break down this process into simple English so even a crypto newbie can understand it.

To kick things off: what is SECP256K1? It’s an elliptic curve cryptography (ECC) algorithm that provides 256-bit security, which means your data is pretty much uncrackable. And by “uncrackable,” we mean it would take a supercomputer billions of years to crack the code.

So how do we create vectors for testing and debugging purposes? Well, let’s start with some basic concepts. A vector is essentially just a list or array that contains data points. In this case, our vectors will contain messages (data) that we want to sign using SECP256K1.

Here are the steps:

Step 1: Import the necessary libraries and functions. We’ll be using hashlib for hashing purposes, binascii for converting data into hexadecimal format, collections for creating a defaultdict (which we’ll use to store our vectors), ecdsa for generating signing keys and public keys, and cryptography_vectors for loading the FIPS ECDSA signing vectors.

# Import necessary libraries
import hashlib # Importing hashlib for hashing purposes
import os # Importing os for operating system related functions
from binascii import hexlify # Importing hexlify from binascii for converting data into hexadecimal format
from collections import defaultdict # Importing defaultdict from collections for creating a defaultdict (which we'll use to store our vectors)
from ecdsa import SECP256k1, SigningKey # Importing SECP256k1 and SigningKey from ecdsa for generating signing keys and public keys
from ecdsa.util import sigdecode_der, sigencode_der # Importing sigdecode_der and sigencode_der from ecdsa.util for decoding and encoding DER signatures
from cryptography_vectors import open_vector_file # Importing open_vector_file from cryptography_vectors for loading the FIPS ECDSA signing vectors
from tests.utils import load_fips_ecdsa_signing_vectors, load_vectors_from_file # Importing load_fips_ecdsa_signing_vectors and load_vectors_from_file from tests.utils for loading vectors from files

Step 2: Define a dictionary of hash types that we’ll be using for our vectors (SHA-1, SHA-224, SHA-256, SHA-384, and SHA-512). We’re also creating a TruncatedHash class to truncate the output of each hash function.

# Define a dictionary of hash types that we'll be using for our vectors (SHA-1, SHA-224, SHA-256, SHA-384, and SHA-512).
# We're also creating a TruncatedHash class to truncate the output of each hash function.

# Create a dictionary called HASHLIB_HASH_TYPES with key-value pairs of hash types and their corresponding hashlib functions.
HASHLIB_HASH_TYPES = {
    "SHA-1": hashlib.sha1,
    "SHA-224": hashlib.sha224,
    "SHA-256": hashlib.sha256,
    "SHA-384": hashlib.sha384,
    "SHA-512": hashlib.sha512,
}

# Create a class called TruncatedHash.
class TruncatedHash:
    # Define the __init__ method which takes in a hasher as a parameter.
    def __init__(self, hasher):
        # Set the hasher attribute to the passed in hasher.
        self.hasher = hasher

    # Define the __call__ method which takes in data as a parameter.
    def __call__(self, data):
        # Update the hasher with the passed in data.
        self.hasher.update(data)
        # Return the TruncatedHash object.
        return self

    # Define the digest method.
    def digest(self):
        # Return the truncated output of the hasher by slicing the first 256 bits (32 bytes) of the digest.
        return self.hasher.digest()[: 256 // 8]

Step 3: Load the FIPS ECDSA signing vectors using load_fips_ecdsa_signing_vectors function from cryptography_vectors library. This will give us a list of dictionaries containing information about each vector, such as digest algorithm and message.

# Define a function to build the vectors from the FIPS ECDSA signing vectors
def build_vectors(fips_vectors):
    # Create a defaultdict to store the vectors
    vectors = defaultdict(list)
    # Loop through each vector in the FIPS vectors list
    for vector in fips_vectors:
        # Append the message from the current vector to the list of messages for the corresponding digest algorithm in the defaultdict
        vectors[vector["digest_algorithm"]].append(vector["message"])

    # ... (rest of the code omitted for brevity)

Step 4: Iterate through each digest algorithm and message, create a hash context using TruncatedHash class, sign the message with SECP256k1, decode the signature into r and s values, print out some information about the vector (message, secret key, public key), and add it to our list of vectors.

# Step 4: Iterate through each digest algorithm and message, create a hash context using TruncatedHash class, sign the message with SECP256k1, decode the signature into r and s values, print out some information about the vector (message, secret key, public key), and add it to our list of vectors.



python
# Iterate through each digest algorithm and message in the vectors dictionary
for digest_algorithm, messages in vectors.items():
    # Check if the digest algorithm is supported by the HASHLIB_HASH_TYPES dictionary
    if digest_algorithm not in HASHLIB_HASH_TYPES:
        # If not supported, skip to the next iteration
        continue

    # Add an empty line to the output
    yield ""

    # Add the algorithm and digest type to the output
    yield f"[K-256,{digest_algorithm}]"

    # Add an empty line to the output
    yield ""

    # Iterate through each message in the current digest algorithm
    for message in messages:
        # Create a hash context using the TruncatedHash class and the corresponding hash function from the HASHLIB_HASH_TYPES dictionary
        hash_func = TruncatedHash(HASHLIB_HASH_TYPES[digest_algorithm]())

        # Generate a secret key using the SECP256k1 curve
        secret_key = SigningKey.generate(curve=SECP256k1)

        # Get the corresponding public key from the secret key
        public_key = secret_key.get_verifying_key()

        # Sign the message using the secret key, hash function, and DER encoding
        signature = secret_key.sign(message, hashfunc=hash_func, sigencode=sigencode_der)

        # Decode the signature into r and s values
        r, s = sigdecode_der(signature, None)

        # Add the message, secret key, and public key information to the output
        yield f"Msg = {hexlify(message)}" # Convert the message to hexadecimal format
        yield f"d = {secret_key.privkey.secret_multiplier:x}" # Get the secret key's private multiplier in hexadecimal format
        yield f"Qx = {public_key.pubkey.point.x:x}" # Get the public key's x-coordinate in hexadecimal format

And that’s it! We now have a list of vectors containing messages, secret keys, and public keys for testing and debugging purposes using SECP256K1 with Cryptography 42.0.0.dev1 documentation.

Hope this tutorial helped you understand the process better! Let us know if you have any questions or feedback in the comments below.

SICORPS