PageRenderTime 1473ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/contrib/testgen/gen_key_io_test_vectors.py

https://github.com/bitcoin/bitcoin
Python | 268 lines | 238 code | 6 blank | 24 comment | 8 complexity | ec1a68fb611bf083848d4a1fd9a5885b MD5 | raw file
  1. #!/usr/bin/env python3
  2. # Copyright (c) 2012-2021 The Bitcoin Core developers
  3. # Distributed under the MIT software license, see the accompanying
  4. # file COPYING or http://www.opensource.org/licenses/mit-license.php.
  5. '''
  6. Generate valid and invalid base58/bech32(m) address and private key test vectors.
  7. Usage:
  8. PYTHONPATH=../../test/functional/test_framework ./gen_key_io_test_vectors.py valid 70 > ../../src/test/data/key_io_valid.json
  9. PYTHONPATH=../../test/functional/test_framework ./gen_key_io_test_vectors.py invalid 70 > ../../src/test/data/key_io_invalid.json
  10. '''
  11. # 2012 Wladimir J. van der Laan
  12. # Released under MIT License
  13. import os
  14. from itertools import islice
  15. from base58 import b58encode_chk, b58decode_chk, b58chars
  16. import random
  17. from segwit_addr import bech32_encode, decode_segwit_address, convertbits, CHARSET, Encoding
  18. # key types
  19. PUBKEY_ADDRESS = 0
  20. SCRIPT_ADDRESS = 5
  21. PUBKEY_ADDRESS_TEST = 111
  22. SCRIPT_ADDRESS_TEST = 196
  23. PUBKEY_ADDRESS_REGTEST = 111
  24. SCRIPT_ADDRESS_REGTEST = 196
  25. PRIVKEY = 128
  26. PRIVKEY_TEST = 239
  27. PRIVKEY_REGTEST = 239
  28. # script
  29. OP_0 = 0x00
  30. OP_1 = 0x51
  31. OP_2 = 0x52
  32. OP_3 = 0x53
  33. OP_16 = 0x60
  34. OP_DUP = 0x76
  35. OP_EQUAL = 0x87
  36. OP_EQUALVERIFY = 0x88
  37. OP_HASH160 = 0xa9
  38. OP_CHECKSIG = 0xac
  39. pubkey_prefix = (OP_DUP, OP_HASH160, 20)
  40. pubkey_suffix = (OP_EQUALVERIFY, OP_CHECKSIG)
  41. script_prefix = (OP_HASH160, 20)
  42. script_suffix = (OP_EQUAL,)
  43. p2wpkh_prefix = (OP_0, 20)
  44. p2wsh_prefix = (OP_0, 32)
  45. p2tr_prefix = (OP_1, 32)
  46. metadata_keys = ['isPrivkey', 'chain', 'isCompressed', 'tryCaseFlip']
  47. # templates for valid sequences
  48. templates = [
  49. # prefix, payload_size, suffix, metadata, output_prefix, output_suffix
  50. # None = N/A
  51. ((PUBKEY_ADDRESS,), 20, (), (False, 'main', None, None), pubkey_prefix, pubkey_suffix),
  52. ((SCRIPT_ADDRESS,), 20, (), (False, 'main', None, None), script_prefix, script_suffix),
  53. ((PUBKEY_ADDRESS_TEST,), 20, (), (False, 'test', None, None), pubkey_prefix, pubkey_suffix),
  54. ((SCRIPT_ADDRESS_TEST,), 20, (), (False, 'test', None, None), script_prefix, script_suffix),
  55. ((PUBKEY_ADDRESS_TEST,), 20, (), (False, 'signet', None, None), pubkey_prefix, pubkey_suffix),
  56. ((SCRIPT_ADDRESS_TEST,), 20, (), (False, 'signet', None, None), script_prefix, script_suffix),
  57. ((PUBKEY_ADDRESS_REGTEST,), 20, (), (False, 'regtest', None, None), pubkey_prefix, pubkey_suffix),
  58. ((SCRIPT_ADDRESS_REGTEST,), 20, (), (False, 'regtest', None, None), script_prefix, script_suffix),
  59. ((PRIVKEY,), 32, (), (True, 'main', False, None), (), ()),
  60. ((PRIVKEY,), 32, (1,), (True, 'main', True, None), (), ()),
  61. ((PRIVKEY_TEST,), 32, (), (True, 'test', False, None), (), ()),
  62. ((PRIVKEY_TEST,), 32, (1,), (True, 'test', True, None), (), ()),
  63. ((PRIVKEY_TEST,), 32, (), (True, 'signet', False, None), (), ()),
  64. ((PRIVKEY_TEST,), 32, (1,), (True, 'signet', True, None), (), ()),
  65. ((PRIVKEY_REGTEST,), 32, (), (True, 'regtest', False, None), (), ()),
  66. ((PRIVKEY_REGTEST,), 32, (1,), (True, 'regtest', True, None), (), ())
  67. ]
  68. # templates for valid bech32 sequences
  69. bech32_templates = [
  70. # hrp, version, witprog_size, metadata, encoding, output_prefix
  71. ('bc', 0, 20, (False, 'main', None, True), Encoding.BECH32, p2wpkh_prefix),
  72. ('bc', 0, 32, (False, 'main', None, True), Encoding.BECH32, p2wsh_prefix),
  73. ('bc', 1, 32, (False, 'main', None, True), Encoding.BECH32M, p2tr_prefix),
  74. ('bc', 2, 2, (False, 'main', None, True), Encoding.BECH32M, (OP_2, 2)),
  75. ('tb', 0, 20, (False, 'test', None, True), Encoding.BECH32, p2wpkh_prefix),
  76. ('tb', 0, 32, (False, 'test', None, True), Encoding.BECH32, p2wsh_prefix),
  77. ('tb', 1, 32, (False, 'test', None, True), Encoding.BECH32M, p2tr_prefix),
  78. ('tb', 3, 16, (False, 'test', None, True), Encoding.BECH32M, (OP_3, 16)),
  79. ('tb', 0, 20, (False, 'signet', None, True), Encoding.BECH32, p2wpkh_prefix),
  80. ('tb', 0, 32, (False, 'signet', None, True), Encoding.BECH32, p2wsh_prefix),
  81. ('tb', 1, 32, (False, 'signet', None, True), Encoding.BECH32M, p2tr_prefix),
  82. ('tb', 3, 32, (False, 'signet', None, True), Encoding.BECH32M, (OP_3, 32)),
  83. ('bcrt', 0, 20, (False, 'regtest', None, True), Encoding.BECH32, p2wpkh_prefix),
  84. ('bcrt', 0, 32, (False, 'regtest', None, True), Encoding.BECH32, p2wsh_prefix),
  85. ('bcrt', 1, 32, (False, 'regtest', None, True), Encoding.BECH32M, p2tr_prefix),
  86. ('bcrt', 16, 40, (False, 'regtest', None, True), Encoding.BECH32M, (OP_16, 40))
  87. ]
  88. # templates for invalid bech32 sequences
  89. bech32_ng_templates = [
  90. # hrp, version, witprog_size, encoding, invalid_bech32, invalid_checksum, invalid_char
  91. ('tc', 0, 20, Encoding.BECH32, False, False, False),
  92. ('bt', 1, 32, Encoding.BECH32M, False, False, False),
  93. ('tb', 17, 32, Encoding.BECH32M, False, False, False),
  94. ('bcrt', 3, 1, Encoding.BECH32M, False, False, False),
  95. ('bc', 15, 41, Encoding.BECH32M, False, False, False),
  96. ('tb', 0, 16, Encoding.BECH32, False, False, False),
  97. ('bcrt', 0, 32, Encoding.BECH32, True, False, False),
  98. ('bc', 0, 16, Encoding.BECH32, True, False, False),
  99. ('tb', 0, 32, Encoding.BECH32, False, True, False),
  100. ('bcrt', 0, 20, Encoding.BECH32, False, False, True),
  101. ('bc', 0, 20, Encoding.BECH32M, False, False, False),
  102. ('tb', 0, 32, Encoding.BECH32M, False, False, False),
  103. ('bcrt', 0, 20, Encoding.BECH32M, False, False, False),
  104. ('bc', 1, 32, Encoding.BECH32, False, False, False),
  105. ('tb', 2, 16, Encoding.BECH32, False, False, False),
  106. ('bcrt', 16, 20, Encoding.BECH32, False, False, False),
  107. ]
  108. def is_valid(v):
  109. '''Check vector v for validity'''
  110. if len(set(v) - set(b58chars)) > 0:
  111. return is_valid_bech32(v)
  112. result = b58decode_chk(v)
  113. if result is None:
  114. return is_valid_bech32(v)
  115. for template in templates:
  116. prefix = bytearray(template[0])
  117. suffix = bytearray(template[2])
  118. if result.startswith(prefix) and result.endswith(suffix):
  119. if (len(result) - len(prefix) - len(suffix)) == template[1]:
  120. return True
  121. return is_valid_bech32(v)
  122. def is_valid_bech32(v):
  123. '''Check vector v for bech32 validity'''
  124. for hrp in ['bc', 'tb', 'bcrt']:
  125. if decode_segwit_address(hrp, v) != (None, None):
  126. return True
  127. return False
  128. def gen_valid_base58_vector(template):
  129. '''Generate valid base58 vector'''
  130. prefix = bytearray(template[0])
  131. payload = bytearray(os.urandom(template[1]))
  132. suffix = bytearray(template[2])
  133. dst_prefix = bytearray(template[4])
  134. dst_suffix = bytearray(template[5])
  135. rv = b58encode_chk(prefix + payload + suffix)
  136. return rv, dst_prefix + payload + dst_suffix
  137. def gen_valid_bech32_vector(template):
  138. '''Generate valid bech32 vector'''
  139. hrp = template[0]
  140. witver = template[1]
  141. witprog = bytearray(os.urandom(template[2]))
  142. encoding = template[4]
  143. dst_prefix = bytearray(template[5])
  144. rv = bech32_encode(encoding, hrp, [witver] + convertbits(witprog, 8, 5))
  145. return rv, dst_prefix + witprog
  146. def gen_valid_vectors():
  147. '''Generate valid test vectors'''
  148. glist = [gen_valid_base58_vector, gen_valid_bech32_vector]
  149. tlist = [templates, bech32_templates]
  150. while True:
  151. for template, valid_vector_generator in [(t, g) for g, l in zip(glist, tlist) for t in l]:
  152. rv, payload = valid_vector_generator(template)
  153. assert is_valid(rv)
  154. metadata = {x: y for x, y in zip(metadata_keys,template[3]) if y is not None}
  155. hexrepr = payload.hex()
  156. yield (rv, hexrepr, metadata)
  157. def gen_invalid_base58_vector(template):
  158. '''Generate possibly invalid vector'''
  159. # kinds of invalid vectors:
  160. # invalid prefix
  161. # invalid payload length
  162. # invalid (randomized) suffix (add random data)
  163. # corrupt checksum
  164. corrupt_prefix = randbool(0.2)
  165. randomize_payload_size = randbool(0.2)
  166. corrupt_suffix = randbool(0.2)
  167. if corrupt_prefix:
  168. prefix = os.urandom(1)
  169. else:
  170. prefix = bytearray(template[0])
  171. if randomize_payload_size:
  172. payload = os.urandom(max(int(random.expovariate(0.5)), 50))
  173. else:
  174. payload = os.urandom(template[1])
  175. if corrupt_suffix:
  176. suffix = os.urandom(len(template[2]))
  177. else:
  178. suffix = bytearray(template[2])
  179. val = b58encode_chk(prefix + payload + suffix)
  180. if random.randint(0,10)<1: # line corruption
  181. if randbool(): # add random character to end
  182. val += random.choice(b58chars)
  183. else: # replace random character in the middle
  184. n = random.randint(0, len(val))
  185. val = val[0:n] + random.choice(b58chars) + val[n+1:]
  186. return val
  187. def gen_invalid_bech32_vector(template):
  188. '''Generate possibly invalid bech32 vector'''
  189. no_data = randbool(0.1)
  190. to_upper = randbool(0.1)
  191. hrp = template[0]
  192. witver = template[1]
  193. witprog = bytearray(os.urandom(template[2]))
  194. encoding = template[3]
  195. if no_data:
  196. rv = bech32_encode(encoding, hrp, [])
  197. else:
  198. data = [witver] + convertbits(witprog, 8, 5)
  199. if template[4] and not no_data:
  200. if template[2] % 5 in {2, 4}:
  201. data[-1] |= 1
  202. else:
  203. data.append(0)
  204. rv = bech32_encode(encoding, hrp, data)
  205. if template[5]:
  206. i = len(rv) - random.randrange(1, 7)
  207. rv = rv[:i] + random.choice(CHARSET.replace(rv[i], '')) + rv[i + 1:]
  208. if template[6]:
  209. i = len(hrp) + 1 + random.randrange(0, len(rv) - len(hrp) - 4)
  210. rv = rv[:i] + rv[i:i + 4].upper() + rv[i + 4:]
  211. if to_upper:
  212. rv = rv.swapcase()
  213. return rv
  214. def randbool(p = 0.5):
  215. '''Return True with P(p)'''
  216. return random.random() < p
  217. def gen_invalid_vectors():
  218. '''Generate invalid test vectors'''
  219. # start with some manual edge-cases
  220. yield "",
  221. yield "x",
  222. glist = [gen_invalid_base58_vector, gen_invalid_bech32_vector]
  223. tlist = [templates, bech32_ng_templates]
  224. while True:
  225. for template, invalid_vector_generator in [(t, g) for g, l in zip(glist, tlist) for t in l]:
  226. val = invalid_vector_generator(template)
  227. if not is_valid(val):
  228. yield val,
  229. if __name__ == '__main__':
  230. import sys
  231. import json
  232. iters = {'valid':gen_valid_vectors, 'invalid':gen_invalid_vectors}
  233. try:
  234. uiter = iters[sys.argv[1]]
  235. except IndexError:
  236. uiter = gen_valid_vectors
  237. try:
  238. count = int(sys.argv[2])
  239. except IndexError:
  240. count = 0
  241. data = list(islice(uiter(), count))
  242. json.dump(data, sys.stdout, sort_keys=True, indent=4)
  243. sys.stdout.write('\n')