makedesc.py 11 KB

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