// Extension ::= SEQUENCE { // extnID OBJECT IDENTIFIER, // critical BOOLEAN DEFAULT FALSE, // extnValue OCTET STRING // } static der::Result CheckExtensionForCriticality(der::Input& input) { uint16_t toSkip; if (ExpectTagAndGetLength(input, der::OIDTag, toSkip) != der::Success) { return der::Failure; } // TODO: maybe we should check the syntax of the OID value if (input.Skip(toSkip) != der::Success) { return der::Failure; } // The only valid explicit encoding of the value is TRUE, so don't even // bother parsing it, since we're going to fail either way. if (input.Peek(der::BOOLEAN)) { return der::Fail(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION); } if (ExpectTagAndGetLength(input, der::OCTET_STRING, toSkip) != der::Success) { return der::Failure; } return input.Skip(toSkip); }
// ResponseData ::= SEQUENCE { // version [0] EXPLICIT Version DEFAULT v1, // responderID ResponderID, // producedAt GeneralizedTime, // responses SEQUENCE OF SingleResponse, // responseExtensions [1] EXPLICIT Extensions OPTIONAL } static inline der::Result ResponseData(der::Input& input, Context& context, const CERTSignedData& signedResponseData, /*const*/ SECItem* certs, size_t numCerts) { uint8_t version; if (der::OptionalVersion(input, version) != der::Success) { return der::Failure; } if (version != der::v1) { // TODO: more specific error code for bad version? return der::Fail(SEC_ERROR_BAD_DER); } // ResponderID ::= CHOICE { // byName [1] Name, // byKey [2] KeyHash } SECItem responderID; uint16_t responderIDLength; ResponderIDType responderIDType = input.Peek(static_cast<uint8_t>(ResponderIDType::byName)) ? ResponderIDType::byName : ResponderIDType::byKey; if (ExpectTagAndGetLength(input, static_cast<uint8_t>(responderIDType), responderIDLength) != der::Success) { return der::Failure; } // TODO: responderID probably needs to have another level of ASN1 tag/length // checked and stripped. if (input.Skip(responderIDLength, responderID) != der::Success) { return der::Failure; } // 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. if (VerifySignature(context, responderIDType, responderID, certs, numCerts, signedResponseData) != SECSuccess) { return der::Failure; } // TODO: Do we even need to parse this? Should we just skip it? PRTime producedAt; if (der::GeneralizedTime(input, producedAt) != der::Success) { return der::Failure; } // 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. if (der::NestedOf(input, der::SEQUENCE, der::SEQUENCE, der::MustNotBeEmpty, bind(SingleResponse, _1, ref(context))) != der::Success) { return der::Failure; } if (!input.AtEnd()) { if (CheckExtensionsForCriticality(input) != der::Success) { return der::Failure; } } return der::Success; }
// BasicOCSPResponse ::= SEQUENCE { // tbsResponseData ResponseData, // signatureAlgorithm AlgorithmIdentifier, // signature BIT STRING, // certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } der::Result BasicResponse(der::Input& input, Context& context) { der::Input::Mark mark(input.GetMark()); uint16_t length; if (der::ExpectTagAndGetLength(input, der::SEQUENCE, length) != der::Success) { return der::Failure; } // The signature covers the entire DER encoding of tbsResponseData, including // the beginning tag and length. However, when we're parsing tbsResponseData, // we want to strip off the tag and length because we don't need it after // we've confirmed it's there and figured out what length it is. der::Input tbsResponseData; if (input.Skip(length, tbsResponseData) != der::Success) { return der::Failure; } CERTSignedData signedData; input.GetSECItem(siBuffer, mark, signedData.data); if (der::Nested(input, der::SEQUENCE, bind(der::AlgorithmIdentifier, _1, ref(signedData.signatureAlgorithm))) != der::Success) { return der::Failure; } if (der::Skip(input, der::BIT_STRING, signedData.signature) != der::Success) { return der::Failure; } if (signedData.signature.len == 0) { return der::Fail(SEC_ERROR_OCSP_BAD_SIGNATURE); } unsigned int unusedBitsAtEnd = signedData.signature.data[0]; // XXX: Really the constraint should be that unusedBitsAtEnd must be less // than 7. But, we suspect there are no valid OCSP response signatures with // non-zero unused bits. It seems like NSS assumes this in various places, so // we enforce it. If we find compatibility issues, we'll know we're wrong. if (unusedBitsAtEnd != 0) { return der::Fail(SEC_ERROR_OCSP_BAD_SIGNATURE); } ++signedData.signature.data; --signedData.signature.len; signedData.signature.len = (signedData.signature.len << 3); // Bytes to bits // Parse certificates, if any SECItem certs[8]; size_t numCerts = 0; if (!input.AtEnd()) { // We ignore the lengths of the wrappers because we'll detect bad lengths // during parsing--too short and we'll run out of input for parsing a cert, // and too long and we'll have leftover data that won't parse as a cert. // [0] wrapper if (der::ExpectTagAndIgnoreLength( input, der::CONSTRUCTED | der::CONTEXT_SPECIFIC | 0) != der::Success) { return der::Failure; } // SEQUENCE wrapper if (der::ExpectTagAndIgnoreLength(input, der::SEQUENCE) != der::Success) { return der::Failure; } // sequence of certificates while (!input.AtEnd()) { if (numCerts == PR_ARRAY_SIZE(certs)) { return der::Fail(SEC_ERROR_BAD_DER); } // Unwrap the SEQUENCE that contains the certificate, which is itself a // SEQUENCE. der::Input::Mark mark(input.GetMark()); if (der::Skip(input, der::SEQUENCE) != der::Success) { return der::Failure; } input.GetSECItem(siBuffer, mark, certs[numCerts]); ++numCerts; } } return ResponseData(tbsResponseData, context, signedData, certs, numCerts); }