Bug 970542, Part 8: IPAddress name constraint tests, r=keeler
authorBrian Smith <brian@briansmith.org>
Sun, 26 Oct 2014 16:57:00 -0700
changeset 218438 95e601e8223122ba0a3e9773403286ba70506641
parent 218437 f6c8d35284f5b0f6c1ff4ad90be75b1184976efe
child 218439 d42a1fc52756e2a374f67483ced04d64d91b4bc0
push id27931
push usercbook@mozilla.com
push dateFri, 05 Dec 2014 12:00:38 +0000
treeherdermozilla-central@e7f3e6fee15e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskeeler
bugs970542
milestone37.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 970542, Part 8: IPAddress name constraint tests, r=keeler
security/pkix/lib/pkixnames.cpp
security/pkix/test/gtest/pkixnames_tests.cpp
security/pkix/test/lib/pkixtestutil.h
--- a/security/pkix/lib/pkixnames.cpp
+++ b/security/pkix/lib/pkixnames.cpp
@@ -1120,35 +1120,45 @@ PresentedDNSIDMatchesReferenceDNSID(
 Result
 MatchPresentedIPAddressWithConstraint(Input presentedID,
                                       Input iPAddressConstraint,
                                       /*out*/ bool& foundMatch)
 {
   if (presentedID.GetLength() != 4 && presentedID.GetLength() != 16) {
     return Result::ERROR_BAD_DER;
   }
+  if (iPAddressConstraint.GetLength() != 8 &&
+      iPAddressConstraint.GetLength() != 32) {
+    return Result::ERROR_BAD_DER;
+  }
 
-  Reader presented(presentedID);
+  // an IPv4 address never matches an IPv6 constraint, and vice versa.
+  if (presentedID.GetLength() * 2 != iPAddressConstraint.GetLength()) {
+    foundMatch = false;
+    return Success;
+  }
+
   Reader constraint(iPAddressConstraint);
   Reader constraintAddress;
-  Result rv = constraint.Skip(presentedID.GetLength(),
+  Result rv = constraint.Skip(iPAddressConstraint.GetLength() / 2u,
                               constraintAddress);
   if (rv != Success) {
     return rv;
   }
   Reader constraintMask;
-  rv = constraint.Skip(presentedID.GetLength(), constraintMask);
+  rv = constraint.Skip(iPAddressConstraint.GetLength() / 2u, constraintMask);
   if (rv != Success) {
     return rv;
   }
   rv = der::End(constraint);
   if (rv != Success) {
     return rv;
   }
 
+  Reader presented(presentedID);
   do {
     uint8_t presentedByte;
     rv = presented.Read(presentedByte);
     if (rv != Success) {
       return rv;
     }
     uint8_t constraintAddressByte;
     rv = constraintAddress.Read(constraintAddressByte);
--- a/security/pkix/test/gtest/pkixnames_tests.cpp
+++ b/security/pkix/test/gtest/pkixnames_tests.cpp
@@ -1059,24 +1059,136 @@ static const uint8_t ipv6_addr_bytes_as_
   "\x11\x22\x33\x44"
   "\x55\x66\x77\x88"
   "\x99\xaa\xbb\xcc"
   "\xdd\xee\xff\x11";
 
 static const uint8_t ipv6_addr_str[] =
   "1122:3344:5566:7788:99aa:bbcc:ddee:ff11";
 
+static const uint8_t ipv6_other_addr_bytes[] = {
+  0xff, 0xee, 0xdd, 0xcc,
+  0xbb, 0xaa, 0x99, 0x88,
+  0x77, 0x66, 0x55, 0x44,
+  0x33, 0x22, 0x11, 0x00,
+};
+
 static const uint8_t ipv4_other_addr_bytes[] = {
   5, 6, 7, 8
 };
 static const uint8_t ipv4_other_addr_str[] = "5.6.7.8";
 static const uint8_t ipv4_other_addr_bytes_FFFFFFFF[] = {
   5, 6, 7, 8, 0xff, 0xff, 0xff, 0xff
 };
 
+static const uint8_t ipv4_addr_00000000_bytes[] = {
+  0, 0, 0, 0
+};
+static const uint8_t ipv4_addr_FFFFFFFF_bytes[] = {
+  0, 0, 0, 0
+};
+
+static const uint8_t ipv4_constraint_all_zeros_bytes[] = {
+  0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static const uint8_t ipv6_addr_all_zeros_bytes[] = {
+  0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+static const uint8_t ipv6_constraint_all_zeros_bytes[] = {
+  0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static const uint8_t ipv4_constraint_CIDR_16_bytes[] = {
+  1, 2, 0, 0, 0xff, 0xff, 0, 0
+};
+static const uint8_t ipv4_constraint_CIDR_17_bytes[] = {
+  1, 2, 0, 0, 0xff, 0xff, 0x80, 0
+};
+
+// The subnet is 1.2.0.0/16 but it is specified as 1.2.3.0/16
+static const uint8_t ipv4_constraint_CIDR_16_bad_addr_bytes[] = {
+  1, 2, 3, 0, 0xff, 0xff, 0, 0
+};
+
+// Masks are supposed to be of the form <ones><zeros>, but this one is of the
+// form <ones><zeros><ones><zeros>.
+static const uint8_t ipv4_constraint_bad_mask_bytes[] = {
+  1, 2, 3, 0, 0xff, 0, 0xff, 0
+};
+
+static const uint8_t ipv6_constraint_CIDR_16_bytes[] = {
+  0x11, 0x22, 0, 0, 0, 0, 0, 0,
+     0,    0, 0, 0, 0, 0, 0, 0,
+  0xff, 0xff, 0, 0, 0, 0, 0, 0,
+     0,    0, 0, 0, 0, 0, 0, 0
+};
+
+// The subnet is 1122::/16 but it is specified as 1122:3344::/16
+static const uint8_t ipv6_constraint_CIDR_16_bad_addr_bytes[] = {
+  0x11, 0x22, 0x33, 0x44, 0, 0, 0, 0,
+     0,    0,    0,    0, 0, 0, 0, 0,
+  0xff, 0xff,    0,    0, 0, 0, 0, 0,
+     0,    0,    0,    0, 0, 0, 0, 0
+};
+
+// Masks are supposed to be of the form <ones><zeros>, but this one is of the
+// form <ones><zeros><ones><zeros>.
+static const uint8_t ipv6_constraint_bad_mask_bytes[] = {
+  0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0, 0,
+     0,    0,    0,    0,    0,    0, 0, 0,
+  0xff, 0xff,    0,    0, 0xff, 0xff, 0, 0,
+     0,    0,    0,    0,    0,    0, 0, 0,
+};
+
+static const uint8_t ipv4_addr_truncated_bytes[] = {
+  1, 2, 3
+};
+static const uint8_t ipv4_addr_overlong_bytes[] = {
+  1, 2, 3, 4, 5
+};
+static const uint8_t ipv4_constraint_truncated_bytes[] = {
+  0, 0, 0, 0,
+  0, 0, 0,
+};
+static const uint8_t ipv4_constraint_overlong_bytes[] = {
+  0, 0, 0, 0,
+  0, 0, 0, 0, 0
+};
+
+static const uint8_t ipv6_addr_truncated_bytes[] = {
+  0x11, 0x22, 0x33, 0x44,
+  0x55, 0x66, 0x77, 0x88,
+  0x99, 0xaa, 0xbb, 0xcc,
+  0xdd, 0xee, 0xff
+};
+static const uint8_t ipv6_addr_overlong_bytes[] = {
+  0x11, 0x22, 0x33, 0x44,
+  0x55, 0x66, 0x77, 0x88,
+  0x99, 0xaa, 0xbb, 0xcc,
+  0xdd, 0xee, 0xff, 0x11, 0x00
+};
+static const uint8_t ipv6_constraint_truncated_bytes[] = {
+  0x11, 0x22, 0, 0, 0, 0, 0, 0,
+     0,    0, 0, 0, 0, 0, 0, 0,
+  0xff, 0xff, 0, 0, 0, 0, 0, 0,
+     0,    0, 0, 0, 0, 0, 0
+};
+static const uint8_t ipv6_constraint_overlong_bytes[] = {
+  0x11, 0x22, 0, 0, 0, 0, 0, 0,
+     0,    0, 0, 0, 0, 0, 0, 0,
+  0xff, 0xff, 0, 0, 0, 0, 0, 0,
+     0,    0, 0, 0, 0, 0, 0, 0, 0
+};
+
 // Note that, for DNSNames, these test cases in CHECK_CERT_HOSTNAME_PARAMS are
 // mostly about testing different scenerios regarding the structure of entries
 // in the subjectAltName and subject of the certificate, than about the how
 // specific presented identifier values are matched against the reference
 // identifier values. This is because we also use the test cases in
 // DNSNAMES_VALIDITY to test CheckCertHostname. Consequently, tests about
 // whether specific presented DNSNames (including wildcards, in particular) are
 // matched against a reference DNSName only need to be added to
@@ -1685,16 +1797,175 @@ static const NameConstraintParams NAME_C
     Result::ERROR_CERT_NOT_IN_NAME_SPACE, Success,
   },
   { ByteString(), DNSName("example.com."),
     GeneralSubtree(DNSName(".")),
     Success, Result::ERROR_CERT_NOT_IN_NAME_SPACE
   },
 
   /////////////////////////////////////////////////////////////////////////////
+  // Basic IP Address constraints (non-CN-ID)
+
+  // The Mozilla CA Policy says this means "no IPv4 addresses allowed."
+  { ByteString(), IPAddress(ipv4_addr_bytes),
+    GeneralSubtree(IPAddress(ipv4_constraint_all_zeros_bytes)),
+    Success, Result::ERROR_CERT_NOT_IN_NAME_SPACE
+  },
+  { ByteString(), IPAddress(ipv4_addr_00000000_bytes),
+    GeneralSubtree(IPAddress(ipv4_constraint_all_zeros_bytes)),
+    Success, Result::ERROR_CERT_NOT_IN_NAME_SPACE
+  },
+  { ByteString(), IPAddress(ipv4_addr_FFFFFFFF_bytes),
+    GeneralSubtree(IPAddress(ipv4_constraint_all_zeros_bytes)),
+    Success, Result::ERROR_CERT_NOT_IN_NAME_SPACE
+  },
+
+  // The Mozilla CA Policy says this means "no IPv6 addresses allowed."
+  { ByteString(), IPAddress(ipv6_addr_bytes),
+    GeneralSubtree(IPAddress(ipv6_constraint_all_zeros_bytes)),
+    Success, Result::ERROR_CERT_NOT_IN_NAME_SPACE
+  },
+  { ByteString(), IPAddress(ipv6_addr_all_zeros_bytes),
+    GeneralSubtree(IPAddress(ipv6_constraint_all_zeros_bytes)),
+    Success, Result::ERROR_CERT_NOT_IN_NAME_SPACE
+  },
+
+  // RFC 5280 doesn't partition IP address constraints into separate IPv4 and
+  // IPv6 categories, so a IPv4 permittedSubtrees constraint excludes all IPv6
+  // addresses, and vice versa.
+  { ByteString(), IPAddress(ipv4_addr_bytes),
+    GeneralSubtree(IPAddress(ipv6_constraint_all_zeros_bytes)),
+    Result::ERROR_CERT_NOT_IN_NAME_SPACE, Success
+  },
+  { ByteString(), IPAddress(ipv6_addr_bytes),
+    GeneralSubtree(IPAddress(ipv4_constraint_all_zeros_bytes)),
+    Result::ERROR_CERT_NOT_IN_NAME_SPACE, Success
+  },
+
+  // IPv4 Subnets
+  { ByteString(), IPAddress(ipv4_addr_bytes),
+    GeneralSubtree(IPAddress(ipv4_constraint_CIDR_16_bytes)),
+    Success, Result::ERROR_CERT_NOT_IN_NAME_SPACE
+  },
+  { ByteString(), IPAddress(ipv4_addr_bytes),
+    GeneralSubtree(IPAddress(ipv4_constraint_CIDR_17_bytes)),
+    Success, Result::ERROR_CERT_NOT_IN_NAME_SPACE
+  },
+  { ByteString(), IPAddress(ipv4_other_addr_bytes),
+    GeneralSubtree(IPAddress(ipv4_constraint_CIDR_16_bytes)),
+    Result::ERROR_CERT_NOT_IN_NAME_SPACE, Success
+  },
+  { // XXX(bug 1089430): We don't reject this even though it is weird.
+    ByteString(), IPAddress(ipv4_addr_bytes),
+    GeneralSubtree(IPAddress(ipv4_constraint_CIDR_16_bad_addr_bytes)),
+    Success, Result::ERROR_CERT_NOT_IN_NAME_SPACE
+  },
+  { // XXX(bug 1089430): We don't reject this even though it is weird.
+    ByteString(), IPAddress(ipv4_other_addr_bytes),
+    GeneralSubtree(IPAddress(ipv4_constraint_bad_mask_bytes)),
+    Result::ERROR_CERT_NOT_IN_NAME_SPACE, Success
+  },
+
+  // IPv6 Subnets
+  { ByteString(), IPAddress(ipv6_addr_bytes),
+    GeneralSubtree(IPAddress(ipv6_constraint_CIDR_16_bytes)),
+    Success, Result::ERROR_CERT_NOT_IN_NAME_SPACE
+  },
+  { ByteString(), IPAddress(ipv6_other_addr_bytes),
+    GeneralSubtree(IPAddress(ipv6_constraint_CIDR_16_bytes)),
+    Result::ERROR_CERT_NOT_IN_NAME_SPACE, Success
+  },
+  { // XXX(bug 1089430): We don't reject this even though it is weird.
+    ByteString(), IPAddress(ipv6_addr_bytes),
+    GeneralSubtree(IPAddress(ipv6_constraint_CIDR_16_bad_addr_bytes)),
+    Success, Result::ERROR_CERT_NOT_IN_NAME_SPACE
+  },
+  { // XXX(bug 1089430): We don't reject this even though it is weird.
+    ByteString(), IPAddress(ipv6_other_addr_bytes),
+    GeneralSubtree(IPAddress(ipv6_constraint_bad_mask_bytes)),
+    Result::ERROR_CERT_NOT_IN_NAME_SPACE, Success
+  },
+
+  // Malformed presented IP addresses and constraints
+
+  { // The presented IPv4 address is empty
+    ByteString(), IPAddress(),
+    GeneralSubtree(IPAddress(ipv4_constraint_all_zeros_bytes)),
+    Result::ERROR_BAD_DER, Result::ERROR_BAD_DER
+  },
+  { // The presented IPv4 address is truncated
+    ByteString(), IPAddress(ipv4_addr_truncated_bytes),
+    GeneralSubtree(IPAddress(ipv4_constraint_all_zeros_bytes)),
+    Result::ERROR_BAD_DER, Result::ERROR_BAD_DER
+  },
+  { // The presented IPv4 address is too long
+    ByteString(), IPAddress(ipv4_addr_overlong_bytes),
+    GeneralSubtree(IPAddress(ipv4_constraint_all_zeros_bytes)),
+    Result::ERROR_BAD_DER, Result::ERROR_BAD_DER
+  },
+  { // The presented IPv4 constraint is empty
+    ByteString(), IPAddress(ipv4_addr_bytes),
+    GeneralSubtree(IPAddress()),
+    Result::ERROR_BAD_DER, Result::ERROR_BAD_DER
+  },
+  { // The presented IPv4 constraint is truncated
+    ByteString(), IPAddress(ipv4_addr_bytes),
+    GeneralSubtree(IPAddress(ipv4_addr_truncated_bytes)),
+    Result::ERROR_BAD_DER, Result::ERROR_BAD_DER
+  },
+  { // The presented IPv4 constraint is too long
+    ByteString(), IPAddress(ipv4_addr_bytes),
+    GeneralSubtree(IPAddress(ipv4_addr_overlong_bytes)),
+    Result::ERROR_BAD_DER, Result::ERROR_BAD_DER
+  },
+  { // The presented IPv6 address is empty
+    ByteString(), IPAddress(),
+    GeneralSubtree(IPAddress(ipv6_constraint_all_zeros_bytes)),
+    Result::ERROR_BAD_DER, Result::ERROR_BAD_DER
+  },
+  { // The presented IPv6 address is truncated
+    ByteString(), IPAddress(ipv6_addr_truncated_bytes),
+    GeneralSubtree(IPAddress(ipv6_constraint_all_zeros_bytes)),
+    Result::ERROR_BAD_DER, Result::ERROR_BAD_DER
+  },
+  { // The presented IPv6 address is too long
+    ByteString(), IPAddress(ipv6_addr_overlong_bytes),
+    GeneralSubtree(IPAddress(ipv6_constraint_all_zeros_bytes)),
+    Result::ERROR_BAD_DER, Result::ERROR_BAD_DER
+  },
+  { // The presented IPv6 constraint is empty
+    ByteString(), IPAddress(ipv6_addr_bytes),
+    GeneralSubtree(IPAddress()),
+    Result::ERROR_BAD_DER, Result::ERROR_BAD_DER
+  },
+  { // The presented IPv6 constraint is truncated
+    ByteString(), IPAddress(ipv6_addr_bytes),
+    GeneralSubtree(IPAddress(ipv6_addr_truncated_bytes)),
+    Result::ERROR_BAD_DER, Result::ERROR_BAD_DER
+  },
+  { // The presented IPv6 constraint is too long
+    ByteString(), IPAddress(ipv6_addr_bytes),
+    GeneralSubtree(IPAddress(ipv6_addr_overlong_bytes)),
+    Result::ERROR_BAD_DER, Result::ERROR_BAD_DER
+  },
+
+  /////////////////////////////////////////////////////////////////////////////
+  // XXX: We don't reject malformed name constraints when there are no names of
+  // that type.
+  { ByteString(), NO_SAN, GeneralSubtree(DNSName("!")),
+    Success, Success
+  },
+  { ByteString(), NO_SAN, GeneralSubtree(IPAddress(ipv4_addr_overlong_bytes)),
+    Success, Success
+  },
+  { ByteString(), NO_SAN, GeneralSubtree(IPAddress(ipv6_addr_overlong_bytes)),
+    Success, Success
+  },
+
+  /////////////////////////////////////////////////////////////////////////////
   // Basic CN-ID DNSName constraint tests.
 
   { // Empty Name is ignored for DNSName constraints.
     ByteString(), NO_SAN, GeneralSubtree(DNSName("a.example.com")),
     Success, Success
   },
   { // Empty CN is ignored for DNSName constraints because it isn't a
     // syntactically-valid DNSName.
--- a/security/pkix/test/lib/pkixtestutil.h
+++ b/security/pkix/test/lib/pkixtestutil.h
@@ -192,16 +192,23 @@ DNSName(const ByteString& name)
 template <size_t L>
 inline ByteString
 DNSName(const char (&bytes)[L])
 {
   return DNSName(ByteString(reinterpret_cast<const uint8_t (&)[L]>(bytes),
                             L - 1));
 }
 
+inline ByteString
+IPAddress()
+{
+  // (2 << 6) means "context-specific", 7 is the GeneralName tag.
+  return TLV((2 << 6) | 7, ByteString());
+}
+
 template <size_t L>
 inline ByteString
 IPAddress(const uint8_t (&bytes)[L])
 {
   // (2 << 6) means "context-specific", 7 is the GeneralName tag.
   return TLV((2 << 6) | 7, ByteString(bytes, L));
 }