summaryrefslogtreecommitdiffstats
path: root/net/base/dnssec_keyset.cc
diff options
context:
space:
mode:
authorIain Merrick <husky@google.com>2010-10-19 14:37:37 +0100
committerIain Merrick <husky@google.com>2010-10-19 14:37:37 +0100
commit3345a6884c488ff3a535c2c9acdd33d74b37e311 (patch)
tree7784b988ef1698cb6967ea1bdf07616237716c6c /net/base/dnssec_keyset.cc
parentefc8475837ec58186051f23bb03542620424f6ce (diff)
downloadexternal_chromium-3345a6884c488ff3a535c2c9acdd33d74b37e311.zip
external_chromium-3345a6884c488ff3a535c2c9acdd33d74b37e311.tar.gz
external_chromium-3345a6884c488ff3a535c2c9acdd33d74b37e311.tar.bz2
Merge Chromium at 7.0.540.0 : Initial merge by git
Not including third_party/icu as it contains huge data files that break Gerrit, and aren't actually used. Change-Id: I428a386e70f3b58cacd28677b8cfda282e891e15
Diffstat (limited to 'net/base/dnssec_keyset.cc')
-rw-r--r--net/base/dnssec_keyset.cc457
1 files changed, 457 insertions, 0 deletions
diff --git a/net/base/dnssec_keyset.cc b/net/base/dnssec_keyset.cc
new file mode 100644
index 0000000..70aa217
--- /dev/null
+++ b/net/base/dnssec_keyset.cc
@@ -0,0 +1,457 @@
+// 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) {
+}
+
+DNSSECKeySet::~DNSSECKeySet() {
+}
+
+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