Bug 1257031 - Return more informative error code when encountering invalid integers rather than SEC_ERROR_BAD_DER. r=keeler
authorCykesiopka <cykesiopka.bmo@gmail.com>
Thu, 21 Apr 2016 16:41:22 -0700
changeset 332585 35edab8d84dbe447525af059eb79f528cd2db24a
parent 332584 cecb988dcacb3532adca881a93ef3fb0585f90f0
child 332586 2195629bf525893e0576b0f6af0f04159459da9e
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-beta@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskeeler
bugs1257031
milestone48.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 1257031 - Return more informative error code when encountering invalid integers rather than SEC_ERROR_BAD_DER. r=keeler Also adds some missing l10n entries to nsserrors.properties (but not for errors that are specific to TLS 1.3, since TLS 1.3 is not yet finalised). MozReview-Commit-ID: A42fmTDTe8W
security/manager/locales/en-US/chrome/pipnss/nsserrors.properties
security/pkix/include/pkix/Result.h
security/pkix/include/pkix/pkixnss.h
security/pkix/lib/pkixder.cpp
security/pkix/lib/pkixnss.cpp
security/pkix/test/gtest/pkixder_pki_types_tests.cpp
security/pkix/test/gtest/pkixder_universal_types_tests.cpp
--- a/security/manager/locales/en-US/chrome/pipnss/nsserrors.properties
+++ b/security/manager/locales/en-US/chrome/pipnss/nsserrors.properties
@@ -128,16 +128,21 @@ SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_VERS
 SSL_ERROR_RX_UNEXPECTED_CERT_STATUS=SSL received an unexpected Certificate Status handshake message.
 SSL_ERROR_UNSUPPORTED_HASH_ALGORITHM=Unsupported hash algorithm used by TLS peer.
 SSL_ERROR_DIGEST_FAILURE=Digest function failed.
 SSL_ERROR_INCORRECT_SIGNATURE_ALGORITHM=Incorrect signature algorithm specified in a digitally-signed element.
 SSL_ERROR_NEXT_PROTOCOL_NO_CALLBACK=The next protocol negotiation extension was enabled, but the callback was cleared prior to being needed.
 SSL_ERROR_NEXT_PROTOCOL_NO_PROTOCOL=The server supports no protocols that the client advertises in the ALPN extension.
 SSL_ERROR_INAPPROPRIATE_FALLBACK_ALERT=The server rejected the handshake because the client downgraded to a lower TLS version than the server supports.
 SSL_ERROR_WEAK_SERVER_CERT_KEY=The server certificate included a public key that was too weak.
+SSL_ERROR_RX_SHORT_DTLS_READ=Not enough room in buffer for DTLS record.
+SSL_ERROR_NO_SUPPORTED_SIGNATURE_ALGORITHM=No supported TLS signature algorithm was configured.
+SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM=The peer used an unsupported combination of signature and hash algorithm.
+SSL_ERROR_MISSING_EXTENDED_MASTER_SECRET=The peer tried to resume without a correct extended_master_secret extension.
+SSL_ERROR_UNEXPECTED_EXTENDED_MASTER_SECRET=The peer tried to resume with an unexpected extended_master_secret extension.
 SEC_ERROR_IO=An I/O error occurred during security authorization.
 SEC_ERROR_LIBRARY_FAILURE=security library failure.
 SEC_ERROR_BAD_DATA=security library: received bad data.
 SEC_ERROR_OUTPUT_LEN=security library: output length error.
 SEC_ERROR_INPUT_LEN=security library has experienced an input length error.
 SEC_ERROR_INVALID_ARGS=security library: invalid arguments.
 SEC_ERROR_INVALID_ALGORITHM=security library: invalid algorithm.
 SEC_ERROR_INVALID_AVA=security library: invalid AVA.
@@ -315,8 +320,9 @@ MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_E
 MOZILLA_PKIX_ERROR_INADEQUATE_KEY_SIZE=The server presented a certificate with a key size that is too small to establish a secure connection.
 MOZILLA_PKIX_ERROR_V1_CERT_USED_AS_CA=An X.509 version 1 certificate that is not a trust anchor was used to issue the server's certificate. X.509 version 1 certificates are deprecated and should not be used to sign other certificates.
 MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE=The server presented a certificate that is not yet valid.
 MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE=A certificate that is not yet valid was used to issue the server's certificate.
 MOZILLA_PKIX_ERROR_SIGNATURE_ALGORITHM_MISMATCH=The signature algorithm in the signature field of the certificate does not match the algorithm in its signatureAlgorithm field.
 MOZILLA_PKIX_ERROR_OCSP_RESPONSE_FOR_CERT_MISSING=The OCSP response does not include a status for the certificate being verified.
 MOZILLA_PKIX_ERROR_VALIDITY_TOO_LONG=The server presented a certificate that is valid for too long.
 MOZILLA_PKIX_ERROR_REQUIRED_TLS_FEATURE_MISSING=A required TLS feature is missing.
+MOZILLA_PKIX_ERROR_INVALID_INTEGER_ENCODING=The server presented a certificate that contains an invalid encoding of an integer. Common causes include negative serial numbers, negative RSA moduli, and encodings that are longer than necessary.
--- a/security/pkix/include/pkix/Result.h
+++ b/security/pkix/include/pkix/Result.h
@@ -182,16 +182,18 @@ static const unsigned int FATAL_ERROR_FL
     MOZILLA_PKIX_MAP(ERROR_SIGNATURE_ALGORITHM_MISMATCH, 48, \
                      MOZILLA_PKIX_ERROR_SIGNATURE_ALGORITHM_MISMATCH) \
     MOZILLA_PKIX_MAP(ERROR_OCSP_RESPONSE_FOR_CERT_MISSING, 49, \
                      MOZILLA_PKIX_ERROR_OCSP_RESPONSE_FOR_CERT_MISSING) \
     MOZILLA_PKIX_MAP(ERROR_VALIDITY_TOO_LONG, 50, \
                      MOZILLA_PKIX_ERROR_VALIDITY_TOO_LONG) \
     MOZILLA_PKIX_MAP(ERROR_REQUIRED_TLS_FEATURE_MISSING, 51, \
                      MOZILLA_PKIX_ERROR_REQUIRED_TLS_FEATURE_MISSING) \
+    MOZILLA_PKIX_MAP(ERROR_INVALID_INTEGER_ENCODING, 52, \
+                     MOZILLA_PKIX_ERROR_INVALID_INTEGER_ENCODING) \
     MOZILLA_PKIX_MAP(FATAL_ERROR_INVALID_ARGS, FATAL_ERROR_FLAG | 1, \
                      SEC_ERROR_INVALID_ARGS) \
     MOZILLA_PKIX_MAP(FATAL_ERROR_INVALID_STATE, FATAL_ERROR_FLAG | 2, \
                      PR_INVALID_STATE_ERROR) \
     MOZILLA_PKIX_MAP(FATAL_ERROR_LIBRARY_FAILURE, FATAL_ERROR_FLAG | 3, \
                      SEC_ERROR_LIBRARY_FAILURE) \
     MOZILLA_PKIX_MAP(FATAL_ERROR_NO_MEMORY, FATAL_ERROR_FLAG | 4, \
                      SEC_ERROR_NO_MEMORY) \
--- a/security/pkix/include/pkix/pkixnss.h
+++ b/security/pkix/include/pkix/pkixnss.h
@@ -80,16 +80,17 @@ enum ErrorCode
   MOZILLA_PKIX_ERROR_V1_CERT_USED_AS_CA = ERROR_BASE + 3,
   MOZILLA_PKIX_ERROR_NO_RFC822NAME_MATCH = ERROR_BASE + 4,
   MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE = ERROR_BASE + 5,
   MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE = ERROR_BASE + 6,
   MOZILLA_PKIX_ERROR_SIGNATURE_ALGORITHM_MISMATCH = ERROR_BASE + 7,
   MOZILLA_PKIX_ERROR_OCSP_RESPONSE_FOR_CERT_MISSING = ERROR_BASE + 8,
   MOZILLA_PKIX_ERROR_VALIDITY_TOO_LONG = ERROR_BASE + 9,
   MOZILLA_PKIX_ERROR_REQUIRED_TLS_FEATURE_MISSING = ERROR_BASE + 10,
+  MOZILLA_PKIX_ERROR_INVALID_INTEGER_ENCODING = ERROR_BASE + 11,
 };
 
 void RegisterErrorTable();
 
 inline SECItem UnsafeMapInputToSECItem(Input input)
 {
   SECItem result = {
     siBuffer,
--- a/security/pkix/lib/pkixder.cpp
+++ b/security/pkix/lib/pkixder.cpp
@@ -497,45 +497,49 @@ IntegralBytes(Reader& input, uint8_t tag
   }
   Reader reader(value);
 
   // There must be at least one byte in the value. (Zero is encoded with a
   // single 0x00 value byte.)
   uint8_t firstByte;
   rv = reader.Read(firstByte);
   if (rv != Success) {
+    if (rv == Result::ERROR_BAD_DER) {
+      return Result::ERROR_INVALID_INTEGER_ENCODING;
+    }
+
     return rv;
   }
 
   // If there is a byte after an initial 0x00/0xFF, then the initial byte
   // indicates a positive/negative integer value with its high bit set/unset.
   bool prefixed = !reader.AtEnd() && (firstByte == 0 || firstByte == 0xff);
 
   if (prefixed) {
     uint8_t nextByte;
     if (reader.Read(nextByte) != Success) {
       return NotReached("Read of one byte failed but not at end.",
                         Result::FATAL_ERROR_LIBRARY_FAILURE);
     }
     if ((firstByte & 0x80) == (nextByte & 0x80)) {
-      return Result::ERROR_BAD_DER;
+      return Result::ERROR_INVALID_INTEGER_ENCODING;
     }
   }
 
   switch (valueRestriction) {
     case IntegralValueRestriction::MustBe0To127:
       if (value.GetLength() != 1 || (firstByte & 0x80) != 0) {
-        return Result::ERROR_BAD_DER;
+        return Result::ERROR_INVALID_INTEGER_ENCODING;
       }
       break;
 
     case IntegralValueRestriction::MustBePositive:
       if ((value.GetLength() == 1 && firstByte == 0) ||
           (firstByte & 0x80) != 0) {
-        return Result::ERROR_BAD_DER;
+        return Result::ERROR_INVALID_INTEGER_ENCODING;
       }
       break;
 
     case IntegralValueRestriction::NoRestriction:
       break;
   }
 
   if (significantBytes) {
--- a/security/pkix/lib/pkixnss.cpp
+++ b/security/pkix/lib/pkixnss.cpp
@@ -199,16 +199,20 @@ RegisterErrorTable()
       "not match the algorithm in its signatureAlgorithm field." },
     { "MOZILLA_PKIX_ERROR_OCSP_RESPONSE_FOR_CERT_MISSING",
       "The OCSP response does not include a status for the certificate being "
       "verified." },
     { "MOZILLA_PKIX_ERROR_VALIDITY_TOO_LONG",
       "The server presented a certificate that is valid for too long." },
     { "MOZILLA_PKIX_ERROR_REQUIRED_TLS_FEATURE_MISSING",
       "A required TLS feature is missing." },
+    { "MOZILLA_PKIX_ERROR_INVALID_INTEGER_ENCODING",
+      "The server presented a certificate that contains an invalid encoding of "
+      "an integer. Common causes include negative serial numbers, negative RSA "
+      "moduli, and encodings that are longer than necessary." },
   };
   // Note that these error strings are not localizable.
   // When these strings change, update the localization information too.
 
   static const PRErrorTable ErrorTable = {
     ErrorTableText,
     "pkixerrors",
     ERROR_BASE,
--- a/security/pkix/test/gtest/pkixder_pki_types_tests.cpp
+++ b/security/pkix/test/gtest/pkixder_pki_types_tests.cpp
@@ -93,17 +93,18 @@ TEST_F(pkixder_pki_types_tests, Certific
   const uint8_t DER_CERT_SERIAL_ZERO_LENGTH[] = {
     0x02,                       // INTEGER
     0x00                        // length
   };
   Input input(DER_CERT_SERIAL_ZERO_LENGTH);
   Reader reader(input);
 
   Input item;
-  ASSERT_EQ(Result::ERROR_BAD_DER, CertificateSerialNumber(reader, item));
+  ASSERT_EQ(Result::ERROR_INVALID_INTEGER_ENCODING,
+            CertificateSerialNumber(reader, item));
 }
 
 TEST_F(pkixder_pki_types_tests, OptionalVersionV1ExplicitEncodingAllowed)
 {
   const uint8_t DER_OPTIONAL_VERSION_V1[] = {
     0xa0, 0x03,                   // context specific 0
     0x02, 0x01, 0x00              // INTEGER(0)
   };
--- a/security/pkix/test/gtest/pkixder_universal_types_tests.cpp
+++ b/security/pkix/test/gtest/pkixder_universal_types_tests.cpp
@@ -223,17 +223,17 @@ TEST_F(pkixder_universal_types_tests, En
     0x0a,                       // ENUMERATED
     0x02,                       // length
     0x00, 0x01                  // value
   };
   Input input(DER_ENUMERATED);
   Reader reader(input);
 
   uint8_t value = 0;
-  ASSERT_EQ(Result::ERROR_BAD_DER, Enumerated(reader, value));
+  ASSERT_EQ(Result::ERROR_INVALID_INTEGER_ENCODING, Enumerated(reader, value));
 }
 
 TEST_F(pkixder_universal_types_tests, EnumeratedOutOfAcceptedRange)
 {
   // Although this is a valid ENUMERATED value according to ASN.1, we
   // intentionally don't support these large values because there are no
   // ENUMERATED values in X.509 certs or OCSP this large, and we're trying to
   // keep the parser simple and fast.
@@ -241,30 +241,30 @@ TEST_F(pkixder_universal_types_tests, En
     0x0a,                       // ENUMERATED
     0x02,                       // length
     0x12, 0x34                  // value
   };
   Input input(DER_ENUMERATED_INVALID_LENGTH);
   Reader reader(input);
 
   uint8_t value = 0;
-  ASSERT_EQ(Result::ERROR_BAD_DER, Enumerated(reader, value));
+  ASSERT_EQ(Result::ERROR_INVALID_INTEGER_ENCODING, Enumerated(reader, value));
 }
 
 TEST_F(pkixder_universal_types_tests, EnumeratedInvalidZeroLength)
 {
   const uint8_t DER_ENUMERATED_INVALID_ZERO_LENGTH[] = {
     0x0a,                       // ENUMERATED
     0x00                        // length
   };
   Input input(DER_ENUMERATED_INVALID_ZERO_LENGTH);
   Reader reader(input);
 
   uint8_t value = 0;
-  ASSERT_EQ(Result::ERROR_BAD_DER, Enumerated(reader, value));
+  ASSERT_EQ(Result::ERROR_INVALID_INTEGER_ENCODING, Enumerated(reader, value));
 }
 
 ////////////////////////////////////////
 // GeneralizedTime and TimeChoice
 //
 // From RFC 5280 section 4.1.2.5.2
 //
 //   For the purposes of this profile, GeneralizedTime values MUST be
@@ -892,87 +892,151 @@ TEST_F(pkixder_universal_types_tests, Ti
   ExpectBadTime(DER_GENERALIZED_TIME_INVALID_FRACTIONAL_SECONDS);
 }
 
 struct IntegerTestParams
 {
   ByteString encoded;
   struct PositiveIntegerParams
   {
-    bool isValid;
+    Result expectedResult;
     Input::size_type significantBytesIfValid;
   } positiveInteger;
-  uint8_t smallNonnegativeIntegerValue;
+  struct SmallNonnegativeIntegerParams
+  {
+    Result expectedResult;
+    uint8_t valueIfValid;
+  } smallNonnegativeInteger;
 };
 
 class pkixder_universal_types_tests_Integer
   : public ::testing::Test
   , public ::testing::WithParamInterface<IntegerTestParams>
 {
 };
 
 #define INVALID 0xFF
 
 static const IntegerTestParams INTEGER_TEST_PARAMS[] =
 {
   // Zero is encoded with one value byte of 0x00.
-  { TLV(2, ByteString()), { false, INVALID }, INVALID },
-  { TLV(2, "\x00"), { false, INVALID }, 0 },
+  { TLV(2, ByteString()),
+    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID },
+    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },
+  { TLV(2, "\x00"),
+    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID },
+    { Success, 0 } },
 
   // Positive single-byte values
-  { TLV(2, "\x01"), { true, 1 }, 1 },
-  { TLV(2, "\x02"), { true, 1 }, 2 },
-  { TLV(2, "\x7e"), { true, 1 }, 0x7e },
-  { TLV(2, "\x7f"), { true, 1 }, 0x7f },
+  { TLV(2, "\x01"), { Success, 1 }, { Success, 1} },
+  { TLV(2, "\x02"), { Success, 1 }, { Success, 2} },
+  { TLV(2, "\x7e"), { Success, 1 }, { Success, 0x7e} },
+  { TLV(2, "\x7f"), { Success, 1 }, { Success, 0x7f} },
 
   // Negative single-byte values
-  { TLV(2, "\x80"), { false, INVALID }, INVALID },
-  { TLV(2, "\x81"), { false, INVALID }, INVALID },
-  { TLV(2, "\xFE"), { false, INVALID }, INVALID },
-  { TLV(2, "\xFF"), { false, INVALID }, INVALID },
+  { TLV(2, "\x80"),
+    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID },
+    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },
+  { TLV(2, "\x81"),
+    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID },
+    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },
+  { TLV(2, "\xFE"),
+    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID },
+    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },
+  { TLV(2, "\xFF"),
+    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID },
+    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },
 
   // Positive two-byte values not starting with 0x00
-  { TLV(2, "\x7F\x00"), { true, 2 }, INVALID },
-  { TLV(2, "\x01\x00"), { true, 2 }, INVALID },
-  { TLV(2, "\x01\x02"), { true, 2 }, INVALID },
+  { TLV(2, "\x7F\x00"),
+    { Success, 2 },
+    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },
+  { TLV(2, "\x01\x00"),
+    { Success, 2 },
+    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },
+  { TLV(2, "\x01\x02"),
+    { Success, 2 },
+    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },
 
   // Negative two-byte values not starting with 0xFF
-  { TLV(2, "\x80\x00"), { false, INVALID }, INVALID },
-  { TLV(2, "\x80\x7F"), { false, INVALID }, INVALID },
-  { TLV(2, "\x80\x80"), { false, INVALID }, INVALID },
-  { TLV(2, "\x80\xFF"), { false, INVALID }, INVALID },
+  { TLV(2, "\x80\x00"),
+    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID },
+    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },
+  { TLV(2, "\x80\x7F"),
+    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID },
+    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },
+  { TLV(2, "\x80\x80"),
+    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID },
+    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },
+  { TLV(2, "\x80\xFF"),
+    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID },
+    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },
 
   // The leading zero is necessary.
-  { TLV(2, "\x00\x80"), { true, 1}, INVALID },
-  { TLV(2, "\x00\x81"), { true, 1}, INVALID },
-  { TLV(2, "\x00\xFF"), { true, 1}, INVALID },
+  { TLV(2, "\x00\x80"),
+    { Success, 1},
+    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },
+  { TLV(2, "\x00\x81"),
+    { Success, 1},
+    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },
+  { TLV(2, "\x00\xFF"),
+    { Success, 1},
+    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },
 
   // The leading zero is unnecessary.
-  { TLV(2, "\x00\x01"), { false, INVALID }, INVALID },
-  { TLV(2, "\x00\x7F"), { false, INVALID }, INVALID },
+  { TLV(2, "\x00\x01"),
+    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID },
+    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },
+  { TLV(2, "\x00\x7F"),
+    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID },
+    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },
 
   // The leading 0xFF is necessary.
-  { TLV(2, "\xFF\x00"), { false, INVALID }, INVALID },
-  { TLV(2, "\xFF\x7F"), { false, INVALID }, INVALID },
+  { TLV(2, "\xFF\x00"),
+    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID },
+    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },
+  { TLV(2, "\xFF\x7F"),
+    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID },
+    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },
 
   // The leading 0xFF is unnecessary.
-  { TLV(2, "\xFF\x80"), { false, INVALID }, INVALID },
-  { TLV(2, "\xFF\xFF"), { false, INVALID }, INVALID },
+  { TLV(2, "\xFF\x80"),
+    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID },
+    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },
+  { TLV(2, "\xFF\xFF"),
+    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID },
+    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },
 
   // Truncated values
-  { TLV(2, 1, ByteString(/*missing value*/)), { false, INVALID }, INVALID },
-  { TLV(2, 3, "\x11\x22" /*truncated*/), { false, INVALID }, INVALID },
-  { TLV(2, 4, "\x11\x22" /*truncated*/), { false, INVALID }, INVALID },
-  { TLV(2, 2, "\x00" /*truncated*/), { false, INVALID }, INVALID },
-  { TLV(2, 2, "\xFF" /*truncated*/), { false, INVALID }, INVALID },
-  { TLV(2, 3, "\x00\x80" /*truncated*/), { false, INVALID }, INVALID },
-  { TLV(2, 3, "\xFF\x00" /*truncated*/), { false, INVALID }, INVALID },
+  { TLV(2, 1, ByteString(/*missing value*/)),
+    { Result::ERROR_BAD_DER, INVALID },
+    { Result::ERROR_BAD_DER, INVALID } },
+  { TLV(2, 3, "\x11\x22" /*truncated*/),
+    { Result::ERROR_BAD_DER, INVALID },
+    { Result::ERROR_BAD_DER, INVALID } },
+  { TLV(2, 4, "\x11\x22" /*truncated*/),
+    { Result::ERROR_BAD_DER, INVALID },
+    { Result::ERROR_BAD_DER, INVALID } },
+  { TLV(2, 2, "\x00" /*truncated*/),
+    { Result::ERROR_BAD_DER, INVALID },
+    { Result::ERROR_BAD_DER, INVALID } },
+  { TLV(2, 2, "\xFF" /*truncated*/),
+    { Result::ERROR_BAD_DER, INVALID },
+    { Result::ERROR_BAD_DER, INVALID } },
+  { TLV(2, 3, "\x00\x80" /*truncated*/),
+    { Result::ERROR_BAD_DER, INVALID },
+    { Result::ERROR_BAD_DER, INVALID } },
+  { TLV(2, 3, "\xFF\x00" /*truncated*/),
+    { Result::ERROR_BAD_DER, INVALID },
+    { Result::ERROR_BAD_DER, INVALID } },
 
   // Misc. larger values
-  { TLV(2, 4, "\x11\x22\x33\x44"), { true, 4 }, INVALID },
+  { TLV(2, 4, "\x11\x22\x33\x44"),
+    { Success, 4 },
+    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },
   { TLV(2,
         "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x00"
         "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x00"
         "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x00"
         "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x00"
         "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x00"
         "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x00"
         "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x00"
@@ -981,48 +1045,45 @@ static const IntegerTestParams INTEGER_T
         "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x00"
         "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x00"
         "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x00"
         "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x00"
         "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x00"
         "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x00"
         "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x00"
         "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x00"),
-    { true, 256 }, INVALID },
+    { Success, 256 },
+    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },
 };
 
 TEST_P(pkixder_universal_types_tests_Integer, Integer)
 {
   const IntegerTestParams& params(GetParam());
   Input input;
   ASSERT_EQ(Success, input.Init(params.encoded.data(),
                                 params.encoded.length()));
   Reader reader(input);
-  Result expectedResult = params.smallNonnegativeIntegerValue != INVALID
-                        ? Success
-                        : Result::ERROR_BAD_DER;
+  Result expectedResult = params.smallNonnegativeInteger.expectedResult;
   uint8_t value;
   ASSERT_EQ(expectedResult, der::Integer(reader, value));
   if (expectedResult == Success) {
-    ASSERT_EQ(params.smallNonnegativeIntegerValue, value);
+    ASSERT_EQ(params.smallNonnegativeInteger.valueIfValid, value);
     ASSERT_TRUE(reader.AtEnd());
   }
 }
 
 TEST_P(pkixder_universal_types_tests_Integer,
        PositiveInteger_without_significantBytes)
 {
   const IntegerTestParams& params(GetParam());
   Input input;
   ASSERT_EQ(Success, input.Init(params.encoded.data(),
                                 params.encoded.length()));
   Reader reader(input);
-  Result expectedResult = params.positiveInteger.isValid
-                        ? Success
-                        : Result::ERROR_BAD_DER;
+  Result expectedResult = params.positiveInteger.expectedResult;
   Input value;
   ASSERT_EQ(expectedResult, der::PositiveInteger(reader, value));
   if (expectedResult == Success) {
     Reader anotherReader(input);
     Input expectedValue;
     ASSERT_EQ(Success, ExpectTagAndGetValue(anotherReader,
                                             der::INTEGER, expectedValue));
     ASSERT_TRUE(InputsAreEqual(expectedValue, value));
@@ -1033,19 +1094,17 @@ TEST_P(pkixder_universal_types_tests_Int
 TEST_P(pkixder_universal_types_tests_Integer,
        PositiveInteger_with_significantBytes)
 {
   const IntegerTestParams& params(GetParam());
   Input input;
   ASSERT_EQ(Success, input.Init(params.encoded.data(),
                                 params.encoded.length()));
   Reader reader(input);
-  Result expectedResult = params.positiveInteger.isValid
-                        ? Success
-                        : Result::ERROR_BAD_DER;
+  Result expectedResult = params.positiveInteger.expectedResult;
   Input value;
   Input::size_type significantBytes = INVALID;
   ASSERT_EQ(expectedResult, der::PositiveInteger(reader, value,
                                                  &significantBytes));
   if (expectedResult == Success) {
     ASSERT_NE(INVALID, params.positiveInteger.significantBytesIfValid);
     ASSERT_EQ(params.positiveInteger.significantBytesIfValid,
               significantBytes);