RTDECL(int) RTCrX509Certificate_VerifySignature(PCRTCRX509CERTIFICATE pThis, PCRTASN1OBJID pAlgorithm, PCRTASN1DYNTYPE pParameters, PCRTASN1BITSTRING pPublicKey, PRTERRINFO pErrInfo) { /* * Validate the input a little. */ AssertPtrReturn(pThis, VERR_INVALID_POINTER); AssertReturn(RTCrX509Certificate_IsPresent(pThis), VERR_INVALID_PARAMETER); AssertPtrReturn(pAlgorithm, VERR_INVALID_POINTER); AssertReturn(RTAsn1ObjId_IsPresent(pAlgorithm), VERR_INVALID_POINTER); if (pParameters) { AssertPtrReturn(pParameters, VERR_INVALID_POINTER); if (pParameters->enmType == RTASN1TYPE_NULL) pParameters = NULL; } AssertPtrReturn(pPublicKey, VERR_INVALID_POINTER); AssertReturn(RTAsn1BitString_IsPresent(pPublicKey), VERR_INVALID_POINTER); /* * Check if the algorithm matches. */ const char *pszCipherOid = RTCrPkixGetCiperOidFromSignatureAlgorithm(&pThis->SignatureAlgorithm.Algorithm); if (!pszCipherOid) return RTErrInfoSetF(pErrInfo, VERR_CR_X509_UNKNOWN_CERT_SIGN_ALGO, "Certificate signature algorithm not known: %s", pThis->SignatureAlgorithm.Algorithm.szObjId); if (RTAsn1ObjId_CompareWithString(pAlgorithm, pszCipherOid) != 0) return RTErrInfoSetF(pErrInfo, VERR_CR_X509_CERT_SIGN_ALGO_MISMATCH, "Certificate signature cipher algorithm mismatch: cert uses %s (%s) while key uses %s", pszCipherOid, pThis->SignatureAlgorithm.Algorithm.szObjId, pAlgorithm->szObjId); /* * Here we should recode the to-be-signed part as DER, but we'll ASSUME * that it's already in DER encoding. This is safe. */ return RTCrPkixPubKeyVerifySignature(&pThis->SignatureAlgorithm.Algorithm, pParameters, pPublicKey, &pThis->SignatureValue, RTASN1CORE_GET_RAW_ASN1_PTR(&pThis->TbsCertificate.SeqCore.Asn1Core), RTASN1CORE_GET_RAW_ASN1_SIZE(&pThis->TbsCertificate.SeqCore.Asn1Core), pErrInfo); }
RTDECL(int) RTCrStoreCertAddFromFile(RTCRSTORE hStore, uint32_t fFlags, const char *pszFilename, PRTERRINFO pErrInfo) { AssertReturn(!fFlags, VERR_INVALID_FLAGS); #if 0 RTCRX509CERTIFICATES Certs; int rc = RTCrX509Certificates_ReadFromFile(pszFilename, 0, &Certs, pErrInfo); if (RT_SUCCESS(rc)) { for (uint32_t i = 0; i < Certs.cCerts; i++) { int rc2 = RTCrStoreCertAddEncoded(hStore, RTCRCERTCTX_F_ENC_X509_DER, RTASN1CORE_GET_RAW_ASN1_PTR(&Certs.paCerts[i].SeqCore.Asn1Core), RTASN1CORE_GET_RAW_ASN1_SIZE(&Certs.paCerts[i].SeqCore.Asn1Core), RT_SUCCESS(rc) ? pErrInfo : NULL); if (RT_FAILURE(rc2) && RT_SUCCESS(rc)) rc = rc2; } RTAsn1Destroy(&Certs.SetCore.Asn1Core); } return rc; #else PCRTCRPEMSECTION pSectionHead; int rc = RTCrPemReadFile(pszFilename, 0, g_aCertificateMarkers, RT_ELEMENTS(g_aCertificateMarkers), &pSectionHead, pErrInfo); if (RT_SUCCESS(rc)) { PCRTCRPEMSECTION pCurSec = pSectionHead; while (pCurSec) { int rc2 = RTCrStoreCertAddEncoded(hStore, RTCRCERTCTX_F_ENC_X509_DER, pCurSec->pbData, pCurSec->cbData, RT_SUCCESS(rc) ? pErrInfo : NULL); if (RT_FAILURE(rc2) && RT_SUCCESS(rc)) rc = rc2; pCurSec = pCurSec->pNext; } RTCrPemFreeSections(pSectionHead); } return rc; #endif }
/** * Deals with authenticated attributes. * * When authenticated attributes are present (checked by caller) we must: * - fish out the content type and check it against the content inof, * - fish out the message digest among and check it against *phDigest, * - compute the message digest of the authenticated attributes and * replace *phDigest with this for the signature verification. * * @returns IPRT status code. * @param pSignerInfo The signer info being verified. * @param pSignedData The signed data. * @param phDigest On input this is the digest of the content. On * output it will (on success) be a reference to * the message digest of the authenticated * attributes. The input reference is consumed. * The caller shall release the output reference. * @param fFlags Flags. * @param pErrInfo Extended error info, optional. */ static int rtCrPkcs7VerifySignerInfoAuthAttribs(PCRTCRPKCS7SIGNERINFO pSignerInfo, PCRTCRPKCS7SIGNEDDATA pSignedData, PRTCRDIGEST phDigest, uint32_t fFlags, PRTERRINFO pErrInfo) { /* * Scan the attributes and validate the two required attributes * (RFC-2315, chapter 9.2, fourth bullet). Checking that we've got exactly * one of each of them is checked by the santiy checker function, so we'll * just assert that it did it's job here. */ uint32_t cContentTypes = 0; uint32_t cMessageDigests = 0; uint32_t i = pSignerInfo->AuthenticatedAttributes.cItems; while (i-- > 0) { PCRTCRPKCS7ATTRIBUTE pAttrib = &pSignerInfo->AuthenticatedAttributes.paItems[i]; if (RTAsn1ObjId_CompareWithString(&pAttrib->Type, RTCR_PKCS9_ID_CONTENT_TYPE_OID) == 0) { AssertReturn(!cContentTypes, VERR_CR_PKCS7_INTERNAL_ERROR); AssertReturn(pAttrib->enmType == RTCRPKCS7ATTRIBUTETYPE_OBJ_IDS, VERR_CR_PKCS7_INTERNAL_ERROR); AssertReturn(pAttrib->uValues.pObjIds->cItems == 1, VERR_CR_PKCS7_INTERNAL_ERROR); if ( !(fFlags & RTCRPKCS7VERIFY_SD_F_COUNTER_SIGNATURE) /* See note about microsoft below. */ && RTAsn1ObjId_Compare(&pAttrib->uValues.pObjIds->paItems[0], &pSignedData->ContentInfo.ContentType) != 0) return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_CONTENT_TYPE_ATTRIB_MISMATCH, "Expected content-type %s, found %s", &pAttrib->uValues.pObjIds->paItems[0], pSignedData->ContentInfo.ContentType.szObjId); cContentTypes++; } else if (RTAsn1ObjId_CompareWithString(&pAttrib->Type, RTCR_PKCS9_ID_MESSAGE_DIGEST_OID) == 0) { AssertReturn(!cMessageDigests, VERR_CR_PKCS7_INTERNAL_ERROR); AssertReturn(pAttrib->enmType == RTCRPKCS7ATTRIBUTETYPE_OCTET_STRINGS, VERR_CR_PKCS7_INTERNAL_ERROR); AssertReturn(pAttrib->uValues.pOctetStrings->cItems == 1, VERR_CR_PKCS7_INTERNAL_ERROR); if (!RTCrDigestMatch(*phDigest, pAttrib->uValues.pOctetStrings->paItems[0].Asn1Core.uData.pv, pAttrib->uValues.pOctetStrings->paItems[0].Asn1Core.cb)) { size_t cbHash = RTCrDigestGetHashSize(*phDigest); if (cbHash != pAttrib->uValues.pOctetStrings->paItems[0].Asn1Core.cb) return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_MESSAGE_DIGEST_ATTRIB_MISMATCH, "Authenticated message-digest attribute mismatch: cbHash=%#zx cbValue=%#x", cbHash, pAttrib->uValues.pOctetStrings->paItems[0].Asn1Core.cb); return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_MESSAGE_DIGEST_ATTRIB_MISMATCH, "Authenticated message-digest attribute mismatch (cbHash=%#zx):\n" "signed: %.*Rhxs\n" "our: %.*Rhxs\n", cbHash, cbHash, pAttrib->uValues.pOctetStrings->paItems[0].Asn1Core.uData.pv, cbHash, RTCrDigestGetHash(*phDigest)); } cMessageDigests++; } } /* * Full error reporting here as we don't currently extensively santiy check * counter signatures. * Note! Microsoft includes content info in their timestamp counter signatures, * at least for vista, despite the RFC-3852 stating counter signatures * "MUST NOT contain a content-type". */ if (RT_UNLIKELY( cContentTypes != 1 && !(fFlags & RTCRPKCS7VERIFY_SD_F_COUNTER_SIGNATURE))) return RTErrInfoSet(pErrInfo, VERR_CR_PKCS7_MISSING_CONTENT_TYPE_ATTRIB, "Missing authenticated content-type attribute."); if (RT_UNLIKELY(cMessageDigests != 1)) return RTErrInfoSet(pErrInfo, VERR_CR_PKCS7_MISSING_MESSAGE_DIGEST_ATTRIB, "Missing authenticated message-digest attribute."); /* * Calculate the digest of the the authenticated attributes for use in the * signature validation. */ if ( pSignerInfo->DigestAlgorithm.Parameters.enmType != RTASN1TYPE_NULL && pSignerInfo->DigestAlgorithm.Parameters.enmType != RTASN1TYPE_NOT_PRESENT) return RTErrInfoSet(pErrInfo, VERR_CR_PKCS7_DIGEST_PARAMS_NOT_IMPL, "Digest algorithm has unsupported parameters"); RTCRDIGEST hDigest; int rc = RTCrDigestCreateByObjId(&hDigest, &pSignerInfo->DigestAlgorithm.Algorithm); if (RT_SUCCESS(rc)) { RTCrDigestRelease(*phDigest); *phDigest = hDigest; /* ASSUMES that the attributes are encoded according to DER. */ uint8_t const *pbData = (uint8_t const *)RTASN1CORE_GET_RAW_ASN1_PTR(&pSignerInfo->AuthenticatedAttributes.SetCore.Asn1Core); uint32_t cbData = RTASN1CORE_GET_RAW_ASN1_SIZE(&pSignerInfo->AuthenticatedAttributes.SetCore.Asn1Core); uint8_t bSetOfTag = ASN1_TAG_SET | ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_CONSTRUCTED; rc = RTCrDigestUpdate(hDigest, &bSetOfTag, sizeof(bSetOfTag)); /* Replace the implict tag with a SET-OF tag. */ if (RT_SUCCESS(rc)) rc = RTCrDigestUpdate(hDigest, pbData + sizeof(bSetOfTag), cbData - sizeof(bSetOfTag)); /* Skip the implicit tag. */ if (RT_SUCCESS(rc)) rc = RTCrDigestFinal(hDigest, NULL, 0); } return rc; }
static int rtCrPkcs7VerifySignedDataUsingOpenSsl(PCRTCRPKCS7CONTENTINFO pContentInfo, uint32_t fFlags, RTCRSTORE hAdditionalCerts, RTCRSTORE hTrustedCerts, void const *pvContent, uint32_t cbContent, PRTERRINFO pErrInfo) { /* * Verify using OpenSSL. */ int rcOssl; unsigned char const *pbRawContent = RTASN1CORE_GET_RAW_ASN1_PTR(&pContentInfo->SeqCore.Asn1Core); PKCS7 *pOsslPkcs7 = NULL; if (d2i_PKCS7(&pOsslPkcs7, &pbRawContent, RTASN1CORE_GET_RAW_ASN1_SIZE(&pContentInfo->SeqCore.Asn1Core)) == pOsslPkcs7) { STACK_OF(X509) *pAddCerts = NULL; if (hAdditionalCerts != NIL_RTCRSTORE) rcOssl = RTCrStoreConvertToOpenSslCertStack(hAdditionalCerts, 0, (void **)&pAddCerts); else { pAddCerts = sk_X509_new_null(); rcOssl = RT_LIKELY(pAddCerts != NULL) ? VINF_SUCCESS : VERR_NO_MEMORY; } if (RT_SUCCESS(rcOssl)) { PCRTCRPKCS7SETOFCERTS pCerts = &pContentInfo->u.pSignedData->Certificates; for (uint32_t i = 0; i < pCerts->cItems; i++) if (pCerts->paItems[i].enmChoice == RTCRPKCS7CERTCHOICE_X509) rtCrOpenSslAddX509CertToStack(pAddCerts, pCerts->paItems[i].u.pX509Cert); X509_STORE *pTrustedCerts = NULL; if (hTrustedCerts != NIL_RTCRSTORE) rcOssl = RTCrStoreConvertToOpenSslCertStore(hTrustedCerts, 0, (void **)&pTrustedCerts); if (RT_SUCCESS(rcOssl)) { rtCrOpenSslInit(); BIO *pBioContent = BIO_new_mem_buf((void *)pvContent, cbContent); if (pBioContent) { uint32_t fOsslFlags = PKCS7_NOCHAIN; fOsslFlags |= PKCS7_NOVERIFY; // temporary hack. if (PKCS7_verify(pOsslPkcs7, pAddCerts, pTrustedCerts, pBioContent, NULL /*out*/, fOsslFlags)) rcOssl = VINF_SUCCESS; else { rcOssl = RTErrInfoSet(pErrInfo, VERR_CR_PKCS7_OSSL_VERIFY_FAILED, "PKCS7_verify failed: "); if (pErrInfo) ERR_print_errors_cb(rtCrOpenSslErrInfoCallback, pErrInfo); } BIO_free(pBioContent); } if (pTrustedCerts) X509_STORE_free(pTrustedCerts); } else rcOssl = RTErrInfoSet(pErrInfo, rcOssl, "RTCrStoreConvertToOpenSslCertStack failed"); if (pAddCerts) sk_X509_pop_free(pAddCerts, X509_free); } else rcOssl = RTErrInfoSet(pErrInfo, rcOssl, "RTCrStoreConvertToOpenSslCertStack failed"); PKCS7_free(pOsslPkcs7); } else rcOssl = RTErrInfoSet(pErrInfo, VERR_CR_PKCS7_OSSL_D2I_FAILED, "d2i_PKCS7 failed"); return rcOssl; }
RTDECL(int) RTCrX509Certificate_VerifySignature(PCRTCRX509CERTIFICATE pThis, PCRTASN1OBJID pAlgorithm, PCRTASN1DYNTYPE pParameters, PCRTASN1BITSTRING pPublicKey, PRTERRINFO pErrInfo) { /* * Validate the input a little. */ AssertPtrReturn(pThis, VERR_INVALID_POINTER); AssertReturn(RTCrX509Certificate_IsPresent(pThis), VERR_INVALID_PARAMETER); AssertPtrReturn(pAlgorithm, VERR_INVALID_POINTER); AssertReturn(RTAsn1ObjId_IsPresent(pAlgorithm), VERR_INVALID_POINTER); if (pParameters) { AssertPtrReturn(pParameters, VERR_INVALID_POINTER); if (pParameters->enmType == RTASN1TYPE_NULL) pParameters = NULL; } AssertPtrReturn(pPublicKey, VERR_INVALID_POINTER); AssertReturn(RTAsn1BitString_IsPresent(pPublicKey), VERR_INVALID_POINTER); /* * Check if the algorithm matches. */ const char *pszCipherOid = RTCrPkixGetCiperOidFromSignatureAlgorithm(&pThis->SignatureAlgorithm.Algorithm); if (!pszCipherOid) return RTErrInfoSetF(pErrInfo, VERR_CR_X509_UNKNOWN_CERT_SIGN_ALGO, "Certificate signature algorithm not known: %s", pThis->SignatureAlgorithm.Algorithm.szObjId); if (RTAsn1ObjId_CompareWithString(pAlgorithm, pszCipherOid) != 0) return RTErrInfoSetF(pErrInfo, VERR_CR_X509_CERT_SIGN_ALGO_MISMATCH, "Certificate signature cipher algorithm mismatch: cert uses %s (%s) while key uses %s", pszCipherOid, pThis->SignatureAlgorithm.Algorithm.szObjId, pAlgorithm->szObjId); /* * Here we should recode the to-be-signed part as DER, but we'll ASSUME * that it's already in DER encoding and only does this if there the * encoded bits are missing. */ if ( pThis->TbsCertificate.SeqCore.Asn1Core.uData.pu8 && pThis->TbsCertificate.SeqCore.Asn1Core.cb > 0) return RTCrPkixPubKeyVerifySignature(&pThis->SignatureAlgorithm.Algorithm, pParameters, pPublicKey, &pThis->SignatureValue, RTASN1CORE_GET_RAW_ASN1_PTR(&pThis->TbsCertificate.SeqCore.Asn1Core), RTASN1CORE_GET_RAW_ASN1_SIZE(&pThis->TbsCertificate.SeqCore.Asn1Core), pErrInfo); uint32_t cbEncoded; int rc = RTAsn1EncodePrepare((PRTASN1CORE)&pThis->TbsCertificate.SeqCore.Asn1Core, RTASN1ENCODE_F_DER, &cbEncoded, pErrInfo); if (RT_SUCCESS(rc)) { void *pvTbsBits = RTMemTmpAlloc(cbEncoded); if (pvTbsBits) { rc = RTAsn1EncodeToBuffer(&pThis->TbsCertificate.SeqCore.Asn1Core, RTASN1ENCODE_F_DER, pvTbsBits, cbEncoded, pErrInfo); if (RT_SUCCESS(rc)) rc = RTCrPkixPubKeyVerifySignature(&pThis->SignatureAlgorithm.Algorithm, pParameters, pPublicKey, &pThis->SignatureValue, pvTbsBits, cbEncoded, pErrInfo); else AssertRC(rc); RTMemTmpFree(pvTbsBits); } else rc = VERR_NO_TMP_MEMORY; } return rc; }