/
calc_hidservice_desc_id.py
90 lines (77 loc) · 4.46 KB
/
calc_hidservice_desc_id.py
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
#!/usr/bin/env python
from time import time
from base64 import b32encode, b32decode, b16decode
from hashlib import sha1
from struct import pack, unpack
import argparse
import datetime, calendar
# When provided with a Tor hidden service 'service_id', this script should output
# the predicted desc_id's which will be used to publish the HS descriptors for this
# HS into the future.
# Based on rend_compute_v2_desc_id() from rendcommon.c in Tor source code.
def compute_desc_ids(service_id_base32, max_replicas, time = int(time()), descriptor_cookie = ""):
desc_ids = []
for replica in range(0, max_replicas):
desc_ids.append(rend_compute_v2_desc_id(service_id_base32, replica, time, descriptor_cookie))
return desc_ids
# Returns base_32 encode desc_id - descriptor-id = H(permanent-id | H(time-period | descriptor-cookie | replica))
def rend_compute_v2_desc_id(service_id_base32, replica, time, descriptor_cookie):#
service_id = b32decode(service_id_base32, 1)
time_period = get_time_period(time, 0, service_id)
secret_id_part = get_secret_id_part_bytes(time_period, descriptor_cookie, replica)
desc_id = rend_get_descriptor_id_bytes(service_id, secret_id_part)
return b32encode(desc_id).lower()
# Calculates time period - time-period = (current-time + permanent-id-byte * 86400 / 256) / 86400
def get_time_period(time, deviation, service_id):
REND_TIME_PERIOD_V2_DESC_VALIDITY = 24 * 60 * 60
return int(((time + ((unpack('B', service_id[0])[0] * REND_TIME_PERIOD_V2_DESC_VALIDITY) ) / 256) ) / REND_TIME_PERIOD_V2_DESC_VALIDITY + deviation)
# Calculate secret_id_part - secret-id-part = H(time-period | descriptor-cookie | replica)
def get_secret_id_part_bytes(time_period, descriptor_cookie, replica):
secret_id_part = sha1()
secret_id_part.update(pack('>I', time_period)[:4]);
if descriptor_cookie:
secret_id_part.update(descriptor_cookie)
secret_id_part.update('{0:02X}'.format(replica).decode('hex'))
return secret_id_part.digest()
def rend_get_descriptor_id_bytes(service_id, secret_id_part):
descriptor_id = sha1()
descriptor_id.update(service_id)
descriptor_id.update(secret_id_part)
return descriptor_id.digest()
# Return the time of day when the desc_id changes for this service
def time_desc_changes(service_id, time = int(time())):
orig_time_period = get_time_period(time, 0, service_id)
while (get_time_period(time, 0, service_id) == orig_time_period):
time += 1
# time_period has now changed. return the time of day.
return time
def main():
REPLICAS = 2
parser = argparse.ArgumentParser(description="This tool allows you to generate the desc_id's that will " \
"be used by hidden services at any past or future date. " \
"By default the current desc_id id's for the hidden service " \
"are returned)")
parser.add_argument('onion_address', help='The hidden service address - e.g. (idnxcnkne4qt76tg.onion)')
parser.add_argument("-d", "--days", type=int,
help="Number of days into the future to generate desc_id's")
parser.add_argument("-p", "--past", action="store_true",
help="Should we generate desc_id's for dates in the past instead of the future")
args = parser.parse_args()
service_id, tld = args.onion_address.split(".")
if tld == 'onion' and len(service_id) == 16 and service_id.isalnum():
time_desc_updates = datetime.datetime.fromtimestamp(time_desc_changes(service_id)) - datetime.timedelta(days = 1)
if args.days: # Calculate desc_id's for future dates
for day in range(0, args.days):
desc_ids = compute_desc_ids(service_id, REPLICAS, calendar.timegm(time_desc_updates.timetuple()))
print time_desc_updates.strftime('%Y-%m-%d %H:%M:%S') + '\t' + '\t'.join(desc_ids)
if args.past:
time_desc_updates = time_desc_updates - datetime.timedelta(days = 1)
else:
time_desc_updates = time_desc_updates + datetime.timedelta(days = 1)
else: # Output the current desc_id's for the specified hidden service
desc_ids = compute_desc_ids(service_id, REPLICAS, calendar.timegm(time_desc_updates.timetuple()))
print time_desc_updates.strftime('%Y-%m-%d %H:%M:%S') + '\t' + '\t'.join(desc_ids)
else:
print "[!] The onion address you provided is not valid"
if __name__ == '__main__':
main()