diff options
author | mnissler@chromium.org <mnissler@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-11 12:13:05 +0000 |
---|---|---|
committer | mnissler@chromium.org <mnissler@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-11 12:13:05 +0000 |
commit | cff5dadeb1be4c526a1b3875c9acf422cbaf834d (patch) | |
tree | da0c0f9f6ff4fd3ede6e5f9a694c13bf8ed8c036 /net | |
parent | 589344c079be45268437f11e55f73fa9ca031d17 (diff) | |
download | chromium_src-cff5dadeb1be4c526a1b3875c9acf422cbaf834d.zip chromium_src-cff5dadeb1be4c526a1b3875c9acf422cbaf834d.tar.gz chromium_src-cff5dadeb1be4c526a1b3875c9acf422cbaf834d.tar.bz2 |
Support signatures in the policy test server.
Remove the old certificate-handling code and make the test server read a
private key file instead. Public key is sent to the client in PKCS#1/DER
format as required by the protocol.
BUG=None
TEST=manual
Review URL: http://codereview.chromium.org/6823004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@81090 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r-- | net/test/test_server.cc | 5 | ||||
-rw-r--r-- | net/tools/testserver/asn1der.py | 61 | ||||
-rw-r--r-- | net/tools/testserver/device_management.py | 82 | ||||
-rwxr-xr-x | net/tools/testserver/testserver.py | 25 |
4 files changed, 138 insertions, 35 deletions
diff --git a/net/test/test_server.cc b/net/test/test_server.cc index de55808..2b61ec6 100644 --- a/net/test/test_server.cc +++ b/net/test/test_server.cc @@ -17,8 +17,8 @@ #include "base/base64.h" #include "base/command_line.h" #include "base/debug/leak_annotations.h" -#include "base/json/json_reader.h" #include "base/file_util.h" +#include "base/json/json_reader.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/path_service.h" @@ -345,9 +345,6 @@ bool TestServer::AddCommandLineArguments(CommandLine* command_line) const { command_line->AppendSwitchASCII("port", base::IntToString(host_port_pair_.port())); command_line->AppendSwitchPath("data-dir", document_root_); - command_line->AppendSwitchPath("policy-cert-chain", - certificates_dir_.AppendASCII("ok_cert.pem")); - command_line->AppendSwitchPath("policy-cert-chain", GetRootCertificatePath()); if (logging::GetMinLogLevel() == logging::LOG_VERBOSE) { command_line->AppendArg("--log-to-console"); diff --git a/net/tools/testserver/asn1der.py b/net/tools/testserver/asn1der.py new file mode 100644 index 0000000..7e57ccf --- /dev/null +++ b/net/tools/testserver/asn1der.py @@ -0,0 +1,61 @@ +#!/usr/bin/python2.5 +# Copyright (c) 2011 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. + +"""Helper module for ASN.1/DER encoding.""" + +import binascii +import struct + +# Tags as defined by ASN.1. +INTEGER = 2 +BIT_STRING = 3 +NULL = 5 +OBJECT_IDENTIFIER = 6 +SEQUENCE = 0x30 + +def Data(tag, data): + """Generic type-length-value encoder. + + Args: + tag: the tag. + data: the data for the given tag. + Returns: + encoded TLV value. + """ + if len(data) == 0: + return struct.pack(">BB", tag, 0); + assert len(data) <= 0xffff; + return struct.pack(">BBH", tag, 0x82, len(data)) + data; + +def Integer(value): + """Encodes an integer. + + Args: + value: the long value. + Returns: + encoded TLV value. + """ + data = '%x' % value + return Data(INTEGER, binascii.unhexlify('00' + '0' * (len(data) % 2) + data)) + +def Bitstring(value): + """Encodes a bit string. + + Args: + value: a string holding the binary data. + Returns: + encoded TLV value. + """ + return Data(BIT_STRING, '\x00' + value) + +def Sequence(values): + """Encodes a sequence of other values. + + Args: + values: the list of values, must be strings holding already encoded data. + Returns: + encoded TLV value. + """ + return Data(SEQUENCE, ''.join(values)) diff --git a/net/tools/testserver/device_management.py b/net/tools/testserver/device_management.py index efd6694..57579b5 100644 --- a/net/tools/testserver/device_management.py +++ b/net/tools/testserver/device_management.py @@ -48,6 +48,7 @@ import sys import time import tlslite import tlslite.api +import tlslite.utils # The name and availability of the json module varies in python versions. try: @@ -58,9 +59,12 @@ except ImportError: except ImportError: json = None +import asn1der import device_management_backend_pb2 as dm import cloud_policy_pb2 as cp +# ASN.1 object identifier for PKCS#1/RSA. +PKCS1_RSA_OID = '\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01' class RequestHandler(object): """Decodes and handles device management requests from clients. @@ -419,23 +423,35 @@ class RequestHandler(object): self.GatherPolicySettings(settings, self._server.policy[msg.policy_type]) + # Figure out the key we want to use. + key = None + if (msg.signature_type == dm.PolicyFetchRequest.SHA1_RSA and + len(self._server.keys)): + key_version = min(max(1, msg.public_key_version), len(self._server.keys)) + key = self._server.keys[key_version - 1] + + # Fill the policy data protobuf. policy_data = dm.PolicyData() - policy_data.policy_value = settings.SerializeToString() policy_data.policy_type = msg.policy_type policy_data.timestamp = int(time.time() * 1000) policy_data.request_token = token_info['device_token']; + policy_data.policy_value = settings.SerializeToString() policy_data.machine_name = token_info['machine_name'] + if key: + policy_data.public_key_version = key_version + policy_data.username = self._server.username + policy_data.device_id = token_info['device_id'] signed_data = policy_data.SerializeToString() response = dm.DeviceManagementResponse() response.error = dm.DeviceManagementResponse.SUCCESS fetch_response = response.policy_response.response.add() fetch_response.policy_data = signed_data - fetch_response.policy_data_signature = ( - self._server.private_key.hashAndSign(signed_data).tostring()) - for certificate in self._server.cert_chain: - fetch_response.certificate_chain.append( - certificate.writeBytes().tostring()) + if key: + fetch_response.policy_data_signature = ( + key['private_key'].hashAndSign(signed_data).tostring()) + if msg.public_key_version != key_version: + fetch_response.new_public_key = key['public_key'] self.DumpMessage('Response', response) @@ -485,16 +501,22 @@ class RequestHandler(object): class TestServer(object): """Handles requests and keeps global service state.""" - def __init__(self, policy_path, policy_cert_chain): + def __init__(self, policy_path, private_key_paths, policy_user): """Initializes the server. Args: policy_path: Names the file to read JSON-formatted policy from. - policy_cert_chain: List of paths to X.509 certificate files of the - certificate chain used for signing responses. + private_key_paths: List of paths to read private keys from. """ self._registered_tokens = {} self.policy = {} + + # There is no way to for the testserver to know the user name belonging to + # the GAIA auth token we received (short of actually talking to GAIA). To + # address this, we have a command line parameter to set the username that + # the server should report to the client. + self.username = policy_user + if json is None: print 'No JSON module, cannot parse policy information' else : @@ -503,19 +525,35 @@ class TestServer(object): except IOError: print 'Failed to load policy from %s' % policy_path - self.private_key = None - self.cert_chain = [] - for cert_path in policy_cert_chain: - try: - cert_text = open(cert_path).read() - except IOError: - print 'Failed to load certificate from %s' % cert_path - certificate = tlslite.api.X509() - certificate.parse(cert_text) - self.cert_chain.append(certificate) - if self.private_key is None: - self.private_key = tlslite.api.parsePEMKey(cert_text, private=True) - assert self.private_key != None + self.keys = [] + if private_key_paths: + # Load specified keys from the filesystem. + for key_path in private_key_paths: + try: + key = tlslite.api.parsePEMKey(open(key_path).read(), private=True) + except IOError: + print 'Failed to load private key from %s' % key_path + continue + + assert key != None + self.keys.append({ 'private_key' : key }) + else: + # Generate a key if none were specified. + key = tlslite.api.generateRSAKey(1024) + assert key != None + self.keys.append({ 'private_key' : key }) + + # Derive the public keys from the loaded private keys. + for entry in self.keys: + key = entry['private_key'] + + algorithm = asn1der.Sequence( + [ asn1der.Data(asn1der.OBJECT_IDENTIFIER, PKCS1_RSA_OID), + asn1der.Data(asn1der.NULL, '') ]) + rsa_pubkey = asn1der.Sequence([ asn1der.Integer(key.n), + asn1der.Integer(key.e) ]) + pubkey = asn1der.Sequence([ algorithm, asn1der.Bitstring(rsa_pubkey) ]) + entry['public_key'] = pubkey; def HandleRequest(self, path, headers, request): """Handles a request. diff --git a/net/tools/testserver/testserver.py b/net/tools/testserver/testserver.py index 2ba417f..2d0febd 100755 --- a/net/tools/testserver/testserver.py +++ b/net/tools/testserver/testserver.py @@ -1216,7 +1216,8 @@ class TestPageHandler(BasePageHandler): policy_path = os.path.join(self.server.data_dir, 'device_management') self.server._device_management_handler = ( device_management.TestServer(policy_path, - self.server.policy_cert_chain)) + self.server.policy_keys, + self.server.policy_user)) http_response, raw_reply = ( self.server._device_management_handler.HandleRequest(self.path, @@ -1354,7 +1355,8 @@ def main(options, args): server.file_root_url = options.file_root_url server_data['port'] = server.server_port server._device_management_handler = None - server.policy_cert_chain = options.policy_cert_chain + server.policy_keys = options.policy_keys + server.policy_user = options.policy_user elif options.server_type == SERVER_SYNC: server = SyncHTTPServer(('127.0.0.1', port), SyncPageHandler) print 'Sync HTTP server started on port %d...' % server.server_port @@ -1458,13 +1460,18 @@ if __name__ == '__main__': option_parser.add_option('', '--startup-pipe', type='int', dest='startup_pipe', help='File handle of pipe to parent process') - option_parser.add_option('', '--policy-cert-chain', action='append', - help='Specify a path to a certificate file to sign ' - 'policy responses. This option may be used ' - 'multiple times to define a certificate chain. ' - 'The first element will be used for signing, ' - 'the last element should be the root ' - 'certificate.') + option_parser.add_option('', '--policy-key', action='append', + dest='policy_keys', + help='Specify a path to a PEM-encoded private key ' + 'to use for policy signing. May be specified ' + 'multiple times in order to load multipe keys into ' + 'the server. The server will generate a random key ' + 'if none is specified on the command line') + option_parser.add_option('', '--policy-user', default='user@example.com', + dest='policy_user', + help='Specify the user name the server should ' + 'report back to the client as the user owning the ' + 'token used for making the policy request.') options, args = option_parser.parse_args() sys.exit(main(options, args)) |