aboutsummaryrefslogtreecommitdiff
path: root/scripts/codegen/makedesc.py
blob: 833951945bfe49944c8684feac4639f7deba9ddb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
#!/usr/bin/python
# Copyright 2014-2015, The Tor Project, Inc.
# See LICENSE for license information

# This is a kludgey python script that uses ctypes and openssl to sign
# router descriptors and extrainfo documents and put all the keys in
# the right places.  There are examples at the end of the file.

# I've used this to make inputs for unit tests.  I wouldn't suggest
# using it for anything else.

import base64
import binascii
import ctypes
import ctypes.util
import hashlib

crypt = ctypes.CDLL(ctypes.util.find_library('crypto'))
BIO_s_mem = crypt.BIO_s_mem
BIO_s_mem.argtypes = []
BIO_s_mem.restype = ctypes.c_void_p

BIO_new = crypt.BIO_new
BIO_new.argtypes = [ctypes.c_void_p]
BIO_new.restype = ctypes.c_void_p

RSA_generate_key = crypt.RSA_generate_key
RSA_generate_key.argtypes = [ctypes.c_int, ctypes.c_ulong, ctypes.c_void_p, ctypes.c_void_p]
RSA_generate_key.restype = ctypes.c_void_p

RSA_private_encrypt = crypt.RSA_private_encrypt
RSA_private_encrypt.argtypes = [
    ctypes.c_int, ctypes.c_char_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int ]
RSA_private_encrypt.restype = ctypes.c_int

i2d_RSAPublicKey = crypt.i2d_RSAPublicKey
i2d_RSAPublicKey.argtypes = [
    ctypes.c_void_p, ctypes.POINTER(ctypes.c_char_p)
]
i2d_RSAPublicKey.restype = ctypes.c_int

def b64(x):
    x = base64.b64encode(x)
    res = []
    for i in xrange(0, len(x), 64):
        res.append(x[i:i+64]+"\n")
    return "".join(res)

def bio_extract(bio):
    buf = ctypes.c_char_p()
    length = crypt.BIO_ctrl(bio, 3, 0, ctypes.byref(buf))
    return ctypes.string_at(buf, length)

def make_key(e=65537):
    rsa = crypt.RSA_generate_key(1024, e, None, None)
    bio = BIO_new(BIO_s_mem())
    crypt.PEM_write_bio_RSAPublicKey(bio, rsa)
    pem = bio_extract(bio).rstrip()
    crypt.BIO_free(bio)

    buf = ctypes.create_string_buffer(1024)
    pBuf = ctypes.c_char_p(ctypes.addressof(buf))
    n = crypt.i2d_RSAPublicKey(rsa, ctypes.byref(pBuf))
    s = buf.raw[:n]
    digest = hashlib.sha1(s).digest()

    return (rsa,pem,digest)

def signdesc(body, args_out=None):
    rsa, ident_pem, id_digest = make_key()
    _, onion_pem, _ = make_key()

    hexdigest = binascii.b2a_hex(id_digest).upper()
    fingerprint = " ".join(hexdigest[i:i+4] for i in range(0,len(hexdigest),4))

    MAGIC = "<<<<<<MAGIC>>>>>>"
    args = {
        "RSA-IDENTITY" : ident_pem,
        "ONION-KEY" : onion_pem,
        "FINGERPRINT" : fingerprint,
        "FINGERPRINT-NOSPACE" : hexdigest,
        "RSA-SIGNATURE" : MAGIC
    }
    if args_out:
        args_out.update(args)
    body = body.format(**args)

    idx = body.rindex("\nrouter-signature")
    end_of_sig = body.index("\n", idx+1)

    signed_part = body[:end_of_sig+1]

    digest = hashlib.sha1(signed_part).digest()
    assert len(digest) == 20

    buf = ctypes.create_string_buffer(1024)
    n = RSA_private_encrypt(20, digest, buf, rsa, 1)
    sig = buf.raw[:n]

    sig = """-----BEGIN SIGNATURE-----
%s
-----END SIGNATURE-----""" % b64(sig).rstrip()
    body = body.replace(MAGIC, sig)

    return body.rstrip()

def emit_ri(name, body, args_out=None):
    print "const char %s[] ="%name
    body = "\n".join(line.rstrip() for line in body.split("\n"))+"\n"
    b = signdesc(body, args_out)
    for line in b.split("\n"):
        print '  "%s\\n"'%line
    print "  ;"

def emit_ei(name, body):
    args = { 'NAME' : name }
    emit_ri(name, body, args)
    args['key'] = "\n".join(
        '  "%s\\n"'%line for line in args['RSA-IDENTITY'].split("\n"))
    print """
const char {NAME}_fp[] = "{FINGERPRINT-NOSPACE}";
const char {NAME}_key[] =
{key};""".format(**args)

if 0:
    emit_ri("minimal",
     """\
router fred 127.0.0.1 9001 0 9002
signing-key
{RSA-IDENTITY}
onion-key
{ONION-KEY}
published 2014-10-05 12:00:00
bandwidth 1000 1000 1000
reject *:*
router-signature
{RSA-SIGNATURE}
""")

if 0:
    emit_ri("maximal",
     """\
router fred 127.0.0.1 9001 0 9002
signing-key
{RSA-IDENTITY}
onion-key
{ONION-KEY}
published 2014-10-05 12:00:00
bandwidth 1000 1000 1000
reject 127.0.0.1:*
accept *:80
reject *:*
ipv6-policy accept 80,100,101
ntor-onion-key s7rSohmz9SXn8WWh1EefTHIsWePthsEntQi0WL+ScVw
uptime 1000
hibernating 0
unrecognized-keywords are just dandy in this format
platform Tor 0.2.4.23 on a Banana PC Jr 6000 Series
contact O.W.Jones
fingerprint {FINGERPRINT}
read-history 900 1,2,3,4
write-history 900 1,2,3,4
extra-info-digest AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
hidden-service-dir
allow-single-hop-exits
family $AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA $BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
caches-extra-info
or-address [::1:2:3:4]:9999
or-address 127.0.0.99:10000
opt fred is a fine router
router-signature
{RSA-SIGNATURE}
""")

if 0:
    emit_ei("maximal",
"""\
extra-info bob {FINGERPRINT-NOSPACE}
published 2014-10-05 20:07:00
opt foobarbaz
read-history 900 1,2,3
write-history 900 1,2,3
dirreq-v2-ips 1
dirreq-v3-ips 100
dirreq-v3-reqs blahblah
dirreq-v2-share blahblah
dirreq-v3-share blahblah
dirreq-v2-resp djfkdj
dirreq-v3-resp djfkdj
dirreq-v2-direct-dl djfkdj
dirreq-v3-direct-dl djfkdj
dirreq-v2-tunneled-dl djfkdj
dirreq-v3-tunneled-dl djfkdj
dirreq-stats-end foobar
entry-ips jfsdfds
entry-stats-end ksdflkjfdkf
cell-stats-end FOO
cell-processed-cells FOO
cell-queued-cells FOO
cell-time-in-queue FOO
cell-circuits-per-decile FOO
exit-stats-end FOO
exit-kibibytes-written FOO
exit-kibibytes-read FOO
exit-streams-opened FOO
router-signature
{RSA-SIGNATURE}
""")

if 0:
    emit_ei("minimal",
"""\
extra-info bob {FINGERPRINT-NOSPACE}
published 2014-10-05 20:07:00
router-signature
{RSA-SIGNATURE}
""")