diff options
author | agl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-08-11 19:50:02 +0000 |
---|---|---|
committer | agl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-08-11 19:50:02 +0000 |
commit | b2471359cfbd4f7b9621ba2542b947841bfadb27 (patch) | |
tree | 241b1e8c58a26a5bbfb1df3c9f5d342c492ad693 /net/base | |
parent | 1b3db78c4451a755eeaadc4cedceccd9e91724c8 (diff) | |
download | chromium_src-b2471359cfbd4f7b9621ba2542b947841bfadb27.zip chromium_src-b2471359cfbd4f7b9621ba2542b947841bfadb27.tar.gz chromium_src-b2471359cfbd4f7b9621ba2542b947841bfadb27.tar.bz2 |
net: add embedded DNSSEC chain support.
Now that the DNS root is signed we have a good trust path in several
TLDs (including .org). This patch enables self-signed certificates to
include a DNSSEC chain as an extension which proves a CERT record,
containing the fingerprint of the public key.
The format of the chain is still undecided, so this is only enabled
with --enable-dnssec-certs.
BUG=none
TEST=net_unittests
http://codereview.chromium.org/2806076
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@55771 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/base')
-rw-r--r-- | net/base/dns_util.h | 13 | ||||
-rw-r--r-- | net/base/dnssec_chain_verifier.cc | 670 | ||||
-rw-r--r-- | net/base/dnssec_chain_verifier.h | 107 | ||||
-rw-r--r-- | net/base/dnssec_keyset.cc | 454 | ||||
-rw-r--r-- | net/base/dnssec_keyset.h | 61 | ||||
-rw-r--r-- | net/base/dnssec_unittest.cc | 403 | ||||
-rw-r--r-- | net/base/ssl_config_service.cc | 12 | ||||
-rw-r--r-- | net/base/ssl_config_service.h | 10 | ||||
-rw-r--r-- | net/base/ssl_config_service_defaults.h | 1 | ||||
-rw-r--r-- | net/base/ssl_config_service_mac.cc | 1 | ||||
-rw-r--r-- | net/base/ssl_config_service_win.cc | 1 |
11 files changed, 1730 insertions, 3 deletions
diff --git a/net/base/dns_util.h b/net/base/dns_util.h index 88c48a8..78dca47 100644 --- a/net/base/dns_util.h +++ b/net/base/dns_util.h @@ -30,10 +30,21 @@ std::string TrimEndingDot(const std::string& host); // http://www.iana.org/assignments/dns-parameters static const uint16 kDNS_TXT = 16; -static const uint16 kDNS_RRSIG = 46; static const uint16 kDNS_CERT = 37; +static const uint16 kDNS_DS = 43; +static const uint16 kDNS_RRSIG = 46; +static const uint16 kDNS_DNSKEY = 48; static const uint16 kDNS_ANY = 0xff; +// http://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml +static const uint8 kDNSSEC_RSA_SHA1 = 5; +static const uint8 kDNSSEC_RSA_SHA1_NSEC3 = 7; +static const uint8 kDNSSEC_RSA_SHA256 = 8; + +// RFC 4509 +static const uint8 kDNSSEC_SHA1 = 1; +static const uint8 kDNSSEC_SHA256 = 2; + } // namespace net #endif // NET_BASE_DNS_UTIL_H_ diff --git a/net/base/dnssec_chain_verifier.cc b/net/base/dnssec_chain_verifier.cc new file mode 100644 index 0000000..96b5b7d --- /dev/null +++ b/net/base/dnssec_chain_verifier.cc @@ -0,0 +1,670 @@ +// Copyright (c) 2010 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. + +#include "net/base/dnssec_chain_verifier.h" + +#include "base/logging.h" +#include "base/scoped_ptr.h" +#include "base/sha1.h" +#include "base/sha2.h" +#include "net/base/dns_util.h" + +// We don't have a location for the spec yet, so we'll include it here until it +// finds a better home. + +/* +When connecting to a host www.example.com, www.example.com may present a certificate which includes a DNSSEC chain embedded in it. The aim of the embedded chain is to prove that the fingerprint of the public key is valid DNSSEC data. This is achieved by proving a CERT record for the target domain. + +Initially, the target domain is constructed by prepending _ssl. For example, the initial target domain for www.example.com is _ssl.www.example.com. + +A DNSSEC chain verifier can be in one of two states: entering a zone, or within a zone. Initially, the verifier is entering the root zone. + +When entering a zone, the verifier reads the following structure: + +uint8 entryKey +uint16 signature length: + // See RRSIG RDATA in RFC 4043 for details + uint8 algorithm + uint8 labels + uint32 ttl + uint32 expires + uint32 begins + uint16 keyid + []byte signature +uint8 numKeys +// for each key: +uint16 key length: + []byte DNSKEY RDATA + +|entryKey| indexes the array of DNSKEYs and MUST be less than |numKeys|. The indexed DNSKEY MUST be a key that the verifier trusts, either because it's the long-term root key, or because of a previously presented DS signature. + +If only a trusted key is needed within this zone, then the signature length MAY be zero. In which case, |entryKey| MUST be 0 and |numKeys| MUST be 1. + +After processing this data, the verifier trusts one or more keys for this zone. + +When within a zone, the verifier reads the following structure: + +dnsName name +uint16 RRtype + +|name| is in DNS format (a series of 8-bit, length prefixed strings). No DNS name compression is permitted. + +|name| must be closer to the current target domain than the current zone. Here, 'closer' is defined as a greater number of matching labels when comparing right to left. + +|RRtype| may be either DS, CERT or CNAME: + +DS: this indicates a zone transition to a new zone named |name|. The verifier reads the following structure: + uint16 signature length: + ... (see above for the signature structure) + uint8 num_ds + // for each DS: + uint8 digest_type + uint16 length + []byte DS DATA + +The verifier is now entering the named zone. It reads ahead and extracts the entry key from the zone entry data and synthisises a DS record for the given digest type and verifies the signature. It then enters the next zone. + + +CERT: |name| MUST match the target domain. The verifier reads the following structure: + uint16 signature length: + ... (see above for the signature structure) + []byte CERT RDATA + +(The format of the CERT RDATA isn't specified here, but the verifier must be able to extract a public key fingerprint in order to validate the original certificate.) + +This terminates the verification. There MUST NOT be any more data in the chain. + + +CNAME: |name| MUST match the target domain. The verifier reads the following structure: + uint16 signature length: + ... (see above for the signature structure) + []byte CNAME RDATA + +This replaces the target domain with a new domain. The new domain is the target of the CNAME with _ssl prepended. The verifier is now in the zone that is the greatest common ancestor of the old and new target domains. (For example, when switching from _ssl.www.example.com to _ssl.www.example2.com, the verifier is now in com.) + + +Example for www.google.com: + +The target domain is www.google.com. + +The verifier enters ., it already trusts the long-term root key and both root keys are presented in order to extend the trust to the smaller root key. + +A DS signature is presented for .com. The verifier is now entering .com. + +All four .com keys are presented. The verifier is now in .com. + +A DS signature is presented for google.com. The verifier is now entering google.com + +As google.com contains only a single DNSKEY, it is included without a signature. The verifier is now in google.com. + +A CNAME is presented for www.google.com pointing to www.l.google.com. The target domain is now www.l.google.com. The verifier is now in google.com. + +A DS signature is presented for l.google.com. The verifier is now entering l.google.com. + +As l.google.com contains only a single DNSKEY, it is included without a signature. The verifier is now in l.google.com. + +A CERT record is presented for www.l.google.com. The verification is complete. +*/ + +// This is the 2048-bit DNS root key: http://www.iana.org/dnssec +// 19036 8 2 49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5 +static const unsigned char kRootKey[] = { + 0x01, 0x01, 0x03, 0x08, 0x03, 0x01, 0x00, 0x01, 0xa8, 0x00, 0x20, 0xa9, 0x55, + 0x66, 0xba, 0x42, 0xe8, 0x86, 0xbb, 0x80, 0x4c, 0xda, 0x84, 0xe4, 0x7e, 0xf5, + 0x6d, 0xbd, 0x7a, 0xec, 0x61, 0x26, 0x15, 0x55, 0x2c, 0xec, 0x90, 0x6d, 0x21, + 0x16, 0xd0, 0xef, 0x20, 0x70, 0x28, 0xc5, 0x15, 0x54, 0x14, 0x4d, 0xfe, 0xaf, + 0xe7, 0xc7, 0xcb, 0x8f, 0x00, 0x5d, 0xd1, 0x82, 0x34, 0x13, 0x3a, 0xc0, 0x71, + 0x0a, 0x81, 0x18, 0x2c, 0xe1, 0xfd, 0x14, 0xad, 0x22, 0x83, 0xbc, 0x83, 0x43, + 0x5f, 0x9d, 0xf2, 0xf6, 0x31, 0x32, 0x51, 0x93, 0x1a, 0x17, 0x6d, 0xf0, 0xda, + 0x51, 0xe5, 0x4f, 0x42, 0xe6, 0x04, 0x86, 0x0d, 0xfb, 0x35, 0x95, 0x80, 0x25, + 0x0f, 0x55, 0x9c, 0xc5, 0x43, 0xc4, 0xff, 0xd5, 0x1c, 0xbe, 0x3d, 0xe8, 0xcf, + 0xd0, 0x67, 0x19, 0x23, 0x7f, 0x9f, 0xc4, 0x7e, 0xe7, 0x29, 0xda, 0x06, 0x83, + 0x5f, 0xa4, 0x52, 0xe8, 0x25, 0xe9, 0xa1, 0x8e, 0xbc, 0x2e, 0xcb, 0xcf, 0x56, + 0x34, 0x74, 0x65, 0x2c, 0x33, 0xcf, 0x56, 0xa9, 0x03, 0x3b, 0xcd, 0xf5, 0xd9, + 0x73, 0x12, 0x17, 0x97, 0xec, 0x80, 0x89, 0x04, 0x1b, 0x6e, 0x03, 0xa1, 0xb7, + 0x2d, 0x0a, 0x73, 0x5b, 0x98, 0x4e, 0x03, 0x68, 0x73, 0x09, 0x33, 0x23, 0x24, + 0xf2, 0x7c, 0x2d, 0xba, 0x85, 0xe9, 0xdb, 0x15, 0xe8, 0x3a, 0x01, 0x43, 0x38, + 0x2e, 0x97, 0x4b, 0x06, 0x21, 0xc1, 0x8e, 0x62, 0x5e, 0xce, 0xc9, 0x07, 0x57, + 0x7d, 0x9e, 0x7b, 0xad, 0xe9, 0x52, 0x41, 0xa8, 0x1e, 0xbb, 0xe8, 0xa9, 0x01, + 0xd4, 0xd3, 0x27, 0x6e, 0x40, 0xb1, 0x14, 0xc0, 0xa2, 0xe6, 0xfc, 0x38, 0xd1, + 0x9c, 0x2e, 0x6a, 0xab, 0x02, 0x64, 0x4b, 0x28, 0x13, 0xf5, 0x75, 0xfc, 0x21, + 0x60, 0x1e, 0x0d, 0xee, 0x49, 0xcd, 0x9e, 0xe9, 0x6a, 0x43, 0x10, 0x3e, 0x52, + 0x4d, 0x62, 0x87, 0x3d, +}; + +// kRootKeyID is the key id for kRootKey +static const uint16 kRootKeyID = 19036; + +namespace net { + +DNSSECChainVerifier::DNSSECChainVerifier(const std::string& target, + const base::StringPiece& chain) + : current_zone_(NULL), + target_(target), + chain_(chain), + ignore_timestamps_(false), + valid_(false), + rrtype_(0) { +} + +DNSSECChainVerifier::~DNSSECChainVerifier() { + for (std::vector<void*>::iterator + i = scratch_pool_.begin(); i != scratch_pool_.end(); i++) { + free(*i); + } + + Zone* next; + for (Zone* cur = current_zone_; cur; cur = next) { + next = cur->prev; + delete cur; + } +} + +void DNSSECChainVerifier::IgnoreTimestamps() { + ignore_timestamps_ = true; +} + +DNSSECChainVerifier::Error DNSSECChainVerifier::Verify() { + Error err; + + err = EnterRoot(); + if (err != OK) + return err; + + for (;;) { + base::StringPiece next_name; + err = LeaveZone(&next_name); + if (err != OK) + return err; + if (valid_) { + if (!chain_.empty()) + return BAD_DATA; // no trailing data allowed. + break; + } + + err = EnterZone(next_name); + if (err != OK) + return err; + } + + return OK; +} + +uint16 DNSSECChainVerifier::rrtype() const { + DCHECK(valid_); + return rrtype_; +} + +const std::vector<base::StringPiece>& DNSSECChainVerifier::rrdatas() const { + DCHECK(valid_); + return rrdatas_; +} + +// U8 reads, and removes, a single byte from |chain_| +bool DNSSECChainVerifier::U8(uint8* v) { + if (chain_.size() < 1) + return false; + *v = chain_[0]; + chain_.remove_prefix(1); + return true; +} + +// U16 reads, and removes, a big-endian uint16 from |chain_| +bool DNSSECChainVerifier::U16(uint16* v) { + if (chain_.size() < 2) + return false; + const uint8* data = reinterpret_cast<const uint8*>(chain_.data()); + *v = static_cast<uint16>(data[0]) << 8 | + static_cast<uint16>(data[1]); + chain_.remove_prefix(2); + return true; +} + +// VariableLength16 reads, and removes, a big-endian, uint16, length-prefixed +// chunk from |chain_| +bool DNSSECChainVerifier::VariableLength16(base::StringPiece* v) { + uint16 length; + if (!U16(&length)) + return false; + if (chain_.size() < length) + return false; + *v = chain_.substr(0, length); + chain_.remove_prefix(length); + return true; +} + +// ReadName reads, and removes, an 8-bit length prefixed DNS name from |chain_| +bool DNSSECChainVerifier::ReadName(base::StringPiece* v) { + base::StringPiece saved = chain_; + unsigned length = 0; + static const uint8 kMaxDNSLabelLen = 63; + + for (;;) { + if (chain_.size() < 1) + return false; + uint8 label_len = chain_.data()[0]; + chain_.remove_prefix(1); + if (label_len > kMaxDNSLabelLen) + return false; + length += 1 + label_len; + + if (label_len == 0) + break; + + if (chain_.size() < label_len) + return false; + chain_.remove_prefix(label_len); + } + + *v = base::StringPiece(saved.data(), length); + return true; +} + +// ReadAheadEntryKey returns the entry key when |chain_| is positioned at the +// start of a zone. +bool DNSSECChainVerifier::ReadAheadEntryKey(base::StringPiece* v) { + base::StringPiece saved = chain_; + + uint8 entry_key; + base::StringPiece sig; + if (!U8(&entry_key) || + !VariableLength16(&sig)) { + return false; + } + + if (!ReadAheadKey(v, entry_key)) + return false; + chain_ = saved; + return true; +} + +// ReadAheadKey returns the entry key when |chain_| is positioned at the start +// of a list of keys. +bool DNSSECChainVerifier::ReadAheadKey(base::StringPiece* v, uint8 entry_key) { + base::StringPiece saved = chain_; + + uint8 num_keys; + if (!U8(&num_keys)) + return false; + + for (unsigned i = 0; i < num_keys; i++) { + if (!VariableLength16(v)) + return false; + if (i == entry_key) { + chain_ = saved; + return true; + } + } + + return false; +} + +bool DNSSECChainVerifier::ReadDNSKEYs(std::vector<base::StringPiece>* out, + bool is_root) { + uint8 num_keys; + if (!U8(&num_keys)) + return false; + + for (unsigned i = 0; i < num_keys; i++) { + base::StringPiece key; + if (!VariableLength16(&key)) + return false; + if (key.size() == 0) { + if (!is_root) + return false; + key = base::StringPiece(reinterpret_cast<const char*>(kRootKey), + sizeof(kRootKey)); + } + + out->push_back(key); + } + + return true; +} + +// DigestKey calculates a DS digest as specified in +// http://tools.ietf.org/html/rfc4034#section-5.1.4 +// name: the DNS form name of the key +// dnskey: the DNSKEY's RRDATA +// digest_type: see http://tools.ietf.org/html/rfc4034#appendix-A.2 +// keyid: the key's id +// algorithm: see http://tools.ietf.org/html/rfc4034#appendix-A.1 +bool DNSSECChainVerifier::DigestKey(base::StringPiece* out, + const base::StringPiece& name, + const base::StringPiece& dnskey, + uint8 digest_type, + uint16 keyid, + uint8 algorithm) { + std::string temp; + uint8 temp2[base::SHA256_LENGTH]; + const uint8* digest; + unsigned digest_len; + + std::string input = name.as_string() + dnskey.as_string(); + + if (digest_type == kDNSSEC_SHA1) { + temp = base::SHA1HashString(input); + digest = reinterpret_cast<const uint8*>(temp.data()); + digest_len = base::SHA1_LENGTH; + } else if (digest_type == kDNSSEC_SHA256) { + base::SHA256HashString(input, temp2, sizeof(temp2)); + digest = temp2; + digest_len = sizeof(temp2); + } else { + return false; + } + + uint8* output = static_cast<uint8*>(malloc(4 + digest_len)); + scratch_pool_.push_back(output); + output[0] = static_cast<uint8>(keyid >> 8); + output[1] = static_cast<uint8>(keyid); + output[2] = algorithm; + output[3] = digest_type; + memcpy(output + 4, digest, digest_len); + *out = base::StringPiece(reinterpret_cast<char*>(output), 4 + digest_len); + return true; +} + +// EnterRoot enters the root zone at the beginning of the chain. This is +// special because no DS record lead us here: we have to validate that the +// entry key is the DNS root key that we already know and trust. Additionally, +// for the root zone only, the keyid of the entry key is prepended to the data. +DNSSECChainVerifier::Error DNSSECChainVerifier::EnterRoot() { + uint16 root_keyid; + + if (!U16(&root_keyid)) + return BAD_DATA; + + if (root_keyid != kRootKeyID) + return UNKNOWN_ROOT_KEY; + + base::StringPiece root_key; + if (!ReadAheadEntryKey(&root_key)) + return BAD_DATA; + + // If the root key is given then it must match the expected root key exactly. + if (root_key.size()) { + if (root_key.size() != sizeof(kRootKey) || + memcmp(root_key.data(), kRootKey, sizeof(kRootKey))) { + return UNKNOWN_ROOT_KEY; + } + } + + base::StringPiece root("", 1); + return EnterZone(root); +} + +// EnterZone enters a new DNS zone. On entry it's assumed that the entry key +// has been validated. +DNSSECChainVerifier::Error DNSSECChainVerifier::EnterZone( + const base::StringPiece& zone) { + Zone* prev = current_zone_; + current_zone_ = new Zone; + current_zone_->prev = prev; + current_zone_->name = zone; + current_zone_->matching_labels = MatchingLabels(target_, zone); + if (ignore_timestamps_) + current_zone_->trusted_keys.IgnoreTimestamps(); + + uint8 entry_key; + base::StringPiece sig; + if (!U8(&entry_key) || + !VariableLength16(&sig)) { + return BAD_DATA; + } + + base::StringPiece key; + if (!ReadAheadKey(&key, entry_key)) + return BAD_DATA; + + if (zone.size() == 1 && key.size() == 0) { + // If a key is omitted in the root zone then it's the root key. + key = base::StringPiece(reinterpret_cast<const char*>(kRootKey), + sizeof(kRootKey)); + } + if (!current_zone_->trusted_keys.AddKey(key)) + return BAD_DATA; + + std::vector<base::StringPiece> dnskeys; + if (!ReadDNSKEYs(&dnskeys, zone.size() == 1)) + return BAD_DATA; + + if (sig.size() == 0) { + // An omitted signature on the keys means that only the entry key is used. + if (dnskeys.size() > 1 || entry_key != 0) + return BAD_DATA; + return OK; + } + + if (!current_zone_->trusted_keys.CheckSignature( + zone, zone, sig, kDNS_DNSKEY, dnskeys)) { + return BAD_SIGNATURE; + } + + // Add all the keys as trusted. + for (unsigned i = 0; i < dnskeys.size(); i++) { + if (i == entry_key) + continue; + current_zone_->trusted_keys.AddKey(dnskeys[i]); + } + + return OK; +} + +// LeaveZone transitions out of the current zone, either by following DS +// records to validate the entry key of the next zone, or because the final +// resource records are given. +DNSSECChainVerifier::Error DNSSECChainVerifier::LeaveZone( + base::StringPiece* next_name) { + base::StringPiece sig; + uint16 rrtype; + Error err; + + if (!ReadName(next_name) || + !U16(&rrtype) || + !VariableLength16(&sig)) { + return BAD_DATA; + } + + std::vector<base::StringPiece> rrdatas; + + if (rrtype == kDNS_DS) { + err = ReadDSSet(&rrdatas, *next_name); + } else if (rrtype == kDNS_CERT) { + err = ReadCERTs(&rrdatas); + } else { + // TODO(agl): add CNAME support. + return UNKNOWN_TERMINAL_RRTYPE; + } + if (err != OK) + return err; + + if (!current_zone_->trusted_keys.CheckSignature( + *next_name, current_zone_->name, sig, rrtype, rrdatas)) { + return BAD_SIGNATURE; + } + + if (rrtype == kDNS_DS) { + // If we are transitioning to another zone then the next zone must be + // 'closer' to the target than the current zone. + if (MatchingLabels(target_, *next_name) <= current_zone_->matching_labels) + return OFF_COURSE; + } else if (rrtype == kDNS_CERT) { + // If this is the final entry in the chain then the name must match target_ + if (next_name->size() != target_.size() || + memcmp(next_name->data(), target_.data(), target_.size())) { + return BAD_TARGET; + } + rrdatas_ = rrdatas; + valid_ = true; + rrtype_ = rrtype; + } else { + NOTREACHED(); + return UNKNOWN_TERMINAL_RRTYPE; + } + + return OK; +} + +// ReadDSSet reads a set of DS records from the chain. DS records which are +// omitted are calculated from the entry key of the next zone. +DNSSECChainVerifier::Error DNSSECChainVerifier::ReadDSSet( + std::vector<base::StringPiece>* rrdatas, + const base::StringPiece& next_name) { + uint8 num_ds; + if (!U8(&num_ds)) + return BAD_DATA; + scoped_array<uint8> digest_types(new uint8[num_ds]); + // lookahead[i] is true iff the i'th DS record was empty and needs to be + // computed by hashing the next entry key. + scoped_array<bool> lookahead(new bool[num_ds]); + rrdatas->resize(num_ds); + + for (unsigned i = 0; i < num_ds; i++) { + uint8 digest_type; + base::StringPiece digest; + if (!U8(&digest_type) || + !VariableLength16(&digest)) { + return BAD_DATA; + } + + digest_types[i] = digest_type; + if (digest.size() > 0) { + (*rrdatas)[i] = digest; + lookahead[i] = false; + } else { + lookahead[i] = true; + } + } + + base::StringPiece next_entry_key; + if (!ReadAheadEntryKey(&next_entry_key)) + return BAD_DATA; + if (next_entry_key.size() < 4) + return BAD_DATA; + uint16 keyid = DNSSECKeySet::DNSKEYToKeyID(next_entry_key); + uint8 algorithm = next_entry_key[3]; + + bool good = false; + for (unsigned i = 0; i < num_ds; i++) { + base::StringPiece digest; + bool have_digest = false; + if (DigestKey(&digest, next_name, next_entry_key, digest_types[i], + keyid, algorithm)) { + have_digest = true; + } + + if (lookahead[i]) { + // If we needed to fill in one of the DS entries, but we can't calculate + // that type of digest, then we can't continue. + if (!have_digest) + return UNKNOWN_DIGEST; + (*rrdatas)[i] = digest; + good = true; + } else { + const base::StringPiece& given_digest = (*rrdatas)[i]; + if (have_digest && + given_digest.size() == digest.size() && + memcmp(given_digest.data(), digest.data(), digest.size()) == 0) { + good = true; + } + } + } + + if (!good) { + // We didn't calculate or match any of the digests. + return NO_DS_LINK; + } + + return OK; +} + +DNSSECChainVerifier::Error DNSSECChainVerifier::ReadCERTs( + std::vector<base::StringPiece>* rrdatas) { + uint8 num_certs; + if (!U8(&num_certs)) + return BAD_DATA; + rrdatas->resize(num_certs); + + for (unsigned i = 0; i < num_certs; i++) { + base::StringPiece rrdata; + if (!VariableLength16(&rrdata)) + return BAD_DATA; + (*rrdatas)[i] = rrdata; + } + + return OK; +} + +// CountLabels returns the number of DNS labels in |a|, which must be in DNS, +// length-prefixed form. +static unsigned CountLabels(base::StringPiece a) { + for (unsigned c = 0;; c++) { + if (!a.size()) + return c; + uint8 label_len = a.data()[0]; + a.remove_prefix(1); + DCHECK_GE(a.size(), label_len); + a.remove_prefix(label_len); + } +} + +// RemoveLeadingLabel removes the first label from |a|, which must be in DNS, +// length-prefixed form. +static void RemoveLeadingLabel(base::StringPiece* a) { + if (!a->size()) + return; + uint8 label_len = a->data()[0]; + a->remove_prefix(1); + a->remove_prefix(label_len); +} + +// MatchingLabels returns the number of labels which |a| and |b| share, +// counting right-to-left from the root. |a| and |b| must be DNS, +// length-prefixed names. All names match at the root label, so this always +// returns a value >= 1. + +// static +unsigned DNSSECChainVerifier::MatchingLabels(base::StringPiece a, + base::StringPiece b) { + unsigned c = 0; + unsigned a_labels = CountLabels(a); + unsigned b_labels = CountLabels(b); + + while (a_labels > b_labels) { + RemoveLeadingLabel(&a); + a_labels--; + } + while (b_labels > a_labels) { + RemoveLeadingLabel(&b); + b_labels--; + } + + for (;;) { + if (!a.size()) { + if (!b.size()) + return c; + return 0; + } + if (!b.size()) + return 0; + uint8 a_length = a.data()[0]; + a.remove_prefix(1); + uint8 b_length = b.data()[0]; + b.remove_prefix(1); + DCHECK_GE(a.size(), a_length); + DCHECK_GE(b.size(), b_length); + + if (a_length == b_length && memcmp(a.data(), b.data(), a_length) == 0) { + c++; + } else { + c = 0; + } + + a.remove_prefix(a_length); + b.remove_prefix(b_length); + } +} + +} // namespace net diff --git a/net/base/dnssec_chain_verifier.h b/net/base/dnssec_chain_verifier.h new file mode 100644 index 0000000..2556564 --- /dev/null +++ b/net/base/dnssec_chain_verifier.h @@ -0,0 +1,107 @@ +// Copyright (c) 2010 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. + +#ifndef NET_BASE_DNSSEC_CHAIN_VERIFIER_H_ +#define NET_BASE_DNSSEC_CHAIN_VERIFIER_H_ + +#include <string> +#include <vector> + +#include "base/string_piece.h" +#include "net/base/dnssec_keyset.h" + +namespace net { + +// DNSSECChainVerifier verifies a chain of DNSSEC records. These records +// eventually prove the validity of a set of resource records for the target +// name. For example, if the fingerprint of a certificate was stored in a CERT +// record for a given domain, then a chain could prove the validity of that +// fingerprint. +class DNSSECChainVerifier { + public: + enum Error { + OK = 0, + BAD_DATA, // The chain was corrupt in some fashion. + UNKNOWN_ROOT_KEY, // The chain is assuming an unknown DNS root. + UNKNOWN_DIGEST, // An omitted DS record used an unknown hash function. + UNKNOWN_TERMINAL_RRTYPE, // The chain proved an unknown RRTYPE. + BAD_SIGNATURE, // One of the signature was incorrect. + NO_DS_LINK, // a DS set didn't include the next entry key. + OFF_COURSE, // the chain is diverging from the target name. + BAD_TARGET, // the chain didn't end up at the target. + }; + + // |target|: the target hostname. This must be in canonical (all + // lower-case), length-prefixed DNS form. For example: + // "\003www\007example\003com\000" + // |chain|: the contents of the chain. + DNSSECChainVerifier(const std::string& target, + const base::StringPiece& chain); + ~DNSSECChainVerifier(); + + // If called, timestamps in the signatures will be ignored. This is for + // testing only. + void IgnoreTimestamps(); + + // Verify verifies the chain. Returns |OK| on success. + Error Verify(); + + // rrtype returns the RRTYPE of the proven resource records. Only call this + // after Verify has returned OK. + uint16 rrtype() const; + // rrdatas returns the contents of the proven resource records. Only call + // this after Verify has returned OK. + const std::vector<base::StringPiece>& rrdatas() const; + + // Exposed for testing only. + static unsigned MatchingLabels(base::StringPiece a, + base::StringPiece b); + + private: + struct Zone { + base::StringPiece name; + // The number of consecutive labels which |name| shares with |target_|, + // counting right-to-left from the root. + unsigned matching_labels; + DNSSECKeySet trusted_keys; + Zone* prev; + }; + + bool U8(uint8*); + bool U16(uint16*); + bool VariableLength16(base::StringPiece*); + bool ReadName(base::StringPiece*); + + bool ReadAheadEntryKey(base::StringPiece*); + bool ReadAheadKey(base::StringPiece*, uint8 entry_key); + bool ReadDNSKEYs(std::vector<base::StringPiece>*, bool is_root); + bool DigestKey(base::StringPiece* digest, + const base::StringPiece& name, + const base::StringPiece& dnskey, + uint8 digest_type, + uint16 keyid, + uint8 algorithm); + + Error EnterRoot(); + Error EnterZone(const base::StringPiece& zone); + Error LeaveZone(base::StringPiece* next_name); + Error ReadDSSet(std::vector<base::StringPiece>*, + const base::StringPiece& next_name); + Error ReadCERTs(std::vector<base::StringPiece>*); + + + Zone* current_zone_; + const std::string target_; + base::StringPiece chain_; + bool ignore_timestamps_; + bool valid_; + uint16 rrtype_; + std::vector<base::StringPiece> rrdatas_; + // A list of pointers which need to be free()ed on destruction. + std::vector<void*> scratch_pool_; +}; + +} // namespace net + +#endif // NET_BASE_DNSSEC_CHAIN_VERIFIER_H_ diff --git a/net/base/dnssec_keyset.cc b/net/base/dnssec_keyset.cc new file mode 100644 index 0000000..0d89ae1 --- /dev/null +++ b/net/base/dnssec_keyset.cc @@ -0,0 +1,454 @@ +// Copyright (c) 2010 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. + +#include "net/base/dnssec_keyset.h" + +#include <cryptohi.h> +#include <cryptoht.h> +#include <keyhi.h> + +#include "base/crypto/signature_verifier.h" +#include "base/logging.h" +#include "base/nss_util.h" +#include "base/scoped_ptr.h" +#include "base/time.h" +#include "net/base/dns_util.h" + +namespace net { + +DNSSECKeySet::DNSSECKeySet() + : ignore_timestamps_(false) { +} + +bool DNSSECKeySet::AddKey(const base::StringPiece& dnskey) { + uint16 keyid = DNSKEYToKeyID(dnskey); + std::string der_encoded = ASN1WrapDNSKEY(dnskey); + if (der_encoded.empty()) + return false; + + keyids_.push_back(keyid); + public_keys_.push_back(der_encoded); + return true; +} + +// static +uint16 DNSSECKeySet::DNSKEYToKeyID(const base::StringPiece& dnskey) { + const unsigned char* data = + reinterpret_cast<const unsigned char*>(dnskey.data()); + + // RFC 4043: App B + uint32 ac = 0; + for (unsigned i = 0; i < dnskey.size(); i++) { + if (i & 1) { + ac += data[i]; + } else { + ac += static_cast<uint32>(data[i]) << 8; + } + } + ac += (ac >> 16) & 0xffff; + return ac; +} + +// These are encoded AlgorithmIdentifiers for the given signature algorithm. +static const unsigned char kRSAWithSHA1[] = { + 0x30, 0xd, 0x6, 0x9, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0xd, 0x1, 0x1, 0x5, 5, 0 +}; + +static const unsigned char kRSAWithSHA256[] = { + 0x30, 0xd, 0x6, 0x9, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0xd, 0x1, 0x1, 0xb, 5, 0 +}; + +bool DNSSECKeySet::CheckSignature( + const base::StringPiece& name, + const base::StringPiece& zone, + const base::StringPiece& signature, + uint16 rrtype, + const std::vector<base::StringPiece>& rrdatas) { + // signature has this format: + // algorithm uint8 + // labels uint8 + // ttl uint32 + // expires uint32 + // begins uint32 + // keyid uint16 + // + // followed by the actual signature. + if (signature.size() < 16) + return false; + const unsigned char* sigdata = + reinterpret_cast<const unsigned char*>(signature.data()); + + uint8 algorithm = sigdata[0]; + uint32 expires = static_cast<uint32>(sigdata[6]) << 24 | + static_cast<uint32>(sigdata[7]) << 16 | + static_cast<uint32>(sigdata[8]) << 8 | + static_cast<uint32>(sigdata[9]); + uint32 begins = static_cast<uint32>(sigdata[10]) << 24 | + static_cast<uint32>(sigdata[11]) << 16 | + static_cast<uint32>(sigdata[12]) << 8 | + static_cast<uint32>(sigdata[13]); + uint16 keyid = static_cast<uint16>(sigdata[14]) << 8 | + static_cast<uint16>(sigdata[15]); + + if (!ignore_timestamps_) { + uint32 now = static_cast<uint32>(base::Time::Now().ToTimeT()); + if (now < begins || now >= expires) + return false; + } + + base::StringPiece sig(signature.data() + 16, signature.size() - 16); + + // You should have RFC 4034, 3.1.8.1 open when reading this code. + unsigned signed_data_len = 0; + signed_data_len += 2; // rrtype + signed_data_len += 16; // (see signature format, above) + signed_data_len += zone.size(); + + for (std::vector<base::StringPiece>::const_iterator + i = rrdatas.begin(); i != rrdatas.end(); i++) { + signed_data_len += name.size(); + signed_data_len += 2; // rrtype + signed_data_len += 2; // class + signed_data_len += 4; // ttl + signed_data_len += 2; // RRDATA length + signed_data_len += i->size(); + } + + scoped_array<unsigned char> signed_data(new unsigned char[signed_data_len]); + unsigned j = 0; + + signed_data[j++] = static_cast<uint8>(rrtype >> 8); + signed_data[j++] = static_cast<uint8>(rrtype); + memcpy(&signed_data[j], sigdata, 16); + j += 16; + memcpy(&signed_data[j], zone.data(), zone.size()); + j += zone.size(); + + for (std::vector<base::StringPiece>::const_iterator + i = rrdatas.begin(); i != rrdatas.end(); i++) { + memcpy(&signed_data[j], name.data(), name.size()); + j += name.size(); + signed_data[j++] = static_cast<uint8>(rrtype >> 8); + signed_data[j++] = static_cast<uint8>(rrtype); + signed_data[j++] = 0; // CLASS (always IN = 1) + signed_data[j++] = 1; + // Copy the TTL from |signature|. + memcpy(&signed_data[j], signature.data() + 2, sizeof(uint32)); + j += sizeof(uint32); + unsigned rrdata_len = i->size(); + signed_data[j++] = rrdata_len >> 8; + signed_data[j++] = rrdata_len; + memcpy(&signed_data[j], i->data(), i->size()); + j += i->size(); + } + + DCHECK_EQ(j, signed_data_len); + + base::StringPiece signature_algorithm; + if (algorithm == kDNSSEC_RSA_SHA1 || + algorithm == kDNSSEC_RSA_SHA1_NSEC3) { + signature_algorithm = base::StringPiece( + reinterpret_cast<const char*>(kRSAWithSHA1), + sizeof(kRSAWithSHA1)); + } else if (algorithm == kDNSSEC_RSA_SHA256) { + signature_algorithm = base::StringPiece( + reinterpret_cast<const char*>(kRSAWithSHA256), + sizeof(kRSAWithSHA256)); + } else { + // Unknown algorithm. + return false; + } + + // Check the signature with each trusted key which has a matching keyid. + DCHECK_EQ(public_keys_.size(), keyids_.size()); + for (unsigned i = 0; i < public_keys_.size(); i++) { + if (keyids_[i] != keyid) + continue; + + if (VerifySignature( + signature_algorithm, sig, public_keys_[i], + base::StringPiece(reinterpret_cast<const char*>(signed_data.get()), + signed_data_len))) { + return true; + } + } + + return false; +} + +void DNSSECKeySet::IgnoreTimestamps() { + ignore_timestamps_ = true; +} + +// This is an ASN.1 encoded AlgorithmIdentifier for RSA +static const unsigned char kASN1AlgorithmIdentifierRSA[] = { + 0x30, // SEQUENCE + 0x0d, // length (11 bytes) + 0x06, // OBJECT IDENTIFER + 0x09, // length (9 bytes) + // OID 1.2.840.113549.1.1.1 (RSA) + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, + // NULL of length 0 + 0x05, 0x00, +}; + +// EncodeASN1Length assumes that |*length| contains the number of DER-encoded, +// length-prefixed ASN.1 bytes to follow and serialises the length to |out[*j]| +// and updates |j| and |length| accordingly. +static void EncodeASN1Length(unsigned char* out, unsigned* j, + unsigned* length) { + if ((*length - 1) < 128) { + (*length) -= 1; + out[(*j)++] = *length; + } else if ((*length - 2) < 256) { + (*length) -= 2; + out[(*j)++] = 0x80 | 1; + out[(*j)++] = *length; + } else { + (*length) -= 3; + out[(*j)++] = 0x80 | 2; + out[(*j)++] = *length >> 8; + out[(*j)++] = *length; + } +} + +// AdvanceForASN1Length returns the number of bytes required to encode a ASN1 +// DER length value of |remaining|. +static unsigned AdvanceForASN1Length(unsigned remaining) { + if (remaining < 128) { + return 1; + } else if (remaining < 256) { + return 2; + } else if (remaining < 65536) { + return 3; + } else { + NOTREACHED(); + return 3; + } +} + +// ASN1WrapDNSKEY converts the DNSKEY RDATA in |dnskey| into the ASN.1 wrapped +// format expected by NSS. To wit: +// SubjectPublicKeyInfo ::= SEQUENCE { +// algorithm AlgorithmIdentifier, +// subjectPublicKey BIT STRING } +std::string DNSSECKeySet::ASN1WrapDNSKEY(const base::StringPiece& dnskey) { + const unsigned char* data = + reinterpret_cast<const unsigned char*>(dnskey.data()); + + if (dnskey.size() < 5 || dnskey.size() > 32767) + return ""; + const uint8 algorithm = data[3]; + if (algorithm != kDNSSEC_RSA_SHA1 && + algorithm != kDNSSEC_RSA_SHA1_NSEC3 && + algorithm != kDNSSEC_RSA_SHA256) { + return ""; + } + + unsigned exp_length; + unsigned exp_offset; + // First we extract the public exponent. + if (data[4] == 0) { + if (dnskey.size() < 7) + return ""; + exp_length = static_cast<unsigned>(data[5]) << 8 | + static_cast<unsigned>(data[6]); + exp_offset = 7; + } else { + exp_length = static_cast<unsigned>(data[4]); + exp_offset = 5; + } + + // We refuse to deal with large public exponents. + if (exp_length > 3) + return ""; + if (dnskey.size() < exp_offset + exp_length) + return ""; + + unsigned exp = 0; + for (unsigned i = 0; i < exp_length; i++) { + exp <<= 8; + exp |= static_cast<unsigned>(data[exp_offset + i]); + } + + unsigned n_offset = exp_offset + exp_length; + unsigned n_length = dnskey.size() - n_offset; + + // Anything smaller than 512 bits is too weak to be trusted. + if (n_length < 64) + return ""; + + // If the MSB of exp is true then we need to prefix a zero byte to stop the + // ASN.1 encoding from being negative. + if (exp & (1 << ((8 * exp_length) - 1))) + exp_length++; + + // Likewise with the modulus + unsigned n_padding = data[n_offset] & 0x80 ? 1 : 0; + + // We now calculate the length of the full ASN.1 encoded public key. We're + // working backwards from the end of the structure. Keep in mind that it's: + // SEQUENCE + // AlgorithmIdentifier + // BITSTRING + // SEQUENCE + // INTEGER + // INTEGER + unsigned length = 0; + length += exp_length; // exponent data + length++; // we know that |exp_length| < 128 + length++; // INTEGER tag for exponent + length += n_length + n_padding; + length += AdvanceForASN1Length(n_length + n_padding); + length++; // INTEGER tag for modulus + length += AdvanceForASN1Length(length); // SEQUENCE length + length++; // SEQUENCE tag + length++; // BITSTRING unused bits + length += AdvanceForASN1Length(length); // BITSTRING length + length++; // BITSTRING tag + length += sizeof(kASN1AlgorithmIdentifierRSA); + length += AdvanceForASN1Length(length); // SEQUENCE length + length++; // SEQUENCE tag + + scoped_array<unsigned char> out(new unsigned char[length]); + + // Now we walk forwards and serialise the ASN.1, undoing the steps above. + unsigned j = 0; + out[j++] = 0x30; // SEQUENCE + length--; + EncodeASN1Length(out.get(), &j, &length); + memcpy(&out[j], kASN1AlgorithmIdentifierRSA, + sizeof(kASN1AlgorithmIdentifierRSA)); + j += sizeof(kASN1AlgorithmIdentifierRSA); + length -= sizeof(kASN1AlgorithmIdentifierRSA); + out[j++] = 3; // BITSTRING tag + length--; + EncodeASN1Length(out.get(), &j, &length); + out[j++] = 0; // BITSTRING unused bits + length--; + out[j++] = 0x30; // SEQUENCE + length--; + EncodeASN1Length(out.get(), &j, &length); + out[j++] = 2; // INTEGER + length--; + unsigned l = n_length + n_padding; + if (l < 128) { + out[j++] = l; + length--; + } else if (l < 256) { + out[j++] = 0x80 | 1; + out[j++] = l; + length -= 2; + } else if (l < 65536) { + out[j++] = 0x80 | 2; + out[j++] = l >> 8; + out[j++] = l; + length -= 3; + } else { + NOTREACHED(); + } + + if (n_padding) { + out[j++] = 0; + length--; + } + memcpy(&out[j], &data[n_offset], n_length); + j += n_length; + length -= n_length; + out[j++] = 2; // INTEGER + length--; + out[j++] = exp_length; + length--; + for (unsigned i = exp_length - 1; i < exp_length; i--) { + out[j++] = exp >> (8 * i); + length--; + } + + DCHECK_EQ(0u, length); + + return std::string(reinterpret_cast<char*>(out.get()), j); +} + +bool DNSSECKeySet::VerifySignature( + base::StringPiece signature_algorithm, + base::StringPiece signature, + base::StringPiece public_key, + base::StringPiece signed_data) { + // This code is largely a copy-and-paste from + // base/crypto/signature_verifier_nss.cc. We can't change + // base::SignatureVerifier to always use NSS because we want the ability to + // be FIPS 140-2 compliant. However, we can't use base::SignatureVerifier + // here because some platforms don't support SHA256 signatures. Therefore, we + // use NSS directly. + + base::EnsureNSSInit(); + + CERTSubjectPublicKeyInfo* spki = NULL; + SECItem spki_der; + spki_der.type = siBuffer; + spki_der.data = (uint8*) public_key.data(); + spki_der.len = public_key.size(); + spki = SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_der); + if (!spki) + return false; + SECKEYPublicKey* pub_key = SECKEY_ExtractPublicKey(spki); + SECKEY_DestroySubjectPublicKeyInfo(spki); // Done with spki. + if (!pub_key) + return false; + + PLArenaPool* arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (!arena) { + SECKEY_DestroyPublicKey(pub_key); + return false; + } + + SECItem sig_alg_der; + sig_alg_der.type = siBuffer; + sig_alg_der.data = (uint8*) signature_algorithm.data(); + sig_alg_der.len = signature_algorithm.size(); + SECAlgorithmID sig_alg_id; + SECStatus rv; + rv = SEC_QuickDERDecodeItem(arena, &sig_alg_id, SECOID_AlgorithmIDTemplate, + &sig_alg_der); + if (rv != SECSuccess) { + SECKEY_DestroyPublicKey(pub_key); + PORT_FreeArena(arena, PR_TRUE); + return false; + } + + SECItem sig; + sig.type = siBuffer; + sig.data = (uint8*) signature.data(); + sig.len = signature.size(); + SECOidTag hash_alg_tag; + VFYContext* vfy_context = + VFY_CreateContextWithAlgorithmID(pub_key, &sig, + &sig_alg_id, &hash_alg_tag, + NULL); + SECKEY_DestroyPublicKey(pub_key); + PORT_FreeArena(arena, PR_TRUE); // Done with sig_alg_id. + if (!vfy_context) { + // A corrupted RSA signature could be detected without the data, so + // VFY_CreateContextWithAlgorithmID may fail with SEC_ERROR_BAD_SIGNATURE + // (-8182). + return false; + } + + rv = VFY_Begin(vfy_context); + if (rv != SECSuccess) { + NOTREACHED(); + return false; + } + rv = VFY_Update(vfy_context, (uint8*) signed_data.data(), signed_data.size()); + if (rv != SECSuccess) { + NOTREACHED(); + return false; + } + rv = VFY_End(vfy_context); + VFY_DestroyContext(vfy_context, PR_TRUE); + + return rv == SECSuccess; +} + +} // namespace net diff --git a/net/base/dnssec_keyset.h b/net/base/dnssec_keyset.h new file mode 100644 index 0000000..7a44916 --- /dev/null +++ b/net/base/dnssec_keyset.h @@ -0,0 +1,61 @@ +// Copyright (c) 2010 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. + +#ifndef NET_BASE_DNSSEC_KEYSET_H_ +#define NET_BASE_DNSSEC_KEYSET_H_ + +#include <string> +#include <vector> + +#include "base/string_piece.h" + +namespace net { + +// DNSSECKeySet function wraps base/crypto/signature_verifier.h to accept +// DNSSEC encodings. (See RFC 4043) +class DNSSECKeySet { + public: + DNSSECKeySet(); + + // AddKey adds a key to the trusted set. + // dnskey: the RRDATA of a DNSKEY. + bool AddKey(const base::StringPiece& dnskey); + + // CheckSignature checks the DNSSEC signature on set of resource records. + // name: the domain that the records are from + // zone: the signing zone + // signature: the RRSIG signature, not include the signing zone. + // rrtype: the type of the resource records + // rrdatas: the RRDATA of the signed resource records, in canonical order. + bool CheckSignature(const base::StringPiece& name, + const base::StringPiece& zone, + const base::StringPiece& signature, + uint16 rrtype, + const std::vector<base::StringPiece>& rrdatas); + + // DNSKEYToKeyID converts the RRDATA of a DNSKEY to its key id. See RFC 4043, + // app B. + static uint16 DNSKEYToKeyID(const base::StringPiece& dnskey); + + // Used for testing: the timestamps on signatures will be ignored to allow + // golden data to remain valid. + void IgnoreTimestamps(); + + private: + bool VerifySignature( + base::StringPiece signature_algorithm, + base::StringPiece signature, + base::StringPiece public_key, + base::StringPiece signed_data); + + std::string ASN1WrapDNSKEY(const base::StringPiece& dnskey); + + bool ignore_timestamps_; + std::vector<uint16> keyids_; + std::vector<std::string> public_keys_; +}; + +} // namespace net + +#endif // NET_BASE_DNSSEC_KEYSET_H_ diff --git a/net/base/dnssec_unittest.cc b/net/base/dnssec_unittest.cc new file mode 100644 index 0000000..2a060d0 --- /dev/null +++ b/net/base/dnssec_unittest.cc @@ -0,0 +1,403 @@ +// Copyright (c) 2010 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. + +#include "base/logging.h" +#include "net/base/dnssec_chain_verifier.h" +#include "net/base/dnssec_keyset.h" +#include "net/base/dns_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +class SignatureVerifierDNSSECTest : public testing::Test { +}; + +class DNSSECChainVerifierTest : public testing::Test { +}; + +} // anonymous namespace + +// This is a DNSKEY record. You can get one with `dig dnskey .` (where '.' can +// be any signed zone). +static const unsigned char kExampleKey[] = { + 0x01, 0x01, 0x03, 0x08, 0x03, 0x01, 0x00, 0x01, 0xa8, 0x00, 0x20, 0xa9, 0x55, + 0x66, 0xba, 0x42, 0xe8, 0x86, 0xbb, 0x80, 0x4c, 0xda, 0x84, 0xe4, 0x7e, 0xf5, + 0x6d, 0xbd, 0x7a, 0xec, 0x61, 0x26, 0x15, 0x55, 0x2c, 0xec, 0x90, 0x6d, 0x21, + 0x16, 0xd0, 0xef, 0x20, 0x70, 0x28, 0xc5, 0x15, 0x54, 0x14, 0x4d, 0xfe, 0xaf, + 0xe7, 0xc7, 0xcb, 0x8f, 0x00, 0x5d, 0xd1, 0x82, 0x34, 0x13, 0x3a, 0xc0, 0x71, + 0x0a, 0x81, 0x18, 0x2c, 0xe1, 0xfd, 0x14, 0xad, 0x22, 0x83, 0xbc, 0x83, 0x43, + 0x5f, 0x9d, 0xf2, 0xf6, 0x31, 0x32, 0x51, 0x93, 0x1a, 0x17, 0x6d, 0xf0, 0xda, + 0x51, 0xe5, 0x4f, 0x42, 0xe6, 0x04, 0x86, 0x0d, 0xfb, 0x35, 0x95, 0x80, 0x25, + 0x0f, 0x55, 0x9c, 0xc5, 0x43, 0xc4, 0xff, 0xd5, 0x1c, 0xbe, 0x3d, 0xe8, 0xcf, + 0xd0, 0x67, 0x19, 0x23, 0x7f, 0x9f, 0xc4, 0x7e, 0xe7, 0x29, 0xda, 0x06, 0x83, + 0x5f, 0xa4, 0x52, 0xe8, 0x25, 0xe9, 0xa1, 0x8e, 0xbc, 0x2e, 0xcb, 0xcf, 0x56, + 0x34, 0x74, 0x65, 0x2c, 0x33, 0xcf, 0x56, 0xa9, 0x03, 0x3b, 0xcd, 0xf5, 0xd9, + 0x73, 0x12, 0x17, 0x97, 0xec, 0x80, 0x89, 0x04, 0x1b, 0x6e, 0x03, 0xa1, 0xb7, + 0x2d, 0x0a, 0x73, 0x5b, 0x98, 0x4e, 0x03, 0x68, 0x73, 0x09, 0x33, 0x23, 0x24, + 0xf2, 0x7c, 0x2d, 0xba, 0x85, 0xe9, 0xdb, 0x15, 0xe8, 0x3a, 0x01, 0x43, 0x38, + 0x2e, 0x97, 0x4b, 0x06, 0x21, 0xc1, 0x8e, 0x62, 0x5e, 0xce, 0xc9, 0x07, 0x57, + 0x7d, 0x9e, 0x7b, 0xad, 0xe9, 0x52, 0x41, 0xa8, 0x1e, 0xbb, 0xe8, 0xa9, 0x01, + 0xd4, 0xd3, 0x27, 0x6e, 0x40, 0xb1, 0x14, 0xc0, 0xa2, 0xe6, 0xfc, 0x38, 0xd1, + 0x9c, 0x2e, 0x6a, 0xab, 0x02, 0x64, 0x4b, 0x28, 0x13, 0xf5, 0x75, 0xfc, 0x21, + 0x60, 0x1e, 0x0d, 0xee, 0x49, 0xcd, 0x9e, 0xe9, 0x6a, 0x43, 0x10, 0x3e, 0x52, + 0x4d, 0x62, 0x87, 0x3d, +}; + +TEST(SignatureVerifierDNSSECTest, KeyID) { + uint16 keyid = net::DNSSECKeySet::DNSKEYToKeyID( + base::StringPiece(reinterpret_cast<const char*>(kExampleKey), + sizeof(kExampleKey))); + ASSERT_EQ(19036u, keyid); +} + +TEST(SignatureVerifierDNSSECTest, ImportKey) { + net::DNSSECKeySet keyset; + + ASSERT_TRUE(keyset.AddKey( + base::StringPiece(reinterpret_cast<const char*>(kExampleKey), + sizeof(kExampleKey)))); +} + +// This is root's DNSSKEY signature +const unsigned char kSignatureData[] = { + 0x08, 0x00, 0x00, 0x01, 0x51, 0x80, 0x4c, 0x59, 0xfe, 0xff, 0x4c, 0x46, 0x38, + 0x80, 0x4a, 0x5c, 0x2e, 0x63, 0xb7, 0x71, 0xf6, 0xb0, 0x32, 0xb7, 0x71, 0xb5, + 0xaa, 0xca, 0xd4, 0x4c, 0xeb, 0xf1, 0x7f, 0xa3, 0x28, 0x00, 0x43, 0x85, 0x76, + 0x9f, 0x46, 0x3d, 0x85, 0x39, 0x8d, 0x66, 0x7a, 0xeb, 0x4c, 0x6e, 0x60, 0x0a, + 0xe6, 0x49, 0x20, 0x47, 0xf9, 0x13, 0x81, 0x3e, 0xc8, 0xf5, 0x09, 0x92, 0x75, + 0xab, 0x87, 0xc6, 0x70, 0x35, 0xdd, 0xd9, 0x9e, 0xb6, 0xd4, 0x09, 0x66, 0xcc, + 0x0d, 0x71, 0x71, 0x3d, 0x4c, 0x96, 0xbc, 0x7d, 0x2c, 0x05, 0x0a, 0x9c, 0xc3, + 0xcd, 0x5d, 0xfa, 0xab, 0xb5, 0x17, 0x55, 0xca, 0x86, 0x31, 0x4b, 0x7b, 0x93, + 0x4a, 0x4b, 0x91, 0xd9, 0xea, 0x71, 0xf8, 0x3f, 0xb3, 0x4f, 0xb4, 0x94, 0xcd, + 0x6f, 0xe4, 0x83, 0xf6, 0xd4, 0xcb, 0xb8, 0x3c, 0x7d, 0xf6, 0x73, 0xf7, 0xf2, + 0x6f, 0xa5, 0x92, 0x25, 0xdc, 0xe5, 0xdd, 0x83, 0x55, 0xef, 0xde, 0x20, 0x00, + 0x64, 0x9e, 0x25, 0x76, 0x70, 0x08, 0x14, 0x29, 0xec, 0x66, 0xa3, 0xfd, 0x48, + 0x3b, 0x67, 0x21, 0x6e, 0x3d, 0x1e, 0x26, 0xb4, 0x74, 0x07, 0x1f, 0x1f, 0x4d, + 0xdf, 0x74, 0xae, 0x04, 0x70, 0xf0, 0x08, 0x3f, 0xe2, 0x6a, 0x39, 0x51, 0x79, + 0x25, 0xd9, 0xc2, 0xf9, 0xa4, 0xb6, 0x38, 0x4a, 0x5f, 0x80, 0x12, 0x4d, 0x98, + 0x7a, 0x3b, 0x8d, 0xb8, 0x3d, 0x51, 0x6b, 0x7c, 0x27, 0xc9, 0xc0, 0xcc, 0x26, + 0x73, 0xef, 0x43, 0x8a, 0x6c, 0x42, 0xa5, 0x2d, 0x11, 0x77, 0x9f, 0xe4, 0xa4, + 0x50, 0xb3, 0x29, 0xe4, 0x5c, 0x04, 0xc7, 0x38, 0xbb, 0xfa, 0x27, 0xfa, 0x02, + 0x76, 0x07, 0x5b, 0x88, 0x39, 0xd8, 0x60, 0x81, 0x9f, 0x36, 0xfc, 0x9c, 0x17, + 0x83, 0x0a, 0x54, 0x59, 0x86, 0x6b, 0xd6, 0x54, 0x5c, 0x9a, 0xba, 0x10, 0xe6, + 0x2e, 0x12, 0x78, 0x85, 0x1c, 0xed, 0x26, 0x79, 0xd4, 0xfc, 0x83, 0x51, +}; + +// The is root's 1024-bit key. +static const unsigned char kRRDATA1[] = { + 1, 0, 3, 8, 3, 1, 0, 1, 189, 96, 112, 56, 65, 148, 127, 253, 50, 88, 20, 197, + 45, 34, 147, 103, 112, 99, 242, 98, 4, 138, 85, 248, 72, 74, 101, 94, 203, + 113, 204, 77, 115, 164, 37, 143, 142, 187, 66, 49, 208, 220, 88, 38, 218, 45, + 139, 19, 220, 58, 46, 163, 197, 13, 41, 224, 230, 165, 106, 212, 230, 5, 201, + 48, 109, 220, 52, 41, 166, 160, 231, 0, 250, 226, 79, 5, 185, 168, 132, 13, + 12, 209, 111, 223, 140, 168, 235, 123, 0, 116, 23, 148, 224, 111, 109, 191, + 183, 115, 79, 155, 15, 200, 8, 38, 86, 30, 71, 12, 39, 190, 233, 115, 54, + 248, 135, 165, 215, 233, 20, 40, 39, 165, 240, 135, 50, 215, 216, 81, +}; + +// The is root's 2048-bit key. +static const unsigned char kRRDATA2[] = { + 1, 1, 3, 8, 3, 1, 0, 1, 168, 0, 32, 169, 85, 102, 186, 66, 232, 134, 187, + 128, 76, 218, 132, 228, 126, 245, 109, 189, 122, 236, 97, 38, 21, 85, 44, + 236, 144, 109, 33, 22, 208, 239, 32, 112, 40, 197, 21, 84, 20, 77, 254, 175, + 231, 199, 203, 143, 0, 93, 209, 130, 52, 19, 58, 192, 113, 10, 129, 24, 44, + 225, 253, 20, 173, 34, 131, 188, 131, 67, 95, 157, 242, 246, 49, 50, 81, 147, + 26, 23, 109, 240, 218, 81, 229, 79, 66, 230, 4, 134, 13, 251, 53, 149, 128, + 37, 15, 85, 156, 197, 67, 196, 255, 213, 28, 190, 61, 232, 207, 208, 103, 25, + 35, 127, 159, 196, 126, 231, 41, 218, 6, 131, 95, 164, 82, 232, 37, 233, 161, + 142, 188, 46, 203, 207, 86, 52, 116, 101, 44, 51, 207, 86, 169, 3, 59, 205, + 245, 217, 115, 18, 23, 151, 236, 128, 137, 4, 27, 110, 3, 161, 183, 45, 10, + 115, 91, 152, 78, 3, 104, 115, 9, 51, 35, 36, 242, 124, 45, 186, 133, 233, + 219, 21, 232, 58, 1, 67, 56, 46, 151, 75, 6, 33, 193, 142, 98, 94, 206, 201, + 7, 87, 125, 158, 123, 173, 233, 82, 65, 168, 30, 187, 232, 169, 1, 212, 211, + 39, 110, 64, 177, 20, 192, 162, 230, 252, 56, 209, 156, 46, 106, 171, 2, 100, + 75, 40, 19, 245, 117, 252, 33, 96, 30, 13, 238, 73, 205, 158, 233, 106, 67, + 16, 62, 82, 77, 98, 135, 61, +}; + +TEST(SignatureVerifierDNSSECTest, VerifySignature) { + net::DNSSECKeySet keyset; + + ASSERT_TRUE(keyset.AddKey( + base::StringPiece(reinterpret_cast<const char*>(kExampleKey), + sizeof(kExampleKey)))); + keyset.IgnoreTimestamps(); + + static const uint16 kDNSKEY = 48; // RRTYPE for DNSKEY + static const char kRootLabel[] = ""; + base::StringPiece root(kRootLabel, 1); + base::StringPiece signature(reinterpret_cast<const char*>(kSignatureData), + sizeof(kSignatureData)); + std::vector<base::StringPiece> rrdatas; + rrdatas.push_back(base::StringPiece(reinterpret_cast<const char*>(kRRDATA1), + sizeof(kRRDATA1))); + rrdatas.push_back(base::StringPiece(reinterpret_cast<const char*>(kRRDATA2), + sizeof(kRRDATA2))); + ASSERT_TRUE(keyset.CheckSignature(root, root, signature, kDNSKEY, rrdatas)); +} + +static std::string FromDNSName(const char* name) { + std::string result; + bool ok = net::DNSDomainFromDot(name, &result); + EXPECT_TRUE(ok); + if (!ok) + result = ""; + return result; +} + +TEST(DNSSECChainVerifierTest, MatchingLabels) { + ASSERT_EQ(1u, net::DNSSECChainVerifier::MatchingLabels( + FromDNSName(""), FromDNSName(""))); + ASSERT_EQ(2u, net::DNSSECChainVerifier::MatchingLabels( + FromDNSName("org"), FromDNSName("org"))); + ASSERT_EQ(3u, net::DNSSECChainVerifier::MatchingLabels( + FromDNSName("foo.org"), FromDNSName("foo.org"))); + ASSERT_EQ(3u, net::DNSSECChainVerifier::MatchingLabels( + FromDNSName("bar.foo.org"), FromDNSName("foo.org"))); + ASSERT_EQ(3u, net::DNSSECChainVerifier::MatchingLabels( + FromDNSName("foo.org"), FromDNSName("bar.foo.org"))); + ASSERT_EQ(1u, net::DNSSECChainVerifier::MatchingLabels( + FromDNSName("foo.org"), FromDNSName("foo.com"))); + ASSERT_EQ(2u, net::DNSSECChainVerifier::MatchingLabels( + FromDNSName("foo.org"), FromDNSName("bar.org"))); +} + +static const unsigned char kChain[] = { + 0x4a, 0x5c, 0x01, 0x01, 0x10, 0x08, 0x00, 0x00, 0x01, 0x51, 0x80, 0x4c, 0x59, + 0xfe, 0xff, 0x4c, 0x46, 0x38, 0x80, 0x4a, 0x5c, 0x2e, 0x63, 0xb7, 0x71, 0xf6, + 0xb0, 0x32, 0xb7, 0x71, 0xb5, 0xaa, 0xca, 0xd4, 0x4c, 0xeb, 0xf1, 0x7f, 0xa3, + 0x28, 0x00, 0x43, 0x85, 0x76, 0x9f, 0x46, 0x3d, 0x85, 0x39, 0x8d, 0x66, 0x7a, + 0xeb, 0x4c, 0x6e, 0x60, 0x0a, 0xe6, 0x49, 0x20, 0x47, 0xf9, 0x13, 0x81, 0x3e, + 0xc8, 0xf5, 0x09, 0x92, 0x75, 0xab, 0x87, 0xc6, 0x70, 0x35, 0xdd, 0xd9, 0x9e, + 0xb6, 0xd4, 0x09, 0x66, 0xcc, 0x0d, 0x71, 0x71, 0x3d, 0x4c, 0x96, 0xbc, 0x7d, + 0x2c, 0x05, 0x0a, 0x9c, 0xc3, 0xcd, 0x5d, 0xfa, 0xab, 0xb5, 0x17, 0x55, 0xca, + 0x86, 0x31, 0x4b, 0x7b, 0x93, 0x4a, 0x4b, 0x91, 0xd9, 0xea, 0x71, 0xf8, 0x3f, + 0xb3, 0x4f, 0xb4, 0x94, 0xcd, 0x6f, 0xe4, 0x83, 0xf6, 0xd4, 0xcb, 0xb8, 0x3c, + 0x7d, 0xf6, 0x73, 0xf7, 0xf2, 0x6f, 0xa5, 0x92, 0x25, 0xdc, 0xe5, 0xdd, 0x83, + 0x55, 0xef, 0xde, 0x20, 0x00, 0x64, 0x9e, 0x25, 0x76, 0x70, 0x08, 0x14, 0x29, + 0xec, 0x66, 0xa3, 0xfd, 0x48, 0x3b, 0x67, 0x21, 0x6e, 0x3d, 0x1e, 0x26, 0xb4, + 0x74, 0x07, 0x1f, 0x1f, 0x4d, 0xdf, 0x74, 0xae, 0x04, 0x70, 0xf0, 0x08, 0x3f, + 0xe2, 0x6a, 0x39, 0x51, 0x79, 0x25, 0xd9, 0xc2, 0xf9, 0xa4, 0xb6, 0x38, 0x4a, + 0x5f, 0x80, 0x12, 0x4d, 0x98, 0x7a, 0x3b, 0x8d, 0xb8, 0x3d, 0x51, 0x6b, 0x7c, + 0x27, 0xc9, 0xc0, 0xcc, 0x26, 0x73, 0xef, 0x43, 0x8a, 0x6c, 0x42, 0xa5, 0x2d, + 0x11, 0x77, 0x9f, 0xe4, 0xa4, 0x50, 0xb3, 0x29, 0xe4, 0x5c, 0x04, 0xc7, 0x38, + 0xbb, 0xfa, 0x27, 0xfa, 0x02, 0x76, 0x07, 0x5b, 0x88, 0x39, 0xd8, 0x60, 0x81, + 0x9f, 0x36, 0xfc, 0x9c, 0x17, 0x83, 0x0a, 0x54, 0x59, 0x86, 0x6b, 0xd6, 0x54, + 0x5c, 0x9a, 0xba, 0x10, 0xe6, 0x2e, 0x12, 0x78, 0x85, 0x1c, 0xed, 0x26, 0x79, + 0xd4, 0xfc, 0x83, 0x51, 0x02, 0x00, 0x88, 0x01, 0x00, 0x03, 0x08, 0x03, 0x01, + 0x00, 0x01, 0xbd, 0x60, 0x70, 0x38, 0x41, 0x94, 0x7f, 0xfd, 0x32, 0x58, 0x14, + 0xc5, 0x2d, 0x22, 0x93, 0x67, 0x70, 0x63, 0xf2, 0x62, 0x04, 0x8a, 0x55, 0xf8, + 0x48, 0x4a, 0x65, 0x5e, 0xcb, 0x71, 0xcc, 0x4d, 0x73, 0xa4, 0x25, 0x8f, 0x8e, + 0xbb, 0x42, 0x31, 0xd0, 0xdc, 0x58, 0x26, 0xda, 0x2d, 0x8b, 0x13, 0xdc, 0x3a, + 0x2e, 0xa3, 0xc5, 0x0d, 0x29, 0xe0, 0xe6, 0xa5, 0x6a, 0xd4, 0xe6, 0x05, 0xc9, + 0x30, 0x6d, 0xdc, 0x34, 0x29, 0xa6, 0xa0, 0xe7, 0x00, 0xfa, 0xe2, 0x4f, 0x05, + 0xb9, 0xa8, 0x84, 0x0d, 0x0c, 0xd1, 0x6f, 0xdf, 0x8c, 0xa8, 0xeb, 0x7b, 0x00, + 0x74, 0x17, 0x94, 0xe0, 0x6f, 0x6d, 0xbf, 0xb7, 0x73, 0x4f, 0x9b, 0x0f, 0xc8, + 0x08, 0x26, 0x56, 0x1e, 0x47, 0x0c, 0x27, 0xbe, 0xe9, 0x73, 0x36, 0xf8, 0x87, + 0xa5, 0xd7, 0xe9, 0x14, 0x28, 0x27, 0xa5, 0xf0, 0x87, 0x32, 0xd7, 0xd8, 0x51, + 0x01, 0x08, 0x01, 0x01, 0x03, 0x08, 0x03, 0x01, 0x00, 0x01, 0xa8, 0x00, 0x20, + 0xa9, 0x55, 0x66, 0xba, 0x42, 0xe8, 0x86, 0xbb, 0x80, 0x4c, 0xda, 0x84, 0xe4, + 0x7e, 0xf5, 0x6d, 0xbd, 0x7a, 0xec, 0x61, 0x26, 0x15, 0x55, 0x2c, 0xec, 0x90, + 0x6d, 0x21, 0x16, 0xd0, 0xef, 0x20, 0x70, 0x28, 0xc5, 0x15, 0x54, 0x14, 0x4d, + 0xfe, 0xaf, 0xe7, 0xc7, 0xcb, 0x8f, 0x00, 0x5d, 0xd1, 0x82, 0x34, 0x13, 0x3a, + 0xc0, 0x71, 0x0a, 0x81, 0x18, 0x2c, 0xe1, 0xfd, 0x14, 0xad, 0x22, 0x83, 0xbc, + 0x83, 0x43, 0x5f, 0x9d, 0xf2, 0xf6, 0x31, 0x32, 0x51, 0x93, 0x1a, 0x17, 0x6d, + 0xf0, 0xda, 0x51, 0xe5, 0x4f, 0x42, 0xe6, 0x04, 0x86, 0x0d, 0xfb, 0x35, 0x95, + 0x80, 0x25, 0x0f, 0x55, 0x9c, 0xc5, 0x43, 0xc4, 0xff, 0xd5, 0x1c, 0xbe, 0x3d, + 0xe8, 0xcf, 0xd0, 0x67, 0x19, 0x23, 0x7f, 0x9f, 0xc4, 0x7e, 0xe7, 0x29, 0xda, + 0x06, 0x83, 0x5f, 0xa4, 0x52, 0xe8, 0x25, 0xe9, 0xa1, 0x8e, 0xbc, 0x2e, 0xcb, + 0xcf, 0x56, 0x34, 0x74, 0x65, 0x2c, 0x33, 0xcf, 0x56, 0xa9, 0x03, 0x3b, 0xcd, + 0xf5, 0xd9, 0x73, 0x12, 0x17, 0x97, 0xec, 0x80, 0x89, 0x04, 0x1b, 0x6e, 0x03, + 0xa1, 0xb7, 0x2d, 0x0a, 0x73, 0x5b, 0x98, 0x4e, 0x03, 0x68, 0x73, 0x09, 0x33, + 0x23, 0x24, 0xf2, 0x7c, 0x2d, 0xba, 0x85, 0xe9, 0xdb, 0x15, 0xe8, 0x3a, 0x01, + 0x43, 0x38, 0x2e, 0x97, 0x4b, 0x06, 0x21, 0xc1, 0x8e, 0x62, 0x5e, 0xce, 0xc9, + 0x07, 0x57, 0x7d, 0x9e, 0x7b, 0xad, 0xe9, 0x52, 0x41, 0xa8, 0x1e, 0xbb, 0xe8, + 0xa9, 0x01, 0xd4, 0xd3, 0x27, 0x6e, 0x40, 0xb1, 0x14, 0xc0, 0xa2, 0xe6, 0xfc, + 0x38, 0xd1, 0x9c, 0x2e, 0x6a, 0xab, 0x02, 0x64, 0x4b, 0x28, 0x13, 0xf5, 0x75, + 0xfc, 0x21, 0x60, 0x1e, 0x0d, 0xee, 0x49, 0xcd, 0x9e, 0xe9, 0x6a, 0x43, 0x10, + 0x3e, 0x52, 0x4d, 0x62, 0x87, 0x3d, 0x03, 0x6f, 0x72, 0x67, 0x00, 0x00, 0x2b, + 0x00, 0x90, 0x08, 0x01, 0x00, 0x02, 0xa3, 0x00, 0x4c, 0x54, 0xb9, 0x00, 0x4c, + 0x4b, 0x70, 0x70, 0xa1, 0x20, 0x5c, 0x2f, 0xd0, 0x29, 0x2c, 0x13, 0xf8, 0xbc, + 0xbb, 0xfe, 0xd2, 0xb2, 0xf2, 0x6a, 0xfa, 0xe0, 0x4a, 0x2e, 0x80, 0xb5, 0x3c, + 0xb9, 0xa1, 0x6f, 0x14, 0x57, 0x9e, 0x80, 0x3f, 0xb8, 0x5d, 0xbc, 0xac, 0x81, + 0x1f, 0x7a, 0xf9, 0x18, 0xda, 0x31, 0x3f, 0x97, 0x68, 0x2b, 0x37, 0x79, 0xb2, + 0xb8, 0xd7, 0x1a, 0x40, 0x35, 0xb1, 0x3b, 0x64, 0x22, 0x0d, 0xb6, 0xb9, 0x17, + 0x64, 0xfe, 0xed, 0x23, 0xaf, 0x17, 0xb2, 0x7f, 0x49, 0xa9, 0x2b, 0xa0, 0x06, + 0xfb, 0xe9, 0x4a, 0x17, 0x88, 0x91, 0x55, 0xe5, 0xb3, 0x5f, 0xee, 0x73, 0xd7, + 0x00, 0x58, 0x21, 0x29, 0x39, 0x7a, 0xe2, 0xc5, 0x03, 0x8f, 0xf2, 0x11, 0xed, + 0xd6, 0xb0, 0xdb, 0x5e, 0x7f, 0x37, 0x46, 0x80, 0xf9, 0x67, 0xfc, 0xdd, 0xbf, + 0xb5, 0x46, 0x4d, 0xe5, 0xcf, 0x3f, 0xf9, 0xb2, 0x08, 0x52, 0x36, 0xf5, 0x7f, + 0x8c, 0x19, 0x88, 0x02, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x01, 0x10, + 0x07, 0x01, 0x00, 0x00, 0x03, 0x84, 0x4c, 0x54, 0x44, 0xe0, 0x4c, 0x41, 0xc1, + 0xd0, 0x53, 0x76, 0x53, 0x1b, 0xac, 0x55, 0xb7, 0x5b, 0xc8, 0xca, 0xe7, 0x05, + 0xe2, 0x19, 0x50, 0x5b, 0xf4, 0x05, 0xc6, 0x11, 0x41, 0xb7, 0xde, 0x29, 0xd8, + 0x71, 0x9b, 0x1d, 0x44, 0x8b, 0x1f, 0xf9, 0x9d, 0x4e, 0x94, 0xc9, 0xe9, 0xfe, + 0xc2, 0x7c, 0x73, 0xd1, 0xc7, 0x9b, 0xbe, 0x15, 0x8f, 0xc4, 0xb6, 0xe7, 0x91, + 0xe4, 0x67, 0xc3, 0xab, 0x2f, 0x89, 0x60, 0xf0, 0x29, 0xfb, 0xb1, 0x0a, 0x05, + 0xc6, 0xf2, 0xf6, 0xb3, 0xf2, 0x4d, 0x68, 0xb5, 0xde, 0x66, 0x45, 0x39, 0xe8, + 0x9b, 0xfa, 0x0f, 0x7d, 0x9d, 0xbc, 0xd4, 0xc5, 0xb7, 0x14, 0x9d, 0xa0, 0x21, + 0x33, 0x04, 0x2a, 0x97, 0x40, 0x98, 0x40, 0x16, 0xfe, 0xe0, 0xcf, 0xdd, 0x22, + 0x94, 0xbe, 0xbc, 0x4c, 0x09, 0x5c, 0xef, 0x0e, 0x99, 0xac, 0xa0, 0xb6, 0xa5, + 0xde, 0xe8, 0xf1, 0x1f, 0xff, 0x84, 0x3a, 0x19, 0x7e, 0x46, 0x26, 0x6f, 0xdc, + 0xfd, 0x15, 0x9d, 0xfd, 0x6e, 0x58, 0xf2, 0x94, 0x02, 0x25, 0x9a, 0x37, 0x99, + 0x9f, 0x27, 0xdb, 0xbe, 0xcd, 0x3a, 0x2b, 0xa8, 0xa3, 0xd6, 0x96, 0x4e, 0xdb, + 0xe5, 0x87, 0x55, 0xd8, 0xd2, 0x6e, 0xa7, 0x09, 0x7d, 0xe4, 0xfc, 0x20, 0x6c, + 0x4d, 0x99, 0xb3, 0x48, 0xf9, 0x63, 0xee, 0x4e, 0xa5, 0x24, 0xcc, 0xb1, 0x99, + 0xf8, 0x0e, 0x1d, 0x6e, 0x1a, 0x88, 0x0b, 0x98, 0xa5, 0x21, 0xdc, 0x43, 0x75, + 0xb5, 0x20, 0x5a, 0xcd, 0xac, 0xb8, 0x08, 0x60, 0x2c, 0xb0, 0x2f, 0x6f, 0xac, + 0x7b, 0xda, 0x16, 0x24, 0xc7, 0xc6, 0x22, 0xf6, 0xc8, 0x47, 0x8a, 0x93, 0x93, + 0x1e, 0x1d, 0xb5, 0xe2, 0x1e, 0xe5, 0xc6, 0x8e, 0xa9, 0xe4, 0xd3, 0x35, 0x97, + 0x31, 0x64, 0xce, 0xd8, 0xa1, 0x16, 0x1d, 0x67, 0x0e, 0x2c, 0x07, 0x2e, 0x67, + 0xef, 0xcc, 0x80, 0x59, 0x35, 0xdd, 0xa0, 0xa8, 0x60, 0x99, 0x4e, 0x8e, 0x04, + 0x00, 0x88, 0x01, 0x00, 0x03, 0x07, 0x03, 0x01, 0x00, 0x01, 0x9c, 0xf3, 0x56, + 0x70, 0x44, 0x1d, 0x37, 0x51, 0xf9, 0x88, 0xa4, 0xf7, 0xf9, 0x1f, 0x60, 0x1b, + 0x14, 0x03, 0xa8, 0xcd, 0xc1, 0xaf, 0x44, 0x6e, 0x5c, 0xdb, 0x83, 0x64, 0x48, + 0x9a, 0x7f, 0xdc, 0x6b, 0x64, 0x46, 0x72, 0xb5, 0xd5, 0x7f, 0xd2, 0xe9, 0x95, + 0x2f, 0x14, 0xf9, 0xff, 0x68, 0xbf, 0x35, 0xb7, 0xcc, 0x13, 0x95, 0xab, 0x07, + 0x4d, 0x5d, 0x47, 0xdb, 0xaa, 0xc0, 0xfc, 0xf9, 0xdc, 0xd4, 0x97, 0x51, 0x64, + 0xbb, 0x8a, 0x66, 0x71, 0xa8, 0x1c, 0xb8, 0x7b, 0xa4, 0xf6, 0xb9, 0x54, 0x98, + 0xf6, 0xa7, 0x41, 0xa8, 0xae, 0x8e, 0x17, 0x2b, 0x7e, 0xdd, 0x60, 0x8b, 0x84, + 0x21, 0xe4, 0x68, 0xdd, 0xfc, 0x34, 0x5b, 0x8f, 0x53, 0x14, 0x93, 0xa3, 0xb1, + 0xd6, 0xcb, 0x9f, 0x26, 0xac, 0xfd, 0x9e, 0xf3, 0x44, 0x8a, 0x35, 0xf6, 0x8b, + 0x20, 0xf8, 0x7a, 0xf7, 0xa2, 0x2c, 0xf3, 0xfd, 0x00, 0x88, 0x01, 0x00, 0x03, + 0x07, 0x03, 0x01, 0x00, 0x01, 0xc9, 0x06, 0x00, 0x53, 0x45, 0x4a, 0x0b, 0xb8, + 0xe2, 0xb0, 0x4e, 0x29, 0xc8, 0x19, 0xb4, 0xa3, 0x63, 0x27, 0xe2, 0xcd, 0xc7, + 0xc7, 0x6d, 0x60, 0x31, 0xeb, 0xc0, 0x82, 0x5f, 0x44, 0x14, 0x96, 0x60, 0x4e, + 0xc8, 0x62, 0xf4, 0xcc, 0xb9, 0x99, 0x7a, 0x19, 0xf0, 0xaf, 0x34, 0xd9, 0x63, + 0xca, 0x40, 0xe3, 0x7b, 0xb6, 0xbc, 0xfa, 0x40, 0x08, 0x1d, 0xe7, 0xc3, 0xa4, + 0xd2, 0x73, 0x3a, 0x32, 0xf2, 0xa4, 0x4c, 0x3c, 0x4f, 0xd6, 0x52, 0x52, 0xc8, + 0x6d, 0xa5, 0xf6, 0xd9, 0x4d, 0x0c, 0xfd, 0xb4, 0x93, 0x8b, 0x61, 0x72, 0xdb, + 0x6e, 0x5f, 0x2d, 0xd9, 0x2d, 0xab, 0x18, 0x2f, 0x87, 0x2d, 0xbf, 0x8d, 0x42, + 0x37, 0x93, 0x41, 0x18, 0xf6, 0x93, 0x97, 0xda, 0x27, 0x31, 0xdc, 0xda, 0xec, + 0x21, 0x16, 0x61, 0xe1, 0xe0, 0x7a, 0x53, 0x26, 0x82, 0xc7, 0x62, 0x99, 0x18, + 0x81, 0x6a, 0x65, 0x01, 0x08, 0x01, 0x01, 0x03, 0x07, 0x03, 0x01, 0x00, 0x01, + 0x8a, 0x58, 0x7e, 0x3d, 0xda, 0x69, 0x1c, 0xf3, 0x93, 0x15, 0x90, 0xa8, 0xc7, + 0x65, 0xed, 0x81, 0x31, 0x63, 0xcd, 0x4d, 0x75, 0x84, 0xaf, 0xfa, 0xa6, 0xb2, + 0xb9, 0x90, 0xe8, 0x76, 0x76, 0x7d, 0x20, 0xc8, 0x74, 0x6f, 0x03, 0x1c, 0x61, + 0xa5, 0x54, 0x77, 0x33, 0x40, 0x6d, 0x57, 0x89, 0xf2, 0x07, 0x7a, 0x8e, 0xad, + 0x6c, 0x47, 0x75, 0x6f, 0x3f, 0xf4, 0x91, 0xdf, 0xa9, 0xa6, 0x1a, 0xcb, 0x1b, + 0x57, 0x85, 0x1d, 0x97, 0x93, 0x91, 0x0e, 0xda, 0xa2, 0x64, 0xfd, 0x93, 0x0c, + 0xd0, 0xc7, 0xc4, 0x49, 0xca, 0x29, 0x35, 0xfe, 0x8d, 0x67, 0xf2, 0xb5, 0x97, + 0x93, 0xed, 0xdd, 0xc0, 0x6d, 0x2c, 0xc1, 0x28, 0x2d, 0x2f, 0xee, 0xe6, 0x6b, + 0x33, 0xa3, 0x36, 0x7a, 0x82, 0x67, 0x97, 0xa8, 0x9d, 0xeb, 0xaa, 0xc4, 0x52, + 0x64, 0x02, 0xda, 0x9f, 0x43, 0xae, 0xb0, 0xe0, 0xf4, 0x5e, 0xad, 0x5c, 0x2f, + 0x42, 0x0f, 0xfc, 0xc2, 0xef, 0xfc, 0xbe, 0x04, 0xd3, 0x69, 0x88, 0xe7, 0x67, + 0x33, 0x90, 0xd7, 0x93, 0xb1, 0xe1, 0x66, 0x6e, 0xeb, 0x6b, 0xd1, 0x3b, 0x96, + 0xd2, 0xf5, 0xde, 0x1d, 0xa6, 0xc7, 0xb9, 0x04, 0x81, 0x4f, 0x1e, 0xea, 0x7a, + 0x49, 0x94, 0x2a, 0x17, 0x8e, 0xb6, 0x88, 0x06, 0x05, 0x03, 0xb6, 0x16, 0x2c, + 0xe3, 0xc5, 0xbf, 0xb1, 0xd4, 0xc3, 0x2e, 0xee, 0xcd, 0xe7, 0xda, 0xe3, 0x08, + 0x6f, 0x9b, 0xa6, 0x29, 0x7e, 0x73, 0xca, 0x19, 0xf5, 0xfe, 0xcd, 0x47, 0x7a, + 0xa6, 0x49, 0x3a, 0x53, 0x3f, 0x59, 0xbc, 0xe9, 0x1a, 0x94, 0x42, 0x75, 0x44, + 0xae, 0x27, 0xeb, 0x1f, 0xc2, 0xa3, 0x0e, 0xa2, 0xfe, 0xdf, 0x0c, 0xd4, 0x74, + 0x5e, 0x40, 0x0a, 0x46, 0x30, 0xb7, 0x55, 0xe1, 0x3d, 0x53, 0xd4, 0xfb, 0x04, + 0x88, 0x97, 0x36, 0xda, 0x01, 0x03, 0x78, 0xf4, 0xf5, 0x01, 0x08, 0x01, 0x01, + 0x03, 0x07, 0x03, 0x01, 0x00, 0x01, 0x94, 0xe3, 0x6c, 0x83, 0xb9, 0x90, 0x8a, + 0x71, 0x59, 0x4b, 0x72, 0x5d, 0xcf, 0x1a, 0xbe, 0xc2, 0xb2, 0x1c, 0x82, 0x19, + 0xf8, 0xb8, 0xc2, 0xd8, 0x3b, 0xfc, 0x9d, 0xa3, 0xbe, 0x4f, 0x3e, 0x97, 0xd9, + 0xfa, 0xb3, 0x0c, 0x2d, 0x5b, 0x76, 0xae, 0xc7, 0x95, 0x9c, 0x2d, 0x91, 0xaa, + 0x93, 0x90, 0xc5, 0x55, 0x27, 0xef, 0x20, 0x13, 0xd1, 0x48, 0xad, 0xe1, 0x89, + 0xe1, 0xcf, 0x06, 0xd4, 0x67, 0x5b, 0x8d, 0x08, 0x1b, 0x3f, 0x53, 0xb2, 0x60, + 0x81, 0xbb, 0x38, 0x74, 0xdc, 0xe2, 0x1b, 0xf9, 0x4f, 0x63, 0x65, 0xc9, 0x6a, + 0xfa, 0x93, 0xa4, 0x05, 0xcf, 0xdf, 0x10, 0xe3, 0x3c, 0x05, 0x20, 0x64, 0xc5, + 0x56, 0xfc, 0x01, 0x86, 0x6a, 0xcc, 0x0d, 0x8b, 0x0e, 0x4e, 0xd5, 0xda, 0x90, + 0xae, 0x90, 0xc0, 0x7a, 0x2f, 0x03, 0x5f, 0xbc, 0xdc, 0x1b, 0x14, 0x00, 0x2c, + 0x65, 0x89, 0xda, 0x70, 0x07, 0x48, 0x50, 0x69, 0xc6, 0xc3, 0xeb, 0x1f, 0x88, + 0xd9, 0x10, 0x83, 0xcd, 0x8b, 0x93, 0xce, 0x3e, 0xb8, 0x26, 0xfd, 0x3f, 0xf5, + 0x7b, 0x17, 0xe8, 0x06, 0x15, 0xdd, 0xe6, 0xdc, 0x82, 0x7e, 0x21, 0x2f, 0x58, + 0xc8, 0x47, 0x67, 0x89, 0x63, 0x25, 0xe5, 0xac, 0x0a, 0x16, 0xc5, 0xdc, 0xf1, + 0x71, 0x6f, 0xff, 0xe7, 0x27, 0x8b, 0xe5, 0x15, 0x56, 0xba, 0x14, 0x71, 0x7a, + 0x39, 0xa7, 0x49, 0x59, 0xc2, 0xbb, 0x19, 0x1f, 0x4b, 0x80, 0x10, 0xe3, 0xce, + 0x4a, 0x1f, 0x6b, 0x69, 0x75, 0xb5, 0x9c, 0x0a, 0x8a, 0x4b, 0x25, 0x9b, 0x3a, + 0xb7, 0x0f, 0x2a, 0xde, 0x35, 0x9c, 0xa5, 0x31, 0xb3, 0x76, 0x1f, 0xef, 0xdf, + 0x17, 0x58, 0x7c, 0xda, 0x50, 0x35, 0xc3, 0xc8, 0x98, 0x59, 0x71, 0x02, 0xe9, + 0xf7, 0x06, 0xd3, 0x91, 0x3c, 0x0d, 0xab, 0xf2, 0xd8, 0xba, 0x30, 0xda, 0x09, + 0x10, 0x75, 0x0a, 0x64, 0x6e, 0x73, 0x73, 0x65, 0x63, 0x2d, 0x65, 0x78, 0x70, + 0x03, 0x6f, 0x72, 0x67, 0x00, 0x00, 0x2b, 0x00, 0x90, 0x07, 0x02, 0x00, 0x01, + 0x51, 0x80, 0x4c, 0x54, 0x44, 0xe0, 0x4c, 0x41, 0xc1, 0xd0, 0xf2, 0x12, 0x0d, + 0x95, 0xb4, 0x6f, 0x7c, 0xfc, 0xde, 0x67, 0xa0, 0x1d, 0x84, 0x49, 0x3e, 0xa8, + 0xe1, 0x07, 0xea, 0xde, 0x75, 0x14, 0x98, 0xa9, 0xcc, 0x09, 0x20, 0x66, 0x59, + 0xd2, 0x40, 0xfd, 0xc6, 0xcf, 0x54, 0x22, 0x4a, 0x13, 0xf9, 0xc2, 0x1e, 0xe3, + 0x48, 0xf1, 0xd0, 0x6d, 0x18, 0x27, 0xe5, 0x8e, 0x92, 0xc2, 0x5f, 0xcc, 0xb8, + 0xd6, 0x1a, 0xbb, 0xfa, 0xe7, 0xac, 0xa8, 0x23, 0x4b, 0x89, 0xef, 0xb0, 0xc7, + 0x9a, 0xaa, 0x00, 0x99, 0x82, 0x3d, 0xa8, 0x25, 0x6b, 0xb3, 0x59, 0xd5, 0x7a, + 0x90, 0xba, 0xc4, 0xa4, 0x79, 0x8c, 0xa4, 0x39, 0xc2, 0xe9, 0x54, 0xc0, 0x4c, + 0x0a, 0x0e, 0x06, 0xcc, 0x25, 0xac, 0xb6, 0x75, 0x45, 0x8c, 0xd9, 0x99, 0x4f, + 0x31, 0xff, 0x3d, 0x40, 0x3b, 0x1c, 0x40, 0x94, 0xed, 0x9d, 0x7f, 0x60, 0x08, + 0x23, 0x63, 0x18, 0x5c, 0x50, 0x6f, 0x26, 0x7b, 0x39, 0x8b, 0x01, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xa8, 0x01, 0x00, 0x03, 0x05, 0x03, 0x01, + 0x00, 0x01, 0xd0, 0x0a, 0x64, 0x8e, 0xb5, 0x90, 0x0b, 0x75, 0xc2, 0xeb, 0x52, + 0x5f, 0xa4, 0xcb, 0xeb, 0x1d, 0x77, 0x8d, 0x84, 0x2c, 0xef, 0xb6, 0x88, 0xd4, + 0x7c, 0x50, 0x4c, 0x52, 0x7b, 0x9d, 0x37, 0x31, 0x36, 0xb2, 0x5b, 0x6c, 0x47, + 0xb4, 0x21, 0x80, 0x61, 0x46, 0xfa, 0x5b, 0x44, 0x50, 0x91, 0x9d, 0xf8, 0xc1, + 0x78, 0x00, 0x78, 0x29, 0xfe, 0xe2, 0x08, 0x65, 0xf8, 0xc9, 0xdf, 0x69, 0x0b, + 0x59, 0x6b, 0xf4, 0x93, 0x9f, 0x8b, 0x25, 0x0b, 0x6b, 0x93, 0x12, 0x06, 0x57, + 0xc8, 0x04, 0x9d, 0x3f, 0x33, 0xbd, 0x2b, 0x35, 0x54, 0xb9, 0x98, 0x75, 0xe4, + 0xb0, 0x49, 0x3c, 0x29, 0xc1, 0xfb, 0x74, 0xbc, 0x91, 0x82, 0x9e, 0xc5, 0x61, + 0xa6, 0x16, 0xe1, 0xf0, 0x8f, 0xe9, 0xe1, 0x23, 0x55, 0x5e, 0xfb, 0xf1, 0xcc, + 0x8a, 0x75, 0x5d, 0xf2, 0x86, 0x89, 0x0a, 0x29, 0xcb, 0xca, 0x33, 0xde, 0x9d, + 0x74, 0x43, 0x0d, 0xde, 0x67, 0xde, 0x71, 0x0f, 0x7f, 0xa3, 0xbb, 0x22, 0x82, + 0x81, 0x66, 0xd1, 0xbd, 0x49, 0x0d, 0x89, 0x45, 0xd7, 0x18, 0xcf, 0x98, 0x2c, + 0x19, 0xaf, 0x63, 0x16, 0x20, 0x91, 0x0a, 0x64, 0x6e, 0x73, 0x73, 0x65, 0x63, + 0x2d, 0x65, 0x78, 0x70, 0x03, 0x6f, 0x72, 0x67, 0x00, 0x00, 0x25, 0x00, 0xb0, + 0x05, 0x02, 0x00, 0x00, 0x00, 0x3c, 0x4c, 0x71, 0x48, 0x0a, 0x4c, 0x49, 0xb6, + 0xcb, 0x83, 0x7f, 0x3d, 0xfa, 0xd9, 0x59, 0x7a, 0xc5, 0xa2, 0xd3, 0x0e, 0x3e, + 0x48, 0xa8, 0x9b, 0xc5, 0xef, 0x43, 0xe3, 0x3e, 0x3a, 0x1a, 0xb2, 0x39, 0x00, + 0x01, 0x73, 0x78, 0x80, 0x26, 0xac, 0xb0, 0x01, 0xe9, 0xc9, 0x7a, 0x0f, 0x5e, + 0xa7, 0xa2, 0x7f, 0xec, 0xa8, 0xf4, 0x42, 0x58, 0xdc, 0xba, 0xc1, 0x9e, 0xc8, + 0xa8, 0xc7, 0x93, 0x9f, 0x13, 0xc5, 0xbe, 0x99, 0xa9, 0xcc, 0x75, 0xd6, 0x7b, + 0xb4, 0xd4, 0x9b, 0x0e, 0x61, 0x96, 0x6d, 0x3f, 0xbf, 0x47, 0x42, 0xae, 0x8c, + 0xd7, 0xc1, 0x09, 0xb3, 0x84, 0x50, 0xac, 0xae, 0xbd, 0xc4, 0x54, 0x77, 0xe0, + 0xfe, 0xa8, 0xc3, 0xd4, 0x68, 0x76, 0x07, 0xdb, 0x14, 0x51, 0x2e, 0x43, 0x15, + 0x2b, 0x67, 0x4b, 0x76, 0x18, 0xbd, 0x04, 0xb3, 0xb7, 0xe5, 0xb7, 0xaf, 0xaa, + 0xb1, 0x4c, 0xa6, 0xd2, 0xbe, 0x2d, 0xdb, 0xa5, 0x2c, 0x1d, 0xe7, 0x67, 0x79, + 0x0b, 0xc4, 0x2e, 0x26, 0x0f, 0x9d, 0x98, 0x1f, 0x21, 0x1f, 0x9e, 0xfe, 0x3f, + 0x67, 0xf3, 0x67, 0x73, 0xe5, 0x88, 0xdf, 0xa8, 0x9f, 0x8c, 0x74, 0x97, 0xc2, + 0x44, 0x29, 0x28, 0x15, 0x0f, 0x1f, 0x83, 0x01, 0x00, 0x19, 0x00, 0x09, 0x00, + 0x00, 0x00, 0x90, 0x38, 0x61, 0x31, 0x19, 0x52, 0x21, 0x14, 0x83, 0x08, 0x44, + 0xeb, 0xdd, 0xe5, 0xc7, 0x1d, 0x16, 0xa2, 0x45, 0x1c, +}; + +TEST(DNSSECChainVerifierTest, TestChain) { + base::StringPiece chain(reinterpret_cast<const char*>(kChain), + sizeof(kChain)); + net::DNSSECChainVerifier verifier(FromDNSName("dnssec-exp.org"), chain); + verifier.IgnoreTimestamps(); + ASSERT_EQ(net::DNSSECChainVerifier::OK, verifier.Verify()); + ASSERT_EQ(net::kDNS_CERT, verifier.rrtype()); +} + +TEST(DNSSECChainVerifierTest, OffCourse) { + base::StringPiece chain(reinterpret_cast<const char*>(kChain), + sizeof(kChain)); + net::DNSSECChainVerifier verifier(FromDNSName("foo.org"), chain); + verifier.IgnoreTimestamps(); + ASSERT_EQ(net::DNSSECChainVerifier::OFF_COURSE, verifier.Verify()); +} + +TEST(DNSSECChainVerifierTest, BadTarget) { + base::StringPiece chain(reinterpret_cast<const char*>(kChain), + sizeof(kChain)); + net::DNSSECChainVerifier verifier(FromDNSName("www.dnssec-exp.org"), chain); + verifier.IgnoreTimestamps(); + ASSERT_EQ(net::DNSSECChainVerifier::BAD_TARGET, verifier.Verify()); +} + +// This is too slow to run all the time. +TEST(DNSSECChainVerifierTest, DISABLED_Fuzz) { + uint8 copy[sizeof(kChain)]; + base::StringPiece chain(reinterpret_cast<const char*>(copy), sizeof(copy)); + static const unsigned bit_length = sizeof(kChain) * 8; + + for (unsigned bit_to_flip = 0; bit_to_flip < bit_length; bit_to_flip++) { + memcpy(copy, kChain, sizeof(copy)); + + unsigned byte = bit_to_flip >> 3; + unsigned bit = bit_to_flip & 7; + copy[byte] ^= (1 << bit); + + net::DNSSECChainVerifier verifier(FromDNSName("dnssec-exp.org"), chain); + verifier.IgnoreTimestamps(); + ASSERT_NE(net::DNSSECChainVerifier::OK, verifier.Verify()); + } +} diff --git a/net/base/ssl_config_service.cc b/net/base/ssl_config_service.cc index 67ed45f..fb85665 100644 --- a/net/base/ssl_config_service.cc +++ b/net/base/ssl_config_service.cc @@ -55,4 +55,16 @@ bool SSLConfigService::IsKnownStrictTLSServer(const std::string& hostname) { return false; } +static bool g_dnssec_enabled = false; + +// static +void SSLConfigService::EnableDNSSEC() { + g_dnssec_enabled = true; +} + +// static +bool SSLConfigService::dnssec_enabled() { + return g_dnssec_enabled; +} + } // namespace net diff --git a/net/base/ssl_config_service.h b/net/base/ssl_config_service.h index 851dd94..d10134e 100644 --- a/net/base/ssl_config_service.h +++ b/net/base/ssl_config_service.h @@ -19,8 +19,8 @@ struct SSLConfig { // Default to SSL 2.0 off, SSL 3.0 on, and TLS 1.0 on. SSLConfig() : rev_checking_enabled(true), ssl2_enabled(false), ssl3_enabled(true), - tls1_enabled(true), ssl3_fallback(false), send_client_cert(false), - verify_ev_cert(false) { + tls1_enabled(true), ssl3_fallback(false), dnssec_enabled(false), + send_client_cert(false), verify_ev_cert(false) { } bool rev_checking_enabled; // True if server certificate revocation @@ -30,6 +30,7 @@ struct SSLConfig { bool tls1_enabled; // True if TLS 1.0 is enabled. bool ssl3_fallback; // True if we are falling back to SSL 3.0 (one still // needs to clear tls1_enabled). + bool dnssec_enabled; // True if we'll accept DNSSEC chains in certificates. // TODO(wtc): move the following members to a new SSLParams structure. They // are not SSL configuration settings. @@ -96,6 +97,11 @@ class SSLConfigService : public base::RefCountedThreadSafe<SSLConfigService> { // http://crbug.com and email the link to agl AT chromium DOT org. static bool IsKnownStrictTLSServer(const std::string& hostname); + // Enables the acceptance of self-signed certificates which contain an + // embedded DNSSEC chain proving their validity. + static void EnableDNSSEC(); + static bool dnssec_enabled(); + protected: friend class base::RefCountedThreadSafe<SSLConfigService>; diff --git a/net/base/ssl_config_service_defaults.h b/net/base/ssl_config_service_defaults.h index e20b616..092b2a53 100644 --- a/net/base/ssl_config_service_defaults.h +++ b/net/base/ssl_config_service_defaults.h @@ -20,6 +20,7 @@ class SSLConfigServiceDefaults : public SSLConfigService { // Store default SSL config settings in |config|. virtual void GetSSLConfig(SSLConfig* config) { *config = default_config_; + config->dnssec_enabled = SSLConfigService::dnssec_enabled(); } private: diff --git a/net/base/ssl_config_service_mac.cc b/net/base/ssl_config_service_mac.cc index 81897c5..792c9ca 100644 --- a/net/base/ssl_config_service_mac.cc +++ b/net/base/ssl_config_service_mac.cc @@ -95,6 +95,7 @@ bool SSLConfigServiceMac::GetSSLConfigNow(SSLConfig* config) { kSSL3EnabledDefaultValue); config->tls1_enabled = SSLVersionIsEnabled(kTLS1EnabledKey, kTLS1EnabledDefaultValue); + config->dnssec_enabled = SSLConfigService::dnssec_enabled(); return true; } diff --git a/net/base/ssl_config_service_win.cc b/net/base/ssl_config_service_win.cc index 513681f..fd15849 100644 --- a/net/base/ssl_config_service_win.cc +++ b/net/base/ssl_config_service_win.cc @@ -75,6 +75,7 @@ bool SSLConfigServiceWin::GetSSLConfigNow(SSLConfig* config) { config->ssl2_enabled = ((protocols & SSL2) != 0); config->ssl3_enabled = ((protocols & SSL3) != 0); config->tls1_enabled = ((protocols & TLS1) != 0); + config->dnssec_enabled = SSLConfigService::dnssec_enabled(); return true; } |