/********************************************************************************/ /* */ /* X509 Support */ /* Written by Ken Goldman */ /* IBM Thomas J. Watson Research Center */ /* */ /* Licenses and Notices */ /* */ /* 1. Copyright Licenses: */ /* */ /* - Trusted Computing Group (TCG) grants to the user of the source code in */ /* this specification (the "Source Code") a worldwide, irrevocable, */ /* nonexclusive, royalty free, copyright license to reproduce, create */ /* derivative works, distribute, display and perform the Source Code and */ /* derivative works thereof, and to grant others the rights granted herein. */ /* */ /* - The TCG rants to the user of the other parts of the specification */ /* (other than the Source Code) the rights to reproduce, distribute, */ /* display, and perform the specification solely for the purpose of */ /* developing products based on such documents. */ /* */ /* 2. Source Code Distribution Conditions: */ /* */ /* - Redistributions of Source Code must retain the above copyright licenses, */ /* this list of conditions and the following disclaimers. */ /* */ /* - Redistributions in binary form must reproduce the above copyright */ /* licenses, this list of conditions and the following disclaimers in the */ /* documentation and/or other materials provided with the distribution. */ /* */ /* 3. Disclaimers: */ /* */ /* - THE COPYRIGHT LICENSES SET FORTH ABOVE DO NOT REPRESENT ANY FORM OF */ /* LICENSE OR WAIVER, EXPRESS OR IMPLIED, BY ESTOPPEL OR OTHERWISE, WITH */ /* RESPECT TO PATENT RIGHTS HELD BY TCG MEMBERS (OR OTHER THIRD PARTIES) */ /* THAT MAY BE NECESSARY TO IMPLEMENT THIS SPECIFICATION OR OTHERWISE. */ /* Contact TCG Administration (admin@trustedcomputinggroup.org) for */ /* information on specification licensing rights available through TCG */ /* membership agreements. */ /* */ /* - THIS SPECIFICATION IS PROVIDED "AS IS" WITH NO EXPRESS OR IMPLIED */ /* WARRANTIES WHATSOEVER, INCLUDING ANY WARRANTY OF MERCHANTABILITY OR */ /* FITNESS FOR A PARTICULAR PURPOSE, ACCURACY, COMPLETENESS, OR */ /* NONINFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS, OR ANY WARRANTY */ /* OTHERWISE ARISING OUT OF ANY PROPOSAL, SPECIFICATION OR SAMPLE. */ /* */ /* - Without limitation, TCG and its members and licensors disclaim all */ /* liability, including liability for infringement of any proprietary */ /* rights, relating to use of information in this specification and to the */ /* implementation of this specification, and TCG disclaims all liability for */ /* cost of procurement of substitute goods or services, lost profits, loss */ /* of use, loss of data or any incidental, consequential, direct, indirect, */ /* or special damages, whether under contract, tort, warranty or otherwise, */ /* arising in any way out of use or reliance upon this specification or any */ /* information herein. */ /* */ /* (c) Copyright IBM Corp. and others, 2019 - 2024 */ /* */ /********************************************************************************/ //** 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 #if CC_CertifyX509 //** 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) { GOTO_ERROR_UNLESS((length = ASN1NextTag(ctx)) >= 0); // If this is not a constructed sequence, then it doesn't belong // in the extensions. GOTO_ERROR_UNLESS(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; } } } GOTO_ERROR_UNLESS(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; // see if any reserved bits are set if(keyUsage.integer & ~(TPMA_X509_KEY_USAGE_ALLOWED_BITS)) return TPM_RCS_RESERVED_BITS; // 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 encipherment. badRestricted = IS_ATTRIBUTE(keyUsage.x509, TPMA_X509_KEY_USAGE, keyEncipherment) && !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); } #endif // CC_CertifyX509