sign_json 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. #!/usr/bin/env python
  2. #
  3. # -*- coding: utf-8 -*-
  4. # Copyright 2020 The Matrix.org Foundation C.I.C.
  5. #
  6. # Licensed under the Apache License, Version 2.0 (the "License");
  7. # you may not use this file except in compliance with the License.
  8. # You may obtain a copy of the License at
  9. #
  10. # http://www.apache.org/licenses/LICENSE-2.0
  11. #
  12. # Unless required by applicable law or agreed to in writing, software
  13. # distributed under the License is distributed on an "AS IS" BASIS,
  14. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. # See the License for the specific language governing permissions and
  16. # limitations under the License.
  17. import argparse
  18. import json
  19. import sys
  20. from json import JSONDecodeError
  21. import yaml
  22. from signedjson.key import read_signing_keys
  23. from signedjson.sign import sign_json
  24. from synapse.util import json_encoder
  25. def main():
  26. parser = argparse.ArgumentParser(
  27. description="""Adds a signature to a JSON object.
  28. Example usage:
  29. $ scripts-dev/sign_json.py -N test -k localhost.signing.key "{}"
  30. {"signatures":{"test":{"ed25519:a_ZnZh":"LmPnml6iM0iR..."}}}
  31. """,
  32. formatter_class=argparse.RawDescriptionHelpFormatter,
  33. )
  34. parser.add_argument(
  35. "-N",
  36. "--server-name",
  37. help="Name to give as the local homeserver. If unspecified, will be "
  38. "read from the config file.",
  39. )
  40. parser.add_argument(
  41. "-k",
  42. "--signing-key-path",
  43. help="Path to the file containing the private ed25519 key to sign the "
  44. "request with.",
  45. )
  46. parser.add_argument(
  47. "-c",
  48. "--config",
  49. default="homeserver.yaml",
  50. help=(
  51. "Path to synapse config file, from which the server name and/or signing "
  52. "key path will be read. Ignored if --server-name and --signing-key-path "
  53. "are both given."
  54. ),
  55. )
  56. input_args = parser.add_mutually_exclusive_group()
  57. input_args.add_argument("input_data", nargs="?", help="Raw JSON to be signed.")
  58. input_args.add_argument(
  59. "-i",
  60. "--input",
  61. type=argparse.FileType("r"),
  62. default=sys.stdin,
  63. help=(
  64. "A file from which to read the JSON to be signed. If neither --input nor "
  65. "input_data are given, JSON will be read from stdin."
  66. ),
  67. )
  68. parser.add_argument(
  69. "-o",
  70. "--output",
  71. type=argparse.FileType("w"),
  72. default=sys.stdout,
  73. help="Where to write the signed JSON. Defaults to stdout.",
  74. )
  75. args = parser.parse_args()
  76. if not args.server_name or not args.signing_key_path:
  77. read_args_from_config(args)
  78. with open(args.signing_key_path) as f:
  79. key = read_signing_keys(f)[0]
  80. json_to_sign = args.input_data
  81. if json_to_sign is None:
  82. json_to_sign = args.input.read()
  83. try:
  84. obj = json.loads(json_to_sign)
  85. except JSONDecodeError as e:
  86. print("Unable to parse input as JSON: %s" % e, file=sys.stderr)
  87. sys.exit(1)
  88. if not isinstance(obj, dict):
  89. print("Input json was not an object", file=sys.stderr)
  90. sys.exit(1)
  91. sign_json(obj, args.server_name, key)
  92. for c in json_encoder.iterencode(obj):
  93. args.output.write(c)
  94. args.output.write("\n")
  95. def read_args_from_config(args: argparse.Namespace) -> None:
  96. with open(args.config, "r") as fh:
  97. config = yaml.safe_load(fh)
  98. if not args.server_name:
  99. args.server_name = config["server_name"]
  100. if not args.signing_key_path:
  101. args.signing_key_path = config["signing_key_path"]
  102. if __name__ == "__main__":
  103. main()