/* 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. | |
*/ | |
#include "Tpm.h" | |
#include "CertifyX509_fp.h" | |
#include "X509.h" | |
#include "TpmASN1_fp.h" | |
#include "X509_spt_fp.h" | |
#include "Attest_spt_fp.h" | |
#if CERTIFYX509_DEBUG | |
#include "Platform_fp.h" | |
#endif | |
#if CC_CertifyX509 // Conditional expansion of this file | |
/*(See part 3 specification) | |
// Certify using an X509-formatted certificate | |
*/ | |
// return type: TPM_RC | |
// TPM_RC_ATTRIBUTES the attributes of 'objectHandle' are not compatible | |
// with the KeyUsage or TPMA_OBJECT values in the | |
// extensions fields | |
// TPM_RC_BINDING the public and private portions of the key are not | |
// properly bound. | |
// TPM_RC_HASH the hash algorithm in the scheme is not supported | |
// TPM_RC_KEY 'signHandle' does not reference a signing key; | |
// TPM_RC_SCHEME the scheme is not compatible with sign key type, | |
// or input scheme is not compatible with default | |
// scheme, or the chosen scheme is not a valid | |
// sign scheme | |
// TPM_RC_VALUE most likely a problem with the format of | |
// 'partialCertificate' | |
TPM_RC | |
TPM2_CertifyX509( | |
CertifyX509_In *in, // IN: input parameter list | |
CertifyX509_Out *out // OUT: output parameter list | |
) | |
{ | |
TPM_RC result; | |
OBJECT *signKey = HandleToObject(in->signHandle); | |
OBJECT *object = HandleToObject(in->objectHandle); | |
HASH_STATE hash; | |
INT16 length; // length for a tagged element | |
ASN1UnmarshalContext ctx; | |
ASN1MarshalContext ctxOut; | |
// certTBS holds an array of pointers and lengths. Each entry references the | |
// corresponding value in a TBSCertificate structure. For example, the 1th | |
// element references the version number | |
stringRef certTBS[REF_COUNT] = {{0}}; | |
#define ALLOWED_SEQUENCES (SUBJECT_PUBLIC_KEY_REF - SIGNATURE_REF) | |
stringRef partial[ALLOWED_SEQUENCES] = {{0}}; | |
INT16 countOfSequences = 0; | |
INT16 i; | |
// | |
#if CERTIFYX509_DEBUG | |
DebugFileInit(); | |
DebugDumpBuffer(in->partialCertificate.t.size, in->partialCertificate.t.buffer, | |
"partialCertificate"); | |
#endif | |
// Input Validation | |
if(in->reserved.b.size != 0) | |
return TPM_RC_SIZE + RC_CertifyX509_reserved; | |
// signing key must be able to sign | |
if(!IsSigningObject(signKey)) | |
return TPM_RCS_KEY + RC_CertifyX509_signHandle; | |
// Pick a scheme for sign. If the input sign scheme is not compatible with | |
// the default scheme, return an error. | |
if(!CryptSelectSignScheme(signKey, &in->inScheme)) | |
return TPM_RCS_SCHEME + RC_CertifyX509_inScheme; | |
// Make sure that the public Key encoding is known | |
if(X509AddPublicKey(NULL, object) == 0) | |
return TPM_RCS_ASYMMETRIC + RC_CertifyX509_objectHandle; | |
// Unbundle 'partialCertificate'. | |
// Initialize the unmarshaling context | |
if(!ASN1UnmarshalContextInitialize(&ctx, in->partialCertificate.t.size, | |
in->partialCertificate.t.buffer)) | |
return TPM_RCS_VALUE + RC_CertifyX509_partialCertificate; | |
// Make sure that this is a constructed SEQUENCE | |
length = ASN1NextTag(&ctx); | |
// Must be a constructed SEQUENCE that uses all of the input parameter | |
if((ctx.tag != (ASN1_CONSTRUCTED_SEQUENCE)) | |
|| ((ctx.offset + length) != in->partialCertificate.t.size)) | |
return TPM_RCS_SIZE + RC_CertifyX509_partialCertificate; | |
// This scans through the contents of the outermost SEQUENCE. This would be the | |
// 'issuer', 'validity', 'subject', 'issuerUniqueID' (optional), | |
// 'subjectUniqueID' (optional), and 'extensions.' | |
while(ctx.offset < ctx.size) | |
{ | |
INT16 startOfElement = ctx.offset; | |
// | |
// Read the next tag and length field. | |
length = ASN1NextTag(&ctx); | |
if(length < 0) | |
break; | |
if(ctx.tag == ASN1_CONSTRUCTED_SEQUENCE) | |
{ | |
partial[countOfSequences].buf = &ctx.buffer[startOfElement]; | |
ctx.offset += length; | |
partial[countOfSequences].len = (INT16)ctx.offset - startOfElement; | |
if(++countOfSequences > ALLOWED_SEQUENCES) | |
break; | |
} | |
else if(ctx.tag == X509_EXTENSIONS) | |
{ | |
if(certTBS[EXTENSIONS_REF].len != 0) | |
return TPM_RCS_VALUE + RC_CertifyX509_partialCertificate; | |
certTBS[EXTENSIONS_REF].buf = &ctx.buffer[startOfElement]; | |
ctx.offset += length; | |
certTBS[EXTENSIONS_REF].len = | |
(INT16)ctx.offset - startOfElement; | |
} | |
else | |
return TPM_RCS_VALUE + RC_CertifyX509_partialCertificate; | |
} | |
// Make sure that we used all of the data and found at least the required | |
// number of elements. | |
if((ctx.offset != ctx.size) || (countOfSequences < 3) | |
|| (countOfSequences > 4) | |
|| (certTBS[EXTENSIONS_REF].buf == NULL)) | |
return TPM_RCS_VALUE + RC_CertifyX509_partialCertificate; | |
// Now that we know how many sequences there were, we can put them where they | |
// belong | |
for(i = 0; i < countOfSequences; i++) | |
certTBS[SUBJECT_KEY_REF - i] = partial[countOfSequences - 1 - i]; | |
// If only three SEQUENCES, then the TPM needs to produce the signature algorithm. | |
// See if it can | |
if((countOfSequences == 3) && | |
(X509AddSigningAlgorithm(NULL, signKey, &in->inScheme) == 0)) | |
return TPM_RCS_SCHEME + RC_CertifyX509_signHandle; | |
// Process the extensions | |
result = X509ProcessExtensions(object, &certTBS[EXTENSIONS_REF]); | |
if(result != TPM_RC_SUCCESS) | |
// If the extension has the TPMA_OBJECT extension and the attributes don't | |
// match, then the error code will be TPM_RCS_ATTRIBUTES. Otherwise, the error | |
// indicates a malformed partialCertificate. | |
return result + ((result == TPM_RCS_ATTRIBUTES) | |
? RC_CertifyX509_objectHandle | |
: RC_CertifyX509_partialCertificate); | |
// Command Output | |
// Create the addedToCertificate values | |
// Build the addedToCertificate from the bottom up. | |
// Initialize the context structure | |
ASN1InitialializeMarshalContext(&ctxOut, sizeof(out->addedToCertificate.t.buffer), | |
out->addedToCertificate.t.buffer); | |
// Place a marker for the overall context | |
ASN1StartMarshalContext(&ctxOut); // SEQUENCE for addedToCertificate | |
// Add the subject public key descriptor | |
certTBS[SUBJECT_PUBLIC_KEY_REF].len = X509AddPublicKey(&ctxOut, object); | |
certTBS[SUBJECT_PUBLIC_KEY_REF].buf = ctxOut.buffer + ctxOut.offset; | |
// If the caller didn't provide the algorithm identifier, create it | |
if(certTBS[SIGNATURE_REF].len == 0) | |
{ | |
certTBS[SIGNATURE_REF].len = X509AddSigningAlgorithm(&ctxOut, signKey, | |
&in->inScheme); | |
certTBS[SIGNATURE_REF].buf = ctxOut.buffer + ctxOut.offset; | |
} | |
// Create the serial number value. Use the out->tbsDigest as scratch. | |
{ | |
TPM2B *digest = &out->tbsDigest.b; | |
// | |
digest->size = (INT16)CryptHashStart(&hash, signKey->publicArea.nameAlg); | |
pAssert(digest->size != 0); | |
// The serial number size is the smaller of the digest and the vendor-defined | |
// value | |
digest->size = MIN(digest->size, SIZE_OF_X509_SERIAL_NUMBER); | |
// Add all the parts of the certificate other than the serial number | |
// and version number | |
for(i = SIGNATURE_REF; i < REF_COUNT; i++) | |
CryptDigestUpdate(&hash, certTBS[i].len, certTBS[i].buf); | |
// throw in the Name of the signing key... | |
CryptDigestUpdate2B(&hash, &signKey->name.b); | |
// ...and the Name of the signed key. | |
CryptDigestUpdate2B(&hash, &object->name.b); | |
// Done | |
CryptHashEnd2B(&hash, digest); | |
} | |
// Add the serial number | |
certTBS[SERIAL_NUMBER_REF].len = | |
ASN1PushInteger(&ctxOut, out->tbsDigest.t.size, out->tbsDigest.t.buffer); | |
certTBS[SERIAL_NUMBER_REF].buf = ctxOut.buffer + ctxOut.offset; | |
// Add the static version number | |
ASN1StartMarshalContext(&ctxOut); | |
ASN1PushUINT(&ctxOut, 2); | |
certTBS[VERSION_REF].len = | |
ASN1EndEncapsulation(&ctxOut, ASN1_APPLICAIION_SPECIFIC); | |
certTBS[VERSION_REF].buf = ctxOut.buffer + ctxOut.offset; | |
// Create a fake tag and length for the TBS in the space used for | |
// 'addedToCertificate' | |
{ | |
for(length = 0, i = 0; i < REF_COUNT; i++) | |
length += certTBS[i].len; | |
// Put a fake tag and length into the buffer for use in the tbsDigest | |
certTBS[ENCODED_SIZE_REF].len = | |
ASN1PushTagAndLength(&ctxOut, ASN1_CONSTRUCTED_SEQUENCE, length); | |
certTBS[ENCODED_SIZE_REF].buf = ctxOut.buffer + ctxOut.offset; | |
// Restore the buffer pointer to add back the number of octets used for the | |
// tag and length | |
ctxOut.offset += certTBS[ENCODED_SIZE_REF].len; | |
} | |
// sanity check | |
if(ctxOut.offset < 0) | |
return TPM_RC_FAILURE; | |
// Create the tbsDigest to sign | |
out->tbsDigest.t.size = CryptHashStart(&hash, in->inScheme.details.any.hashAlg); | |
for(i = 0; i < REF_COUNT; i++) | |
CryptDigestUpdate(&hash, certTBS[i].len, certTBS[i].buf); | |
CryptHashEnd2B(&hash, &out->tbsDigest.b); | |
#if CERTIFYX509_DEBUG | |
{ | |
BYTE fullTBS[4096]; | |
BYTE *fill = fullTBS; | |
int j; | |
for (j = 0; j < REF_COUNT; j++) | |
{ | |
MemoryCopy(fill, certTBS[j].buf, certTBS[j].len); | |
fill += certTBS[j].len; | |
} | |
DebugDumpBuffer((int)(fill - &fullTBS[0]), fullTBS, "\nfull TBS"); | |
} | |
#endif | |
// Finish up the processing of addedToCertificate | |
// Create the actual tag and length for the addedToCertificate structure | |
out->addedToCertificate.t.size = | |
ASN1EndEncapsulation(&ctxOut, ASN1_CONSTRUCTED_SEQUENCE); | |
// Now move all the addedToContext to the start of the buffer | |
MemoryCopy(out->addedToCertificate.t.buffer, ctxOut.buffer + ctxOut.offset, | |
out->addedToCertificate.t.size); | |
#if CERTIFYX509_DEBUG | |
DebugDumpBuffer(out->addedToCertificate.t.size, out->addedToCertificate.t.buffer, | |
"\naddedToCertificate"); | |
#endif | |
// only thing missing is the signature | |
result = CryptSign(signKey, &in->inScheme, &out->tbsDigest, &out->signature); | |
return result; | |
} | |
#endif // CC_CertifyX509 |