2020-11-24 15:53:00 +03:00
|
|
|
#!/usr/bin/env python
|
|
|
|
#
|
2023-11-21 23:29:58 +03:00
|
|
|
# This file is licensed under the Affero General Public License (AGPL) version 3.
|
2020-11-24 15:53:00 +03:00
|
|
|
#
|
2024-01-23 14:26:48 +03:00
|
|
|
# Copyright 2020 The Matrix.org Foundation C.I.C.
|
2023-11-21 23:29:58 +03:00
|
|
|
# Copyright (C) 2023 New Vector, Ltd
|
|
|
|
#
|
|
|
|
# This program is free software: you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU Affero General Public License as
|
|
|
|
# published by the Free Software Foundation, either version 3 of the
|
|
|
|
# License, or (at your option) any later version.
|
|
|
|
#
|
|
|
|
# See the GNU Affero General Public License for more details:
|
|
|
|
# <https://www.gnu.org/licenses/agpl-3.0.html>.
|
|
|
|
#
|
|
|
|
# Originally licensed under the Apache License, Version 2.0:
|
|
|
|
# <http://www.apache.org/licenses/LICENSE-2.0>.
|
|
|
|
#
|
|
|
|
# [This file includes modifications made by New Vector Limited]
|
2020-11-24 15:53:00 +03:00
|
|
|
#
|
|
|
|
#
|
|
|
|
import argparse
|
|
|
|
import json
|
|
|
|
import sys
|
|
|
|
from json import JSONDecodeError
|
|
|
|
|
|
|
|
import yaml
|
|
|
|
from signedjson.key import read_signing_keys
|
|
|
|
from signedjson.sign import sign_json
|
|
|
|
|
2021-12-02 18:18:40 +03:00
|
|
|
from synapse.api.room_versions import KNOWN_ROOM_VERSIONS
|
|
|
|
from synapse.crypto.event_signing import add_hashes_and_signatures
|
2020-11-24 15:53:00 +03:00
|
|
|
from synapse.util import json_encoder
|
|
|
|
|
|
|
|
|
2022-04-27 16:10:31 +03:00
|
|
|
def main() -> None:
|
2020-11-24 15:53:00 +03:00
|
|
|
parser = argparse.ArgumentParser(
|
|
|
|
description="""Adds a signature to a JSON object.
|
|
|
|
|
|
|
|
Example usage:
|
|
|
|
|
|
|
|
$ scripts-dev/sign_json.py -N test -k localhost.signing.key "{}"
|
|
|
|
{"signatures":{"test":{"ed25519:a_ZnZh":"LmPnml6iM0iR..."}}}
|
|
|
|
""",
|
|
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
|
|
)
|
|
|
|
|
|
|
|
parser.add_argument(
|
|
|
|
"-N",
|
|
|
|
"--server-name",
|
|
|
|
help="Name to give as the local homeserver. If unspecified, will be "
|
|
|
|
"read from the config file.",
|
|
|
|
)
|
|
|
|
|
|
|
|
parser.add_argument(
|
|
|
|
"-k",
|
|
|
|
"--signing-key-path",
|
|
|
|
help="Path to the file containing the private ed25519 key to sign the "
|
|
|
|
"request with.",
|
|
|
|
)
|
|
|
|
|
2021-10-20 20:49:20 +03:00
|
|
|
parser.add_argument(
|
|
|
|
"-K",
|
|
|
|
"--signing-key",
|
|
|
|
help="The private ed25519 key to sign the request with.",
|
|
|
|
)
|
|
|
|
|
2020-11-24 15:53:00 +03:00
|
|
|
parser.add_argument(
|
|
|
|
"-c",
|
|
|
|
"--config",
|
|
|
|
default="homeserver.yaml",
|
|
|
|
help=(
|
|
|
|
"Path to synapse config file, from which the server name and/or signing "
|
2021-10-20 20:49:20 +03:00
|
|
|
"key path will be read. Ignored if --server-name and --signing-key(-path) "
|
2020-11-24 15:53:00 +03:00
|
|
|
"are both given."
|
|
|
|
),
|
|
|
|
)
|
|
|
|
|
2021-12-02 18:18:40 +03:00
|
|
|
parser.add_argument(
|
|
|
|
"--sign-event-room-version",
|
|
|
|
type=str,
|
|
|
|
help=(
|
|
|
|
"Sign the JSON as an event for the given room version, rather than raw JSON. "
|
|
|
|
"This means that we will add a 'hashes' object, and redact the event before "
|
|
|
|
"signing."
|
|
|
|
),
|
|
|
|
)
|
|
|
|
|
2020-11-24 15:53:00 +03:00
|
|
|
input_args = parser.add_mutually_exclusive_group()
|
|
|
|
|
|
|
|
input_args.add_argument("input_data", nargs="?", help="Raw JSON to be signed.")
|
|
|
|
|
|
|
|
input_args.add_argument(
|
|
|
|
"-i",
|
|
|
|
"--input",
|
|
|
|
type=argparse.FileType("r"),
|
|
|
|
default=sys.stdin,
|
|
|
|
help=(
|
|
|
|
"A file from which to read the JSON to be signed. If neither --input nor "
|
|
|
|
"input_data are given, JSON will be read from stdin."
|
|
|
|
),
|
|
|
|
)
|
|
|
|
|
|
|
|
parser.add_argument(
|
|
|
|
"-o",
|
|
|
|
"--output",
|
|
|
|
type=argparse.FileType("w"),
|
|
|
|
default=sys.stdout,
|
|
|
|
help="Where to write the signed JSON. Defaults to stdout.",
|
|
|
|
)
|
|
|
|
|
|
|
|
args = parser.parse_args()
|
|
|
|
|
2021-10-20 20:49:20 +03:00
|
|
|
if not args.server_name or not (args.signing_key_path or args.signing_key):
|
2020-11-24 15:53:00 +03:00
|
|
|
read_args_from_config(args)
|
|
|
|
|
2021-10-20 20:49:20 +03:00
|
|
|
if args.signing_key:
|
|
|
|
keys = read_signing_keys([args.signing_key])
|
|
|
|
else:
|
|
|
|
with open(args.signing_key_path) as f:
|
|
|
|
keys = read_signing_keys(f)
|
2020-11-24 15:53:00 +03:00
|
|
|
|
|
|
|
json_to_sign = args.input_data
|
|
|
|
if json_to_sign is None:
|
|
|
|
json_to_sign = args.input.read()
|
|
|
|
|
|
|
|
try:
|
|
|
|
obj = json.loads(json_to_sign)
|
|
|
|
except JSONDecodeError as e:
|
|
|
|
print("Unable to parse input as JSON: %s" % e, file=sys.stderr)
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
if not isinstance(obj, dict):
|
|
|
|
print("Input json was not an object", file=sys.stderr)
|
|
|
|
sys.exit(1)
|
|
|
|
|
2021-12-02 18:18:40 +03:00
|
|
|
if args.sign_event_room_version:
|
|
|
|
room_version = KNOWN_ROOM_VERSIONS.get(args.sign_event_room_version)
|
|
|
|
if not room_version:
|
|
|
|
print(
|
|
|
|
f"Unknown room version {args.sign_event_room_version}", file=sys.stderr
|
|
|
|
)
|
|
|
|
sys.exit(1)
|
|
|
|
add_hashes_and_signatures(room_version, obj, args.server_name, keys[0])
|
|
|
|
else:
|
|
|
|
sign_json(obj, args.server_name, keys[0])
|
|
|
|
|
2020-11-24 15:53:00 +03:00
|
|
|
for c in json_encoder.iterencode(obj):
|
|
|
|
args.output.write(c)
|
|
|
|
args.output.write("\n")
|
|
|
|
|
|
|
|
|
|
|
|
def read_args_from_config(args: argparse.Namespace) -> None:
|
2023-08-15 15:11:20 +03:00
|
|
|
with open(args.config) as fh:
|
2020-11-24 15:53:00 +03:00
|
|
|
config = yaml.safe_load(fh)
|
|
|
|
if not args.server_name:
|
|
|
|
args.server_name = config["server_name"]
|
2021-10-20 20:49:20 +03:00
|
|
|
if not args.signing_key_path and not args.signing_key:
|
|
|
|
if "signing_key" in config:
|
|
|
|
args.signing_key = config["signing_key"]
|
|
|
|
elif "signing_key_path" in config:
|
|
|
|
args.signing_key_path = config["signing_key_path"]
|
|
|
|
else:
|
|
|
|
print(
|
|
|
|
"A signing key must be given on the commandline or in the config file.",
|
|
|
|
file=sys.stderr,
|
|
|
|
)
|
|
|
|
sys.exit(1)
|
2020-11-24 15:53:00 +03:00
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|