Bitcoin Core
31.1.0
P2P Digital Currency
Toggle main menu visibility
Loading...
Searching...
No Matches
src
secp256k1
tools
tests_wycheproof_generate_ecdh.py
Go to the documentation of this file.
1
#!/usr/bin/env python3
2
# Copyright (c) 2024 Random "Randy" Lattice and Sean Andersen
3
# Distributed under the MIT software license, see the accompanying
4
# file COPYING or https://www.opensource.org/licenses/mit-license.php.
5
'''
6
Generate a C file with ECDH testvectors from the Wycheproof project.
7
'''
8
9
import
json
10
import
sys
11
12
from
binascii
import
hexlify, unhexlify
13
from
wycheproof_utils
import
to_c_array
14
15
def
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_r1572491546
18
flags_to_skip = {
"InvalidAsn"
,
"WrongCurve"
}
19
return
any(flag
in
test_vector_flags
for
flag
in
flags_to_skip)
20
21
def
should_skip_tcid
(test_vector_tcid):
22
# We skip some test case IDs that have a public key whose custom ASN.1 representation explicitly
23
# encodes some curve parameters that are invalid. libsecp256k1 never parses this part so we do
24
# not care testing those. See https://github.com/bitcoin-core/secp256k1/pull/1492#discussion_r1572491546
25
tcids_to_skip = [496, 497, 502, 503, 504, 505, 507]
26
return
test_vector_tcid
in
tcids_to_skip
27
28
# Rudimentary ASN.1 DER public key parser.
29
# This should not be used for anything other than parsing Wycheproof test vectors.
30
def
parse_der_pk
(s):
31
tag = s[0]
32
L = int(s[1])
33
offset = 0
34
if
L & 0x80:
35
if
L == 0x81:
36
L = int(s[2])
37
offset = 1
38
elif
L == 0x82:
39
L = 256 * int(s[2]) + int(s[3])
40
offset = 2
41
else
:
42
raise
ValueError(
"invalid L"
)
43
value = s[(offset + 2):(L + 2 + offset)]
44
rest = s[(L + 2 + offset):]
45
46
if
len(rest) > 0
or
tag == 0x06:
# OBJECT IDENTIFIER
47
return
parse_der_pk
(rest)
48
if
tag == 0x03:
# BIT STRING
49
return
value
50
if
tag == 0x30:
# SEQUENCE
51
return
parse_der_pk
(value)
52
raise
ValueError(
"unknown tag"
)
53
54
def
parse_public_key
(pk):
55
der_pub_key =
parse_der_pk
(unhexlify(pk))
# Convert back to str and strip off the `0x`
56
return
hexlify(der_pub_key).decode()[2:]
57
58
def
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 zeroes
61
normalized = sk[-64:].zfill(64)
62
if
len(normalized) != 64:
63
raise
ValueError(
"private key must be exactly 64 characters long."
)
64
return
normalized
65
66
def
normalize_expected_result
(er):
67
result_mapping = {
"invalid"
: 0,
"valid"
: 1,
"acceptable"
: 1}
68
return
result_mapping[er]
69
70
filename_input = sys.argv[1]
71
72
with
open(filename_input)
as
f:
73
doc = json.load(f)
74
75
num_vectors = 0
76
offset_sk_running, offset_pk_running, offset_shared = 0, 0, 0
77
test_vectors_out =
""
78
private_keys =
""
79
shared_secrets =
""
80
public_keys =
""
81
cache_sks = {}
82
cache_public_keys = {}
83
84
for
group
in
doc[
'testGroups'
]:
85
assert
group[
"type"
] ==
"EcdhTest"
86
assert
group[
"curve"
] ==
"secp256k1"
87
for
test_vector
in
group[
'tests'
]:
88
if
should_skip_flags
(test_vector[
'flags'
])
or
should_skip_tcid
(test_vector[
'tcId'
]):
89
continue
90
91
public_key =
parse_public_key
(test_vector[
'public'
])
92
private_key =
normalize_private_key
(test_vector[
'private'
])
93
expected_result =
normalize_expected_result
(test_vector[
'result'
])
94
95
# // 2 to convert hex to byte length
96
shared_size = len(test_vector[
'shared'
]) // 2
97
sk_size = len(private_key) // 2
98
pk_size = len(public_key) // 2
99
100
new_sk =
False
101
sk = to_c_array(private_key)
102
sk_offset = offset_sk_running
103
104
# check for repeated sk
105
if
sk
not
in
cache_sks:
106
if
num_vectors != 0
and
sk_size != 0:
107
private_keys +=
",\n "
108
cache_sks[sk] = offset_sk_running
109
private_keys += sk
110
new_sk =
True
111
else
:
112
sk_offset = cache_sks[sk]
113
114
new_pk =
False
115
pk = to_c_array(public_key)
if
public_key !=
'0x'
else
''
116
117
pk_offset = offset_pk_running
118
# check for repeated pk
119
if
pk
not
in
cache_public_keys:
120
if
num_vectors != 0
and
len(pk) != 0:
121
public_keys +=
",\n "
122
cache_public_keys[pk] = offset_pk_running
123
public_keys += pk
124
new_pk =
True
125
else
:
126
pk_offset = cache_public_keys[pk]
127
128
129
shared_secrets +=
",\n "
if
num_vectors
and
shared_size
else
""
130
shared_secrets += to_c_array(test_vector[
'shared'
])
131
wycheproof_tcid = test_vector[
'tcId'
]
132
133
test_vectors_out +=
" /"
+
"* tcId: "
+ str(test_vector[
'tcId'
]) +
". "
+ test_vector[
'comment'
] +
" *"
+
"/\n"
134
test_vectors_out += f
" {{{pk_offset}, {pk_size}, {sk_offset}, {sk_size}, {offset_shared}, {shared_size}, {expected_result}, {wycheproof_tcid} }},\n"
135
if
new_sk:
136
offset_sk_running += sk_size
137
if
new_pk:
138
offset_pk_running += pk_size
139
offset_shared += shared_size
140
num_vectors += 1
141
142
struct_definition =
"""
143
typedef struct {
144
size_t pk_offset;
145
size_t pk_len;
146
size_t sk_offset;
147
size_t sk_len;
148
size_t shared_offset;
149
size_t shared_len;
150
int expected_result;
151
int wycheproof_tcid;
152
} wycheproof_ecdh_testvector;
153
"""
154
155
print(
"/* Note: this file was autogenerated using tests_wycheproof_ecdh.py. Do not edit. */"
)
156
print(f
"#define SECP256K1_ECDH_WYCHEPROOF_NUMBER_TESTVECTORS ({num_vectors})"
)
157
158
print(struct_definition)
159
160
print(
"static const unsigned char wycheproof_ecdh_private_keys[] = { "
+ private_keys +
"};\n"
)
161
print(
"static const unsigned char wycheproof_ecdh_public_keys[] = { "
+ public_keys +
"};\n"
)
162
print(
"static const unsigned char wycheproof_ecdh_shared_secrets[] = { "
+ shared_secrets +
"};\n"
)
163
164
print(
"static const wycheproof_ecdh_testvector testvectors[SECP256K1_ECDH_WYCHEPROOF_NUMBER_TESTVECTORS] = {"
)
165
print(test_vectors_out)
166
print(
"};"
)
tests_wycheproof_generate_ecdh.parse_public_key
parse_public_key(pk)
Definition
tests_wycheproof_generate_ecdh.py:54
tests_wycheproof_generate_ecdh.normalize_private_key
normalize_private_key(sk)
Definition
tests_wycheproof_generate_ecdh.py:58
tests_wycheproof_generate_ecdh.should_skip_flags
should_skip_flags(test_vector_flags)
Definition
tests_wycheproof_generate_ecdh.py:15
tests_wycheproof_generate_ecdh.parse_der_pk
parse_der_pk(s)
Definition
tests_wycheproof_generate_ecdh.py:30
tests_wycheproof_generate_ecdh.should_skip_tcid
should_skip_tcid(test_vector_tcid)
Definition
tests_wycheproof_generate_ecdh.py:21
tests_wycheproof_generate_ecdh.normalize_expected_result
normalize_expected_result(er)
Definition
tests_wycheproof_generate_ecdh.py:66
Generated on
for Bitcoin Core by
1.17.0