summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authormnissler@chromium.org <mnissler@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-04-11 12:13:05 +0000
committermnissler@chromium.org <mnissler@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-04-11 12:13:05 +0000
commitcff5dadeb1be4c526a1b3875c9acf422cbaf834d (patch)
treeda0c0f9f6ff4fd3ede6e5f9a694c13bf8ed8c036 /net
parent589344c079be45268437f11e55f73fa9ca031d17 (diff)
downloadchromium_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.cc5
-rw-r--r--net/tools/testserver/asn1der.py61
-rw-r--r--net/tools/testserver/device_management.py82
-rwxr-xr-xnet/tools/testserver/testserver.py25
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))