mirror of https://github.com/acidanthera/audk.git
455 lines
11 KiB
C
455 lines
11 KiB
C
/** @file
|
|
X.509 Certificate Handler Wrapper Implementation over OpenSSL.
|
|
|
|
Copyright (c) 2010 - 2011, Intel Corporation. All rights reserved.<BR>
|
|
This program and the accompanying materials
|
|
are licensed and made available under the terms and conditions of the BSD License
|
|
which accompanies this distribution. The full text of the license may be found at
|
|
http://opensource.org/licenses/bsd-license.php
|
|
|
|
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|
|
|
**/
|
|
|
|
#include "InternalCryptLib.h"
|
|
#include <openssl/x509.h>
|
|
|
|
|
|
/**
|
|
Construct a X509 object from DER-encoded certificate data.
|
|
|
|
If Cert is NULL, then ASSERT().
|
|
If SingleX509Cert is NULL, then ASSERT().
|
|
|
|
@param[in] Cert Pointer to the DER-encoded certificate data.
|
|
@param[in] CertSize The size of certificate data in bytes.
|
|
@param[out] SingleX509Cert The generated X509 object.
|
|
|
|
@retval TRUE The X509 object generation succeeded.
|
|
@retval FALSE The operation failed.
|
|
|
|
**/
|
|
BOOLEAN
|
|
EFIAPI
|
|
X509ConstructCertificate (
|
|
IN CONST UINT8 *Cert,
|
|
IN UINTN CertSize,
|
|
OUT UINT8 **SingleX509Cert
|
|
)
|
|
{
|
|
BIO *CertBio;
|
|
X509 *X509Cert;
|
|
BOOLEAN Status;
|
|
|
|
//
|
|
// ASSERT if Cert is NULL or SingleX509Cert is NULL.
|
|
//
|
|
ASSERT (Cert != NULL);
|
|
ASSERT (SingleX509Cert != NULL);
|
|
ASSERT (CertSize <= INT_MAX);
|
|
|
|
Status = FALSE;
|
|
|
|
//
|
|
// Read DER-encoded X509 Certificate and Construct X509 object.
|
|
//
|
|
CertBio = BIO_new (BIO_s_mem ());
|
|
BIO_write (CertBio, Cert, (int) CertSize);
|
|
if (CertBio == NULL) {
|
|
goto _Exit;
|
|
}
|
|
X509Cert = d2i_X509_bio (CertBio, NULL);
|
|
if (X509Cert == NULL) {
|
|
goto _Exit;
|
|
}
|
|
|
|
*SingleX509Cert = (UINT8 *) X509Cert;
|
|
Status = TRUE;
|
|
|
|
_Exit:
|
|
//
|
|
// Release Resources.
|
|
//
|
|
BIO_free (CertBio);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Construct a X509 stack object from a list of DER-encoded certificate data.
|
|
|
|
If X509Stack is NULL, then ASSERT().
|
|
|
|
@param[in, out] X509Stack On input, pointer to an existing X509 stack object.
|
|
On output, pointer to the X509 stack object with new
|
|
inserted X509 certificate.
|
|
@param ... A list of DER-encoded single certificate data followed
|
|
by certificate size. A NULL terminates the list. The
|
|
pairs are the arguments to X509ConstructCertificate().
|
|
|
|
@retval TRUE The X509 stack construction succeeded.
|
|
@retval FALSE The construction operation failed.
|
|
|
|
**/
|
|
BOOLEAN
|
|
EFIAPI
|
|
X509ConstructCertificateStack (
|
|
IN OUT UINT8 **X509Stack,
|
|
...
|
|
)
|
|
{
|
|
UINT8 *Cert;
|
|
UINTN CertSize;
|
|
X509 *X509Cert;
|
|
STACK_OF(X509) *CertStack;
|
|
BOOLEAN Status;
|
|
VA_LIST Args;
|
|
UINTN Index;
|
|
|
|
//
|
|
// ASSERT if input X509Stack is NULL.
|
|
//
|
|
ASSERT (X509Stack != NULL);
|
|
|
|
Status = FALSE;
|
|
|
|
//
|
|
// Initialize X509 stack object.
|
|
//
|
|
CertStack = (STACK_OF(X509) *) (*X509Stack);
|
|
if (CertStack == NULL) {
|
|
CertStack = sk_X509_new_null ();
|
|
if (CertStack == NULL) {
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
VA_START (Args, X509Stack);
|
|
|
|
for (Index = 0; ; Index++) {
|
|
//
|
|
// If Cert is NULL, then it is the end of the list.
|
|
//
|
|
Cert = VA_ARG (Args, UINT8 *);
|
|
if (Cert == NULL) {
|
|
break;
|
|
}
|
|
|
|
CertSize = VA_ARG (Args, UINTN);
|
|
|
|
//
|
|
// Construct X509 Object from the given DER-encoded certificate data.
|
|
//
|
|
Status = X509ConstructCertificate (
|
|
(CONST UINT8 *) Cert,
|
|
CertSize,
|
|
(UINT8 **) &X509Cert
|
|
);
|
|
if (!Status) {
|
|
X509_free (X509Cert);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Insert the new X509 object into X509 stack object.
|
|
//
|
|
sk_X509_push (CertStack, X509Cert);
|
|
}
|
|
|
|
VA_END (Args);
|
|
|
|
if (!Status) {
|
|
sk_X509_pop_free (CertStack, X509_free);
|
|
} else {
|
|
*X509Stack = (UINT8 *) CertStack;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Release the specified X509 object.
|
|
|
|
If X509Cert is NULL, then ASSERT().
|
|
|
|
@param[in] X509Cert Pointer to the X509 object to be released.
|
|
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
X509Free (
|
|
IN VOID *X509Cert
|
|
)
|
|
{
|
|
ASSERT (X509Cert != NULL);
|
|
|
|
//
|
|
// Free OpenSSL X509 object.
|
|
//
|
|
X509_free ((X509 *) X509Cert);
|
|
}
|
|
|
|
/**
|
|
Release the specified X509 stack object.
|
|
|
|
If X509Stack is NULL, then ASSERT().
|
|
|
|
@param[in] X509Stack Pointer to the X509 stack object to be released.
|
|
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
X509StackFree (
|
|
IN VOID *X509Stack
|
|
)
|
|
{
|
|
ASSERT (X509Stack != NULL);
|
|
|
|
//
|
|
// Free OpenSSL X509 stack object.
|
|
//
|
|
sk_X509_pop_free ((STACK_OF(X509) *) X509Stack, X509_free);
|
|
}
|
|
|
|
/**
|
|
Retrieve the subject bytes from one X.509 certificate.
|
|
|
|
@param[in] Cert Pointer to the DER-encoded X509 certificate.
|
|
@param[in] CertSize Size of the X509 certificate in bytes.
|
|
@param[out] CertSubject Pointer to the retrieved certificate subject bytes.
|
|
@param[in, out] SubjectSize The size in bytes of the CertSubject buffer on input,
|
|
and the size of buffer returned CertSubject on output.
|
|
|
|
If Cert is NULL, then ASSERT().
|
|
If SubjectSize is NULL, then ASSERT().
|
|
|
|
@retval TRUE The certificate subject retrieved successfully.
|
|
@retval FALSE Invalid certificate, or the SubjectSize is too small for the result.
|
|
The SubjectSize will be updated with the required size.
|
|
|
|
**/
|
|
BOOLEAN
|
|
EFIAPI
|
|
X509GetSubjectName (
|
|
IN CONST UINT8 *Cert,
|
|
IN UINTN CertSize,
|
|
OUT UINT8 *CertSubject,
|
|
IN OUT UINTN *SubjectSize
|
|
)
|
|
{
|
|
BOOLEAN Status;
|
|
X509 *X509Cert;
|
|
X509_NAME *X509Name;
|
|
|
|
//
|
|
// ASSERT if Cert is NULL or SubjectSize is NULL.
|
|
//
|
|
ASSERT (Cert != NULL);
|
|
ASSERT (SubjectSize != NULL);
|
|
|
|
Status = FALSE;
|
|
X509Cert = NULL;
|
|
|
|
//
|
|
// Read DER-encoded X509 Certificate and Construct X509 object.
|
|
//
|
|
Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **) &X509Cert);
|
|
if ((X509Cert == NULL) || (!Status)) {
|
|
goto _Exit;
|
|
}
|
|
|
|
//
|
|
// Retrieve subject name from certificate object.
|
|
//
|
|
X509Name = X509_get_subject_name (X509Cert);
|
|
if (*SubjectSize < (UINTN) X509Name->bytes->length) {
|
|
*SubjectSize = (UINTN) X509Name->bytes->length;
|
|
goto _Exit;
|
|
}
|
|
*SubjectSize = (UINTN) X509Name->bytes->length;
|
|
if (CertSubject != NULL) {
|
|
CopyMem (CertSubject, (UINT8 *)X509Name->bytes->data, *SubjectSize);
|
|
Status = TRUE;
|
|
}
|
|
|
|
_Exit:
|
|
//
|
|
// Release Resources.
|
|
//
|
|
X509_free (X509Cert);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Retrieve the RSA Public Key from one DER-encoded X509 certificate.
|
|
|
|
@param[in] Cert Pointer to the DER-encoded X509 certificate.
|
|
@param[in] CertSize Size of the X509 certificate in bytes.
|
|
@param[out] RsaContext Pointer to new-generated RSA context which contain the retrieved
|
|
RSA public key component. Use RsaFree() function to free the
|
|
resource.
|
|
|
|
If Cert is NULL, then ASSERT().
|
|
If RsaContext is NULL, then ASSERT().
|
|
|
|
@retval TRUE RSA Public Key was retrieved successfully.
|
|
@retval FALSE Fail to retrieve RSA public key from X509 certificate.
|
|
|
|
**/
|
|
BOOLEAN
|
|
EFIAPI
|
|
RsaGetPublicKeyFromX509 (
|
|
IN CONST UINT8 *Cert,
|
|
IN UINTN CertSize,
|
|
OUT VOID **RsaContext
|
|
)
|
|
{
|
|
BOOLEAN Status;
|
|
EVP_PKEY *Pkey;
|
|
X509 *X509Cert;
|
|
|
|
//
|
|
// ASSERT if Cert is NULL or RsaContext is NULL.
|
|
//
|
|
ASSERT (Cert != NULL);
|
|
ASSERT (RsaContext != NULL);
|
|
|
|
Status = FALSE;
|
|
Pkey = NULL;
|
|
X509Cert = NULL;
|
|
|
|
//
|
|
// Read DER-encoded X509 Certificate and Construct X509 object.
|
|
//
|
|
Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **) &X509Cert);
|
|
if ((X509Cert == NULL) || (!Status)) {
|
|
goto _Exit;
|
|
}
|
|
|
|
//
|
|
// Retrieve and check EVP_PKEY data from X509 Certificate.
|
|
//
|
|
Pkey = X509_get_pubkey (X509Cert);
|
|
if ((Pkey == NULL) || (Pkey->type != EVP_PKEY_RSA)) {
|
|
goto _Exit;
|
|
}
|
|
|
|
//
|
|
// Duplicate RSA Context from the retrieved EVP_PKEY.
|
|
//
|
|
if ((*RsaContext = RSAPublicKey_dup (Pkey->pkey.rsa)) != NULL) {
|
|
Status = TRUE;
|
|
}
|
|
|
|
_Exit:
|
|
//
|
|
// Release Resources.
|
|
//
|
|
X509_free (X509Cert);
|
|
EVP_PKEY_free (Pkey);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Verify one X509 certificate was issued by the trusted CA.
|
|
|
|
@param[in] Cert Pointer to the DER-encoded X509 certificate to be verified.
|
|
@param[in] CertSize Size of the X509 certificate in bytes.
|
|
@param[in] CACert Pointer to the DER-encoded trusted CA certificate.
|
|
@param[in] CACertSize Size of the CA Certificate in bytes.
|
|
|
|
If Cert is NULL, then ASSERT().
|
|
If CACert is NULL, then ASSERT().
|
|
|
|
@retval TRUE The certificate was issued by the trusted CA.
|
|
@retval FALSE Invalid certificate or the certificate was not issued by the given
|
|
trusted CA.
|
|
|
|
**/
|
|
BOOLEAN
|
|
EFIAPI
|
|
X509VerifyCert (
|
|
IN CONST UINT8 *Cert,
|
|
IN UINTN CertSize,
|
|
IN CONST UINT8 *CACert,
|
|
IN UINTN CACertSize
|
|
)
|
|
{
|
|
BOOLEAN Status;
|
|
X509 *X509Cert;
|
|
X509 *X509CACert;
|
|
X509_STORE *CertStore;
|
|
X509_STORE_CTX CertCtx;
|
|
|
|
//
|
|
// ASSERT if Cert is NULL or CACert is NULL.
|
|
//
|
|
ASSERT (Cert != NULL);
|
|
ASSERT (CACert != NULL);
|
|
|
|
Status = FALSE;
|
|
X509Cert = NULL;
|
|
X509CACert = NULL;
|
|
CertStore = NULL;
|
|
|
|
//
|
|
// Register & Initialize necessary digest algorithms for certificate verification.
|
|
//
|
|
EVP_add_digest (EVP_md5());
|
|
EVP_add_digest (EVP_sha1());
|
|
EVP_add_digest (EVP_sha256());
|
|
|
|
//
|
|
// Read DER-encoded certificate to be verified and Construct X509 object.
|
|
//
|
|
Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **) &X509Cert);
|
|
if ((X509Cert == NULL) || (!Status)) {
|
|
goto _Exit;
|
|
}
|
|
|
|
//
|
|
// Read DER-encoded root certificate and Construct X509 object.
|
|
//
|
|
Status = X509ConstructCertificate (CACert, CACertSize, (UINT8 **) &X509CACert);
|
|
if ((X509CACert == NULL) || (!Status)) {
|
|
goto _Exit;
|
|
}
|
|
|
|
//
|
|
// Set up X509 Store for trusted certificate.
|
|
//
|
|
CertStore = X509_STORE_new ();
|
|
if (CertStore == NULL) {
|
|
goto _Exit;
|
|
}
|
|
if (!(X509_STORE_add_cert (CertStore, X509CACert))) {
|
|
goto _Exit;
|
|
}
|
|
|
|
//
|
|
// Set up X509_STORE_CTX for the subsequent verification operation.
|
|
//
|
|
if (!X509_STORE_CTX_init (&CertCtx, CertStore, X509Cert, NULL)) {
|
|
goto _Exit;
|
|
}
|
|
|
|
//
|
|
// X509 Certificate Verification.
|
|
//
|
|
Status = (BOOLEAN) X509_verify_cert (&CertCtx);
|
|
X509_STORE_CTX_cleanup (&CertCtx);
|
|
|
|
_Exit:
|
|
//
|
|
// Release Resources.
|
|
//
|
|
X509_free (X509Cert);
|
|
X509_free (X509CACert);
|
|
X509_STORE_free (CertStore);
|
|
|
|
return Status;
|
|
}
|