summaryrefslogtreecommitdiffstats
path: root/net/base/dnssec_chain_verifier.cc
diff options
context:
space:
mode:
Diffstat (limited to 'net/base/dnssec_chain_verifier.cc')
-rw-r--r--net/base/dnssec_chain_verifier.cc995
1 files changed, 0 insertions, 995 deletions
diff --git a/net/base/dnssec_chain_verifier.cc b/net/base/dnssec_chain_verifier.cc
deleted file mode 100644
index 4ea79b3..0000000
--- a/net/base/dnssec_chain_verifier.cc
+++ /dev/null
@@ -1,995 +0,0 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/base/dnssec_chain_verifier.h"
-
-#include "base/logging.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/sha1.h"
-#include "base/string_util.h"
-#include "crypto/sha2.h"
-#include "net/base/asn1_util.h"
-#include "net/base/dns_util.h"
-#include "net/base/dnssec_keyset.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.
-*/
-
-namespace {
-
-// This is the 2048-bit DNS root key: http://www.iana.org/dnssec
-// 19036 8 2 49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5
-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
-const uint16 kRootKeyID = 19036;
-
-// CountLabels returns the number of DNS labels in |a|, which must be in DNS,
-// length-prefixed form.
-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.
-void RemoveLeadingLabel(base::StringPiece* a) {
- if (!a->size())
- return;
- uint8 label_len = a->data()[0];
- a->remove_prefix(1);
- a->remove_prefix(label_len);
-}
-
-} // namespace
-
-namespace net {
-
-struct DNSSECChainVerifier::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;
-};
-
-DNSSECChainVerifier::DNSSECChainVerifier(const std::string& target,
- const base::StringPiece& chain)
- : current_zone_(NULL),
- target_(target),
- chain_(chain),
- ignore_timestamps_(false),
- valid_(false),
- already_entered_zone_(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;
- }
-
- if (already_entered_zone_) {
- already_entered_zone_ = false;
- } else {
- 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_;
-}
-
-// 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);
- }
-}
-
-// 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.empty()) {
- 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[crypto::kSHA256Length];
- 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::kSHA1Length;
- } else if (digest_type == kDNSSEC_SHA256) {
- crypto::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.empty()) {
- // 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.empty()) {
- // 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;
-}
-
-// IsValidTerminalRRType returns true if the given RR type is one that we
-// accept as the terminal record in a DNSSEC chain.
-bool DNSSECChainVerifier::IsValidTerminalRRType(uint16 rrtype) {
- switch (rrtype) {
- case kDNS_CAA:
- case kDNS_CERT:
- case kDNS_TLSA:
- case kDNS_TXT:
- return true;
- }
-
- return false;
-}
-
-// 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 (IsValidTerminalRRType(rrtype)) {
- err = ReadGenericRRs(&rrdatas);
- } else if (rrtype == kDNS_CNAME) {
- err = ReadCNAME(&rrdatas);
- } else {
- 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 (IsValidTerminalRRType(rrtype)) {
- // 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 if (rrtype == kDNS_CNAME) {
- // A CNAME must match the current target. Then we update the current target
- // and unwind the chain to the closest common ancestor.
- if (next_name->size() != target_.size() ||
- memcmp(next_name->data(), target_.data(), target_.size())) {
- return BAD_TARGET;
- }
- DCHECK_EQ(1u, rrdatas.size());
- target_ = rrdatas[0].as_string();
- // We unwind the zones until the current zone is a (non-strict) subset of
- // the new target.
- while (MatchingLabels(target_, current_zone_->name) <
- CountLabels(current_zone_->name)) {
- Zone* prev = current_zone_->prev;
- delete current_zone_;
- current_zone_ = prev;
- if (!current_zone_) {
- NOTREACHED();
- return BAD_DATA;
- }
- }
- already_entered_zone_ = true;
- } 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;
- lookahead[i] = digest.empty();
- if (!digest.empty())
- (*rrdatas)[i] = digest;
- }
-
- 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::ReadGenericRRs(
- std::vector<base::StringPiece>* rrdatas) {
- uint8 num_rrs;
- if (!U8(&num_rrs))
- return BAD_DATA;
- rrdatas->resize(num_rrs);
-
- for (unsigned i = 0; i < num_rrs; i++) {
- base::StringPiece rrdata;
- if (!VariableLength16(&rrdata))
- return BAD_DATA;
- (*rrdatas)[i] = rrdata;
- }
-
- return OK;
-}
-
-DNSSECChainVerifier::Error DNSSECChainVerifier::ReadCNAME(
- std::vector<base::StringPiece>* rrdatas) {
- base::StringPiece name;
- if (!ReadName(&name))
- return BAD_DATA;
-
- rrdatas->resize(1);
- (*rrdatas)[0] = name;
- return OK;
-}
-
-struct CAAHashTargetOID {
- uint8 length;
- uint8 bytes[13];
- DnsCAARecord::Policy::HashTarget value;
-};
-
-// kCAAHashTargets maps from the DER encoding of a hash target OID to our
-// internal enumeration values.
-static CAAHashTargetOID kCAAHashTargets[] = {
- { 5, "\x06\x03\x55\x04\x24", DnsCAARecord::Policy::USER_CERTIFICATE },
- { 5, "\x06\x03\x55\x04\x25", DnsCAARecord::Policy::CA_CERTIFICATE },
- { 12 , "\x06\x0a\x2b\x06\x01\x04\x01\xd6\x79\x02\x03\x01",
- DnsCAARecord::Policy::SUBJECT_PUBLIC_KEY_INFO},
- { 0 },
-};
-
-struct CAAHashFunctionOID {
- uint8 length;
- uint8 bytes[12];
- int value;
-};
-
-// The values here are taken from NSS's freebl/hasht.h. Sadly, we cannot
-// include it because it pulls in #defines that conflict with Chromium headers.
-// However, these values are part of NSS's public API and so are stable.
-static const int kHashNone = 0;
-static const int kHashSHA256 = 4;
-static const int kHashSHA384 = 5;
-static const int kHashSHA512 = 6;
-
-// kCAAHashFunctions maps from the DER encoding of hash function OIDs to NSS's
-// hash function enumeration.
-static CAAHashFunctionOID kCAAHashFunctions[] = {
- { 11, "\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01", kHashSHA256 },
- { 11, "\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02", kHashSHA384 },
- { 11, "\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03", kHashSHA512 },
- // WARNING: if you update this, be sure to update DigestLength too.
- { 0 },
-};
-
-// DigestLength returns the expected length of the digest for a given hash
-// function.
-static unsigned DigestLength(int algorithm) {
- switch (algorithm) {
- case kHashSHA256:
- return 32;
- case kHashSHA384:
- return 48;
- case kHashSHA512:
- return 64;
- default:
- CHECK_NE(algorithm, algorithm);
- return 0;
- }
-}
-
-DnsCAARecord::Policy::Policy() {
-}
-
-DnsCAARecord::Policy::~Policy() {
-}
-
-// ParseEnumeratedOID parses an OID from |i| and sets |*result| to one of the
-// values from |values|, depending on the OID found. |i| is then advanced over
-// the OID.
-//
-// If no OID is found then DISCARD is returned, unless |critical| is true, in
-// which case |UNKNOWN_CRITICAL| is returned.
-template<typename A, typename Enum>
-static DnsCAARecord::ParseResult ParseEnumeratedOID(base::StringPiece* i,
- bool critical,
- const A* values,
- Enum* result) {
- base::StringPiece element;
- if (!asn1::ParseElement(i, asn1::kOID, &element, NULL))
- return DnsCAARecord::SYNTAX_ERROR;
- unsigned j;
- for (j = 0; values[j].length; j++) {
- if (element.size() == values[j].length &&
- memcmp(element.data(), values[j].bytes, element.size()) == 0) {
- *result = values[j].value;
- return DnsCAARecord::SUCCESS;
- }
- }
-
- if (critical)
- return DnsCAARecord::UNKNOWN_CRITICAL;
- return DnsCAARecord::DISCARD;
-}
-
-// static
-DnsCAARecord::ParseResult DnsCAARecord::Parse(
- const std::vector<base::StringPiece>& rrdatas,
- DnsCAARecord::Policy* output) {
- // see http://tools.ietf.org/html/draft-hallambaker-donotissue-04
- output->authorized_hashes.clear();
-
- for (std::vector<base::StringPiece>::const_iterator
- ii = rrdatas.begin(); ii != rrdatas.end(); ++ii) {
- base::StringPiece i = *ii;
-
- if (i.size() < 2)
- return SYNTAX_ERROR;
- const bool critical = (i[0] & 128) != 0;
- const bool must_be_zero = (i[0] & 4) != 0;
- const bool relying_use = (i[0] & 2) != 0;
- const unsigned tag_length = i[1];
- i.remove_prefix(2);
-
- if (must_be_zero)
- return UNKNOWN_CRITICAL;
-
- if (!relying_use)
- continue;
-
- if (tag_length == 0 || tag_length > i.size())
- return SYNTAX_ERROR;
-
- const std::string tag(i.data(), tag_length);
- i.remove_prefix(tag_length);
-
- if (tag_length == 4 && LowerCaseEqualsASCII(tag, "auth")) {
- Policy::Hash hash;
-
- // AuthData ::= SEQUENCE {
- // odi ObjectDigestIdentifier
- // port INTEGER
- // }
- // ObjectDigestIdentifier ::= SEQUENCE {
- // type OBJECT IDENTIFIER,
- // digestAlgorithm OBJECT IDENTIFIER,
- // digest OCTET STRING
- // }
- base::StringPiece seq;
- if (!asn1::GetElement(&i, asn1::kSEQUENCE, &seq))
- return SYNTAX_ERROR;
-
- base::StringPiece odi;
- if (!asn1::GetElement(&seq, asn1::kSEQUENCE, &odi))
- return SYNTAX_ERROR;
-
- // type
- ParseResult r = ParseEnumeratedOID(&odi, critical, kCAAHashTargets,
- &hash.target);
- if (r == DISCARD)
- continue;
- if (r != SUCCESS)
- return r;
-
- // hash function
- r = ParseEnumeratedOID(&odi, critical, kCAAHashFunctions,
- &hash.algorithm);
- if (r == DISCARD)
- continue;
- if (r != SUCCESS)
- return r;
-
- base::StringPiece hash_bytes;
- if (!asn1::GetElement(&odi, asn1::kOCTETSTRING, &hash_bytes))
- return SYNTAX_ERROR;
-
- if (hash_bytes.size() != DigestLength(hash.algorithm))
- return SYNTAX_ERROR;
- hash.data = hash_bytes.as_string();
-
- base::StringPiece port_bytes;
- if (!asn1::GetElement(&seq, asn1::kINTEGER, &port_bytes))
- return SYNTAX_ERROR;
-
- // The port is 1, 2 or 3 bytes. The three byte form is needed because
- // values >= 0x8000 would be treated as signed unless padded with a
- // leading zero byte.
- if (port_bytes.size() == 0 || port_bytes.size() > 3)
- return SYNTAX_ERROR;
- hash.port = reinterpret_cast<const uint8*>(port_bytes.data())[0];
- if (port_bytes.size() > 1) {
- hash.port <<= 8;
- hash.port |= reinterpret_cast<const uint8*>(port_bytes.data())[1];
- }
- if (port_bytes.size() > 2) {
- hash.port <<= 8;
- hash.port |= reinterpret_cast<const uint8*>(port_bytes.data())[2];
- }
- if (hash.port > 65535)
- return SYNTAX_ERROR;
-
- output->authorized_hashes.push_back(hash);
- } else if (critical) {
- return UNKNOWN_CRITICAL;
- }
- }
-
- if (output->authorized_hashes.empty())
- return DISCARD;
-
- return SUCCESS;
-}
-
-void DnsTLSARecord::Parse(
- const std::vector<base::StringPiece>& rrdatas,
- std::vector<DnsTLSARecord::Match>* output) {
- // see https://tools.ietf.org/html/rfc6698#section-2.1
- output->clear();
-
- for (std::vector<base::StringPiece>::const_iterator
- ii = rrdatas.begin(); ii != rrdatas.end(); ++ii) {
- base::StringPiece i = *ii;
-
- if (i.size() < 3)
- continue;
-
- const uint8 cert_usage = i[0];
- const uint8 selector = i[1];
- const uint8 match_type = i[2];
- i.remove_prefix(3);
-
- if (cert_usage != 3) {
- // Type 3 is a "domain issued certificate" - i.e. a certificate (or
- // public key) which is granted authority by the TLSA record.
- continue;
- }
-
- Match match;
-
- switch (selector) {
- case 0:
- match.target = Match::CERTIFICATE;
- break;
- case 1:
- match.target = Match::SUBJECT_PUBLIC_KEY_INFO;
- break;
- default:
- continue;
- }
-
- switch (match_type) {
- case 0:
- match.algorithm = kHashNone;
- break;
- case 1:
- match.algorithm = kHashSHA256;
- break;
- case 2:
- match.algorithm = kHashSHA512;
- break;
- default:
- continue;
- }
-
- if (match.algorithm != kHashNone &&
- i.size() != DigestLength(match.algorithm)) {
- continue;
- }
-
- match.data = i.as_string();
- output->push_back(match);
- }
-}
-
-} // namespace net