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

Side by Side 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, 9 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 unified diff | 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 »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 # Copyright (c) 2016 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 """Utility for generating experimental API tokens
7
8 usage: generate_token.py [-h] [--key-file KEY_FILE]
9 [--expire-days EXPIRE_DAYS |
10 --expire-timestamp EXPIRE_TIMESTAMP]
11 origin trial_name
12
13 Run "generate_token.py -h" for more help on usage.
14 """
15 import argparse
16 import base64
17 import re
18 import os
19 import sys
20 import time
21 import urlparse
22
23 script_dir = os.path.dirname(os.path.realpath(__file__))
24 sys.path.insert(0, os.path.join(script_dir, 'third_party', 'ed25519'))
25 import ed25519
26
27
28 # Matches a valid DNS name label (alphanumeric plus hyphens, except at the ends,
29 # no longer than 63 ASCII characters)
30 DNS_LABEL_REGEX = re.compile(r"^(?!-)[a-z\d-]{1,63}(?<!-)$", re.IGNORECASE)
31
32 def HostnameFromArg(arg):
33 """Determines whether a string represents a valid hostname.
34
35 Returns the canonical hostname if its argument is valid, or None otherwise.
36 """
37 if not arg or len(arg) > 255:
38 return None
39 if arg[-1] == ".":
40 arg = arg[:-1]
41 if all(DNS_LABEL_REGEX.match(label) for label in arg.split(".")):
42 return arg.lower()
43
44 def OriginFromArg(arg):
45 """Constructs the origin for the token from a command line argument.
46
47 Returns None if this is not possible (neither a valid hostname nor a
48 valid origin URL was provided.)
49 """
50 # Does it look like a hostname?
51 hostname = HostnameFromArg(arg)
52 if hostname:
53 return "https://" + hostname + ":443"
54 # If not, try to construct an origin URL from the argument
55 origin = urlparse.urlparse(arg)
56 if not origin or not origin.scheme or not origin.netloc:
57 raise argparse.ArgumentTypeError("%s is not a hostname or a URL" % arg)
58 # HTTPS or HTTP only
59 if origin.scheme not in ('https','http'):
60 raise argparse.ArgumentTypeError("%s does not use a recognized URL scheme" %
61 arg)
62 # Add default port if it is not specified
63 try:
64 port = origin.port
65 except ValueError:
66 raise argparse.ArgumentTypeError("%s is not a hostname or a URL" % arg)
67 if not port:
68 port = {"https": 443, "http": 80}[origin.scheme]
69 # Strip any extra components and return the origin URL:
70 return "{0}://{1}:{2}".format(origin.scheme, origin.hostname, port)
71
72 def ExpiryFromArgs(args):
73 if args.expire_timestamp:
74 return int(args.expire_timestamp)
75 return (int(time.time()) + (int(args.expire_days) * 86400))
76
77 def GenerateTokenData(origin, api_name, expiry):
78 return "{0}|{1}|{2}".format(origin, api_name, expiry)
79
80 def Sign(private_key, data):
81 return ed25519.signature(data, private_key[:32], private_key[32:])
82
83 def FormatToken(version, signature, data):
84 return version + "|" + base64.b64encode(signature) + "|" + data
85
86 def main():
87 parser = argparse.ArgumentParser(
88 description="Generate tokens for enabling experimental APIs")
89 parser.add_argument("origin",
90 help="Origin for which to enable the API. This can be "
91 "either a hostname (default scheme HTTPS, default "
92 "port 443) or a URL.",
93 type=OriginFromArg)
94 parser.add_argument("trial_name",
95 help="Feature to enable. The current list of "
96 "experimental feature trials can be found in "
97 "RuntimeFeatures.in")
98 parser.add_argument("--key-file",
99 help="Ed25519 private key file to sign the token with",
100 default="eftest.key")
101 expiry_group = parser.add_mutually_exclusive_group()
102 expiry_group.add_argument("--expire-days",
103 help="Days from now when the token should exipire",
104 type=int,
105 default=42)
106 expiry_group.add_argument("--expire-timestamp",
107 help="Exact time (seconds since 1970-01-01 "
108 "00:00:00 UTC) when the token should exipire",
109 type=int)
110
111 args = parser.parse_args()
112 expiry = ExpiryFromArgs(args)
113
114 key_file = open(os.path.expanduser(args.key_file), mode="rb")
115 private_key = key_file.read(64)
116
117 # Validate that the key file read was a proper Ed25519 key -- running the
118 # publickey method on the first half of the key should return the second
119 # half.
120 if (len(private_key) < 64 or
121 ed25519.publickey(private_key[:32]) != private_key[32:]):
122 print("Unable to use the specified private key file.")
123 sys.exit(1)
124
125 token_data = GenerateTokenData(args.origin, args.trial_name, expiry)
126 signature = Sign(private_key, token_data)
127
128 # Verify that that the signature is correct before printing it.
129 try:
130 ed25519.checkvalid(signature, token_data, private_key[32:])
131 except Exception, exc:
132 print "There was an error generating the signature."
133 print "(The original error was: %s)" % exc
134 sys.exit(1)
135
136 # Output a properly-formatted token. Version 1 is hard-coded, as it is
137 # the only defined token version.
138 print FormatToken("1", signature, token_data)
139
140 if __name__ == "__main__":
141 main()
OLDNEW
« 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