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

Side by Side Diff: tools/origin_trials/generate_token.py

Issue 2411803002: Support subdomain matching in trial tokens (Closed)
Patch Set: Add fuzzer data for subdomain tokens Created 4 years, 2 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
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright (c) 2016 The Chromium Authors. All rights reserved. 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 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """Utility for generating experimental API tokens 6 """Utility for generating experimental API tokens
7 7
8 usage: generate_token.py [-h] [--key-file KEY_FILE] 8 usage: generate_token.py [-h] [--key-file KEY_FILE]
9 [--expire-days EXPIRE_DAYS | 9 [--expire-days EXPIRE_DAYS |
10 --expire-timestamp EXPIRE_TIMESTAMP] 10 --expire-timestamp EXPIRE_TIMESTAMP]
11 [--is_subdomain | --no-subdomain]
11 origin trial_name 12 origin trial_name
12 13
13 Run "generate_token.py -h" for more help on usage. 14 Run "generate_token.py -h" for more help on usage.
14 """ 15 """
15 import argparse 16 import argparse
16 import base64 17 import base64
17 from datetime import datetime 18 from datetime import datetime
18 import json 19 import json
19 import re 20 import re
20 import os 21 import os
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
78 if not port: 79 if not port:
79 port = {"https": 443, "http": 80}[origin.scheme] 80 port = {"https": 443, "http": 80}[origin.scheme]
80 # Strip any extra components and return the origin URL: 81 # Strip any extra components and return the origin URL:
81 return "{0}://{1}:{2}".format(origin.scheme, origin.hostname, port) 82 return "{0}://{1}:{2}".format(origin.scheme, origin.hostname, port)
82 83
83 def ExpiryFromArgs(args): 84 def ExpiryFromArgs(args):
84 if args.expire_timestamp: 85 if args.expire_timestamp:
85 return int(args.expire_timestamp) 86 return int(args.expire_timestamp)
86 return (int(time.time()) + (int(args.expire_days) * 86400)) 87 return (int(time.time()) + (int(args.expire_days) * 86400))
87 88
88 def GenerateTokenData(origin, api_name, expiry): 89 def GenerateTokenData(origin, is_subdomain, feature_name, expiry):
89 return json.dumps({"origin": origin, 90 data = {"origin": origin,
90 "feature": api_name, 91 "feature": feature_name,
91 "expiry": expiry}).encode('utf-8') 92 "expiry": expiry}
93 if is_subdomain is not None:
iclelland 2016/10/13 15:51:23 Could this be just if is_subdomain: ? (Is ther
chasej 2016/10/13 16:05:42 There isn't an obvious need - I debated back and f
iclelland 2016/10/13 16:06:46 sgtm
94 data["isSubdomain"] = is_subdomain
95 return json.dumps(data).encode('utf-8')
92 96
93 def GenerateDataToSign(version, data): 97 def GenerateDataToSign(version, data):
94 return version + struct.pack(">I",len(data)) + data 98 return version + struct.pack(">I",len(data)) + data
95 99
96 def Sign(private_key, data): 100 def Sign(private_key, data):
97 return ed25519.signature(data, private_key[:32], private_key[32:]) 101 return ed25519.signature(data, private_key[:32], private_key[32:])
98 102
99 def FormatToken(version, signature, data): 103 def FormatToken(version, signature, data):
100 return base64.b64encode(version + signature + 104 return base64.b64encode(version + signature +
101 struct.pack(">I",len(data)) + data) 105 struct.pack(">I",len(data)) + data)
102 106
103 def main(): 107 def main():
104 default_key_file_absolute = os.path.join(script_dir, DEFAULT_KEY_FILE) 108 default_key_file_absolute = os.path.join(script_dir, DEFAULT_KEY_FILE)
105 109
106 parser = argparse.ArgumentParser( 110 parser = argparse.ArgumentParser(
107 description="Generate tokens for enabling experimental APIs") 111 description="Generate tokens for enabling experimental features")
108 parser.add_argument("origin", 112 parser.add_argument("origin",
109 help="Origin for which to enable the API. This can be " 113 help="Origin for which to enable the feature. This can "
110 "either a hostname (default scheme HTTPS, default " 114 "be either a hostname (default scheme HTTPS, "
111 "port 443) or a URL.", 115 "default port 443) or a URL.",
112 type=OriginFromArg) 116 type=OriginFromArg)
113 parser.add_argument("trial_name", 117 parser.add_argument("trial_name",
114 help="Feature to enable. The current list of " 118 help="Feature to enable. The current list of "
115 "experimental feature trials can be found in " 119 "experimental feature trials can be found in "
116 "RuntimeFeatures.in") 120 "RuntimeFeatures.in")
117 parser.add_argument("--key-file", 121 parser.add_argument("--key-file",
118 help="Ed25519 private key file to sign the token with", 122 help="Ed25519 private key file to sign the token with",
119 default=default_key_file_absolute) 123 default=default_key_file_absolute)
124
125 subdomain_group = parser.add_mutually_exclusive_group()
126 subdomain_group.add_argument("--is-subdomain",
127 help="Token will enable the feature for all "
128 "subdomains that match the origin",
129 dest="is_subdomain",
130 action="store_true")
131 subdomain_group.add_argument("--no-subdomain",
132 help="Token will only match the specified "
133 "origin (default behavior)",
134 dest="is_subdomain",
135 action="store_false")
136 parser.set_defaults(is_subdomain=None)
137
120 expiry_group = parser.add_mutually_exclusive_group() 138 expiry_group = parser.add_mutually_exclusive_group()
121 expiry_group.add_argument("--expire-days", 139 expiry_group.add_argument("--expire-days",
122 help="Days from now when the token should exipire", 140 help="Days from now when the token should expire",
123 type=int, 141 type=int,
124 default=42) 142 default=42)
125 expiry_group.add_argument("--expire-timestamp", 143 expiry_group.add_argument("--expire-timestamp",
126 help="Exact time (seconds since 1970-01-01 " 144 help="Exact time (seconds since 1970-01-01 "
127 "00:00:00 UTC) when the token should exipire", 145 "00:00:00 UTC) when the token should expire",
128 type=int) 146 type=int)
129 147
130 args = parser.parse_args() 148 args = parser.parse_args()
131 expiry = ExpiryFromArgs(args) 149 expiry = ExpiryFromArgs(args)
132 150
133 key_file = open(os.path.expanduser(args.key_file), mode="rb") 151 key_file = open(os.path.expanduser(args.key_file), mode="rb")
134 private_key = key_file.read(64) 152 private_key = key_file.read(64)
135 153
136 # Validate that the key file read was a proper Ed25519 key -- running the 154 # Validate that the key file read was a proper Ed25519 key -- running the
137 # publickey method on the first half of the key should return the second 155 # publickey method on the first half of the key should return the second
138 # half. 156 # half.
139 if (len(private_key) < 64 or 157 if (len(private_key) < 64 or
140 ed25519.publickey(private_key[:32]) != private_key[32:]): 158 ed25519.publickey(private_key[:32]) != private_key[32:]):
141 print("Unable to use the specified private key file.") 159 print("Unable to use the specified private key file.")
142 sys.exit(1) 160 sys.exit(1)
143 161
144 token_data = GenerateTokenData(args.origin, args.trial_name, expiry) 162 token_data = GenerateTokenData(args.origin, args.is_subdomain,
163 args.trial_name, expiry)
145 data_to_sign = GenerateDataToSign(VERSION, token_data) 164 data_to_sign = GenerateDataToSign(VERSION, token_data)
146 signature = Sign(private_key, data_to_sign) 165 signature = Sign(private_key, data_to_sign)
147 166
148 # Verify that that the signature is correct before printing it. 167 # Verify that that the signature is correct before printing it.
149 try: 168 try:
150 ed25519.checkvalid(signature, data_to_sign, private_key[32:]) 169 ed25519.checkvalid(signature, data_to_sign, private_key[32:])
151 except Exception, exc: 170 except Exception, exc:
152 print "There was an error generating the signature." 171 print "There was an error generating the signature."
153 print "(The original error was: %s)" % exc 172 print "(The original error was: %s)" % exc
154 sys.exit(1) 173 sys.exit(1)
155 174
156 175
157 # Output the token details 176 # Output the token details
158 print "Token details:" 177 print "Token details:"
159 print " Origin: %s" % args.origin 178 print " Origin: %s" % args.origin
179 print " Is Subdomain: %s" % args.is_subdomain
160 print " Feature: %s" % args.trial_name 180 print " Feature: %s" % args.trial_name
161 print " Expiry: %d (%s UTC)" % (expiry, datetime.utcfromtimestamp(expiry)) 181 print " Expiry: %d (%s UTC)" % (expiry, datetime.utcfromtimestamp(expiry))
162 print 182 print
163 183
164 # Output the properly-formatted token. 184 # Output the properly-formatted token.
165 print FormatToken(VERSION, signature, token_data) 185 print FormatToken(VERSION, signature, token_data)
166 186
167 if __name__ == "__main__": 187 if __name__ == "__main__":
168 main() 188 main()
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698