makedesc.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. #!/usr/bin/env python
  2. # Copyright 2014-2019, The Tor Project, Inc.
  3. # See LICENSE for license information
  4. # This is a kludgey python script that uses ctypes and openssl to sign
  5. # router descriptors and extrainfo documents and put all the keys in
  6. # the right places. There are examples at the end of the file.
  7. # I've used this to make inputs for unit tests. I wouldn't suggest
  8. # using it for anything else.
  9. # Future imports for Python 2.7, mandatory in 3.0
  10. from __future__ import division
  11. from __future__ import print_function
  12. from __future__ import unicode_literals
  13. import base64
  14. import binascii
  15. import ctypes
  16. import ctypes.util
  17. import hashlib
  18. import optparse
  19. import os
  20. import re
  21. import struct
  22. import time
  23. import slow_ed25519
  24. import slownacl_curve25519
  25. import ed25519_exts_ref
  26. try:
  27. xrange # Python 2
  28. except NameError:
  29. xrange = range # Python 3
  30. # Pull in the openssl stuff we need.
  31. crypt = ctypes.CDLL(ctypes.util.find_library('crypto'))
  32. BIO_s_mem = crypt.BIO_s_mem
  33. BIO_s_mem.argtypes = []
  34. BIO_s_mem.restype = ctypes.c_void_p
  35. BIO_new = crypt.BIO_new
  36. BIO_new.argtypes = [ctypes.c_void_p]
  37. BIO_new.restype = ctypes.c_void_p
  38. crypt.BIO_free.argtypes = [ctypes.c_void_p]
  39. crypt.BIO_free.restype = ctypes.c_int
  40. crypt.BIO_ctrl.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_long, ctypes.c_void_p ]
  41. crypt.BIO_ctrl.restype = ctypes.c_long
  42. crypt.PEM_write_bio_RSAPublicKey.argtypes = [ ctypes.c_void_p, ctypes.c_void_p ]
  43. crypt.PEM_write_bio_RSAPublicKey.restype = ctypes.c_int
  44. RSA_generate_key = crypt.RSA_generate_key
  45. RSA_generate_key.argtypes = [ctypes.c_int, ctypes.c_ulong, ctypes.c_void_p, ctypes.c_void_p]
  46. RSA_generate_key.restype = ctypes.c_void_p
  47. RSA_private_encrypt = crypt.RSA_private_encrypt
  48. RSA_private_encrypt.argtypes = [
  49. ctypes.c_int, ctypes.c_char_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int ]
  50. RSA_private_encrypt.restype = ctypes.c_int
  51. i2d_RSAPublicKey = crypt.i2d_RSAPublicKey
  52. i2d_RSAPublicKey.argtypes = [
  53. ctypes.c_void_p, ctypes.POINTER(ctypes.c_char_p)
  54. ]
  55. i2d_RSAPublicKey.restype = ctypes.c_int
  56. HEADER = """\
  57. router fred 127.0.0.1 9001 0 9002
  58. identity-ed25519
  59. {d.ED_CERT}
  60. signing-key
  61. {d.RSA_IDENTITY}
  62. master-key-ed25519 {d.ED_IDENTITY}
  63. onion-key
  64. {d.RSA_ONION_KEY}
  65. ntor-onion-key {d.NTOR_ONION_KEY}
  66. ntor-onion-key-crosscert {d.NTOR_CROSSCERT_SIGN}
  67. {d.NTOR_CROSSCERT}
  68. onion-key-crosscert
  69. {d.RSA_CROSSCERT_ED}
  70. """
  71. FOOTER="""
  72. """
  73. def rsa_sign(msg, rsa):
  74. buf = ctypes.create_string_buffer(2048)
  75. n = RSA_private_encrypt(len(msg), msg, buf, rsa, 1)
  76. if n <= 0:
  77. raise Exception()
  78. return buf.raw[:n]
  79. def b64(x1):
  80. x = binascii.b2a_base64(x1)
  81. res = []
  82. for i in xrange(0, len(x), 64):
  83. res.append((x[i:i+64]).decode("ascii"))
  84. return "\n".join(res)
  85. def bio_extract(bio):
  86. buf = ctypes.c_char_p()
  87. length = crypt.BIO_ctrl(bio, 3, 0, ctypes.byref(buf))
  88. return ctypes.string_at(buf, length)
  89. def make_rsa_key(e=65537):
  90. rsa = crypt.RSA_generate_key(1024, e, None, None)
  91. bio = BIO_new(BIO_s_mem())
  92. crypt.PEM_write_bio_RSAPublicKey(bio, rsa)
  93. pem = bio_extract(bio).rstrip()
  94. crypt.BIO_free(bio)
  95. buf = ctypes.create_string_buffer(1024)
  96. pBuf = ctypes.c_char_p(ctypes.addressof(buf))
  97. n = crypt.i2d_RSAPublicKey(rsa, ctypes.byref(pBuf))
  98. s = buf.raw[:n]
  99. digest = hashlib.sha1(s).digest()
  100. pem = pem.decode("ascii")
  101. return (rsa,pem,digest)
  102. def makeEdSigningKeyCert(sk_master, pk_master, pk_signing, date,
  103. includeSigning=False, certType=1):
  104. assert len(pk_signing) == len(pk_master) == 32
  105. expiration = struct.pack(b"!L", date//3600)
  106. if includeSigning:
  107. extensions = b"\x01\x00\x20\x04\x00%s"%(pk_master)
  108. else:
  109. extensions = b"\x00"
  110. signed = b"\x01%s%s\x01%s%s" % (
  111. bytes([certType]), expiration, pk_signing, extensions)
  112. signature = ed25519_exts_ref.signatureWithESK(signed, sk_master, pk_master)
  113. assert len(signature) == 64
  114. return signed+signature
  115. def objwrap(identifier, body):
  116. return ("-----BEGIN {0}-----\n"
  117. "{1}"
  118. "-----END {0}-----").format(identifier, body)
  119. MAGIC1 = "<<<<<<MAGIC>>>>>>"
  120. MAGIC2 = "<<<<<!#!#!#XYZZY#!#!#!>>>>>"
  121. class OnDemandKeys(object):
  122. def __init__(self, certDate=None):
  123. if certDate is None:
  124. certDate = int(time.time()) + 86400
  125. self.certDate = certDate
  126. self.rsa_id = None
  127. self.rsa_onion_key = None
  128. self.ed_id_sk = None
  129. self.ntor_sk = None
  130. self.ntor_crosscert = None
  131. self.rsa_crosscert_ed = None
  132. self.rsa_crosscert_noed = None
  133. @property
  134. def RSA_IDENTITY(self):
  135. if self.rsa_id is None:
  136. self.rsa_id, self.rsa_ident_pem, self.rsa_id_digest = make_rsa_key()
  137. return self.rsa_ident_pem
  138. @property
  139. def RSA_ID_DIGEST(self):
  140. self.RSA_IDENTITY
  141. return self.rsa_id_digest
  142. @property
  143. def RSA_FINGERPRINT_NOSPACE(self):
  144. return binascii.b2a_hex(self.RSA_ID_DIGEST).upper().decode("ascii")
  145. @property
  146. def RSA_ONION_KEY(self):
  147. if self.rsa_onion_key is None:
  148. self.rsa_onion_key, self.rsa_onion_pem, _ = make_rsa_key()
  149. return self.rsa_onion_pem
  150. @property
  151. def RSA_FINGERPRINT(self):
  152. hexdigest = self.RSA_FINGERPRINT_NOSPACE
  153. return " ".join(hexdigest[i:i+4] for i in range(0,len(hexdigest),4))
  154. @property
  155. def RSA_SIGNATURE(self):
  156. return MAGIC1
  157. @property
  158. def ED_SIGNATURE(self):
  159. return MAGIC2
  160. @property
  161. def NTOR_ONION_KEY(self):
  162. if self.ntor_sk is None:
  163. self.ntor_sk = slownacl_curve25519.Private()
  164. self.ntor_pk = self.ntor_sk.get_public()
  165. return base64.b64encode(self.ntor_pk.serialize()).decode("ascii")
  166. @property
  167. def ED_CERT(self):
  168. if self.ed_id_sk is None:
  169. self.ed_id_sk = ed25519_exts_ref.expandSK(os.urandom(32))
  170. self.ed_signing_sk = ed25519_exts_ref.expandSK(os.urandom(32))
  171. self.ed_id_pk = ed25519_exts_ref.publickeyFromESK(self.ed_id_sk)
  172. self.ed_signing_pk = ed25519_exts_ref.publickeyFromESK(self.ed_signing_sk)
  173. self.ed_cert = makeEdSigningKeyCert(self.ed_id_sk, self.ed_id_pk, self.ed_signing_pk, self.certDate, includeSigning=True, certType=4)
  174. return objwrap('ED25519 CERT', b64(self.ed_cert))
  175. @property
  176. def ED_IDENTITY(self):
  177. self.ED_CERT
  178. return binascii.b2a_base64(self.ed_id_pk).strip().decode("ascii")
  179. @property
  180. def NTOR_CROSSCERT(self):
  181. if self.ntor_crosscert is None:
  182. self.ED_CERT
  183. self.NTOR_ONION_KEY
  184. ed_privkey = self.ntor_sk.serialize() + os.urandom(32)
  185. ed_pub0 = ed25519_exts_ref.publickeyFromESK(ed_privkey)
  186. sign = ((ed_pub0[31]) & 255) >> 7
  187. self.ntor_crosscert = makeEdSigningKeyCert(self.ntor_sk.serialize() + os.urandom(32), ed_pub0, self.ed_id_pk, self.certDate, certType=10)
  188. self.ntor_crosscert_sign = sign
  189. return objwrap('ED25519 CERT', b64(self.ntor_crosscert))
  190. @property
  191. def NTOR_CROSSCERT_SIGN(self):
  192. self.NTOR_CROSSCERT
  193. return self.ntor_crosscert_sign
  194. @property
  195. def RSA_CROSSCERT_NOED(self):
  196. if self.rsa_crosscert_noed is None:
  197. self.RSA_ONION_KEY
  198. signed = self.RSA_ID_DIGEST
  199. self.rsa_crosscert_noed = rsa_sign(signed, self.rsa_onion_key)
  200. return objwrap("CROSSCERT",b64(self.rsa_crosscert_noed))
  201. @property
  202. def RSA_CROSSCERT_ED(self):
  203. if self.rsa_crosscert_ed is None:
  204. self.RSA_ONION_KEY
  205. self.ED_CERT
  206. signed = self.RSA_ID_DIGEST + self.ed_id_pk
  207. self.rsa_crosscert_ed = rsa_sign(signed, self.rsa_onion_key)
  208. return objwrap("CROSSCERT",b64(self.rsa_crosscert_ed))
  209. def sign_desc(self, body):
  210. idx = body.rfind("\nrouter-sig-ed25519 ")
  211. if idx >= 0:
  212. self.ED_CERT
  213. signed_part = body[:idx+len("\nrouter-sig-ed25519 ")]
  214. signed_part = "Tor router descriptor signature v1" + signed_part
  215. digest = hashlib.sha256(signed_part.encode("utf-8")).digest()
  216. ed_sig = ed25519_exts_ref.signatureWithESK(digest,
  217. self.ed_signing_sk, self.ed_signing_pk)
  218. body = body.replace(MAGIC2, base64.b64encode(ed_sig).decode("ascii").replace("=",""))
  219. self.RSA_IDENTITY
  220. idx = body.rindex("\nrouter-signature")
  221. end_of_sig = body.index("\n", idx+1)
  222. signed_part = body[:end_of_sig+1]
  223. digest = hashlib.sha1(signed_part.encode("utf-8")).digest()
  224. assert len(digest) == 20
  225. rsasig = rsa_sign(digest, self.rsa_id)
  226. body = body.replace(MAGIC1, objwrap("SIGNATURE", b64(rsasig)))
  227. return body
  228. def signdesc(body, args_out=None):
  229. rsa, ident_pem, id_digest = make_rsa_key()
  230. _, onion_pem, _ = make_rsa_key()
  231. need_ed = '{ED25519-CERT}' in body or '{ED25519-SIGNATURE}' in body
  232. if need_ed:
  233. sk_master = os.urandom(32)
  234. sk_signing = os.urandom(32)
  235. pk_master = slow_ed25519.pubkey(sk_master)
  236. pk_signing = slow_ed25519.pubkey(sk_signing)
  237. hexdigest = binascii.b2a_hex(id_digest).upper()
  238. fingerprint = " ".join(hexdigest[i:i+4] for i in range(0,len(hexdigest),4))
  239. MAGIC = "<<<<<<MAGIC>>>>>>"
  240. MORE_MAGIC = "<<<<<!#!#!#XYZZY#!#!#!>>>>>"
  241. args = {
  242. "RSA-IDENTITY" : ident_pem,
  243. "ONION-KEY" : onion_pem,
  244. "FINGERPRINT" : fingerprint,
  245. "FINGERPRINT-NOSPACE" : hexdigest,
  246. "RSA-SIGNATURE" : MAGIC
  247. }
  248. if need_ed:
  249. args['ED25519-CERT'] = makeEdSigningKeyCert(
  250. sk_master, pk_master, pk_signing)
  251. args['ED25519-SIGNATURE'] = MORE_MAGIC
  252. if args_out:
  253. args_out.update(args)
  254. body = body.format(**args)
  255. idx = body.rindex("\nrouter-signature")
  256. end_of_sig = body.index("\n", idx+1)
  257. signed_part = body[:end_of_sig+1]
  258. digest = hashlib.sha1(signed_part).digest()
  259. assert len(digest) == 20
  260. buf = ctypes.create_string_buffer(1024)
  261. n = RSA_private_encrypt(20, digest, buf, rsa, 1)
  262. sig = buf.raw[:n]
  263. sig = """-----BEGIN SIGNATURE-----
  264. %s
  265. -----END SIGNATURE-----""" % b64(sig).rstrip()
  266. body = body.replace(MAGIC, sig)
  267. return body.rstrip()
  268. def print_c_string(ident, body):
  269. print("static const char %s[] =" % ident)
  270. for line in body.split("\n"):
  271. print(' "%s\\n"' %(line))
  272. print(" ;")
  273. def emit_ri(name, body):
  274. info = OnDemandKeys()
  275. body = body.format(d=info)
  276. body = info.sign_desc(body)
  277. print_c_string("EX_RI_%s"%name.upper(), body)
  278. def emit_ei(name, body, fields):
  279. info = OnDemandKeys()
  280. body = body.format(d=info)
  281. body = info.sign_desc(body)
  282. print_c_string("EX_EI_%s"%name.upper(), body)
  283. print('ATTR_UNUSED static const char EX_EI_{NAME}_FP[] = "{d.RSA_FINGERPRINT_NOSPACE}";'.format(
  284. d=info, NAME=name.upper()))
  285. print("ATTR_UNUSED")
  286. print_c_string("EX_EI_%s_KEY"%name.upper(), info.RSA_IDENTITY)
  287. def analyze(s):
  288. while s:
  289. fields = {}
  290. s_pre = s
  291. while s.startswith(":::"):
  292. first,s=s.split("\n", 1)
  293. m = re.match(r'^:::(\w+)=(.*)',first)
  294. if not m:
  295. raise ValueError(first)
  296. k,v = m.groups()
  297. fields[k] = v
  298. if "name" not in fields:
  299. print(repr(s_pre))
  300. idx = s.find(":::")
  301. if idx != -1:
  302. body = s[:idx].rstrip()
  303. s = s[idx:]
  304. else:
  305. body = s.rstrip()
  306. s = ""
  307. yield (fields, body)
  308. def emit_entry(fields, s):
  309. try:
  310. name = fields['name']
  311. tp = fields['type']
  312. except KeyError:
  313. raise ValueError("missing required field")
  314. if tp == 'ei':
  315. emit_ei(name, s, fields)
  316. elif tp == 'ri':
  317. emit_ri(name, s)
  318. else:
  319. raise ValueError("unrecognized type")
  320. def process_file(s):
  321. print("""\
  322. /* These entries are automatically generated by makedesc.py to make sure
  323. * that their keys and signatures are right except when otherwise
  324. * specified. */
  325. """)
  326. for (fields, s) in analyze(s):
  327. emit_entry(fields, s)
  328. if __name__ == '__main__':
  329. import sys
  330. for fn in sys.argv[1:]:
  331. process_file(open(fn).read())