blob: 30f54d2328dadb7d7140f76c6daeacb66530f962 [file] [log] [blame]
/* Microsoft Reference Implementation for TPM 2.0
*
* The copyright in this software is being made available under the BSD License,
* included below. This software may be subject to other third party and
* contributor rights, including patent rights, and no such rights are granted
* under this license.
*
* Copyright (c) Microsoft Corporation
*
* All rights reserved.
*
* BSD License
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this list
* of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ""AS IS""
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
//** Includes
#include "Tpm.h"
#include "TpmASN1.h"
#include "TpmASN1_fp.h"
#define _X509_SPT_
#include "X509.h"
#include "X509_spt_fp.h"
#if ALG_RSA
# include "X509_RSA_fp.h"
#endif // ALG_RSA
#if ALG_ECC
# include "X509_ECC_fp.h"
#endif // ALG_ECC
#if ALG_SM2
//# include "X509_SM2_fp.h"
#endif // ALG_RSA
//** Unmarshaling Functions
//*** X509FindExtensionByOID()
// This will search a list of X509 extensions to find an extension with the
// requested OID. If the extension is found, the output context ('ctx') is set up
// to point to the OID in the extension.
// Return Type: BOOL
// TRUE(1) success
// FALSE(0) failure (could be catastrophic)
BOOL
X509FindExtensionByOID(
ASN1UnmarshalContext *ctxIn, // IN: the context to search
ASN1UnmarshalContext *ctx, // OUT: the extension context
const BYTE *OID // IN: oid to search for
)
{
INT16 length;
//
pAssert(ctxIn != NULL);
// Make the search non-destructive of the input if ctx provided. Otherwise, use
// the provided context.
if (ctx == NULL)
ctx = ctxIn;
// if the provided search context is different from the context of the extension,
// then copy the search context to the search context.
else if(ctx != ctxIn)
*ctx = *ctxIn;
// Now, search in the extension context
for(;ctx->size > ctx->offset; ctx->offset += length)
{
VERIFY((length = ASN1NextTag(ctx)) >= 0);
// If this is not a constructed sequence, then it doesn't belong
// in the extensions.
VERIFY(ctx->tag == ASN1_CONSTRUCTED_SEQUENCE);
// Make sure that this entry could hold the OID
if (length >= OID_SIZE(OID))
{
// See if this is a match for the provided object identifier.
if (MemoryEqual(OID, &(ctx->buffer[ctx->offset]), OID_SIZE(OID)))
{
// Return with ' ctx' set to point to the start of the OID with the size
// set to be the size of the SEQUENCE
ctx->buffer += ctx->offset;
ctx->offset = 0;
ctx->size = length;
return TRUE;
}
}
}
VERIFY(ctx->offset == ctx->size);
return FALSE;
Error:
ctxIn->size = -1;
ctx->size = -1;
return FALSE;
}
//*** X509GetExtensionBits()
// This function will extract a bit field from an extension. If the extension doesn't
// contain a bit string, it will fail.
// Return Type: BOOL
// TRUE(1) success
// FALSE(0) failure
UINT32
X509GetExtensionBits(
ASN1UnmarshalContext *ctx,
UINT32 *value
)
{
INT16 length;
//
while (((length = ASN1NextTag(ctx)) > 0) && (ctx->size > ctx->offset))
{
// Since this is an extension, the extension value will be in an OCTET STRING
if (ctx->tag == ASN1_OCTET_STRING)
{
return ASN1GetBitStringValue(ctx, value);
}
ctx->offset += length;
}
ctx->size = -1;
return FALSE;
}
//***X509ProcessExtensions()
// This function is used to process the TPMA_OBJECT and KeyUsage extensions. It is not
// in the CertifyX509.c code because it makes the code harder to follow.
// Return Type: TPM_RC
// TPM_RCS_ATTRIBUTES the attributes of object are not consistent with
// the extension setting
// TPM_RC_VALUE problem parsing the extensions
TPM_RC
X509ProcessExtensions(
OBJECT *object, // IN: The object with the attributes to
// check
stringRef *extension // IN: The start and length of the extensions
)
{
ASN1UnmarshalContext ctx;
ASN1UnmarshalContext extensionCtx;
INT16 length;
UINT32 value;
TPMA_OBJECT attributes = object->publicArea.objectAttributes;
//
if(!ASN1UnmarshalContextInitialize(&ctx, extension->len, extension->buf)
|| ((length = ASN1NextTag(&ctx)) < 0)
|| (ctx.tag != X509_EXTENSIONS))
return TPM_RCS_VALUE;
if( ((length = ASN1NextTag(&ctx)) < 0)
|| (ctx.tag != (ASN1_CONSTRUCTED_SEQUENCE)))
return TPM_RCS_VALUE;
// Get the extension for the TPMA_OBJECT if there is one
if(X509FindExtensionByOID(&ctx, &extensionCtx, OID_TCG_TPMA_OBJECT) &&
X509GetExtensionBits(&extensionCtx, &value))
{
// If an keyAttributes extension was found, it must be exactly the same as the
// attributes of the object.
// NOTE: MemoryEqual() is used rather than a simple UINT32 compare to avoid
// type-punned pointer warning/error.
if(!MemoryEqual(&value, &attributes, sizeof(value)))
return TPM_RCS_ATTRIBUTES;
}
// Make sure the failure to find the value wasn't because of a fatal error
else if(extensionCtx.size < 0)
return TPM_RCS_VALUE;
// Get the keyUsage extension. This one is required
if(X509FindExtensionByOID(&ctx, &extensionCtx, OID_KEY_USAGE_EXTENSION) &&
X509GetExtensionBits(&extensionCtx, &value))
{
x509KeyUsageUnion keyUsage;
BOOL badSign;
BOOL badDecrypt;
BOOL badFixedTPM;
BOOL badRestricted;
//
keyUsage.integer = value;
// For KeyUsage:
// 1) 'sign' is SET if Key Usage includes signing
badSign = ((KEY_USAGE_SIGN.integer & keyUsage.integer) != 0)
&& !IS_ATTRIBUTE(attributes, TPMA_OBJECT, sign);
// 2) 'decrypt' is SET if Key Usage includes decryption uses
badDecrypt = ((KEY_USAGE_DECRYPT.integer & keyUsage.integer) != 0)
&& !IS_ATTRIBUTE(attributes, TPMA_OBJECT, decrypt);
// 3) 'fixedTPM' is SET if Key Usage is non-repudiation
badFixedTPM = IS_ATTRIBUTE(keyUsage.x509, TPMA_X509_KEY_USAGE,
nonrepudiation)
&& !IS_ATTRIBUTE(attributes, TPMA_OBJECT, fixedTPM);
// 4)'restricted' is SET if Key Usage is for key agreement.
badRestricted = IS_ATTRIBUTE(keyUsage.x509, TPMA_X509_KEY_USAGE, keyAgreement)
&& !IS_ATTRIBUTE(attributes, TPMA_OBJECT, restricted);
if(badSign || badDecrypt || badFixedTPM || badRestricted)
return TPM_RCS_VALUE;
}
else
// The KeyUsage extension is required
return TPM_RCS_VALUE;
return TPM_RC_SUCCESS;
}
//** Marshaling Functions
//*** X509AddSigningAlgorithm()
// This creates the singing algorithm data.
// Return Type: INT16
// > 0 number of octets added
// <= 0 failure
INT16
X509AddSigningAlgorithm(
ASN1MarshalContext *ctx,
OBJECT *signKey,
TPMT_SIG_SCHEME *scheme
)
{
switch(signKey->publicArea.type)
{
#if ALG_RSA
case TPM_ALG_RSA:
return X509AddSigningAlgorithmRSA(signKey, scheme, ctx);
#endif // ALG_RSA
#if ALG_ECC
case TPM_ALG_ECC:
return X509AddSigningAlgorithmECC(signKey, scheme, ctx);
#endif // ALG_ECC
#if ALG_SM2
case TPM_ALG_SM2:
break; // no signing algorithm for SM2 yet
// return X509AddSigningAlgorithmSM2(signKey, scheme, ctx);
#endif // ALG_SM2
default:
break;
}
return 0;
}
//*** X509AddPublicKey()
// This function will add the publicKey description to the DER data. If fillPtr is
// NULL, then no data is transferred and this function will indicate if the TPM
// has the values for DER-encoding of the public key.
// Return Type: INT16
// > 0 number of octets added
// == 0 failure
INT16
X509AddPublicKey(
ASN1MarshalContext *ctx,
OBJECT *object
)
{
switch(object->publicArea.type)
{
#if ALG_RSA
case TPM_ALG_RSA:
return X509AddPublicRSA(object, ctx);
#endif
#if ALG_ECC
case TPM_ALG_ECC:
return X509AddPublicECC(object, ctx);
#endif
#if ALG_SM2
case TPM_ALG_SM2:
break;
#endif
default:
break;
}
return FALSE;
}
//*** X509PushAlgorithmIdentifierSequence()
// The function adds the algorithm identifier sequence.
// Return Type: INT16
// > 0 number of bytes added
// == 0 failure
INT16
X509PushAlgorithmIdentifierSequence(
ASN1MarshalContext *ctx,
const BYTE *OID
)
{
// An algorithm ID sequence is:
// SEQUENCE
// OID
// NULL
ASN1StartMarshalContext(ctx); // hash algorithm
ASN1PushNull(ctx);
ASN1PushOID(ctx, OID);
return ASN1EndEncapsulation(ctx, ASN1_CONSTRUCTED_SEQUENCE);
}