makedesc.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. #!/usr/bin/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. import base64
  10. import binascii
  11. import ctypes
  12. import ctypes.util
  13. import hashlib
  14. import optparse
  15. import os
  16. import re
  17. import struct
  18. import time
  19. import UserDict
  20. import slow_ed25519
  21. import slownacl_curve25519
  22. import ed25519_exts_ref
  23. # Pull in the openssl stuff we need.
  24. crypt = ctypes.CDLL(ctypes.util.find_library('crypto'))
  25. BIO_s_mem = crypt.BIO_s_mem
  26. BIO_s_mem.argtypes = []
  27. BIO_s_mem.restype = ctypes.c_void_p
  28. BIO_new = crypt.BIO_new
  29. BIO_new.argtypes = [ctypes.c_void_p]
  30. BIO_new.restype = ctypes.c_void_p
  31. crypt.BIO_free.argtypes = [ctypes.c_void_p]
  32. crypt.BIO_free.restype = ctypes.c_int
  33. crypt.BIO_ctrl.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_long, ctypes.c_void_p ]
  34. crypt.BIO_ctrl.restype = ctypes.c_long
  35. crypt.PEM_write_bio_RSAPublicKey.argtypes = [ ctypes.c_void_p, ctypes.c_void_p ]
  36. crypt.PEM_write_bio_RSAPublicKey.restype = ctypes.c_int
  37. RSA_generate_key = crypt.RSA_generate_key
  38. RSA_generate_key.argtypes = [ctypes.c_int, ctypes.c_ulong, ctypes.c_void_p, ctypes.c_void_p]
  39. RSA_generate_key.restype = ctypes.c_void_p
  40. RSA_private_encrypt = crypt.RSA_private_encrypt
  41. RSA_private_encrypt.argtypes = [
  42. ctypes.c_int, ctypes.c_char_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int ]
  43. RSA_private_encrypt.restype = ctypes.c_int
  44. i2d_RSAPublicKey = crypt.i2d_RSAPublicKey
  45. i2d_RSAPublicKey.argtypes = [
  46. ctypes.c_void_p, ctypes.POINTER(ctypes.c_char_p)
  47. ]
  48. i2d_RSAPublicKey.restype = ctypes.c_int
  49. def rsa_sign(msg, rsa):
  50. buf = ctypes.create_string_buffer(1024)
  51. n = RSA_private_encrypt(len(msg), msg, buf, rsa, 1)
  52. if n <= 0:
  53. raise Exception()
  54. return buf.raw[:n]
  55. def b64(x):
  56. x = base64.b64encode(x)
  57. res = []
  58. for i in xrange(0, len(x), 64):
  59. res.append(x[i:i+64]+"\n")
  60. return "".join(res)
  61. def bio_extract(bio):
  62. buf = ctypes.c_char_p()
  63. length = crypt.BIO_ctrl(bio, 3, 0, ctypes.byref(buf))
  64. return ctypes.string_at(buf, length)
  65. def make_rsa_key(e=65537):
  66. rsa = crypt.RSA_generate_key(1024, e, None, None)
  67. bio = BIO_new(BIO_s_mem())
  68. crypt.PEM_write_bio_RSAPublicKey(bio, rsa)
  69. pem = bio_extract(bio).rstrip()
  70. crypt.BIO_free(bio)
  71. buf = ctypes.create_string_buffer(1024)
  72. pBuf = ctypes.c_char_p(ctypes.addressof(buf))
  73. n = crypt.i2d_RSAPublicKey(rsa, ctypes.byref(pBuf))
  74. s = buf.raw[:n]
  75. digest = hashlib.sha1(s).digest()
  76. return (rsa,pem,digest)
  77. def makeEdSigningKeyCert(sk_master, pk_master, pk_signing, date,
  78. includeSigning=False, certType=1):
  79. assert len(pk_signing) == len(pk_master) == 32
  80. expiration = struct.pack("!L", date//3600)
  81. if includeSigning:
  82. extensions = "\x01\x00\x20\x04\x00%s"%(pk_master)
  83. else:
  84. extensions = "\x00"
  85. signed = "\x01%s%s\x01%s%s" % (
  86. chr(certType), expiration, pk_signing, extensions)
  87. signature = ed25519_exts_ref.signatureWithESK(signed, sk_master, pk_master)
  88. assert len(signature) == 64
  89. return signed+signature
  90. def objwrap(identifier, body):
  91. return ("-----BEGIN {0}-----\n"
  92. "{1}"
  93. "-----END {0}-----").format(identifier, body)
  94. MAGIC1 = "<<<<<<MAGIC>>>>>>"
  95. MAGIC2 = "<<<<<!#!#!#XYZZY#!#!#!>>>>>"
  96. class OnDemandKeys(object):
  97. def __init__(self, certDate=None):
  98. if certDate is None:
  99. certDate = time.time() + 86400
  100. self.certDate = certDate
  101. self.rsa_id = None
  102. self.rsa_onion_key = None
  103. self.ed_id_sk = None
  104. self.ntor_sk = None
  105. self.ntor_crosscert = None
  106. self.rsa_crosscert_ed = None
  107. self.rsa_crosscert_noed = None
  108. @property
  109. def RSA_IDENTITY(self):
  110. if self.rsa_id is None:
  111. self.rsa_id, self.rsa_ident_pem, self.rsa_id_digest = make_rsa_key()
  112. return self.rsa_ident_pem
  113. @property
  114. def RSA_ID_DIGEST(self):
  115. self.RSA_IDENTITY
  116. return self.rsa_id_digest
  117. @property
  118. def RSA_FINGERPRINT_NOSPACE(self):
  119. return binascii.b2a_hex(self.RSA_ID_DIGEST).upper()
  120. @property
  121. def RSA_ONION_KEY(self):
  122. if self.rsa_onion_key is None:
  123. self.rsa_onion_key, self.rsa_onion_pem, _ = make_rsa_key()
  124. return self.rsa_onion_pem
  125. @property
  126. def RSA_FINGERPRINT(self):
  127. hexdigest = self.RSA_FINGERPRINT_NOSPACEK
  128. return " ".join(hexdigest[i:i+4] for i in range(0,len(hexdigest),4))
  129. @property
  130. def RSA_SIGNATURE(self):
  131. return MAGIC1
  132. @property
  133. def ED_SIGNATURE(self):
  134. return MAGIC2
  135. @property
  136. def NTOR_ONION_KEY(self):
  137. if self.ntor_sk is None:
  138. self.ntor_sk = slownacl_curve25519.Private()
  139. self.ntor_pk = self.ntor_sk.get_public()
  140. return base64.b64encode(self.ntor_pk.serialize())
  141. @property
  142. def ED_CERT(self):
  143. if self.ed_id_sk is None:
  144. self.ed_id_sk = ed25519_exts_ref.expandSK(os.urandom(32))
  145. self.ed_signing_sk = ed25519_exts_ref.expandSK(os.urandom(32))
  146. self.ed_id_pk = ed25519_exts_ref.publickeyFromESK(self.ed_id_sk)
  147. self.ed_signing_pk = ed25519_exts_ref.publickeyFromESK(self.ed_signing_sk)
  148. self.ed_cert = makeEdSigningKeyCert(self.ed_id_sk, self.ed_id_pk, self.ed_signing_pk, self.certDate, includeSigning=True, certType=4)
  149. return objwrap('ED25519 CERT', b64(self.ed_cert))
  150. @property
  151. def NTOR_CROSSCERT(self):
  152. if self.ntor_crosscert is None:
  153. self.ED_CERT
  154. self.NTOR_ONION_KEY
  155. ed_privkey = self.ntor_sk.serialize() + os.urandom(32)
  156. ed_pub0 = ed25519_exts_ref.publickeyFromESK(ed_privkey)
  157. sign = (ord(ed_pub0[31]) & 255) >> 7
  158. self.ntor_crosscert = makeEdSigningKeyCert(self.ntor_sk.serialize() + os.urandom(32), ed_pub0, self.ed_id_pk, self.certDate, certType=10)
  159. self.ntor_crosscert_sign = sign
  160. return objwrap('ED25519 CERT', b64(self.ntor_crosscert))
  161. @property
  162. def NTOR_CROSSCERT_SIGN(self):
  163. self.NTOR_CROSSCERT
  164. return self.ntor_crosscert_sign
  165. @property
  166. def RSA_CROSSCERT_NOED(self):
  167. if self.rsa_crosscert_noed is None:
  168. self.RSA_ONION_KEY
  169. signed = self.RSA_ID_DIGEST
  170. self.rsa_crosscert_noed = rsa_sign(signed, self.rsa_onion_key)
  171. return objwrap("CROSSCERT",b64(self.rsa_crosscert_noed))
  172. @property
  173. def RSA_CROSSCERT_ED(self):
  174. if self.rsa_crosscert_ed is None:
  175. self.RSA_ONION_KEY
  176. self.ED_CERT
  177. signed = self.RSA_ID_DIGEST + self.ed_id_pk
  178. self.rsa_crosscert_ed = rsa_sign(signed, self.rsa_onion_key)
  179. return objwrap("CROSSCERT",b64(self.rsa_crosscert_ed))
  180. def sign_desc(self, body):
  181. idx = body.rfind("\nrouter-sig-ed25519 ")
  182. if idx >= 0:
  183. self.ED_CERT
  184. signed_part = body[:idx+len("\nrouter-sig-ed25519 ")]
  185. signed_part = "Tor router descriptor signature v1" + signed_part
  186. digest = hashlib.sha256(signed_part).digest()
  187. ed_sig = ed25519_exts_ref.signatureWithESK(digest,
  188. self.ed_signing_sk, self.ed_signing_pk)
  189. body = body.replace(MAGIC2, base64.b64encode(ed_sig).replace("=",""))
  190. idx = body.rindex("\nrouter-signature")
  191. end_of_sig = body.index("\n", idx+1)
  192. signed_part = body[:end_of_sig+1]
  193. digest = hashlib.sha1(signed_part).digest()
  194. assert len(digest) == 20
  195. rsasig = rsa_sign(digest, self.rsa_id)
  196. body = body.replace(MAGIC1, objwrap("SIGNATURE", b64(rsasig)))
  197. return body
  198. def signdesc(body, args_out=None):
  199. rsa, ident_pem, id_digest = make_key()
  200. _, onion_pem, _ = make_key()
  201. need_ed = '{ED25519-CERT}' in body or '{ED25519-SIGNATURE}' in body
  202. if need_ed:
  203. sk_master = os.urandom(32)
  204. sk_signing = os.urandom(32)
  205. pk_master = slow_ed25519.pubkey(sk_master)
  206. pk_signing = slow_ed25519.pubkey(sk_signing)
  207. hexdigest = binascii.b2a_hex(id_digest).upper()
  208. fingerprint = " ".join(hexdigest[i:i+4] for i in range(0,len(hexdigest),4))
  209. MAGIC = "<<<<<<MAGIC>>>>>>"
  210. MORE_MAGIC = "<<<<<!#!#!#XYZZY#!#!#!>>>>>"
  211. args = {
  212. "RSA-IDENTITY" : ident_pem,
  213. "ONION-KEY" : onion_pem,
  214. "FINGERPRINT" : fingerprint,
  215. "FINGERPRINT-NOSPACE" : hexdigest,
  216. "RSA-SIGNATURE" : MAGIC
  217. }
  218. if need_ed:
  219. args['ED25519-CERT'] = makeEdSigningKeyCert(
  220. sk_master, pk_master, pk_signing)
  221. args['ED25519-SIGNATURE'] = MORE_MAGIC
  222. if args_out:
  223. args_out.update(args)
  224. body = body.format(**args)
  225. idx = body.rindex("\nrouter-signature")
  226. end_of_sig = body.index("\n", idx+1)
  227. signed_part = body[:end_of_sig+1]
  228. digest = hashlib.sha1(signed_part).digest()
  229. assert len(digest) == 20
  230. buf = ctypes.create_string_buffer(1024)
  231. n = RSA_private_encrypt(20, digest, buf, rsa, 1)
  232. sig = buf.raw[:n]
  233. sig = """-----BEGIN SIGNATURE-----
  234. %s
  235. -----END SIGNATURE-----""" % b64(sig).rstrip()
  236. body = body.replace(MAGIC, sig)
  237. return body.rstrip()
  238. def print_c_string(ident, body):
  239. print "static const char %s[] =" % ident
  240. for line in body.split("\n"):
  241. print ' "%s\\n"' %(line)
  242. print " ;"
  243. def emit_ri(name, body):
  244. info = OnDemandKeys()
  245. body = body.format(d=info)
  246. body = info.sign_desc(body)
  247. print_c_string("EX_RI_%s"%name.upper(), body)
  248. def emit_ei(name, body):
  249. info = OnDemandKeys()
  250. body = body.format(d=info)
  251. body = info.sign_desc(body)
  252. print_c_string("EX_EI_%s"%name.upper(), body)
  253. print 'const char EX_EI_{NAME}_FP[] = "{d.RSA_FINGERPRINT_NOSPACE}";'.format(
  254. d=info, NAME=name.upper())
  255. print_c_string("EX_EI_%s_KEY"%name.upper(), info.RSA_IDENTITY)
  256. def analyze(s):
  257. fields = {}
  258. while s.startswith(":::"):
  259. first,s=s.split("\n", 1)
  260. m = re.match(r'^:::(\w+)=(.*)',first)
  261. if not m:
  262. raise ValueError(first)
  263. k,v = m.groups()
  264. fields[k] = v
  265. return fields, s
  266. def process_file(s):
  267. fields, s = analyze(s)
  268. try:
  269. name = fields['name']
  270. tp = fields['type']
  271. except KeyError:
  272. raise ValueError("missing required field")
  273. if tp == 'ei':
  274. emit_ei(name, s)
  275. elif tp == 'ri':
  276. emit_ri(name, s)
  277. else:
  278. raise ValueError("unrecognized type")
  279. if __name__ == '__main__':
  280. import sys
  281. for fn in sys.argv[1:]:
  282. process_file(open(fn).read())