summaryrefslogtreecommitdiffstats
path: root/net/base
diff options
context:
space:
mode:
authoragl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-08-11 19:50:02 +0000
committeragl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-08-11 19:50:02 +0000
commitb2471359cfbd4f7b9621ba2542b947841bfadb27 (patch)
tree241b1e8c58a26a5bbfb1df3c9f5d342c492ad693 /net/base
parent1b3db78c4451a755eeaadc4cedceccd9e91724c8 (diff)
downloadchromium_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.h13
-rw-r--r--net/base/dnssec_chain_verifier.cc670
-rw-r--r--net/base/dnssec_chain_verifier.h107
-rw-r--r--net/base/dnssec_keyset.cc454
-rw-r--r--net/base/dnssec_keyset.h61
-rw-r--r--net/base/dnssec_unittest.cc403
-rw-r--r--net/base/ssl_config_service.cc12
-rw-r--r--net/base/ssl_config_service.h10
-rw-r--r--net/base/ssl_config_service_defaults.h1
-rw-r--r--net/base/ssl_config_service_mac.cc1
-rw-r--r--net/base/ssl_config_service_win.cc1
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;
}