Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1#!/usr/bin/env python32# Copyright (c) 2024 Random "Randy" Lattice and Sean Andersen3# Distributed under the MIT software license, see the accompanying4# file COPYING or https://www.opensource.org/licenses/mit-license.php.5'''6Generate a C file with ECDH testvectors from the Wycheproof project.7'''9import json10import sys12from binascii import hexlify, unhexlify13from wycheproof_utils import to_c_array15def should_skip_flags(test_vector_flags):16# skip these vectors because they are for ASN.1 encoding issues and other curves.17# for more details, see https://github.com/bitcoin-core/secp256k1/pull/1492#discussion_r157249154618flags_to_skip = {"InvalidAsn", "WrongCurve"}19return any(flag in test_vector_flags for flag in flags_to_skip)21def should_skip_tcid(test_vector_tcid):22# We skip some test case IDs that have a public key whose custom ASN.1 representation explicitly23# encodes some curve parameters that are invalid. libsecp256k1 never parses this part so we do24# not care testing those. See https://github.com/bitcoin-core/secp256k1/pull/1492#discussion_r157249154625tcids_to_skip = [496, 497, 502, 503, 504, 505, 507]26return test_vector_tcid in tcids_to_skip28# Rudimentary ASN.1 DER public key parser.29# This should not be used for anything other than parsing Wycheproof test vectors.30def parse_der_pk(s):31tag = s[0]32L = int(s[1])33offset = 034if L & 0x80:35if L == 0x81:36L = int(s[2])37offset = 138elif L == 0x82:39L = 256 * int(s[2]) + int(s[3])40offset = 241else:42raise ValueError("invalid L")43value = s[(offset + 2):(L + 2 + offset)]44rest = s[(L + 2 + offset):]46if len(rest) > 0 or tag == 0x06: # OBJECT IDENTIFIER47return parse_der_pk(rest)48if tag == 0x03: # BIT STRING49return value50if tag == 0x30: # SEQUENCE51return parse_der_pk(value)52raise ValueError("unknown tag")54def parse_public_key(pk):55der_pub_key = parse_der_pk(unhexlify(pk)) # Convert back to str and strip off the `0x`56return hexlify(der_pub_key).decode()[2:]58def normalize_private_key(sk):59# Ensure the private key is at most 64 characters long, retaining the last 64 if longer.60# In the wycheproof test vectors, some private keys have leading zeroes61normalized = sk[-64:].zfill(64)62if len(normalized) != 64:63raise ValueError("private key must be exactly 64 characters long.")64return normalized66def normalize_expected_result(er):67result_mapping = {"invalid": 0, "valid": 1, "acceptable": 1}68return result_mapping[er]70filename_input = sys.argv[1]72with open(filename_input) as f:73doc = json.load(f)75num_vectors = 076offset_sk_running, offset_pk_running, offset_shared = 0, 0, 077test_vectors_out = ""78private_keys = ""79shared_secrets = ""80public_keys = ""81cache_sks = {}82cache_public_keys = {}84for group in doc['testGroups']:85assert group["type"] == "EcdhTest"86assert group["curve"] == "secp256k1"87for test_vector in group['tests']:88if should_skip_flags(test_vector['flags']) or should_skip_tcid(test_vector['tcId']):89continue91public_key = parse_public_key(test_vector['public'])92private_key = normalize_private_key(test_vector['private'])93expected_result = normalize_expected_result(test_vector['result'])95# // 2 to convert hex to byte length96shared_size = len(test_vector['shared']) // 297sk_size = len(private_key) // 298pk_size = len(public_key) // 2100new_sk = False101sk = to_c_array(private_key)102sk_offset = offset_sk_running104# check for repeated sk105if sk not in cache_sks:106if num_vectors != 0 and sk_size != 0:107private_keys += ",\n "108cache_sks[sk] = offset_sk_running109private_keys += sk110new_sk = True111else:112sk_offset = cache_sks[sk]114new_pk = False115pk = to_c_array(public_key) if public_key != '0x' else ''117pk_offset = offset_pk_running118# check for repeated pk119if pk not in cache_public_keys:120if num_vectors != 0 and len(pk) != 0:121public_keys += ",\n "122cache_public_keys[pk] = offset_pk_running123public_keys += pk124new_pk = True125else:126pk_offset = cache_public_keys[pk]129shared_secrets += ",\n " if num_vectors and shared_size else ""130shared_secrets += to_c_array(test_vector['shared'])131wycheproof_tcid = test_vector['tcId']133test_vectors_out += " /" + "* tcId: " + str(test_vector['tcId']) + ". " + test_vector['comment'] + " *" + "/\n"134test_vectors_out += f" {{{pk_offset}, {pk_size}, {sk_offset}, {sk_size}, {offset_shared}, {shared_size}, {expected_result}, {wycheproof_tcid} }},\n"135if new_sk:136offset_sk_running += sk_size137if new_pk:138offset_pk_running += pk_size139offset_shared += shared_size140num_vectors += 1142struct_definition = """143typedef struct {144size_t pk_offset;145size_t pk_len;146size_t sk_offset;147size_t sk_len;148size_t shared_offset;149size_t shared_len;150int expected_result;151int wycheproof_tcid;152} wycheproof_ecdh_testvector;153"""155print("/* Note: this file was autogenerated using tests_wycheproof_ecdh.py. Do not edit. */")156print(f"#define SECP256K1_ECDH_WYCHEPROOF_NUMBER_TESTVECTORS ({num_vectors})")158print(struct_definition)160print("static const unsigned char wycheproof_ecdh_private_keys[] = { " + private_keys + "};\n")161print("static const unsigned char wycheproof_ecdh_public_keys[] = { " + public_keys + "};\n")162print("static const unsigned char wycheproof_ecdh_shared_secrets[] = { " + shared_secrets + "};\n")164print("static const wycheproof_ecdh_testvector testvectors[SECP256K1_ECDH_WYCHEPROOF_NUMBER_TESTVECTORS] = {")165print(test_vectors_out)166print("};")
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.