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