-
-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathtor-auth-x25519-gen.py
More file actions
executable file
·108 lines (88 loc) · 3.37 KB
/
Copy pathtor-auth-x25519-gen.py
File metadata and controls
executable file
·108 lines (88 loc) · 3.37 KB
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
#!/usr/bin/env python3
"""Tor v3 Onion authentication credential generator script.
This module outputs Tor Onion v3 Onion credentials and generates the
associated X25519 cryptographic keypairs for use in those credential
files. The PyNaCl library is required. To install it, for example:
```sh
pip install pynacl
```
"""
import base64
import sys, argparse, re
try:
import nacl.public
except ImportError:
print(
'PyNaCl is required: install with "pip install pynacl" and try again.',
file=sys.stderr
)
exit(1)
def key_str(key):
# bytes to base 32
key_bytes = bytes(key)
key_b32 = base64.b32encode(key_bytes)
# strip trailing ====
assert key_b32[-4:] == b'===='
key_b32 = key_b32[:-4]
# change from b'ASDF' to ASDF
s = key_b32.decode('utf-8')
return s
def validate_onion(v):
"""Ensure the given Onion domain is valid.
Args:
v (str): The given value.
Returns:
str: The validated value.
Raises:
ArgumentTypeError: If `v` does not validate.
>>> validate_onion('value with incorrect ending')
Traceback (most recent call last):
...
argparse.ArgumentTypeError: Onion domain must end in ".onion"
>>> validate_onion('tooShortValue.onion')
Traceback (most recent call last):
...
argparse.ArgumentTypeError: Onion domains must be 56 lowercased alphanumeric characters.
>>> validate_onion('Onions Must Be 56 Entirely Lowercased Alphanumeric Chars.onion')
Traceback (most recent call last):
...
argparse.ArgumentTypeError: Onion domains must be 56 lowercased alphanumeric characters.
>>> validate_onion('rh5d6reakhpvuxe2t3next6um6iiq4jf43m7gmdrphfhopfpnoglzcyd.onion')
'rh5d6reakhpvuxe2t3next6um6iiq4jf43m7gmdrphfhopfpnoglzcyd'
"""
onion = v.split('.')[0]
if not v.endswith('.onion'):
raise argparse.ArgumentTypeError('Onion domain must end in ".onion"')
if not re.match('[a-z0-9]{56}', onion):
raise argparse.ArgumentTypeError('Onion domains must be 56 lowercased alphanumeric characters.')
return onion
def main():
parser = argparse.ArgumentParser(description="Generate a new X25519 keypair and output Tor Onion service credential file contents.")
parser.add_argument(
"-d", "--onion-domain",
help="Onion domain for which to create a keypair. Include the `.onion` part.",
type=validate_onion
)
parser.add_argument(
"-f", "--credential-file",
help="Where to write the `.auth` and `.auth_private` Tor Onion credential files."
)
args = parser.parse_args()
# Generate keys.
priv_key = nacl.public.PrivateKey.generate()
pub_key = priv_key.public_key
if args.credential_file:
if not args.onion_domain:
print(
'Cannot write credential files with no Onion! Try again with `-d <some_onion_domain.onion>`.',
file=sys.stderr
)
with open('{}.auth_private'.format(args.credential_file), 'w') as fh:
fh.write('{}:descriptor:x25519:{}'.format(args.onion_domain, key_str(priv_key)))
with open('{}.auth'.format(args.credential_file), 'w') as fh:
fh.write('descriptor:x25519:{}'.format(key_str(pub_key)))
else:
print('secret: {}'.format(key_str(priv_key)))
print('public: {}'.format(key_str(pub_key)))
if __name__ == '__main__':
exit(main())