|
| 1 | +import psycopg2 |
| 2 | +import yaml |
| 3 | +import sys |
| 4 | +import json |
| 5 | +import time |
| 6 | +import hashlib |
| 7 | +from syutil.base64util import encode_base64 |
| 8 | +from syutil.crypto.signing_key import read_signing_keys |
| 9 | +from syutil.crypto.jsonsign import sign_json |
| 10 | +from syutil.jsonutil import encode_canonical_json |
| 11 | + |
| 12 | + |
| 13 | +def select_v1_keys(connection): |
| 14 | + cursor = connection.cursor() |
| 15 | + cursor.execute("SELECT server_name, key_id, verify_key FROM server_signature_keys") |
| 16 | + rows = cursor.fetchall() |
| 17 | + cursor.close() |
| 18 | + results = {} |
| 19 | + for server_name, key_id, verify_key in rows: |
| 20 | + results.setdefault(server_name, {})[key_id] = encode_base64(verify_key) |
| 21 | + return results |
| 22 | + |
| 23 | + |
| 24 | +def select_v1_certs(connection): |
| 25 | + cursor = connection.cursor() |
| 26 | + cursor.execute("SELECT server_name, tls_certificate FROM server_tls_certificates") |
| 27 | + rows = cursor.fetchall() |
| 28 | + cursor.close() |
| 29 | + results = {} |
| 30 | + for server_name, tls_certificate in rows: |
| 31 | + results[server_name] = tls_certificate |
| 32 | + return results |
| 33 | + |
| 34 | + |
| 35 | +def select_v2_json(connection): |
| 36 | + cursor = connection.cursor() |
| 37 | + cursor.execute("SELECT server_name, key_id, key_json FROM server_keys_json") |
| 38 | + rows = cursor.fetchall() |
| 39 | + cursor.close() |
| 40 | + results = {} |
| 41 | + for server_name, key_id, key_json in rows: |
| 42 | + results.setdefault(server_name, {})[key_id] = json.loads(str(key_json).decode("utf-8")) |
| 43 | + return results |
| 44 | + |
| 45 | + |
| 46 | +def convert_v1_to_v2(server_name, valid_until, keys, certificate): |
| 47 | + return { |
| 48 | + "old_verify_keys": {}, |
| 49 | + "server_name": server_name, |
| 50 | + "verify_keys": { |
| 51 | + key_id: {"key": key} |
| 52 | + for key_id, key in keys.items() |
| 53 | + }, |
| 54 | + "valid_until_ts": valid_until, |
| 55 | + "tls_fingerprints": [fingerprint(certificate)], |
| 56 | + } |
| 57 | + |
| 58 | + |
| 59 | +def fingerprint(certificate): |
| 60 | + finger = hashlib.sha256(certificate) |
| 61 | + return {"sha256": encode_base64(finger.digest())} |
| 62 | + |
| 63 | + |
| 64 | +def rows_v2(server, json): |
| 65 | + valid_until = json["valid_until_ts"] |
| 66 | + key_json = encode_canonical_json(json) |
| 67 | + for key_id in json["verify_keys"]: |
| 68 | + yield (server, key_id, "-", valid_until, valid_until, buffer(key_json)) |
| 69 | + |
| 70 | + |
| 71 | +def main(): |
| 72 | + config = yaml.load(open(sys.argv[1])) |
| 73 | + valid_until = int(time.time() / (3600 * 24)) * 1000 * 3600 * 24 |
| 74 | + |
| 75 | + server_name = config["server_name"] |
| 76 | + signing_key = read_signing_keys(open(config["signing_key_path"]))[0] |
| 77 | + |
| 78 | + database = config["database"] |
| 79 | + assert database["name"] == "psycopg2", "Can only convert for postgresql" |
| 80 | + args = database["args"] |
| 81 | + args.pop("cp_max") |
| 82 | + args.pop("cp_min") |
| 83 | + connection = psycopg2.connect(**args) |
| 84 | + keys = select_v1_keys(connection) |
| 85 | + certificates = select_v1_certs(connection) |
| 86 | + json = select_v2_json(connection) |
| 87 | + |
| 88 | + result = {} |
| 89 | + for server in keys: |
| 90 | + if not server in json: |
| 91 | + v2_json = convert_v1_to_v2( |
| 92 | + server, valid_until, keys[server], certificates[server] |
| 93 | + ) |
| 94 | + v2_json = sign_json(v2_json, server_name, signing_key) |
| 95 | + result[server] = v2_json |
| 96 | + |
| 97 | + yaml.safe_dump(result, sys.stdout, default_flow_style=False) |
| 98 | + |
| 99 | + rows = list( |
| 100 | + row for server, json in result.items() |
| 101 | + for row in rows_v2(server, json) |
| 102 | + ) |
| 103 | + |
| 104 | + cursor = connection.cursor() |
| 105 | + cursor.executemany( |
| 106 | + "INSERT INTO server_keys_json (" |
| 107 | + " server_name, key_id, from_server," |
| 108 | + " ts_added_ms, ts_valid_until_ms, key_json" |
| 109 | + ") VALUES (%s, %s, %s, %s, %s, %s)", |
| 110 | + rows |
| 111 | + ) |
| 112 | + connection.commit() |
| 113 | + |
| 114 | + |
| 115 | +if __name__ == '__main__': |
| 116 | + main() |
0 commit comments