blob: e9f1b0efddd7c39288107b5dc7cf45f269d013f1 [file] [log] [blame]
/** @file
PKCS#7 SignedData Sign Wrapper and PKCS#7 SignedData Verification Wrapper
Implementation over mbedtls.
RFC 8422 - Elliptic Curve Cryptography (ECC) Cipher Suites
FIPS 186-4 - Digital Signature Standard (DSS)
Copyright (c) 2024, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "CryptPkcs7Internal.h"
#include <mbedtls/pkcs7.h>
/* Profile for backward compatibility. Allows RSA 1024, unlike the default
profile. */
STATIC mbedtls_x509_crt_profile gCompatProfile =
{
/* Hashes from SHA-256 and above. Note that this selection
* should be aligned with ssl_preset_default_hashes in ssl_tls.c. */
#ifndef DISABLE_SHA1_DEPRECATED_INTERFACES
MBEDTLS_X509_ID_FLAG (MBEDTLS_MD_SHA1) |
#endif
MBEDTLS_X509_ID_FLAG (MBEDTLS_MD_SHA256) |
MBEDTLS_X509_ID_FLAG (MBEDTLS_MD_SHA384) |
MBEDTLS_X509_ID_FLAG (MBEDTLS_MD_SHA512),
0xFFFFFFF, /* Any PK alg */
/* Curves at or above 128-bit security level. Note that this selection
* should be aligned with ssl_preset_default_curves in ssl_tls.c. */
MBEDTLS_X509_ID_FLAG (MBEDTLS_ECP_DP_SECP256R1) |
MBEDTLS_X509_ID_FLAG (MBEDTLS_ECP_DP_SECP384R1) |
MBEDTLS_X509_ID_FLAG (MBEDTLS_ECP_DP_SECP521R1) |
MBEDTLS_X509_ID_FLAG (MBEDTLS_ECP_DP_BP256R1) |
MBEDTLS_X509_ID_FLAG (MBEDTLS_ECP_DP_BP384R1) |
MBEDTLS_X509_ID_FLAG (MBEDTLS_ECP_DP_BP512R1) |
0,
1024,
};
/**
Init MbedtlsPkcs7.
@param[in] Pkcs7 MbedtlsPkcs7.
**/
STATIC
VOID
MbedTlsPkcs7Init (
MbedtlsPkcs7 *Pkcs7
)
{
ZeroMem (Pkcs7, sizeof (MbedtlsPkcs7));
}
/**
Get Pkcs7 Next Content Len.
@param[in] Ptr The start of the buffer.
@param[in] End The end of the buffer.
@param[out] Len MbedtlsPkcs7 Content Len.
@retval 0 Success.
@retval negative A negative MBEDTLS_ERR_ASN1_XXX error code on failure.
**/
STATIC
INT32
MbedTlsPkcs7GetNextContentLen (
UINT8 **Ptr,
UINT8 *End,
UINTN *Len
)
{
INT32 Ret;
Ret = mbedtls_asn1_get_tag (Ptr, End, Len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_CONTEXT_SPECIFIC);
return Ret;
}
/**
Get Pkcs7 Version..
@param[in] Ptr The start of the buffer.
@param[in] End The end of the buffer.
@param[out] Ver MbedtlsPkcs7 Version.
@retval 0 Success.
@retval negative A negative MBEDTLS_ERR_ASN1_XXX error code on failure.
**/
STATIC
INT32
MbedTlsPkcs7GetVersion (
UINT8 **Ptr,
UINT8 *End,
INT32 *Ver
)
{
INT32 Ret;
Ret = mbedtls_asn1_get_int (Ptr, End, Ver);
return Ret;
}
/**
ContentInfo ::= SEQUENCE {
contentType ContentType,
content
[0] EXPLICIT ANY DEFINED BY contentType OPTIONAL }.
@param[in] Ptr The start of the buffer.
@param[in] End The end of the buffer.
@param[out] Pkcs7 MbedtlsPkcs7.
@retval 0 Success.
@retval negative A negative MBEDTLS_ERR_ASN1_XXX error code on failure.
**/
STATIC
INT32
Pkcs7GetContentInfoType (
UINT8 **Ptr,
UINT8 *End,
mbedtls_asn1_buf *Pkcs7
)
{
UINTN Len;
int Ret;
Len = 0;
Ret = mbedtls_asn1_get_tag (
Ptr,
End,
&Len,
MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE
);
if (Ret == 0) {
Ret = mbedtls_asn1_get_tag (Ptr, End, &Len, MBEDTLS_ASN1_OID);
}
if (Ret == 0) {
Pkcs7->tag = MBEDTLS_ASN1_OID;
Pkcs7->len = Len;
Pkcs7->p = *Ptr;
}
return Ret;
}
/**
DigestAlgorithmIdentifier ::= AlgorithmIdentifier.
@param[in] Ptr The start of the buffer.
@param[in] End The end of the buffer.
@param[out] Alg MbedtlsPkcs7 AlgorithmIdentifier.
@retval 0 Success.
@retval negative A negative MBEDTLS_ERR_ASN1_XXX error code on failure.
**/
STATIC
INT32
MbedTlsPkcs7GetDigestAlgorithm (
UINT8 **Ptr,
UINT8 *End,
mbedtls_x509_buf *Alg
)
{
INT32 Ret;
Ret = mbedtls_asn1_get_alg_null (Ptr, End, Alg);
return Ret;
}
/**
DigestAlgorithmIdentifiers :: SET of DigestAlgorithmIdentifier.
@param[in] Ptr The start of the buffer.
@param[in] End The end of the buffer.
@param[out] Alg MbedtlsPkcs7 AlgorithmIdentifier.
@retval 0 Success.
@retval negative A negative MBEDTLS_ERR_ASN1_XXX error code on failure.
**/
STATIC
INT32
MbedTlsPkcs7GetDigestAlgorithmSet (
UINT8 **Ptr,
UINT8 *End,
mbedtls_x509_buf *Alg
)
{
UINTN Len;
INT32 Ret;
Len = 0;
Ret = mbedtls_asn1_get_tag (
Ptr,
End,
&Len,
MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SET
);
if (Ret == 0) {
End = *Ptr + Len;
// assume only one digest algorithm
Ret = mbedtls_asn1_get_alg_null (Ptr, End, Alg);
}
return Ret;
}
/**
certificates :: SET OF ExtendedCertificateOrCertificate,
ExtendedCertificateOrCertificate ::= CHOICE {
certificate Certificate -- x509,
extendedCertificate[0] IMPLICIT ExtendedCertificate }.
@param[in] Ptr The start of the buffer.
@param[in] Plen The buffer len.
@param[out] Certs mbedtls_x509_crt cert.
@retval 0 Success.
@retval negative A negative MBEDTLS_ERR_ASN1_XXX error code on failure.
**/
STATIC
INT32
MbedTlsPkcs7GetCertificates (
UINT8 **Ptr,
INTN Plen,
mbedtls_x509_crt *Certs
)
{
INT32 Ret;
Ret = mbedtls_x509_crt_parse (Certs, *Ptr, Plen);
return Ret;
}
/**
EncryptedDigest ::= OCTET STRING.
@param[in] Ptr The start of the buffer.
@param[in] End The end of the buffer.
@param[out] Signature Signature.
@retval 0 Success.
@retval negative A negative MBEDTLS_ERR_ASN1_XXX error code on failure.
**/
STATIC
INT32
Pkcs7GetSignature (
UINT8 **Ptr,
UINT8 *End,
mbedtls_asn1_buf *Signature
)
{
INT32 Ret;
UINTN Len;
Len = 0;
Ret = mbedtls_asn1_get_tag (Ptr, End, &Len, MBEDTLS_ASN1_OCTET_STRING);
if (Ret == 0) {
Signature->tag = MBEDTLS_ASN1_OCTET_STRING;
Signature->len = Len;
Signature->p = *Ptr;
}
return Ret;
}
/**
SignerInfo ::= SEQUENCE {
version Version;
issuerAndSerialNumber IssuerAndSerialNumber,
digestAlgorithm DigestAlgorithmIdentifier,
authenticatedAttributes
[0] IMPLICIT Attributes OPTIONAL,
digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier,
encryptedDigest EncryptedDigest,
unauthenticatedAttributes
[1] IMPLICIT Attributes OPTIONAL.
@param[in] Ptr The start of the buffer.
@param[in] End The end of the buffer.
@param[out] SignersSet MbedtlsPkcs7SignerInfo.
@retval 0 Success.
@retval negative A negative MBEDTLS_ERR_ASN1_XXX error code on failure.
**/
STATIC
INT32
MbedTlsPkcs7GetSignersInfoSet (
UINT8 **Ptr,
UINT8 *End,
MbedtlsPkcs7SignerInfo *SignersSet
)
{
UINT8 *EndSet;
INT32 Ret;
UINTN Len;
UINT8 *TempP;
Len = 0;
Ret = mbedtls_asn1_get_tag (
Ptr,
End,
&Len,
MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SET
);
if (Ret == 0) {
EndSet = *Ptr + Len;
Ret = mbedtls_asn1_get_tag (
Ptr,
EndSet,
&Len,
MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE
);
}
if (Ret == 0) {
Ret = mbedtls_asn1_get_int (Ptr, EndSet, &SignersSet->Version);
}
if (Ret == 0) {
Ret = mbedtls_asn1_get_tag (
Ptr,
EndSet,
&Len,
MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE
);
}
if (Ret == 0) {
SignersSet->IssuerRaw.p = *Ptr;
Ret = mbedtls_asn1_get_tag (
Ptr,
EndSet,
&Len,
MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE
);
}
if (Ret == 0) {
Ret = mbedtls_x509_get_name (Ptr, *Ptr + Len, &SignersSet->Issuer);
}
if (Ret == 0) {
SignersSet->IssuerRaw.len = *Ptr - SignersSet->IssuerRaw.p;
Ret = mbedtls_x509_get_serial (Ptr, EndSet, &SignersSet->Serial);
}
if (Ret == 0) {
Ret = MbedTlsPkcs7GetDigestAlgorithm (Ptr, EndSet, &SignersSet->AlgIdentifier);
}
// OPTIONAL AuthenticatedAttributes
if (Ret == 0) {
TempP = *Ptr;
if (mbedtls_asn1_get_tag (&TempP, EndSet, &Len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_CONTEXT_SPECIFIC) == 0) {
SignersSet->AuthAttr.len = Len + (TempP - *Ptr);
SignersSet->AuthAttr.p = *Ptr;
*Ptr = TempP + Len;
} else {
SignersSet->AuthAttr.p = NULL;
}
}
if (Ret == 0) {
Ret = MbedTlsPkcs7GetDigestAlgorithm (Ptr, EndSet, &SignersSet->SigAlgIdentifier);
}
if (Ret == 0) {
Ret = Pkcs7GetSignature (Ptr, End, &SignersSet->Sig);
}
if (Ret == 0) {
SignersSet->Next = NULL;
}
return Ret;
}
/**
SignedData ::= SEQUENCE {
version Version,
digestAlgorithms DigestAlgorithmIdentifiers,
contentInfo ContentInfo,
certificates
[0] IMPLICIT ExtendedCertificatesAndCertificates
OPTIONAL,
crls
[0] IMPLICIT CertificateRevocationLists OPTIONAL,
signerInfos SignerInfos }.
@param[in] Buffer The start of the buffer.
@param[in] BufferLen The len the buffer.
@param[out] SignedData MbedtlsPkcs7SignedData.
@retval 0 Success.
@retval negative A negative MBEDTLS_ERR_ASN1_XXX error code on failure.
**/
STATIC
INT32
Pkcs7GetSignedData (
UINT8 *Buffer,
INTN BufferLen,
MbedtlsPkcs7SignedData *SignedData
)
{
UINT8 *Ptr;
UINT8 *End;
UINTN Len;
INT32 Ret;
UINT8 *CertP;
UINTN CertLen;
UINT8 *OldCertP;
UINTN TotalCertLen;
mbedtls_x509_crt *MoreCert;
UINT8 CertNum;
mbedtls_x509_crt *LastCert;
mbedtls_x509_crt *TempCrt;
Len = 0;
Ptr = Buffer;
End = Buffer + BufferLen;
MoreCert = NULL;
Ret = mbedtls_asn1_get_tag (
&Ptr,
End,
&Len,
MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE
);
if (Ret == 0) {
// version
Ret = MbedTlsPkcs7GetVersion (&Ptr, End, &SignedData->Version);
}
if ((Ret == 0) && (SignedData->Version != 1)) {
Ret = -1;
}
if (Ret == 0) {
// digest algorithm
Ret = MbedTlsPkcs7GetDigestAlgorithmSet (
&Ptr,
End,
&SignedData->DigestAlgorithms
);
}
if (Ret == 0) {
if (
#ifndef DISABLE_SHA1_DEPRECATED_INTERFACES
((SignedData->DigestAlgorithms.len == sizeof (MBEDTLS_OID_DIGEST_ALG_SHA1) - 1) &&
(CompareMem (
SignedData->DigestAlgorithms.p,
MBEDTLS_OID_DIGEST_ALG_SHA1,
SignedData->DigestAlgorithms.len
) == 0)) ||
#endif
((SignedData->DigestAlgorithms.len == sizeof (MBEDTLS_OID_DIGEST_ALG_SHA256) - 1) &&
(CompareMem (
SignedData->DigestAlgorithms.p,
MBEDTLS_OID_DIGEST_ALG_SHA256,
SignedData->DigestAlgorithms.len
) == 0)) ||
((SignedData->DigestAlgorithms.len == sizeof (MBEDTLS_OID_DIGEST_ALG_SHA384) - 1) &&
(CompareMem (
SignedData->DigestAlgorithms.p,
MBEDTLS_OID_DIGEST_ALG_SHA384,
SignedData->DigestAlgorithms.len
) == 0)) ||
((SignedData->DigestAlgorithms.len == sizeof (MBEDTLS_OID_DIGEST_ALG_SHA512) - 1) &&
(CompareMem (
SignedData->DigestAlgorithms.p,
MBEDTLS_OID_DIGEST_ALG_SHA512,
SignedData->DigestAlgorithms.len
) == 0)))
{
Ret = 0;
} else {
Ret = -1;
}
}
if (Ret == 0) {
Ret = Pkcs7GetContentInfoType (&Ptr, End, &SignedData->ContentInfo.Oid);
}
if (Ret == 0) {
// move to next
Ptr = Ptr + SignedData->ContentInfo.Oid.len;
Ret = MbedTlsPkcs7GetNextContentLen (&Ptr, End, &Len);
CertP = Ptr + Len;
// move to actual cert, if there are more [0]
if (MbedTlsPkcs7GetNextContentLen (&CertP, End, &CertLen) == 0) {
Len = CertLen;
Ptr = CertP;
}
}
// certificates: may have many certs
CertP = Ptr;
TotalCertLen = 0;
MoreCert = &SignedData->Certificates;
CertNum = 0;
while (TotalCertLen < Len) {
OldCertP = CertP;
Ret = mbedtls_asn1_get_tag (&CertP, End, &CertLen, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
if (Ret != 0) {
goto Out;
}
// cert total len
CertLen = CertLen + (CertP - OldCertP);
// move to next cert
CertP = OldCertP + CertLen;
// change TotalCertLen
TotalCertLen += CertLen;
mbedtls_x509_crt_init (MoreCert);
Ret = MbedTlsPkcs7GetCertificates (&OldCertP, CertLen, MoreCert);
if (Ret != 0) {
goto Out;
}
CertNum++;
MoreCert->next = mbedtls_calloc (1, sizeof (mbedtls_x509_crt));
MoreCert = MoreCert->next;
}
if (TotalCertLen != Len) {
Ret = -1;
goto Out;
}
LastCert = &(SignedData->Certificates);
while (CertNum--) {
if (CertNum == 0) {
LastCert->next = NULL;
break;
} else {
LastCert = LastCert->next;
}
}
// signers info
if (Ret == 0) {
Ptr = Ptr + Len;
Ret = MbedTlsPkcs7GetSignersInfoSet (&Ptr, End, &SignedData->SignerInfos);
}
Out:
if (Ret == 0) {
if (MoreCert != NULL) {
mbedtls_x509_crt_free (MoreCert);
MoreCert = NULL;
}
} else {
if (SignedData->Certificates.next != NULL) {
TempCrt = SignedData->Certificates.next;
mbedtls_x509_crt_free (TempCrt);
}
}
return Ret;
}
/**
Parse MbedtlsPkcs7 to Der format.
@param[in] Buffer The start of the buffer.
@param[in] BufferLen The len the buffer.
@param[out] Pkcs7 MbedtlsPkcs7.
@retval 0 Success.
@retval negative A negative MBEDTLS_ERR_ASN1_XXX error code on failure.
**/
STATIC
INT32
MbedtlsPkcs7ParseDer (
CONST UINT8 *Buffer,
INTN BufferLen,
MbedtlsPkcs7 *Pkcs7
)
{
UINT8 *Ptr;
UINT8 *End;
UINTN Len;
INT32 Ret;
if (Pkcs7 == NULL) {
return -1;
}
Len = 0;
Ptr = (UINT8 *)Buffer;
End = Ptr + BufferLen;
Ret = Pkcs7GetContentInfoType (&Ptr, End, &Pkcs7->ContentTypeOid);
if (Ret != 0) {
goto Out;
}
if ((CompareMem (Pkcs7->ContentTypeOid.p, MBEDTLS_OID_PKCS7_DATA, Pkcs7->ContentTypeOid.len) == 0) ||
(CompareMem (Pkcs7->ContentTypeOid.p, MBEDTLS_OID_PKCS7_ENCRYPTED_DATA, Pkcs7->ContentTypeOid.len) == 0) ||
(CompareMem (Pkcs7->ContentTypeOid.p, MBEDTLS_OID_PKCS7_ENVELOPED_DATA, Pkcs7->ContentTypeOid.len) == 0) ||
(CompareMem (Pkcs7->ContentTypeOid.p, MBEDTLS_OID_PKCS7_SIGNED_AND_ENVELOPED_DATA, Pkcs7->ContentTypeOid.len) == 0) ||
(CompareMem (Pkcs7->ContentTypeOid.p, MBEDTLS_OID_PKCS7_DIGESTED_DATA, Pkcs7->ContentTypeOid.len) == 0))
{
// Invalid PKCS7 data type;
Ret = -1;
goto Out;
}
if (CompareMem (Pkcs7->ContentTypeOid.p, MBEDTLS_OID_PKCS7_SIGNED_DATA, Pkcs7->ContentTypeOid.len) != 0) {
// Invalid PKCS7 data type;
Ret = -1;
goto Out;
}
// Content type is SignedData
Ptr = Ptr + Pkcs7->ContentTypeOid.len;
Ret = MbedTlsPkcs7GetNextContentLen (&Ptr, End, &Len);
if (Ret != 0) {
goto Out;
}
Ret = Pkcs7GetSignedData (Ptr, Len, &Pkcs7->SignedData);
if (Ret != 0) {
goto Out;
}
Out:
return Ret;
}
/**
MbedtlsPkcs7 verify MbedtlsPkcs7SignerInfo.
@param[in] SignerInfo MbedtlsPkcs7 SignerInfo.
@param[in] Cert cert.
@param[in] Data Pointer for data.
@param[in] DataLen The len the buffer.
@retval 0 Success.
@retval negative A negative MBEDTLS_ERR_ASN1_XXX error code on failure.
**/
STATIC
INT32
MbedtlsPkcs7SignedDataVerifySigners (
MbedtlsPkcs7SignerInfo *SignerInfo,
mbedtls_x509_crt *Cert,
CONST UINT8 *Data,
INTN DataLen
)
{
INT32 Ret;
UINT8 Hash[MBEDTLS_MD_MAX_SIZE];
mbedtls_pk_context Pk;
CONST mbedtls_md_info_t *MdInfo;
INTN HashLen;
UINT8 TempAuthAttr;
Pk = Cert->pk;
ZeroMem (Hash, MBEDTLS_MD_MAX_SIZE);
// all the hash algo
#ifndef DISABLE_SHA1_DEPRECATED_INTERFACES
MdInfo = mbedtls_md_info_from_type (MBEDTLS_MD_SHA1);
HashLen = mbedtls_md_get_size (MdInfo);
mbedtls_md (MdInfo, Data, DataLen, Hash);
if (SignerInfo->AuthAttr.p != NULL) {
TempAuthAttr = *(SignerInfo->AuthAttr.p);
*(SignerInfo->AuthAttr.p) = MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SET;
mbedtls_md (MdInfo, SignerInfo->AuthAttr.p, SignerInfo->AuthAttr.len, Hash);
// Restore content
*(SignerInfo->AuthAttr.p) = TempAuthAttr;
}
Ret = mbedtls_pk_verify (&Pk, MBEDTLS_MD_SHA1, Hash, HashLen, SignerInfo->Sig.p, SignerInfo->Sig.len);
if (Ret == 0) {
return Ret;
}
#endif
MdInfo = mbedtls_md_info_from_type (MBEDTLS_MD_SHA256);
HashLen = mbedtls_md_get_size (MdInfo);
ZeroMem (Hash, MBEDTLS_MD_MAX_SIZE);
mbedtls_md (MdInfo, Data, DataLen, Hash);
if (SignerInfo->AuthAttr.p != NULL) {
TempAuthAttr = *(SignerInfo->AuthAttr.p);
*(SignerInfo->AuthAttr.p) = MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SET;
mbedtls_md (MdInfo, SignerInfo->AuthAttr.p, SignerInfo->AuthAttr.len, Hash);
// Restore content
*(SignerInfo->AuthAttr.p) = TempAuthAttr;
}
Ret = mbedtls_pk_verify (&Pk, MBEDTLS_MD_SHA256, Hash, HashLen, SignerInfo->Sig.p, SignerInfo->Sig.len);
if (Ret == 0) {
return Ret;
}
MdInfo = mbedtls_md_info_from_type (MBEDTLS_MD_SHA384);
HashLen = mbedtls_md_get_size (MdInfo);
ZeroMem (Hash, MBEDTLS_MD_MAX_SIZE);
mbedtls_md (MdInfo, Data, DataLen, Hash);
if (SignerInfo->AuthAttr.p != NULL) {
TempAuthAttr = *(SignerInfo->AuthAttr.p);
*(SignerInfo->AuthAttr.p) = MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SET;
mbedtls_md (MdInfo, SignerInfo->AuthAttr.p, SignerInfo->AuthAttr.len, Hash);
// Restore content
*(SignerInfo->AuthAttr.p) = TempAuthAttr;
}
Ret = mbedtls_pk_verify (&Pk, MBEDTLS_MD_SHA384, Hash, HashLen, SignerInfo->Sig.p, SignerInfo->Sig.len);
if (Ret == 0) {
return Ret;
}
MdInfo = mbedtls_md_info_from_type (MBEDTLS_MD_SHA512);
HashLen = mbedtls_md_get_size (MdInfo);
ZeroMem (Hash, MBEDTLS_MD_MAX_SIZE);
mbedtls_md (MdInfo, Data, DataLen, Hash);
if (SignerInfo->AuthAttr.p != NULL) {
TempAuthAttr = *(SignerInfo->AuthAttr.p);
*(SignerInfo->AuthAttr.p) = MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SET;
mbedtls_md (MdInfo, SignerInfo->AuthAttr.p, SignerInfo->AuthAttr.len, Hash);
// Restore content
*(SignerInfo->AuthAttr.p) = TempAuthAttr;
}
Ret = mbedtls_pk_verify (&Pk, MBEDTLS_MD_SHA512, Hash, HashLen, SignerInfo->Sig.p, SignerInfo->Sig.len);
if (Ret == 0) {
return Ret;
}
return Ret;
}
/**
Find signer cert in MbedtlsPkcs7SignerInfo.
@param[in] SignerInfo MbedtlsPkcs7 SignerInfo.
@param[in] Certs MbedtlsPkcs7 SignerInfo certs.
@retval cert Signer Cert.
**/
STATIC
mbedtls_x509_crt *
MbedTlsPkcs7FindSignerCert (
MbedtlsPkcs7SignerInfo *SignerInfo,
mbedtls_x509_crt *Certs
)
{
mbedtls_x509_crt *Cert;
Cert = Certs;
while (Cert != NULL) {
if ((Cert->serial.p == NULL) || (Cert->issuer_raw.p == NULL)) {
return NULL;
}
if ((Cert->issuer_raw.len == SignerInfo->IssuerRaw.len) &&
(CompareMem (Cert->issuer_raw.p, SignerInfo->IssuerRaw.p, Cert->issuer_raw.len) == 0) &&
(Cert->serial.len == SignerInfo->Serial.len) &&
(CompareMem (Cert->serial.p, SignerInfo->Serial.p, Cert->serial.len) == 0))
{
break;
}
Cert = Cert->next;
}
return Cert;
}
/**
verify cert.
@param[in] Ca CA cert.
@param[in] CaCrl CRL.
@param[in] End Cert which need be verified.
@retval TRUE Verify successfully.
@retval FALSE Verify failed.
**/
STATIC
BOOLEAN
MbedTlsPkcs7VerifyCert (
mbedtls_x509_crt *Ca,
mbedtls_x509_crl *CaCrl,
mbedtls_x509_crt *End
)
{
INT32 Ret;
UINT32 VFlag;
mbedtls_x509_crt_profile Profile;
VFlag = 0;
CopyMem (&Profile, &gCompatProfile, sizeof (mbedtls_x509_crt_profile));
Ret = mbedtls_x509_crt_verify_with_profile (End, Ca, CaCrl, &Profile, NULL, &VFlag, NULL, NULL);
return Ret == 0;
}
/**
verify cert chain.
@param[in] Pkcs7 MbedtlsPkcs7.
@param[in] Ca CA cert.
@param[in] End Cert which need be verified.
@retval TRUE Verify successfully.
@retval FALSE Verify failed.
**/
STATIC
BOOLEAN
MbedTlsPkcs7VerifyCertChain (
MbedtlsPkcs7 *Pkcs7,
mbedtls_x509_crt *Ca,
mbedtls_x509_crt *End
)
{
mbedtls_x509_crt *AllCert;
mbedtls_x509_crt *InterCert;
AllCert = &(Pkcs7->SignedData.Certificates);
InterCert = NULL;
while (AllCert != NULL) {
if ((AllCert->next == End) && (MbedTlsPkcs7VerifyCert (AllCert, NULL, End))) {
InterCert = AllCert;
break;
}
AllCert = AllCert->next;
}
if (InterCert == NULL) {
return FALSE;
}
if (MbedTlsPkcs7VerifyCert (Ca, &(Pkcs7->SignedData.Crls), InterCert)) {
return TRUE;
} else {
return MbedTlsPkcs7VerifyCertChain (Pkcs7, Ca, InterCert);
}
}
/**
MbedTlsPkcs7 Verify SignedData.
@param[in] Pkcs7 MbedtlsPkcs7.
@param[in] TrustCert CA cert.
@param[in] Data Pointer for data.
@param[in] DataLen The len the buffer.
@retval TRUE Verify successfully.
@retval FALSE Verify failed.
**/
STATIC
BOOLEAN
MbedTlsPkcs7SignedDataVerify (
MbedtlsPkcs7 *Pkcs7,
mbedtls_x509_crt *TrustCert,
CONST UINT8 *Data,
INTN DataLen
)
{
MbedtlsPkcs7SignerInfo *SignerInfo;
mbedtls_x509_crt *Cert;
mbedtls_x509_crt *AllCert;
BOOLEAN Result;
SignerInfo = &(Pkcs7->SignedData.SignerInfos);
Result = TRUE;
//
// Traverse signers and verify each signers
//
while (SignerInfo != NULL) {
Result = FALSE;
// 1. Find signers cert
Cert = MbedTlsPkcs7FindSignerCert (SignerInfo, &(Pkcs7->SignedData.Certificates));
if (Cert != NULL) {
// 2. Check signer cert is trusted by trustCert
if (MbedTlsPkcs7VerifyCert (TrustCert, &(Pkcs7->SignedData.Crls), Cert)) {
// root cert verify pass
Result = TRUE;
} else {
if (MbedTlsPkcs7VerifyCertChain (Pkcs7, TrustCert, Cert)) {
Result = TRUE;
} else {
Result = FALSE;
}
}
if (Result == TRUE) {
// 3. Check signed data
AllCert = &(Pkcs7->SignedData.Certificates);
while (AllCert != NULL) {
if (MbedtlsPkcs7SignedDataVerifySigners (SignerInfo, AllCert, Data, DataLen) == 0) {
return TRUE;
}
AllCert = AllCert->next;
}
Result = FALSE;
}
}
// move to next
SignerInfo = SignerInfo->Next;
}
return Result;
}
/**
Check input P7Data is a wrapped ContentInfo structure or not. If not construct
a new structure to wrap P7Data.
Caution: This function may receive untrusted input.
UEFI Authenticated Variable is external input, so this function will do basic
check for PKCS#7 data structure.
@param[in] P7Data Pointer to the PKCS#7 message to verify.
@param[in] P7Length Length of the PKCS#7 message in bytes.
@param[out] WrapFlag If TRUE P7Data is a ContentInfo structure, otherwise
return FALSE.
@param[out] WrapData If return status of this function is TRUE:
1) when WrapFlag is TRUE, pointer to P7Data.
2) when WrapFlag is FALSE, pointer to a new ContentInfo
structure. It's caller's responsibility to free this
buffer.
@param[out] WrapDataSize Length of ContentInfo structure in bytes.
@retval TRUE The operation is finished successfully.
@retval FALSE The operation is failed due to lack of resources.
**/
BOOLEAN
WrapPkcs7Data (
IN CONST UINT8 *P7Data,
IN UINTN P7Length,
OUT BOOLEAN *WrapFlag,
OUT UINT8 **WrapData,
OUT UINTN *WrapDataSize
)
{
BOOLEAN Wrapped;
UINT8 *SignedData;
//
// Check whether input P7Data is a wrapped ContentInfo structure or not.
//
Wrapped = FALSE;
if ((P7Data[4] == MBEDTLS_ASN1_OID) && (P7Data[5] == sizeof (MBEDTLS_OID_PKCS7_SIGNED_DATA) - 1)) {
if (CompareMem (P7Data + 6, MBEDTLS_OID_PKCS7_SIGNED_DATA, sizeof (MBEDTLS_OID_PKCS7_SIGNED_DATA) - 1) == 0) {
if ((P7Data[15] == (MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_CONTEXT_SPECIFIC)) && (P7Data[16] == 0x82)) {
Wrapped = TRUE;
}
}
}
if (Wrapped) {
*WrapData = (UINT8 *)P7Data;
*WrapDataSize = P7Length;
} else {
//
// Wrap PKCS#7 signeddata to a ContentInfo structure - add a header in 19 bytes.
//
*WrapDataSize = P7Length + 19;
*WrapData = AllocateZeroPool (*WrapDataSize);
if (*WrapData == NULL) {
*WrapFlag = Wrapped;
return FALSE;
}
SignedData = *WrapData;
//
// Part1: 0x30, 0x82.
//
SignedData[0] = MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE;
SignedData[1] = 0x82;
//
// Part2: Length1 = P7Length + 19 - 4, in big endian.
//
SignedData[2] = (UINT8)(((UINT16)(*WrapDataSize - 4)) >> 8);
SignedData[3] = (UINT8)(((UINT16)(*WrapDataSize - 4)) & 0xff);
//
// Part3: 0x06, 0x09.
//
SignedData[4] = MBEDTLS_ASN1_OID;
SignedData[5] = sizeof (MBEDTLS_OID_PKCS7_SIGNED_DATA) - 1;
//
// Part4: OID value -- 0x2A 0x86 0x48 0x86 0xF7 0x0D 0x01 0x07 0x02.
//
CopyMem (SignedData + 6, MBEDTLS_OID_PKCS7_SIGNED_DATA, sizeof (MBEDTLS_OID_PKCS7_SIGNED_DATA) - 1);
//
// Part5: 0xA0, 0x82.
//
SignedData[15] = MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_CONTEXT_SPECIFIC;
SignedData[16] = 0x82;
//
// Part6: Length2 = P7Length, in big endian.
//
SignedData[17] = (UINT8)(((UINT16)P7Length) >> 8);
SignedData[18] = (UINT8)(((UINT16)P7Length) & 0xff);
//
// Part7: P7Data.
//
CopyMem (SignedData + 19, P7Data, P7Length);
}
*WrapFlag = Wrapped;
return TRUE;
}
/**
Verifies the validity of a PKCS#7 signed data as described in "PKCS #7:
Cryptographic Message Syntax Standard". The input signed data could be wrapped
in a ContentInfo structure.
If P7Data, TrustedCert or InData is NULL, then return FALSE.
If P7Length, CertLength or DataLength overflow, then return FALSE.
If this interface is not supported, then return FALSE.
@param[in] P7Data Pointer to the PKCS#7 message to verify.
@param[in] P7Length Length of the PKCS#7 message in bytes.
@param[in] TrustedCert Pointer to a trusted/root certificate encoded in DER, which
is used for certificate chain verification.
@param[in] CertLength Length of the trusted certificate in bytes.
@param[in] InData Pointer to the content to be verified.
@param[in] DataLength Length of InData in bytes.
@retval TRUE The specified PKCS#7 signed data is valid.
@retval FALSE Invalid PKCS#7 signed data.
@retval FALSE This interface is not supported.
**/
BOOLEAN
EFIAPI
Pkcs7Verify (
IN CONST UINT8 *P7Data,
IN UINTN P7Length,
IN CONST UINT8 *TrustedCert,
IN UINTN CertLength,
IN CONST UINT8 *InData,
IN UINTN DataLength
)
{
BOOLEAN Status;
UINT8 *WrapData;
UINTN WrapDataSize;
BOOLEAN Wrapped;
MbedtlsPkcs7 Pkcs7;
INT32 Ret;
mbedtls_x509_crt Crt;
mbedtls_x509_crt *TempCrt;
//
// Check input parameters.
//
if ((P7Data == NULL) || (TrustedCert == NULL) || (InData == NULL) ||
(P7Length > INT_MAX) || (CertLength > INT_MAX) || (DataLength > INT_MAX))
{
return FALSE;
}
Status = WrapPkcs7Data (P7Data, P7Length, &Wrapped, &WrapData, &WrapDataSize);
if (!Status) {
return FALSE;
}
Status = FALSE;
MbedTlsPkcs7Init (&Pkcs7);
mbedtls_x509_crt_init (&Crt);
Ret = MbedtlsPkcs7ParseDer (WrapData, (INT32)WrapDataSize, &Pkcs7);
if (Ret != 0) {
goto Cleanup;
}
Ret = mbedtls_x509_crt_parse_der (&Crt, TrustedCert, CertLength);
if (Ret != 0) {
goto Cleanup;
}
Status = MbedTlsPkcs7SignedDataVerify (&Pkcs7, &Crt, InData, (INT32)DataLength);
Cleanup:
if (&Crt != NULL) {
mbedtls_x509_crt_free (&Crt);
}
if (Pkcs7.SignedData.Certificates.next != NULL) {
TempCrt = Pkcs7.SignedData.Certificates.next;
mbedtls_x509_crt_free (TempCrt);
}
return Status;
}
/**
Wrap function to use free() to free allocated memory for certificates.
@param[in] Certs Pointer to the certificates to be freed.
**/
VOID
EFIAPI
Pkcs7FreeSigners (
IN UINT8 *Certs
)
{
if (Certs == NULL) {
return;
}
FreePool (Certs);
}
/**
Get the signer's certificates from PKCS#7 signed data as described in "PKCS #7:
Cryptographic Message Syntax Standard". The input signed data could be wrapped
in a ContentInfo structure.
If P7Data, CertStack, StackLength, TrustedCert or CertLength is NULL, then
return FALSE. If P7Length overflow, then return FALSE.
Caution: This function may receive untrusted input.
UEFI Authenticated Variable is external input, so this function will do basic
check for PKCS#7 data structure.
@param[in] P7Data Pointer to the PKCS#7 message to verify.
@param[in] P7Length Length of the PKCS#7 message in bytes.
@param[out] CertStack Pointer to Signer's certificates retrieved from P7Data.
It's caller's responsibility to free the buffer with
Pkcs7FreeSigners().
This data structure is EFI_CERT_STACK type.
@param[out] StackLength Length of signer's certificates in bytes.
@param[out] TrustedCert Pointer to a trusted certificate from Signer's certificates.
It's caller's responsibility to free the buffer with
Pkcs7FreeSigners().
@param[out] CertLength Length of the trusted certificate in bytes.
@retval TRUE The operation is finished successfully.
@retval FALSE Error occurs during the operation.
**/
BOOLEAN
EFIAPI
Pkcs7GetSigners (
IN CONST UINT8 *P7Data,
IN UINTN P7Length,
OUT UINT8 **CertStack,
OUT UINTN *StackLength,
OUT UINT8 **TrustedCert,
OUT UINTN *CertLength
)
{
MbedtlsPkcs7SignerInfo *SignerInfo;
mbedtls_x509_crt *Cert;
MbedtlsPkcs7 Pkcs7;
BOOLEAN Status;
UINT8 *WrapData;
UINTN WrapDataSize;
BOOLEAN Wrapped;
mbedtls_x509_crt *TempCrt;
UINTN CertSize;
UINT8 Index;
UINT8 *CertBuf;
UINT8 *OldBuf;
UINTN BufferSize;
UINTN OldSize;
if ((P7Data == NULL) || (CertStack == NULL) || (StackLength == NULL) ||
(TrustedCert == NULL) || (CertLength == NULL) || (P7Length > INT_MAX))
{
return FALSE;
}
Status = WrapPkcs7Data (P7Data, P7Length, &Wrapped, &WrapData, &WrapDataSize);
if (!Status) {
return FALSE;
}
Status = FALSE;
CertBuf = NULL;
OldBuf = NULL;
Cert = NULL;
MbedTlsPkcs7Init (&Pkcs7);
if (MbedtlsPkcs7ParseDer (WrapData, (INT32)WrapDataSize, &Pkcs7) != 0) {
goto _Exit;
}
SignerInfo = &(Pkcs7.SignedData.SignerInfos);
//
// Traverse each signers
//
// Convert CertStack to buffer in following format:
// UINT8 CertNumber;
// UINT32 Cert1Length;
// UINT8 Cert1[];
// UINT32 Cert2Length;
// UINT8 Cert2[];
// ...
// UINT32 CertnLength;
// UINT8 Certn[];
//
BufferSize = sizeof (UINT8);
OldSize = BufferSize;
Index = 0;
while (SignerInfo != NULL) {
// Find signers cert
Cert = MbedTlsPkcs7FindSignerCert (SignerInfo, &(Pkcs7.SignedData.Certificates));
if (Cert == NULL) {
goto _Exit;
}
CertSize = Cert->raw.len;
OldSize = BufferSize;
OldBuf = CertBuf;
BufferSize = OldSize + CertSize + sizeof (UINT32);
CertBuf = AllocateZeroPool (BufferSize);
if (CertBuf == NULL) {
goto _Exit;
}
if (OldBuf != NULL) {
CopyMem (CertBuf, OldBuf, OldSize);
FreePool (OldBuf);
OldBuf = NULL;
}
WriteUnaligned32 ((UINT32 *)(CertBuf + OldSize), (UINT32)CertSize);
CopyMem (CertBuf + OldSize + sizeof (UINT32), Cert->raw.p, CertSize);
Index++;
// move to next
SignerInfo = SignerInfo->Next;
}
if (CertBuf != NULL) {
//
// Update CertNumber.
//
CertBuf[0] = Index;
*CertLength = BufferSize - OldSize - sizeof (UINT32);
*TrustedCert = AllocateZeroPool (*CertLength);
if (*TrustedCert == NULL) {
goto _Exit;
}
CopyMem (*TrustedCert, CertBuf + OldSize + sizeof (UINT32), *CertLength);
*CertStack = CertBuf;
*StackLength = BufferSize;
Status = TRUE;
}
_Exit:
//
// Release Resources
//
if (!Status && (CertBuf != NULL)) {
FreePool (CertBuf);
*CertStack = NULL;
}
if (Status) {
if (Pkcs7.SignedData.Certificates.next != NULL) {
TempCrt = Pkcs7.SignedData.Certificates.next;
mbedtls_x509_crt_free (TempCrt);
}
}
if (OldBuf != NULL) {
FreePool (OldBuf);
}
return Status;
}
/**
Retrieves all embedded certificates from PKCS#7 signed data as described in "PKCS #7:
Cryptographic Message Syntax Standard", and outputs two certificate lists chained and
unchained to the signer's certificates.
The input signed data could be wrapped in a ContentInfo structure.
@param[in] P7Data Pointer to the PKCS#7 message.
@param[in] P7Length Length of the PKCS#7 message in bytes.
@param[out] SignerChainCerts Pointer to the certificates list chained to signer's
certificate. It's caller's responsibility to free the buffer
with Pkcs7FreeSigners().
This data structure is EFI_CERT_STACK type.
@param[out] ChainLength Length of the chained certificates list buffer in bytes.
@param[out] UnchainCerts Pointer to the unchained certificates lists. It's caller's
responsibility to free the buffer with Pkcs7FreeSigners().
This data structure is EFI_CERT_STACK type.
@param[out] UnchainLength Length of the unchained certificates list buffer in bytes.
@retval TRUE The operation is finished successfully.
@retval FALSE Error occurs during the operation.
**/
BOOLEAN
EFIAPI
Pkcs7GetCertificatesList (
IN CONST UINT8 *P7Data,
IN UINTN P7Length,
OUT UINT8 **SignerChainCerts,
OUT UINTN *ChainLength,
OUT UINT8 **UnchainCerts,
OUT UINTN *UnchainLength
)
{
ASSERT (FALSE);
return FALSE;
}