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

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

Issue 2605563003: Revert of Validate origins when generating subdomain tokens (Closed)
Patch Set: Created 3 years, 12 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 [--is_subdomain | --no-subdomain]
12 origin trial_name 12 origin trial_name
13 13
14 Run "generate_token.py -h" for more help on usage. 14 Run "generate_token.py -h" for more help on usage.
15 """ 15 """
16 import argparse 16 import argparse
17 import base64 17 import base64
18 from datetime import datetime 18 from datetime import datetime
19 import json 19 import json
20 import re 20 import re
21 import os 21 import os
22 import struct 22 import struct
23 import subprocess
24 import sys 23 import sys
25 import time 24 import time
26 import urlparse 25 import urlparse
27 26
28 script_dir = os.path.dirname(os.path.realpath(__file__)) 27 script_dir = os.path.dirname(os.path.realpath(__file__))
29 sys.path.insert(0, os.path.join(script_dir, 'third_party', 'ed25519')) 28 sys.path.insert(0, os.path.join(script_dir, 'third_party', 'ed25519'))
30 import ed25519 29 import ed25519
31 30
32 31
33 # Matches a valid DNS name label (alphanumeric plus hyphens, except at the ends, 32 # Matches a valid DNS name label (alphanumeric plus hyphens, except at the ends,
34 # no longer than 63 ASCII characters) 33 # no longer than 63 ASCII characters)
35 DNS_LABEL_REGEX = re.compile(r"^(?!-)[a-z\d-]{1,63}(?<!-)$", re.IGNORECASE) 34 DNS_LABEL_REGEX = re.compile(r"^(?!-)[a-z\d-]{1,63}(?<!-)$", re.IGNORECASE)
36 35
37 # This script generates Version 2 tokens. 36 # This script generates Version 2 tokens.
38 VERSION = "\x02" 37 VERSION = "\x02"
39 38
40 # Default key file, relative to script_dir. 39 # Default key file, relative to script_dir.
41 DEFAULT_KEY_FILE = 'eftest.key' 40 DEFAULT_KEY_FILE = 'eftest.key'
42 41
43 # Default location of validate subdomain utility, relative to script_dir.
44 DEFAULT_TARGET_PATH = '../../out/Default/'
45
46 def HostnameFromArg(arg): 42 def HostnameFromArg(arg):
47 """Determines whether a string represents a valid hostname. 43 """Determines whether a string represents a valid hostname.
48 44
49 Returns the canonical hostname if its argument is valid, or None otherwise. 45 Returns the canonical hostname if its argument is valid, or None otherwise.
50 """ 46 """
51 if not arg or len(arg) > 255: 47 if not arg or len(arg) > 255:
52 return None 48 return None
53 if arg[-1] == ".": 49 if arg[-1] == ".":
54 arg = arg[:-1] 50 arg = arg[:-1]
55 if "." not in arg and arg != "localhost": 51 if "." not in arg and arg != "localhost":
(...skipping 27 matching lines...) Expand all
83 if not port: 79 if not port:
84 port = {"https": 443, "http": 80}[origin.scheme] 80 port = {"https": 443, "http": 80}[origin.scheme]
85 # Strip any extra components and return the origin URL: 81 # Strip any extra components and return the origin URL:
86 return "{0}://{1}:{2}".format(origin.scheme, origin.hostname, port) 82 return "{0}://{1}:{2}".format(origin.scheme, origin.hostname, port)
87 83
88 def ExpiryFromArgs(args): 84 def ExpiryFromArgs(args):
89 if args.expire_timestamp: 85 if args.expire_timestamp:
90 return int(args.expire_timestamp) 86 return int(args.expire_timestamp)
91 return (int(time.time()) + (int(args.expire_days) * 86400)) 87 return (int(time.time()) + (int(args.expire_days) * 86400))
92 88
93 def ValidateSubdomainTokenOrigin(origin, target_path):
94 """ Calls validate_subdomain_origin utility to check the origin
95
96 If the utility is not found, prints a warning for manual validation, and
97 returns True
98 """
99 utility_path = "%s/validate_subdomain_origin" % target_path
100 if not os.path.exists(utility_path):
101 print "WARNING!"
102 print "Origin not validated for use in subdomain token"
103 print " (missing '%s' utility)" % utility_path
104 print "Must manually check origin against the Public Suffix List"
105 print
106 return True
107
108 rc = subprocess.call([utility_path, "--quiet", origin])
109 if (rc < 0 or rc > 4):
110 print("Unexpected return code from %s: %d" % (utility_path, rc))
111 sys.exit(1)
112
113 return rc == 0
114
115 def GenerateTokenData(origin, is_subdomain, feature_name, expiry): 89 def GenerateTokenData(origin, is_subdomain, feature_name, expiry):
116 data = {"origin": origin, 90 data = {"origin": origin,
117 "feature": feature_name, 91 "feature": feature_name,
118 "expiry": expiry} 92 "expiry": expiry}
119 if is_subdomain is not None: 93 if is_subdomain is not None:
120 data["isSubdomain"] = is_subdomain 94 data["isSubdomain"] = is_subdomain
121 return json.dumps(data).encode('utf-8') 95 return json.dumps(data).encode('utf-8')
122 96
123 def GenerateDataToSign(version, data): 97 def GenerateDataToSign(version, data):
124 return version + struct.pack(">I",len(data)) + data 98 return version + struct.pack(">I",len(data)) + data
125 99
126 def Sign(private_key, data): 100 def Sign(private_key, data):
127 return ed25519.signature(data, private_key[:32], private_key[32:]) 101 return ed25519.signature(data, private_key[:32], private_key[32:])
128 102
129 def FormatToken(version, signature, data): 103 def FormatToken(version, signature, data):
130 return base64.b64encode(version + signature + 104 return base64.b64encode(version + signature +
131 struct.pack(">I",len(data)) + data) 105 struct.pack(">I",len(data)) + data)
132 106
133 def main(): 107 def main():
134 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)
135 default_target_path_absolute = os.path.join(script_dir, DEFAULT_TARGET_PATH)
136 109
137 parser = argparse.ArgumentParser( 110 parser = argparse.ArgumentParser(
138 description="Generate tokens for enabling experimental features") 111 description="Generate tokens for enabling experimental features")
139 parser.add_argument("origin", 112 parser.add_argument("origin",
140 help="Origin for which to enable the feature. This can " 113 help="Origin for which to enable the feature. This can "
141 "be either a hostname (default scheme HTTPS, " 114 "be either a hostname (default scheme HTTPS, "
142 "default port 443) or a URL.", 115 "default port 443) or a URL.",
143 type=OriginFromArg) 116 type=OriginFromArg)
144 parser.add_argument("trial_name", 117 parser.add_argument("trial_name",
145 help="Feature to enable. The current list of " 118 help="Feature to enable. The current list of "
(...skipping 19 matching lines...) Expand all
165 expiry_group = parser.add_mutually_exclusive_group() 138 expiry_group = parser.add_mutually_exclusive_group()
166 expiry_group.add_argument("--expire-days", 139 expiry_group.add_argument("--expire-days",
167 help="Days from now when the token should expire", 140 help="Days from now when the token should expire",
168 type=int, 141 type=int,
169 default=42) 142 default=42)
170 expiry_group.add_argument("--expire-timestamp", 143 expiry_group.add_argument("--expire-timestamp",
171 help="Exact time (seconds since 1970-01-01 " 144 help="Exact time (seconds since 1970-01-01 "
172 "00:00:00 UTC) when the token should expire", 145 "00:00:00 UTC) when the token should expire",
173 type=int) 146 type=int)
174 147
175 parser.add_argument("--target",
176 help="Path to the output directory for compiled "
177 "resources",
178 default=default_target_path_absolute)
179
180 args = parser.parse_args() 148 args = parser.parse_args()
181 expiry = ExpiryFromArgs(args) 149 expiry = ExpiryFromArgs(args)
182 150
183 key_file = open(os.path.expanduser(args.key_file), mode="rb") 151 key_file = open(os.path.expanduser(args.key_file), mode="rb")
184 private_key = key_file.read(64) 152 private_key = key_file.read(64)
185 153
186 # 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
187 # 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
188 # half. 156 # half.
189 if (len(private_key) < 64 or 157 if (len(private_key) < 64 or
190 ed25519.publickey(private_key[:32]) != private_key[32:]): 158 ed25519.publickey(private_key[:32]) != private_key[32:]):
191 print("Unable to use the specified private key file.") 159 print("Unable to use the specified private key file.")
192 sys.exit(1) 160 sys.exit(1)
193 161
194 # For subdomain tokens, validate that the origin is allowed
195 if args.is_subdomain:
196 target_path = os.path.expanduser(args.target)
197 if not ValidateSubdomainTokenOrigin(args.origin, target_path):
198 print "The specified origin is not valid for use in a subdomain token."
199 sys.exit(1)
200
201 token_data = GenerateTokenData(args.origin, args.is_subdomain, 162 token_data = GenerateTokenData(args.origin, args.is_subdomain,
202 args.trial_name, expiry) 163 args.trial_name, expiry)
203 data_to_sign = GenerateDataToSign(VERSION, token_data) 164 data_to_sign = GenerateDataToSign(VERSION, token_data)
204 signature = Sign(private_key, data_to_sign) 165 signature = Sign(private_key, data_to_sign)
205 166
206 # Verify that that the signature is correct before printing it. 167 # Verify that that the signature is correct before printing it.
207 try: 168 try:
208 ed25519.checkvalid(signature, data_to_sign, private_key[32:]) 169 ed25519.checkvalid(signature, data_to_sign, private_key[32:])
209 except Exception, exc: 170 except Exception, exc:
210 print "There was an error generating the signature." 171 print "There was an error generating the signature."
211 print "(The original error was: %s)" % exc 172 print "(The original error was: %s)" % exc
212 sys.exit(1) 173 sys.exit(1)
213 174
214 175
215 # Output the token details 176 # Output the token details
216 print "Token details:" 177 print "Token details:"
217 print " Origin: %s" % args.origin 178 print " Origin: %s" % args.origin
218 print " Is Subdomain: %s" % args.is_subdomain 179 print " Is Subdomain: %s" % args.is_subdomain
219 print " Feature: %s" % args.trial_name 180 print " Feature: %s" % args.trial_name
220 print " Expiry: %d (%s UTC)" % (expiry, datetime.utcfromtimestamp(expiry)) 181 print " Expiry: %d (%s UTC)" % (expiry, datetime.utcfromtimestamp(expiry))
221 print 182 print
222 183
223 # Output the properly-formatted token. 184 # Output the properly-formatted token.
224 print FormatToken(VERSION, signature, token_data) 185 print FormatToken(VERSION, signature, token_data)
225 186
226 if __name__ == "__main__": 187 if __name__ == "__main__":
227 main() 188 main()
OLDNEW
« no previous file with comments | « build/config/linux/gconf/BUILD.gn ('k') | tools/origin_trials/validate_subdomain_origin/BUILD.gn » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698