Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(126)

Unified Diff: tools/origin_trials/generate_token.py

Issue 1578793002: Add experimental framework token generation tool (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Add a header to satisfy licencecheck.pl, which can't check the LICENCE file. Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « tools/origin_trials/eftest.key ('k') | tools/origin_trials/generate_token_unittest.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/origin_trials/generate_token.py
diff --git a/tools/origin_trials/generate_token.py b/tools/origin_trials/generate_token.py
new file mode 100755
index 0000000000000000000000000000000000000000..54e61c6448254458b3f3ce28824791bc6defac48
--- /dev/null
+++ b/tools/origin_trials/generate_token.py
@@ -0,0 +1,141 @@
+#!/usr/bin/env python
+# Copyright (c) 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Utility for generating experimental API tokens
+
+usage: generate_token.py [-h] [--key-file KEY_FILE]
+ [--expire-days EXPIRE_DAYS |
+ --expire-timestamp EXPIRE_TIMESTAMP]
+ origin trial_name
+
+Run "generate_token.py -h" for more help on usage.
+"""
+import argparse
+import base64
+import re
+import os
+import sys
+import time
+import urlparse
+
+script_dir = os.path.dirname(os.path.realpath(__file__))
+sys.path.insert(0, os.path.join(script_dir, 'third_party', 'ed25519'))
+import ed25519
+
+
+# Matches a valid DNS name label (alphanumeric plus hyphens, except at the ends,
+# no longer than 63 ASCII characters)
+DNS_LABEL_REGEX = re.compile(r"^(?!-)[a-z\d-]{1,63}(?<!-)$", re.IGNORECASE)
+
+def HostnameFromArg(arg):
+ """Determines whether a string represents a valid hostname.
+
+ Returns the canonical hostname if its argument is valid, or None otherwise.
+ """
+ if not arg or len(arg) > 255:
+ return None
+ if arg[-1] == ".":
+ arg = arg[:-1]
+ if all(DNS_LABEL_REGEX.match(label) for label in arg.split(".")):
+ return arg.lower()
+
+def OriginFromArg(arg):
+ """Constructs the origin for the token from a command line argument.
+
+ Returns None if this is not possible (neither a valid hostname nor a
+ valid origin URL was provided.)
+ """
+ # Does it look like a hostname?
+ hostname = HostnameFromArg(arg)
+ if hostname:
+ return "https://" + hostname + ":443"
+ # If not, try to construct an origin URL from the argument
+ origin = urlparse.urlparse(arg)
+ if not origin or not origin.scheme or not origin.netloc:
+ raise argparse.ArgumentTypeError("%s is not a hostname or a URL" % arg)
+ # HTTPS or HTTP only
+ if origin.scheme not in ('https','http'):
+ raise argparse.ArgumentTypeError("%s does not use a recognized URL scheme" %
+ arg)
+ # Add default port if it is not specified
+ try:
+ port = origin.port
+ except ValueError:
+ raise argparse.ArgumentTypeError("%s is not a hostname or a URL" % arg)
+ if not port:
+ port = {"https": 443, "http": 80}[origin.scheme]
+ # Strip any extra components and return the origin URL:
+ return "{0}://{1}:{2}".format(origin.scheme, origin.hostname, port)
+
+def ExpiryFromArgs(args):
+ if args.expire_timestamp:
+ return int(args.expire_timestamp)
+ return (int(time.time()) + (int(args.expire_days) * 86400))
+
+def GenerateTokenData(origin, api_name, expiry):
+ return "{0}|{1}|{2}".format(origin, api_name, expiry)
+
+def Sign(private_key, data):
+ return ed25519.signature(data, private_key[:32], private_key[32:])
+
+def FormatToken(version, signature, data):
+ return version + "|" + base64.b64encode(signature) + "|" + data
+
+def main():
+ parser = argparse.ArgumentParser(
+ description="Generate tokens for enabling experimental APIs")
+ parser.add_argument("origin",
+ help="Origin for which to enable the API. This can be "
+ "either a hostname (default scheme HTTPS, default "
+ "port 443) or a URL.",
+ type=OriginFromArg)
+ parser.add_argument("trial_name",
+ help="Feature to enable. The current list of "
+ "experimental feature trials can be found in "
+ "RuntimeFeatures.in")
+ parser.add_argument("--key-file",
+ help="Ed25519 private key file to sign the token with",
+ default="eftest.key")
+ expiry_group = parser.add_mutually_exclusive_group()
+ expiry_group.add_argument("--expire-days",
+ help="Days from now when the token should exipire",
+ type=int,
+ default=42)
+ expiry_group.add_argument("--expire-timestamp",
+ help="Exact time (seconds since 1970-01-01 "
+ "00:00:00 UTC) when the token should exipire",
+ type=int)
+
+ args = parser.parse_args()
+ expiry = ExpiryFromArgs(args)
+
+ key_file = open(os.path.expanduser(args.key_file), mode="rb")
+ private_key = key_file.read(64)
+
+ # Validate that the key file read was a proper Ed25519 key -- running the
+ # publickey method on the first half of the key should return the second
+ # half.
+ if (len(private_key) < 64 or
+ ed25519.publickey(private_key[:32]) != private_key[32:]):
+ print("Unable to use the specified private key file.")
+ sys.exit(1)
+
+ token_data = GenerateTokenData(args.origin, args.trial_name, expiry)
+ signature = Sign(private_key, token_data)
+
+ # Verify that that the signature is correct before printing it.
+ try:
+ ed25519.checkvalid(signature, token_data, private_key[32:])
+ except Exception, exc:
+ print "There was an error generating the signature."
+ print "(The original error was: %s)" % exc
+ sys.exit(1)
+
+ # Output a properly-formatted token. Version 1 is hard-coded, as it is
+ # the only defined token version.
+ print FormatToken("1", signature, token_data)
+
+if __name__ == "__main__":
+ main()
« no previous file with comments | « tools/origin_trials/eftest.key ('k') | tools/origin_trials/generate_token_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698