// ResponseData ::= SEQUENCE { // version [0] EXPLICIT Version DEFAULT v1, // responderID ResponderID, // producedAt GeneralizedTime, // responses SEQUENCE OF SingleResponse, // responseExtensions [1] EXPLICIT Extensions OPTIONAL } static inline Result ResponseData(Reader& input, Context& context, const SignedDataWithSignature& signedResponseData, const DERArray& certs) { der::Version version; Result rv = der::OptionalVersion(input, version); if (rv != Success) { return rv; } if (version != der::Version::v1) { // TODO: more specific error code for bad version? return Result::ERROR_BAD_DER; } // ResponderID ::= CHOICE { // byName [1] Name, // byKey [2] KeyHash } Input responderID; ResponderIDType responderIDType = input.Peek(static_cast<uint8_t>(ResponderIDType::byName)) ? ResponderIDType::byName : ResponderIDType::byKey; rv = der::ExpectTagAndGetValue(input, static_cast<uint8_t>(responderIDType), responderID); if (rv != Success) { return rv; } // This is the soonest we can verify the signature. We verify the signature // right away to follow the principal of minimizing the processing of data // before verifying its signature. rv = VerifySignature(context, responderIDType, responderID, certs, signedResponseData); if (rv != Success) { return rv; } // TODO: Do we even need to parse this? Should we just skip it? Time producedAt(Time::uninitialized); rv = der::GeneralizedTime(input, producedAt); if (rv != Success) { return rv; } // We don't accept an empty sequence of responses. In practice, a legit OCSP // responder will never return an empty response, and handling the case of an // empty response makes things unnecessarily complicated. rv = der::NestedOf(input, der::SEQUENCE, der::SEQUENCE, der::EmptyAllowed::No, bind(SingleResponse, _1, ref(context))); if (rv != Success) { return rv; } return der::OptionalExtensions(input, der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 1, ExtensionNotUnderstood); }
static Result OptionalNull(Reader& input) { if (input.Peek(NULLTag)) { return Null(input); } return Success; }
Result BackCert::Init() { Result rv; // Certificate ::= SEQUENCE { // tbsCertificate TBSCertificate, // signatureAlgorithm AlgorithmIdentifier, // signatureValue BIT STRING } Reader tbsCertificate; // The scope of |input| and |certificate| are limited to this block so we // don't accidentally confuse them for tbsCertificate later. { Reader certificate; rv = der::ExpectTagAndGetValueAtEnd(der, der::SEQUENCE, certificate); if (rv != Success) { return rv; } rv = der::SignedData(certificate, tbsCertificate, signedData); if (rv != Success) { return rv; } rv = der::End(certificate); if (rv != Success) { return rv; } } // TBSCertificate ::= SEQUENCE { // version [0] EXPLICIT Version DEFAULT v1, // serialNumber CertificateSerialNumber, // signature AlgorithmIdentifier, // issuer Name, // validity Validity, // subject Name, // subjectPublicKeyInfo SubjectPublicKeyInfo, // issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, // -- If present, version MUST be v2 or v3 // subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, // -- If present, version MUST be v2 or v3 // extensions [3] EXPLICIT Extensions OPTIONAL // -- If present, version MUST be v3 // } rv = der::OptionalVersion(tbsCertificate, version); if (rv != Success) { return rv; } rv = der::CertificateSerialNumber(tbsCertificate, serialNumber); if (rv != Success) { return rv; } rv = der::ExpectTagAndGetValue(tbsCertificate, der::SEQUENCE, signature); if (rv != Success) { return rv; } rv = der::ExpectTagAndGetTLV(tbsCertificate, der::SEQUENCE, issuer); if (rv != Success) { return rv; } rv = der::ExpectTagAndGetValue(tbsCertificate, der::SEQUENCE, validity); if (rv != Success) { return rv; } // TODO(bug XXXXXXX): We rely on the the caller of mozilla::pkix to validate // that the name is syntactically valid, if they care. In Gecko we do this // implicitly by parsing the certificate into a CERTCertificate object. // Instead of relying on the caller to do this, we should do it ourselves. rv = der::ExpectTagAndGetTLV(tbsCertificate, der::SEQUENCE, subject); if (rv != Success) { return rv; } rv = der::ExpectTagAndGetTLV(tbsCertificate, der::SEQUENCE, subjectPublicKeyInfo); if (rv != Success) { return rv; } static const uint8_t CSC = der::CONTEXT_SPECIFIC | der::CONSTRUCTED; // According to RFC 5280, all fields below this line are forbidden for // certificate versions less than v3. However, for compatibility reasons, // we parse v1/v2 certificates in the same way as v3 certificates. So if // these fields appear in a v1 certificate, they will be used. // Ignore issuerUniqueID if present. if (tbsCertificate.Peek(CSC | 1)) { rv = der::ExpectTagAndSkipValue(tbsCertificate, CSC | 1); if (rv != Success) { return rv; } } // Ignore subjectUniqueID if present. if (tbsCertificate.Peek(CSC | 2)) { rv = der::ExpectTagAndSkipValue(tbsCertificate, CSC | 2); if (rv != Success) { return rv; } } rv = der::OptionalExtensions( tbsCertificate, CSC | 3, [this](Reader& extnID, const Input& extnValue, bool critical, /*out*/ bool& understood) { return RememberExtension(extnID, extnValue, critical, understood); }); if (rv != Success) { return rv; } // The Netscape Certificate Type extension is an obsolete // Netscape-proprietary mechanism that we ignore in favor of the standard // extensions. However, some CAs have issued certificates with the Netscape // Cert Type extension marked critical. Thus, for compatibility reasons, we // "understand" this extension by ignoring it when it is not critical, and // by ensuring that the equivalent standardized extensions are present when // it is marked critical, based on the assumption that the information in // the Netscape Cert Type extension is consistent with the information in // the standard extensions. // // Here is a mapping between the Netscape Cert Type extension and the // standard extensions: // // Netscape Cert Type | BasicConstraints.cA | Extended Key Usage // --------------------+-----------------------+---------------------- // SSL Server | false | id_kp_serverAuth // SSL Client | false | id_kp_clientAuth // S/MIME Client | false | id_kp_emailProtection // Object Signing | false | id_kp_codeSigning // SSL Server CA | true | id_pk_serverAuth // SSL Client CA | true | id_kp_clientAuth // S/MIME CA | true | id_kp_emailProtection // Object Signing CA | true | id_kp_codeSigning if (criticalNetscapeCertificateType.GetLength() > 0 && (basicConstraints.GetLength() == 0 || extKeyUsage.GetLength() == 0)) { return Result::ERROR_UNKNOWN_CRITICAL_EXTENSION; } return der::End(tbsCertificate); }
// SingleResponse ::= SEQUENCE { // certID CertID, // certStatus CertStatus, // thisUpdate GeneralizedTime, // nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL, // singleExtensions [1] EXPLICIT Extensions{{re-ocsp-crl | // re-ocsp-archive-cutoff | // CrlEntryExtensions, ...} // } OPTIONAL } static inline Result SingleResponse(Reader& input, Context& context) { bool match = false; Result rv = der::Nested(input, der::SEQUENCE, bind(CertID, _1, cref(context), ref(match))); if (rv != Success) { return rv; } if (!match) { // This response does not reference the certificate we're interested in. // By consuming the rest of our input and returning successfully, we can // continue processing and examine another response that might have what // we want. input.SkipToEnd(); return Success; } // CertStatus ::= CHOICE { // good [0] IMPLICIT NULL, // revoked [1] IMPLICIT RevokedInfo, // unknown [2] IMPLICIT UnknownInfo } // // In the event of multiple SingleResponses for a cert that have conflicting // statuses, we use the following precedence rules: // // * revoked overrides good and unknown // * good overrides unknown if (input.Peek(static_cast<uint8_t>(CertStatus::Good))) { rv = der::ExpectTagAndEmptyValue(input, static_cast<uint8_t>(CertStatus::Good)); if (rv != Success) { return rv; } if (context.certStatus != CertStatus::Revoked) { context.certStatus = CertStatus::Good; } } else if (input.Peek(static_cast<uint8_t>(CertStatus::Revoked))) { // We don't need any info from the RevokedInfo structure, so we don't even // parse it. TODO: We should mention issues like this in the explanation of // why we treat invalid OCSP responses equivalently to revoked for OCSP // stapling. rv = der::ExpectTagAndSkipValue(input, static_cast<uint8_t>(CertStatus::Revoked)); if (rv != Success) { return rv; } context.certStatus = CertStatus::Revoked; } else { rv = der::ExpectTagAndEmptyValue(input, static_cast<uint8_t>(CertStatus::Unknown)); if (rv != Success) { return rv; } } // http://tools.ietf.org/html/rfc6960#section-3.2 // 5. The time at which the status being indicated is known to be // correct (thisUpdate) is sufficiently recent; // 6. When available, the time at or before which newer information will // be available about the status of the certificate (nextUpdate) is // greater than the current time. Time thisUpdate(Time::uninitialized); rv = der::GeneralizedTime(input, thisUpdate); if (rv != Success) { return rv; } static const uint64_t SLOP_SECONDS = Time::ONE_DAY_IN_SECONDS; Time timePlusSlop(context.time); rv = timePlusSlop.AddSeconds(SLOP_SECONDS); if (rv != Success) { return rv; } if (thisUpdate > timePlusSlop) { return Result::ERROR_OCSP_FUTURE_RESPONSE; } Time notAfter(Time::uninitialized); static const uint8_t NEXT_UPDATE_TAG = der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 0; if (input.Peek(NEXT_UPDATE_TAG)) { Time nextUpdate(Time::uninitialized); rv = der::Nested(input, NEXT_UPDATE_TAG, bind(der::GeneralizedTime, _1, ref(nextUpdate))); if (rv != Success) { return rv; } if (nextUpdate < thisUpdate) { return Result::ERROR_OCSP_MALFORMED_RESPONSE; } notAfter = thisUpdate; if (notAfter.AddSeconds(context.maxLifetimeInDays * Time::ONE_DAY_IN_SECONDS) != Success) { // This could only happen if we're dealing with times beyond the year // 10,000AD. return Result::ERROR_OCSP_FUTURE_RESPONSE; } if (nextUpdate <= notAfter) { notAfter = nextUpdate; } } else { // NSS requires all OCSP responses without a nextUpdate to be recent. // Match that stricter behavior. notAfter = thisUpdate; if (notAfter.AddSeconds(Time::ONE_DAY_IN_SECONDS) != Success) { // This could only happen if we're dealing with times beyond the year // 10,000AD. return Result::ERROR_OCSP_FUTURE_RESPONSE; } } // Add some slop to hopefully handle clock-skew. Time notAfterPlusSlop(notAfter); rv = notAfterPlusSlop.AddSeconds(SLOP_SECONDS); if (rv != Success) { // This could only happen if we're dealing with times beyond the year // 10,000AD. return Result::ERROR_OCSP_FUTURE_RESPONSE; } if (context.time > notAfterPlusSlop) { context.expired = true; } rv = der::OptionalExtensions(input, der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 1, ExtensionNotUnderstood); if (rv != Success) { return rv; } if (context.thisUpdate) { *context.thisUpdate = thisUpdate; } if (context.validThrough) { *context.validThrough = notAfterPlusSlop; } return Success; }
Result BackCert::Init() { Result rv; // Certificate ::= SEQUENCE { // tbsCertificate TBSCertificate, // signatureAlgorithm AlgorithmIdentifier, // signatureValue BIT STRING } Reader tbsCertificate; // The scope of |input| and |certificate| are limited to this block so we // don't accidentally confuse them for tbsCertificate later. { Reader input(der); Reader certificate; rv = der::ExpectTagAndGetValue(input, der::SEQUENCE, certificate); if (rv != Success) { return rv; } rv = der::End(input); if (rv != Success) { return rv; } rv = der::SignedData(certificate, tbsCertificate, signedData); if (rv != Success) { return rv; } rv = der::End(certificate); if (rv != Success) { return rv; } } // TBSCertificate ::= SEQUENCE { // version [0] EXPLICIT Version DEFAULT v1, // serialNumber CertificateSerialNumber, // signature AlgorithmIdentifier, // issuer Name, // validity Validity, // subject Name, // subjectPublicKeyInfo SubjectPublicKeyInfo, // issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, // -- If present, version MUST be v2 or v3 // subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, // -- If present, version MUST be v2 or v3 // extensions [3] EXPLICIT Extensions OPTIONAL // -- If present, version MUST be v3 // } rv = der::OptionalVersion(tbsCertificate, version); if (rv != Success) { return rv; } rv = der::CertificateSerialNumber(tbsCertificate, serialNumber); if (rv != Success) { return rv; } // XXX: Ignored. What are we supposed to check? This seems totally redundant // with Certificate.signatureAlgorithm. Is it important to check that they // are consistent with each other? It doesn't seem to matter! SignatureAlgorithm signature; rv = der::SignatureAlgorithmIdentifier(tbsCertificate, signature); if (rv != Success) { return rv; } rv = der::ExpectTagAndGetTLV(tbsCertificate, der::SEQUENCE, issuer); if (rv != Success) { return rv; } rv = der::ExpectTagAndGetValue(tbsCertificate, der::SEQUENCE, validity); if (rv != Success) { return rv; } // TODO(bug XXXXXXX): We rely on the the caller of mozilla::pkix to validate // that the name is syntactically valid, if they care. In Gecko we do this // implicitly by parsing the certificate into a CERTCertificate object. // Instead of relying on the caller to do this, we should do it ourselves. rv = der::ExpectTagAndGetTLV(tbsCertificate, der::SEQUENCE, subject); if (rv != Success) { return rv; } // TODO(bug XXXXXXX): We defer parsing/validating subjectPublicKeyInfo to // the point where the public key is needed. For end-entity certificates, we // assume that the caller will extract the public key and use it somehow; if // they don't do that then we'll never know whether the key is invalid. On // the other hand, if the caller never uses the key then in some ways it // doesn't matter. Regardless, we should parse and validate // subjectPublicKeyKeyInfo internally. rv = der::ExpectTagAndGetTLV(tbsCertificate, der::SEQUENCE, subjectPublicKeyInfo); if (rv != Success) { return rv; } static const uint8_t CSC = der::CONTEXT_SPECIFIC | der::CONSTRUCTED; // RFC 5280 says: "These fields MUST only appear if the version is 2 or 3 // (Section 4.1.2.1). These fields MUST NOT appear if the version is 1." if (version != der::Version::v1) { // Ignore issuerUniqueID if present. if (tbsCertificate.Peek(CSC | 1)) { rv = der::ExpectTagAndSkipValue(tbsCertificate, CSC | 1); if (rv != Success) { return rv; } } // Ignore subjectUniqueID if present. if (tbsCertificate.Peek(CSC | 2)) { rv = der::ExpectTagAndSkipValue(tbsCertificate, CSC | 2); if (rv != Success) { return rv; } } } // Extensions were added in v3, so only accept extensions in v3 certificates. // v4 certificates are not defined but there are some certificates issued // with v4 that expect v3 decoding. For compatibility reasons we handle them // as v3 certificates. if (version == der::Version::v3 || version == der::Version::v4) { rv = der::OptionalExtensions(tbsCertificate, CSC | 3, bind(&BackCert::RememberExtension, this, _1, _2, _3, _4)); if (rv != Success) { return rv; } // The Netscape Certificate Type extension is an obsolete // Netscape-proprietary mechanism that we ignore in favor of the standard // extensions. However, some CAs have issued certificates with the Netscape // Cert Type extension marked critical. Thus, for compatibility reasons, we // "understand" this extension by ignoring it when it is not critical, and // by ensuring that the equivalent standardized extensions are present when // it is marked critical, based on the assumption that the information in // the Netscape Cert Type extension is consistent with the information in // the standard extensions. // // Here is a mapping between the Netscape Cert Type extension and the // standard extensions: // // Netscape Cert Type | BasicConstraints.cA | Extended Key Usage // --------------------+-----------------------+---------------------- // SSL Server | false | id_kp_serverAuth // SSL Client | false | id_kp_clientAuth // S/MIME Client | false | id_kp_emailProtection // Object Signing | false | id_kp_codeSigning // SSL Server CA | true | id_pk_serverAuth // SSL Client CA | true | id_kp_clientAuth // S/MIME CA | true | id_kp_emailProtection // Object Signing CA | true | id_kp_codeSigning if (criticalNetscapeCertificateType.GetLength() > 0 && (basicConstraints.GetLength() == 0 || extKeyUsage.GetLength() == 0)) { return Result::ERROR_UNKNOWN_CRITICAL_EXTENSION; } } return der::End(tbsCertificate); }