// From http://tools.ietf.org/html/rfc6960#section-4.1.1: // "The hash shall be calculated over the value (excluding tag and length) of // the subject public key field in the issuer's certificate." // // From http://tools.ietf.org/html/rfc6960#appendix-B.1: // KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key // -- (i.e., the SHA-1 hash of the value of the // -- BIT STRING subjectPublicKey [excluding // -- the tag, length, and number of unused // -- bits] in the responder's certificate) static Result MatchKeyHash(TrustDomain& trustDomain, Input keyHash, const Input subjectPublicKeyInfo, /*out*/ bool& match) { if (keyHash.GetLength() != TrustDomain::DIGEST_LENGTH) { return Result::ERROR_OCSP_MALFORMED_RESPONSE; } static uint8_t hashBuf[TrustDomain::DIGEST_LENGTH]; Result rv = KeyHash(trustDomain, subjectPublicKeyInfo, hashBuf, sizeof hashBuf); if (rv != Success) { return rv; } Input computed(hashBuf); match = InputsAreEqual(computed, keyHash); return Success; }
// The code that executes in the inner loop of BuildForward Result PathBuildingStep::Check(Input potentialIssuerDER, /*optional*/ const Input* additionalNameConstraints, /*out*/ bool& keepGoing) { BackCert potentialIssuer(potentialIssuerDER, EndEntityOrCA::MustBeCA, &subject); Result rv = potentialIssuer.Init(); if (rv != Success) { return RecordResult(rv, keepGoing); } // RFC5280 4.2.1.1. Authority Key Identifier // RFC5280 4.2.1.2. Subject Key Identifier // Loop prevention, done as recommended by RFC4158 Section 5.2 // TODO: this doesn't account for subjectAltNames! // TODO(perf): This probably can and should be optimized in some way. bool loopDetected = false; for (const BackCert* prev = potentialIssuer.childCert; !loopDetected && prev != nullptr; prev = prev->childCert) { if (InputsAreEqual(potentialIssuer.GetSubjectPublicKeyInfo(), prev->GetSubjectPublicKeyInfo()) && InputsAreEqual(potentialIssuer.GetSubject(), prev->GetSubject())) { // XXX: error code return RecordResult(Result::ERROR_UNKNOWN_ISSUER, keepGoing); } } if (potentialIssuer.GetNameConstraints()) { rv = CheckNameConstraints(*potentialIssuer.GetNameConstraints(), subject, requiredEKUIfPresent); if (rv != Success) { return RecordResult(rv, keepGoing); } } if (additionalNameConstraints) { rv = CheckNameConstraints(*additionalNameConstraints, subject, requiredEKUIfPresent); if (rv != Success) { return RecordResult(rv, keepGoing); } } // RFC 5280, Section 4.2.1.3: "If the keyUsage extension is present, then the // subject public key MUST NOT be used to verify signatures on certificates // or CRLs unless the corresponding keyCertSign or cRLSign bit is set." rv = BuildForward(trustDomain, potentialIssuer, time, KeyUsage::keyCertSign, requiredEKUIfPresent, requiredPolicy, nullptr, subCACount); if (rv != Success) { return RecordResult(rv, keepGoing); } rv = trustDomain.VerifySignedData(subject.GetSignedData(), potentialIssuer.GetSubjectPublicKeyInfo()); if (rv != Success) { return RecordResult(rv, keepGoing); } CertID certID(subject.GetIssuer(), potentialIssuer.GetSubjectPublicKeyInfo(), subject.GetSerialNumber()); rv = trustDomain.CheckRevocation(subject.endEntityOrCA, certID, time, stapledOCSPResponse, subject.GetAuthorityInfoAccess()); if (rv != Success) { return RecordResult(rv, keepGoing); } return RecordResult(Success, keepGoing); }
// CertID ::= SEQUENCE { // hashAlgorithm AlgorithmIdentifier, // issuerNameHash OCTET STRING, -- Hash of issuer's DN // issuerKeyHash OCTET STRING, -- Hash of issuer's public key // serialNumber CertificateSerialNumber } static inline Result CertID(Reader& input, const Context& context, /*out*/ bool& match) { match = false; DigestAlgorithm hashAlgorithm; Result rv = der::DigestAlgorithmIdentifier(input, hashAlgorithm); if (rv != Success) { if (rv == Result::ERROR_INVALID_ALGORITHM) { // Skip entries that are hashed with algorithms we don't support. input.SkipToEnd(); return Success; } return rv; } Input issuerNameHash; rv = der::ExpectTagAndGetValue(input, der::OCTET_STRING, issuerNameHash); if (rv != Success) { return rv; } Input issuerKeyHash; rv = der::ExpectTagAndGetValue(input, der::OCTET_STRING, issuerKeyHash); if (rv != Success) { return rv; } Input serialNumber; rv = der::CertificateSerialNumber(input, serialNumber); if (rv != Success) { return rv; } if (!InputsAreEqual(serialNumber, context.certID.serialNumber)) { // This does not reference the certificate we're interested in. // Consume the rest of the input and return successfully to // potentially continue processing other responses. input.SkipToEnd(); return Success; } // TODO: support SHA-2 hashes. if (hashAlgorithm != DigestAlgorithm::sha1) { // Again, not interested in this response. Consume input, return success. input.SkipToEnd(); return Success; } if (issuerNameHash.GetLength() != TrustDomain::DIGEST_LENGTH) { return Result::ERROR_OCSP_MALFORMED_RESPONSE; } // From http://tools.ietf.org/html/rfc6960#section-4.1.1: // "The hash shall be calculated over the DER encoding of the // issuer's name field in the certificate being checked." uint8_t hashBuf[TrustDomain::DIGEST_LENGTH]; rv = context.trustDomain.DigestBuf(context.certID.issuer, hashBuf, sizeof(hashBuf)); if (rv != Success) { return rv; } Input computed(hashBuf); if (!InputsAreEqual(computed, issuerNameHash)) { // Again, not interested in this response. Consume input, return success. input.SkipToEnd(); return Success; } return MatchKeyHash(context.trustDomain, issuerKeyHash, context.certID.issuerSubjectPublicKeyInfo, match); }