Skip to content

Commit

Permalink
Revert "Revert "net: add OCSP tests.""
Browse files Browse the repository at this point in the history
(First landed in r127486, reverted in r127493 because it broke on
Windows XP.)

I was getting increasingly unhappy altering EV and revocation checking
semantics without any tests. We historically haven't had tests because
online revocation checking is inherently flaky so I amended testserver
with the minimum code to be able to sign and vend OCSP responses.

These tests do not test the final EV/CRLSet/revocation checking
semantics. They are intended to be altered in future CLs.

BUG=none
TEST=net_unittests

https://chromiumcodereview.appspot.com/9663017/

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@127518 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
agl@chromium.org committed Mar 19, 2012
1 parent eaf60d8 commit 131d13b
Show file tree
Hide file tree
Showing 9 changed files with 865 additions and 37 deletions.
3 changes: 3 additions & 0 deletions net/data/ssl/certificates/README
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,6 @@ unit tests.
net/socket/ssl_client_socket_unittest.cc. These chains are valid until
26 Feb 2022 and are generated by
net/data/ssl/scripts/generate-redundant-test-chains.sh.

- ocsp-test-root.pem : A root certificate for the code in
net/tools/testserver/minica.py
51 changes: 51 additions & 0 deletions net/data/ssl/certificates/ocsp-test-root.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 1 (0x1)
Signature Algorithm: sha1WithRSAEncryption
Issuer: CN=Testing CA
Validity
Not Before: Jan 1 06:00:00 2010 GMT
Not After : Dec 1 06:00:00 2032 GMT
Subject: CN=Testing CA
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public Key: (1024 bit)
Modulus (1024 bit):
00:a7:19:98:f2:93:0b:fe:73:d0:31:a8:7f:13:3d:
2f:37:8e:ee:ee:d5:2a:77:e4:4d:0f:c9:ff:6f:07:
ff:32:cb:f3:da:99:9d:e4:ed:65:83:2a:fc:b0:80:
7f:98:78:75:06:53:9d:25:8a:0c:e3:c2:c7:79:67:
65:30:99:a9:03:4a:9b:11:5a:87:6c:39:a8:c4:e4:
ed:4a:cd:0c:64:09:59:46:fb:39:ee:eb:47:a0:70:
4d:bb:01:8a:cf:48:c3:a1:c4:b8:95:fc:40:9f:b4:
a3:40:a9:86:b1:af:c4:55:19:ab:9e:ca:47:c3:01:
85:c7:71:c6:4a:a5:ec:f0:7d
Exponent: 3 (0x3)
X509v3 extensions:
X509v3 Basic Constraints: critical
CA:TRUE, pathlen:0
X509v3 Certificate Policies:
Policy: 1.3.6.1.4.1.11129.2.4.1

Signature Algorithm: sha1WithRSAEncryption
48:0c:c9:ab:8f:f2:cc:80:f1:1f:b3:3a:45:18:de:ab:c5:e0:
d7:d4:64:a0:c4:86:2e:fc:58:3a:d7:86:ba:02:4e:29:95:72:
9f:20:5d:43:b2:41:4e:7c:a4:86:a1:df:b3:ab:7e:46:cb:af:
41:7d:c2:2b:b4:d3:22:d3:67:3e:13:ef:b6:9f:5c:8a:0d:3c:
a7:58:eb:a9:21:d2:9b:6b:e5:b6:4f:d6:7c:22:a7:b3:18:82:
b2:16:7d:d6:5c:7d:c9:46:be:91:49:e8:d2:42:95:cd:f8:8a:
91:50:e7:5b:2a:26:68:ef:e7:e7:c6:24:d1:3c:01:9d:6c:48:
a4:f5
-----BEGIN CERTIFICATE-----
MIIB0DCCATmgAwIBAgIBATANBgkqhkiG9w0BAQUFADAVMRMwEQYDVQQDEwpUZXN0
aW5nIENBMB4XDTEwMDEwMTA2MDAwMFoXDTMyMTIwMTA2MDAwMFowFTETMBEGA1UE
AxMKVGVzdGluZyBDQTCBnTANBgkqhkiG9w0BAQEFAAOBiwAwgYcCgYEApxmY8pML
/nPQMah/Ez0vN47u7tUqd+RND8n/bwf/Msvz2pmd5O1lgyr8sIB/mHh1BlOdJYoM
48LHeWdlMJmpA0qbEVqHbDmoxOTtSs0MZAlZRvs57utHoHBNuwGKz0jDocS4lfxA
n7SjQKmGsa/EVRmrnspHwwGFx3HGSqXs8H0CAQOjMjAwMBIGA1UdEwEB/wQIMAYB
Af8CAQAwGgYDVR0gAQEABBAwDjAMBgorBgEEAdZ5AgQBMA0GCSqGSIb3DQEBBQUA
A4GBAEgMyauP8syA8R+zOkUY3qvF4NfUZKDEhi78WDrXhroCTimVcp8gXUOyQU58
pIah37OrfkbLr0F9wiu00yLTZz4T77afXIoNPKdY66kh0ptr5bZP1nwip7MYgrIW
fdZcfclGvpFJ6NJClc34ipFQ51sqJmjv5+fGJNE8AZ1sSKT1
-----END CERTIFICATE-----
12 changes: 3 additions & 9 deletions net/ocsp/nss_ocsp.cc
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ class OCSPIOLoop {
void StartUsing() {
base::AutoLock autolock(lock_);
used_ = true;
io_loop_ = MessageLoopForIO::current();
DCHECK(io_loop_);
}

// Called on IO loop.
Expand Down Expand Up @@ -456,8 +458,7 @@ class OCSPServerSession {
OCSPIOLoop::OCSPIOLoop()
: shutdown_(false),
used_(false),
io_loop_(MessageLoopForIO::current()) {
DCHECK(io_loop_);
io_loop_(NULL) {
}

OCSPIOLoop::~OCSPIOLoop() {
Expand Down Expand Up @@ -512,13 +513,6 @@ void OCSPIOLoop::AddRequest(OCSPRequestSession* request) {
}

void OCSPIOLoop::RemoveRequest(OCSPRequestSession* request) {
{
// Ignore if we've already shutdown.
base::AutoLock auto_lock(lock_);
if (shutdown_)
return;
}

DCHECK(ContainsKey(requests_, request));
requests_.erase(request);
}
Expand Down
44 changes: 36 additions & 8 deletions net/test/base_test_server.cc
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ void GetCiphersList(int cipher, base::ListValue* values) {

BaseTestServer::HTTPSOptions::HTTPSOptions()
: server_certificate(CERT_OK),
ocsp_status(OCSP_OK),
request_client_certificate(false),
bulk_ciphers(HTTPSOptions::BULK_CIPHER_ANY),
record_resume(false) {}
Expand All @@ -79,12 +80,31 @@ FilePath BaseTestServer::HTTPSOptions::GetCertificateFile() const {
// This chain uses its own dedicated test root certificate to avoid
// side-effects that may affect testing.
return FilePath(FILE_PATH_LITERAL("redundant-server-chain.pem"));
case CERT_AUTO:
return FilePath();
default:
NOTREACHED();
}
return FilePath();
}

std::string BaseTestServer::HTTPSOptions::GetOCSPArgument() const {
if (server_certificate != CERT_AUTO)
return "";

switch (ocsp_status) {
case OCSP_OK:
return "ok";
case OCSP_REVOKED:
return "revoked";
case OCSP_INVALID:
return "invalid";
default:
NOTREACHED();
return "";
}
}

const char BaseTestServer::kLocalhost[] = "127.0.0.1";
const char BaseTestServer::kGDataAuthToken[] = "testtoken";

Expand Down Expand Up @@ -309,17 +329,25 @@ bool BaseTestServer::GenerateArguments(base::DictionaryValue* arguments) const {
arguments->Set("log-to-console", base::Value::CreateNullValue());

if (type_ == TYPE_HTTPS) {
arguments->Set("https", base::Value::CreateNullValue());

// Check the certificate arguments of the HTTPS server.
FilePath certificate_path(certificates_dir_);
certificate_path = certificate_path.Append(
https_options_.GetCertificateFile());
if (certificate_path.IsAbsolute() &&
!file_util::PathExists(certificate_path)) {
LOG(ERROR) << "Certificate path " << certificate_path.value()
<< " doesn't exist. Can't launch https server.";
return false;
FilePath certificate_file(https_options_.GetCertificateFile());
if (!certificate_file.value().empty()) {
certificate_path = certificate_path.Append(certificate_file);
if (certificate_path.IsAbsolute() &&
!file_util::PathExists(certificate_path)) {
LOG(ERROR) << "Certificate path " << certificate_path.value()
<< " doesn't exist. Can't launch https server.";
return false;
}
arguments->SetString("cert-and-key-file", certificate_path.value());
}
arguments->SetString("https", certificate_path.value());

std::string ocsp_arg = https_options_.GetOCSPArgument();
if (!ocsp_arg.empty())
arguments->SetString("ocsp", ocsp_arg);

// Check the client certificate related arguments.
if (https_options_.request_client_certificate)
Expand Down
21 changes: 21 additions & 0 deletions net/test/base_test_server.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ class BaseTestServer {
struct HTTPSOptions {
enum ServerCertificate {
CERT_OK,

// CERT_AUTO causes the testserver to generate a test certificate issued
// by "Testing CA" (see net/data/ssl/certificates/ocsp-test-root.pem).
CERT_AUTO,

CERT_MISMATCHED_NAME,
CERT_EXPIRED,
// Cross-signed certificate to test PKIX path building. Contains an
Expand All @@ -55,6 +60,14 @@ class BaseTestServer {
CERT_CHAIN_WRONG_ROOT,
};

// OCSPStatus enumerates the types of OCSP response that the testserver
// can produce.
enum OCSPStatus {
OCSP_OK,
OCSP_REVOKED,
OCSP_INVALID,
};

// Bitmask of bulk encryption algorithms that the test server supports
// and that can be selectively enabled or disabled.
enum BulkCipher {
Expand Down Expand Up @@ -83,9 +96,17 @@ class BaseTestServer {
// |server_certificate|.
FilePath GetCertificateFile() const;

// GetOCSPArgument returns the value of any OCSP argument to testserver or
// the empty string if there is none.
std::string GetOCSPArgument() const;

// The certificate to use when serving requests.
ServerCertificate server_certificate;

// If |server_certificate==CERT_AUTO| then this determines the type of OCSP
// response returned.
OCSPStatus ocsp_status;

// True if a CertificateRequest should be sent to the client during
// handshaking.
bool request_client_certificate;
Expand Down
165 changes: 165 additions & 0 deletions net/tools/testserver/asn1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
# Copyright (c) 2012 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.

# This file implements very minimal ASN.1, DER serialization.

import types


def ToDER(obj):
'''ToDER converts the given object into DER encoding'''
if type(obj) == types.NoneType:
# None turns into NULL
return TagAndLength(5, 0)
if type(obj) == types.StringType:
# Strings are PRINTABLESTRING
return TagAndLength(19, len(obj)) + obj
if type(obj) == types.BooleanType:
val = "\x00"
if obj:
val = "\xff"
return TagAndLength(1, 1) + val
if type(obj) == types.IntType or type(obj) == types.LongType:
big_endian = []
val = obj
while val != 0:
big_endian.append(val & 0xff)
val >>= 8

if len(big_endian) == 0 or big_endian[-1] >= 128:
big_endian.append(0)

big_endian.reverse()
return TagAndLength(2, len(big_endian)) + ToBytes(big_endian)

return obj.ToDER()


def ToBytes(array_of_bytes):
'''ToBytes converts the array of byte values into a binary string'''
return ''.join([chr(x) for x in array_of_bytes])


def TagAndLength(tag, length):
der = [tag]
if length < 128:
der.append(length)
elif length < 256:
der.append(0x81)
der.append(length)
elif length < 65535:
der.append(0x82)
der.append(length >> 8)
der.append(length & 0xff)
else:
assert False

return ToBytes(der)


class Raw(object):
'''Raw contains raw DER encoded bytes that are used verbatim'''
def __init__(self, der):
self.der = der

def ToDER(self):
return self.der


class Explicit(object):
'''Explicit prepends an explicit tag'''
def __init__(self, tag, child):
self.tag = tag
self.child = child

def ToDER(self):
der = ToDER(self.child)
tag = self.tag
tag |= 0x80 # content specific
tag |= 0x20 # complex
return TagAndLength(tag, len(der)) + der


class ENUMERATED(object):
def __init__(self, value):
self.value = value

def ToDER(self):
return TagAndLength(10, 1) + chr(self.value)


class SEQUENCE(object):
def __init__(self, children):
self.children = children

def ToDER(self):
der = ''.join([ToDER(x) for x in self.children])
return TagAndLength(0x30, len(der)) + der


class SET(object):
def __init__(self, children):
self.children = children

def ToDER(self):
der = ''.join([ToDER(x) for x in self.children])
return TagAndLength(0x31, len(der)) + der


class OCTETSTRING(object):
def __init__(self, val):
self.val = val

def ToDER(self):
return TagAndLength(4, len(self.val)) + self.val


class OID(object):
def __init__(self, parts):
self.parts = parts

def ToDER(self):
if len(self.parts) < 2 or self.parts[0] > 6 or self.parts[1] >= 40:
assert False

der = [self.parts[0]*40 + self.parts[1]]
for x in self.parts[2:]:
if x == 0:
der.append(0)
else:
octets = []
while x != 0:
v = x & 0x7f
if len(octets) > 0:
v |= 0x80
octets.append(v)
x >>= 7
octets.reverse()
der = der + octets

return TagAndLength(6, len(der)) + ToBytes(der)


class UTCTime(object):
def __init__(self, time_str):
self.time_str = time_str

def ToDER(self):
return TagAndLength(23, len(self.time_str)) + self.time_str


class GeneralizedTime(object):
def __init__(self, time_str):
self.time_str = time_str

def ToDER(self):
return TagAndLength(24, len(self.time_str)) + self.time_str


class BitString(object):
def __init__(self, bits):
self.bits = bits

def ToDER(self):
return TagAndLength(3, 1 + len(self.bits)) + "\x00" + self.bits
Loading

0 comments on commit 131d13b

Please sign in to comment.