| OLD | NEW |
| (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 from third_party import ed25519 |
| 24 |
| 25 HOSTNAME_REGEX = re.compile( |
| 26 r"""^ |
| 27 # Zero or more components, where each one is either a single alphanumeric |
| 28 (([a-z0-9]| |
| 29 # Or multiple alphanumerics with internal hyphens |
| 30 [a-z0-9][a-z0-9-]*[a-z0-9]) |
| 31 # Separated by periods |
| 32 \.)* |
| 33 # And followed by a final component. |
| 34 ([a-z0-9]|[a-z0-9][a-z0-9-]*[a-z0-9])$""", |
| 35 re.IGNORECASE | re.VERBOSE |
| 36 ) |
| 37 |
| 38 def OriginFromArgs(arg): |
| 39 """Constructs the origin for the token from the command line arguments. |
| 40 |
| 41 Returns None if this is not possible (neither a valid hostname nor a |
| 42 valid origin URL was provided.) |
| 43 """ |
| 44 # Does it look like a hostname? |
| 45 if HOSTNAME_REGEX.match(arg): |
| 46 return 'https://'+arg |
| 47 # Try to construct an origin URL from the argument |
| 48 origin = urlparse.urlparse(arg) |
| 49 if not origin or not origin.scheme or not origin.netloc: |
| 50 raise argparse.ArgumentTypeError("%s is not a hostname or a URL" % arg) |
| 51 # Strip any other components and return the URL: |
| 52 return urlparse.urlunparse(origin[:2] + ('', )*4) |
| 53 |
| 54 def ExpiryFromArgs(args): |
| 55 if args.expire_timestamp: |
| 56 return int(args.expire_timestamp) |
| 57 return ((int(time.time()) + (int(args.expire_days) * 86400)) * 1000) |
| 58 |
| 59 def GenerateTokenData(origin, apiName, expiry): |
| 60 return "{0}|{1}|{2}".format(origin, apiName, expiry) |
| 61 |
| 62 def Sign(private_key, data): |
| 63 return ed25519.signature(data, private_key[:32], private_key[32:]) |
| 64 |
| 65 def FormatToken(signature, data): |
| 66 return base64.b64encode(signature) + "|" + data |
| 67 |
| 68 def main(): |
| 69 parser = argparse.ArgumentParser( |
| 70 description="Generate tokens for enabling experimental APIs") |
| 71 parser.add_argument('origin', |
| 72 help="Origin for which to enable the API. This can be a " |
| 73 "bare hostname (in which case https will be " |
| 74 "assumed,) or a valid origin URL (hostname, " |
| 75 "protocol and port can all be included.)", |
| 76 type=OriginFromArgs) |
| 77 parser.add_argument('trial_name', |
| 78 help="Feature to enable. The current list of " |
| 79 "experimental feature trials can be found in " |
| 80 "RuntimeFeatures.in") |
| 81 parser.add_argument('--key-file', |
| 82 help='Ed25519 private key file to sign the token with', |
| 83 default="eftest.key") |
| 84 expiry_group = parser.add_mutually_exclusive_group() |
| 85 expiry_group.add_argument('--expire-days', |
| 86 help='Days from now when the token should exipire', |
| 87 type=int, |
| 88 default=42) |
| 89 expiry_group.add_argument('--expire-timestamp', |
| 90 help="Exact time (milliseconds since 1970-01-01 " |
| 91 "00:00:00 UTC) when the token should exipire", |
| 92 type=int) |
| 93 |
| 94 args = parser.parse_args() |
| 95 expiry = ExpiryFromArgs(args) |
| 96 |
| 97 key_file = open(os.path.expanduser(args.key_file)) |
| 98 private_key = key_file.read() |
| 99 |
| 100 # Validate that the key file read was a proper Ed25519 key -- running the |
| 101 # publickey method on the first half of the key should return the second |
| 102 # half. |
| 103 if (len(private_key) != 64 or |
| 104 ed25519.publickey(private_key[:32]) != private_key[32:]): |
| 105 print("Unable to use the specified private key file.") |
| 106 sys.exit(1) |
| 107 |
| 108 token_data = GenerateTokenData(args.hostname, args.api_name, expiry) |
| 109 signature = Sign(private_key, token_data) |
| 110 |
| 111 # Verify that that the signature is correct before printing it. |
| 112 try: |
| 113 ed25519.checkvalid(signature, token_data, private_key[32:]) |
| 114 except Exception: |
| 115 print "There was an error generating the signature." |
| 116 sys.exit(1) |
| 117 |
| 118 print FormatToken(signature, token_data) |
| 119 |
| 120 if __name__ == "__main__": |
| 121 main() |
| OLD | NEW |