// 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(Input& input, Context& context, const SignedDataWithSignature& signedResponseData, /*const*/ SECItem* certs, size_t numCerts) { 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 Fail(SEC_ERROR_BAD_DER); } // ResponderID ::= CHOICE { // byName [1] Name, // byKey [2] KeyHash } SECItem 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, numCerts, signedResponseData); if (rv != Success) { return rv; } // TODO: Do we even need to parse this? Should we just skip it? PRTime producedAt; 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); }
Result BackCert::Init() { Result rv; // Certificate ::= SEQUENCE { // tbsCertificate TBSCertificate, // signatureAlgorithm AlgorithmIdentifier, // signatureValue BIT STRING } Input tbsCertificate; // The scope of |input| and |certificate| are limited to this block so we // don't accidentally confuse them for tbsCertificate later. { Input input; rv = input.Init(der.data, der.len); if (rv != Success) { return rv; } Input 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. if (version == der::Version::v3) { rv = der::OptionalExtensions(tbsCertificate, CSC | 3, bind(&BackCert::RememberExtension, this, _1, _2, _3)); if (rv != Success) { return rv; } } 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(Input& 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::ExpectTagAndLength(input, static_cast<uint8_t>(CertStatus::Good), 0); 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::ExpectTagAndLength(input, static_cast<uint8_t>(CertStatus::Unknown), 0); 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. const PRTime maxLifetime = context.maxLifetimeInDays * ONE_DAY; PRTime thisUpdate; rv = der::GeneralizedTime(input, thisUpdate); if (rv != Success) { return rv; } if (thisUpdate > context.time + SLOP) { return Fail(SEC_ERROR_OCSP_FUTURE_RESPONSE); } PRTime notAfter; static const uint8_t NEXT_UPDATE_TAG = der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 0; if (input.Peek(NEXT_UPDATE_TAG)) { PRTime nextUpdate; rv = der::Nested(input, NEXT_UPDATE_TAG, bind(der::GeneralizedTime, _1, ref(nextUpdate))); if (rv != Success) { return rv; } if (nextUpdate < thisUpdate) { return Fail(SEC_ERROR_OCSP_MALFORMED_RESPONSE); } if (nextUpdate - thisUpdate <= maxLifetime) { notAfter = nextUpdate; } else { notAfter = thisUpdate + maxLifetime; } } else { // NSS requires all OCSP responses without a nextUpdate to be recent. // Match that stricter behavior. notAfter = thisUpdate + ONE_DAY; } if (context.time < SLOP) { // prevent underflow return Fail(SEC_ERROR_INVALID_ARGS); } if (context.time - SLOP > notAfter) { 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 = notAfter; } return Success; }