This repository has been archived by the owner on Feb 2, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhv.py
executable file
·149 lines (122 loc) · 4.54 KB
/
hv.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
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
#!/usr/bin/env python3
import os
import json
import subprocess
import click
import requests
def is_valid_fingerprint(fingerprint):
"""
Verifies that the fingerprint is a valid, 40 digit hex number
"""
if len(fingerprint) != 40:
return False
allowed = "0123456789ABCDEF"
for c in fingerprint.upper():
if c not in allowed:
return False
return True
def get_pubkey(fingerprint):
"""
Query the gpg keyring and returns an ASCII armored public key
"""
out = subprocess.check_output(["gpg2", "--armor", "--export", fingerprint])
# If the key does not exist in the local keyring, try fetching from keyserver.ubuntu.com next
if len(out) == 0:
url = f"https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x{fingerprint}"
click.echo(f"Loading {url}")
r = requests.get(url)
if r.status_code != 200:
return ""
else:
# Import the key locally
p = subprocess.Popen(["gpg2", "--import"], stdin=subprocess.PIPE)
p.communicate(r.content)
return r.content.decode()
return out.decode()
@click.command()
@click.argument("keylist_filename")
def main(keylist_filename):
api_endpoint = "https://keys.openpgp.org/vks/v1"
# Verify that keylist_filename is a valid keylist
if not os.path.exists(keylist_filename):
click.echo("Invalid keylist_filename")
return
try:
with open(keylist_filename) as f:
keylist = json.load(f)
except:
click.echo("Not a valid JSON file")
return
invalid = False
if "keys" not in keylist:
invalid = True
else:
if type(keylist["keys"]) != list:
invalid = True
if invalid:
click.echo("Not a valid keylist")
return
# Make a dictionary mapping fingerprints to emails and ASCII-armored pubkeys, by querying your gpg keyring
keys = {}
for key in keylist["keys"]:
fingerprint = key["fingerprint"]
if not is_valid_fingerprint(fingerprint):
click.echo(f"Skipping invalid fingerprint: {fingerprint}")
else:
keys[fingerprint] = {"pubkey": get_pubkey(fingerprint)}
# Upload each key to the keyserver
for fingerprint in keys:
if keys[fingerprint]["pubkey"] != "":
click.echo(f"uploading {fingerprint}")
# Upload the pubkey
r = requests.post(
f"{api_endpoint}/upload", json={"keytext": keys[fingerprint]["pubkey"]},
)
response = r.json()
# Add the token and status to keys dict
try:
keys[fingerprint]["token"] = response["token"]
keys[fingerprint]["status"] = response["status"]
except KeyError:
print(f"KeyError ({fingerprint}): {response}")
click.echo()
# Loop through each key, displaying the verification status
needs_verification_statuses = ["unpublished", "pending"]
for fingerprint in keys:
if keys[fingerprint]["pubkey"] == "":
click.echo(
f"{fingerprint} not found in local keyring or SKS keyserver, skipping"
)
addresses = []
if "status" in keys[fingerprint]:
for address in keys[fingerprint]["status"]:
if keys[fingerprint]["status"][address] in needs_verification_statuses:
addresses.append(address)
keys[fingerprint]["addresses"] = addresses
if len(addresses) > 0:
click.echo(f"{fingerprint} needs verification: {addresses}")
click.echo()
if click.confirm(
"Do you want to request verification emails for all of these keys?"
):
for fingerprint in keys:
if len(keys[fingerprint]["addresses"]) > 0:
click.echo(
f"requesting verification for {keys[fingerprint]['addresses']}"
)
# Request verification
r = requests.post(
f"{api_endpoint}/request-verify",
json={
"token": keys[fingerprint]["token"],
"addresses": keys[fingerprint]["addresses"],
},
)
# Gracefully handle errors
if r.status_code != 200:
click.echo(f"status_code: {r.status_code}")
response = r.json()
if "error" in response:
click.echo(f"Error: {response['error']}")
if __name__ == "__main__":
main()