summaryrefslogtreecommitdiffstats
path: root/src/tpm12/tpm_crypto_freebl.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 21:41:43 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 21:41:43 +0000
commit92cccad89d1c12b39165d5f0ed7ccd2d44965a1a (patch)
treef59a2764cd8c50959050a428bd8fc935138df750 /src/tpm12/tpm_crypto_freebl.c
parentInitial commit. (diff)
downloadlibtpms-upstream/0.9.2.tar.xz
libtpms-upstream/0.9.2.zip
Adding upstream version 0.9.2.upstream/0.9.2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/tpm12/tpm_crypto_freebl.c')
-rw-r--r--src/tpm12/tpm_crypto_freebl.c2652
1 files changed, 2652 insertions, 0 deletions
diff --git a/src/tpm12/tpm_crypto_freebl.c b/src/tpm12/tpm_crypto_freebl.c
new file mode 100644
index 0000000..272c264
--- /dev/null
+++ b/src/tpm12/tpm_crypto_freebl.c
@@ -0,0 +1,2652 @@
+/********************************************************************************/
+/* */
+/* Platform Dependent Crypto */
+/* Written by Ken Goldman */
+/* IBM Thomas J. Watson Research Center */
+/* $Id: tpm_crypto_freebl.c 4655 2011-12-21 21:03:15Z kgoldman $ */
+/* */
+/* (c) Copyright IBM Corporation 2006, 2010. */
+/* */
+/* All rights reserved. */
+/* */
+/* 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. */
+/* */
+/* Neither the names of the IBM Corporation nor the names of its */
+/* contributors may be used to endorse or promote products derived from */
+/* this software without specific prior written permission. */
+/* */
+/* 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. */
+/********************************************************************************/
+
+/* This is the FreeBL implementation
+
+ setenv CVSROOT :pserver:anonymous@cvsmirror.mozilla.org:/cvsroot
+ cvs co mosilla/nsprpub
+ gmake nss_build_all
+*/
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "blapi.h"
+#include <gmp.h>
+
+#include "tpm_cryptoh.h"
+#include "tpm_debug.h"
+#include "tpm_error.h"
+#include "tpm_key.h"
+#include "tpm_io.h"
+#include "tpm_load.h"
+#include "tpm_memory.h"
+#include "tpm_process.h"
+#include "tpm_types.h"
+
+#include "tpm_crypto.h"
+
+/* The TPM OAEP encoding parameter */
+static const unsigned char tpm_oaep_pad_str[] = { 'T', 'C', 'P', 'A' };
+
+/* pre-calculate hash of the constant tpm_oaep_pad_str, used often in the OAEP padding
+ calculations */
+static const unsigned char pHashConst[TPM_DIGEST_SIZE];
+
+/* ASN.1 industry standard SHA1 with RSA object identifier */
+static unsigned char sha1Oid[] = {
+ 0x30, 0x21, 0x30, 0x09, 0x06, 0x05,
+ 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05,
+ 0x00, 0x04, 0x14};
+
+
+/*
+ local prototypes
+*/
+
+static void TPM_RSAPrivateKeyInit(RSAPrivateKey *rsa_pri_key);
+static TPM_RESULT TPM_RSAGeneratePublicToken(RSAPublicKey *rsa_pub_key,
+ unsigned char *narr,
+ uint32_t nbytes,
+ unsigned char *earr,
+ uint32_t ebytes);
+static TPM_RESULT TPM_RSAGeneratePrivateToken(RSAPrivateKey *rsa_pri_key,
+ unsigned char *narr,
+ uint32_t nbytes,
+ unsigned char *earr,
+ uint32_t ebytes,
+ unsigned char *darr,
+ uint32_t dbytes);
+static TPM_RESULT TPM_RSASignSHA1(unsigned char *signature,
+ unsigned int *signature_length,
+ const unsigned char *message,
+ size_t message_size,
+ RSAPrivateKey *rsa_pri_key);
+static TPM_RESULT TPM_RSASignDER(unsigned char *signature,
+ unsigned int *signature_length,
+ const unsigned char *message,
+ size_t message_size,
+ RSAPrivateKey *rsa_pri_key);
+
+static TPM_RESULT TPM_RandomNonZero(BYTE *buffer, size_t bytes);
+
+static TPM_RESULT TPM_PKCS1_PaddingType1Add(unsigned char *output,
+ uint32_t outputLength,
+ const unsigned char *input,
+ uint32_t inputLength);
+static TPM_RESULT TPM_PKCS1_PaddingType1Check(uint32_t *padLength,
+ unsigned char *input,
+ uint32_t inputLength);
+static TPM_RESULT TPM_PKCS1_PaddingType2Add(unsigned char *encodedMessage,
+ uint32_t encodedMessageLength,
+ const unsigned char *message,
+ uint32_t messageLength);
+static TPM_RESULT TPM_PKCS1_PaddingType2Check(unsigned char *outputData,
+ uint32_t *outputDataLength,
+ uint32_t outputDataSize,
+ unsigned char *inputData,
+ uint32_t inputDataLength);
+
+static TPM_RESULT TPM_memcpyPad(unsigned char **bin_out,
+ unsigned char *bin_in,
+ uint32_t bin_in_length,
+ uint32_t padBytes);
+
+
+/* TPM_SYMMETRIC_KEY_DATA is a crypto library platform dependent symmetric key structure
+ */
+
+#ifdef TPM_AES
+
+/* local prototype and structure for AES */
+
+/* AES requires data lengths that are a multiple of the block size */
+#define TPM_AES_BITS 128
+/* The AES block size is always 16 bytes */
+#define TPM_AES_BLOCK_SIZE 16
+
+/* Since the AES key is often derived by truncating the session shared secret, test that it's not
+ too large
+*/
+
+#if (TPM_AES_BLOCK_SIZE > TPM_SECRET_SIZE)
+#error TPM_AES_BLOCK_SIZE larger than TPM_SECRET_SIZE
+#endif
+
+/* The AES initial CTR value is derived from a nonce. */
+
+#if (TPM_AES_BLOCK_SIZE > TPM_NONCE_SIZE)
+#error TPM_AES_BLOCK_SIZE larger than TPM_NONCE_SIZE
+#endif
+
+typedef struct tdTPM_SYMMETRIC_KEY_DATA {
+ TPM_TAG tag;
+ TPM_BOOL valid;
+ TPM_BOOL fill;
+ unsigned char userKey[TPM_AES_BLOCK_SIZE];
+} TPM_SYMMETRIC_KEY_DATA;
+
+#endif /* TPM_AES */
+
+/*
+ Crypto library Initialization function
+*/
+
+TPM_RESULT TPM_Crypto_Init()
+{
+ TPM_RESULT rc = 0;
+ SECStatus rv = SECSuccess;
+
+ printf("TPM_Crypto_Init: FreeBL library\n");
+ /* initialize the random number generator */
+ if (rc == 0) {
+ printf(" TPM_Crypto_Init: Initializing RNG\n");
+ rv = RNG_RNGInit();
+ if (rv != SECSuccess) {
+ printf("TPM_Crypto_Init: Error (fatal), RNG_RNGInit rv %d\n", rv);
+ rc = TPM_FAIL;
+ }
+ }
+ /* add additional seed entropy to the random number generator */
+ if (rc == 0) {
+ printf(" TPM_Crypto_Init: Seeding RNG\n");
+ RNG_SystemInfoForRNG();
+ }
+ if (rc == 0) {
+ rv = BL_Init();
+ if (rv != SECSuccess) {
+ printf("TPM_Crypto_Init: Error (fatal), BL_Init rv %d\n", rv);
+ rc =TPM_FAIL ;
+ }
+ }
+ /* pre-calculate hash of the constant tpm_oaep_pad_str, used often in the OAEP padding
+ calculations */
+ if (rc == 0) {
+ rc = TPM_SHA1((unsigned char *)pHashConst, /* cast once to precalculate the constant */
+ sizeof(tpm_oaep_pad_str), tpm_oaep_pad_str,
+ 0, NULL);
+ TPM_PrintFour("TPM_Crypto_Init: pHashConst", pHashConst);
+ }
+ return rc;
+}
+
+/* TPM_Crypto_TestSpecific() performs any library specific tests
+
+ For FreeBL
+*/
+
+TPM_RESULT TPM_Crypto_TestSpecific()
+{
+ TPM_RESULT rc = 0;
+
+ /* Saving the SHA-1 context is fragile code, so test at startup */
+ void *context1;
+ void *context2;
+ unsigned char buffer1[] = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
+ unsigned char expect1[] = {0x84,0x98,0x3E,0x44,0x1C,
+ 0x3B,0xD2,0x6E,0xBA,0xAE,
+ 0x4A,0xA1,0xF9,0x51,0x29,
+ 0xE5,0xE5,0x46,0x70,0xF1};
+ TPM_DIGEST actual;
+ int not_equal;
+ TPM_STORE_BUFFER sbuffer;
+ const unsigned char *stream;
+ uint32_t stream_size;
+
+ printf(" TPM_Crypto_TestSpecific: Test 1 - SHA1 two parts\n");
+ context1 = NULL; /* freed @1 */
+ context2 = NULL; /* freed @2 */
+ TPM_Sbuffer_Init(&sbuffer); /* freed @3 */
+
+ if (rc== 0) {
+ rc = TPM_SHA1InitCmd(&context1); /* freed @1 */
+ }
+ /* digest the first part of the array */
+ if (rc== 0) {
+ rc = TPM_SHA1UpdateCmd(context1, buffer1, 16);
+ }
+ /* store the SHA1 context */
+ if (rc== 0) {
+ rc = TPM_Sha1Context_Store(&sbuffer, context1);
+ }
+ /* load the SHA1 context */
+ if (rc== 0) {
+ TPM_Sbuffer_Get(&sbuffer, &stream, &stream_size);
+ rc = TPM_Sha1Context_Load
+ (&context2, (unsigned char **)&stream, &stream_size); /* freed @2 */
+ }
+ /* digest the rest of the array */
+ if (rc== 0) {
+ rc = TPM_SHA1UpdateCmd(context2, buffer1 + 16, sizeof(buffer1) - 17);
+ }
+ /* get the digest result */
+ if (rc== 0) {
+ rc = TPM_SHA1FinalCmd(actual, context2);
+ }
+ /* check the result */
+ if (rc == 0) {
+ not_equal = memcmp(expect1, actual, TPM_DIGEST_SIZE);
+ if (not_equal) {
+ printf("TPM_Crypto_TestSpecific: Error in test 1\n");
+ TPM_PrintFour("\texpect", expect1);
+ TPM_PrintFour("\tactual", actual);
+ rc = TPM_FAILEDSELFTEST;
+ }
+ }
+ TPM_SHA1Delete(&context1); /* @1 */
+ TPM_SHA1Delete(&context2); /* @2 */
+ TPM_Sbuffer_Delete(&sbuffer); /* @3 */
+ return rc;
+}
+
+/*
+ Random Number Functions
+*/
+
+/* TPM_Random() fills 'buffer' with 'bytes' bytes.
+ */
+
+TPM_RESULT TPM_Random(BYTE *buffer, size_t bytes)
+{
+ TPM_RESULT rc = 0;
+ SECStatus rv = SECSuccess;
+
+ printf(" TPM_Random: Requesting %lu bytes\n", (unsigned long)bytes);
+ /* generate the random bytes */
+ if (rc == 0) {
+ rv = RNG_GenerateGlobalRandomBytes(buffer, bytes);
+ if (rv != SECSuccess) {
+ printf("TPM_Random: Error (fatal) in RNG_GenerateGlobalRandomBytes rv %d\n", rv);
+ rc = TPM_FAIL;
+ }
+ }
+ return rc;
+}
+
+/* TPM_Random() fills 'buffer' with 'bytes' non-zero bytes
+
+ This is used for PKCS padding.
+*/
+
+static TPM_RESULT TPM_RandomNonZero(BYTE *buffer, size_t bytes)
+{
+ TPM_RESULT rc = 0;
+ size_t i;
+ SECStatus rv = SECSuccess;
+
+ printf(" TPM_RandomNonZero: Requesting %lu bytes\n", (unsigned long)bytes);
+ for (i = 0 ; (rc == 0) && (i < bytes) ; ) {
+ rv = RNG_GenerateGlobalRandomBytes(buffer, 1);
+ if (rv != SECSuccess) {
+ printf("TPM_Random: Error (fatal) in RNG_GenerateGlobalRandomBytes rv %d\n", rv);
+ rc = TPM_FAIL;
+ }
+ else {
+ if (*buffer != 0x00) {
+ buffer++;
+ i++;
+ }
+ }
+ }
+ return rc;
+}
+
+/* TPM_StirRandomCmd() adds the supplied entropy to the random number generator
+ */
+
+TPM_RESULT TPM_StirRandomCmd(TPM_SIZED_BUFFER *inData)
+{
+ TPM_RESULT rc = 0;
+ SECStatus rv = SECSuccess;
+
+ printf(" TPM_StirRandomCmd:\n");
+ if (rc == 0) {
+ /* add the seeding material */
+ rv = RNG_RandomUpdate(inData->buffer, inData->size);
+ if (rv != SECSuccess) {
+ printf("TPM_StirRandom: Error (fatal) in RNG_RandomUpdate rv %d\n", rv);
+ rc = TPM_FAIL;
+ }
+ }
+ return rc;
+}
+
+/*
+ RSA Functions
+*/
+
+/* TPM_RSAPrivateKeyInit() NULLs all the structure members in preparation for constructing an RSA
+ key token from byte arrays using RSA_PopulatePrivateKey()
+*/
+
+static void TPM_RSAPrivateKeyInit(RSAPrivateKey *rsa_pri_key)
+{
+ rsa_pri_key->arena = NULL;
+ rsa_pri_key->publicExponent.type = siBuffer;
+ rsa_pri_key->publicExponent.data = NULL;
+ rsa_pri_key->publicExponent.len = 0;
+ rsa_pri_key->modulus.type = siBuffer;
+ rsa_pri_key->modulus.data = NULL;
+ rsa_pri_key->modulus.len = 0;
+ rsa_pri_key->privateExponent.type = siBuffer;
+ rsa_pri_key->privateExponent.data = NULL;
+ rsa_pri_key->privateExponent.len = 0;
+ rsa_pri_key->prime1.type = siBuffer;
+ rsa_pri_key->prime1.data = NULL;
+ rsa_pri_key->prime1.len = 0;
+ rsa_pri_key->prime2.type = siBuffer;
+ rsa_pri_key->prime2.data = NULL;
+ rsa_pri_key->prime2.len = 0;
+ rsa_pri_key->exponent1.type = siBuffer;
+ rsa_pri_key->exponent1.data = NULL;
+ rsa_pri_key->exponent1.len = 0;
+ rsa_pri_key->exponent2.type = siBuffer;
+ rsa_pri_key->exponent2.data = NULL;
+ rsa_pri_key->exponent2.len = 0;
+ rsa_pri_key->coefficient.type = siBuffer;
+ rsa_pri_key->coefficient.data = NULL;
+ rsa_pri_key->coefficient.len = 0;
+ return;
+}
+
+/* Generate an RSA key pair of size 'num_bits' using public exponent 'earr'
+
+ 'n', 'p', 'q', 'd' must be freed by the caller
+*/
+
+TPM_RESULT TPM_RSAGenerateKeyPair(unsigned char **n, /* public key - modulus */
+ unsigned char **p, /* private key prime */
+ unsigned char **q, /* private key prime */
+ unsigned char **d, /* private key (private exponent) */
+ int num_bits, /* key size in bits */
+ const unsigned char *earr, /* public exponent as an array */
+ uint32_t e_size)
+{
+ TPM_RESULT rc = 0;
+ SECItem publicExponent = { 0, 0, 0};
+ RSAPrivateKey *rsaPrivateKey = NULL; /* freed @1 */
+ unsigned long e; /* public exponent */
+
+ printf(" TPM_RSAGenerateKeyPair:\n");
+ /* initialize in case of error */
+ *n = NULL;
+ *p = NULL;
+ *q = NULL;
+ *d = NULL;
+ /* check that num_bits is a multiple of 16. If not, the primes p and q will not be a multiple
+ of 8 and will not fit well in a byte */
+ if (rc == 0) {
+ if ((num_bits % 16) != 0) {
+ printf("TPM_RSAGenerateKeyPair: Error, num_bits %d is not a multiple of 16\n",
+ num_bits);
+ rc = TPM_BAD_KEY_PROPERTY;
+ }
+ }
+ /* convert the e array to an unsigned long */
+ if (rc == 0) {
+ rc = TPM_LoadLong(&e, earr, e_size);
+ }
+ /* validate the public exponent against a list of legal values. Some values (e.g. even numbers)
+ can hang the key generator. */
+ if (rc == 0) {
+ rc = TPM_RSA_exponent_verify(e);
+ }
+ /* generate the key pair */
+ if (rc == 0) {
+ printf(" TPM_RSAGenerateKeyPair: num_bits %d exponent %08lx\n", num_bits, e);
+ publicExponent.type = siBuffer;
+ publicExponent.data = (unsigned char *)earr;
+ publicExponent.len = e_size;
+ /* Generate and return a new RSA public and private key token */
+ rsaPrivateKey = RSA_NewKey(num_bits, &publicExponent); /* freed @1 */
+ if (rsaPrivateKey == NULL) {
+ printf("TPM_RSAGenerateKeyPair: Error (fatal) calling RSA_NewKey()\n");
+ rc = TPM_FAIL;
+ }
+ }
+ /* Key parts can some times have leading zeros, and some crypto libraries truncate. However,
+ the TPM expects fixed lengths. These calls restore any removed padding */
+ /* load n */
+ if (rc == 0) {
+ rc = TPM_memcpyPad(n, /* freed by caller */
+ rsaPrivateKey->modulus.data,
+ rsaPrivateKey->modulus.len,
+ num_bits/8); /* required length */
+ }
+ /* load p */
+ if (rc == 0) {
+ rc = TPM_memcpyPad(p, /* freed by caller */
+ rsaPrivateKey->prime1.data,
+ rsaPrivateKey->prime1.len,
+ num_bits/16); /* required length */
+ }
+ /* load q */
+ if (rc == 0) {
+ rc = TPM_memcpyPad(q, /* freed by caller */
+ rsaPrivateKey->prime2.data,
+ rsaPrivateKey->prime2.len,
+ num_bits/16); /* required length */
+ }
+ /* load d */
+ if (rc == 0) {
+ rc = TPM_memcpyPad(d, /* freed by caller */
+ rsaPrivateKey->privateExponent.data,
+ rsaPrivateKey->privateExponent.len,
+ num_bits/8); /* required length */
+ }
+ /* on error, free the components and set back to NULL so subsequent free is safe */
+ if (rc != 0) {
+ free(*n);
+ free(*p);
+ free(*q);
+ free(*d);
+ *n = NULL;
+ *p = NULL;
+ *q = NULL;
+ *d = NULL;
+ }
+ if (rsaPrivateKey != NULL) {
+ PORT_FreeArena(rsaPrivateKey->arena, PR_TRUE); /* @1 */
+ }
+ return rc;
+}
+
+/* TPM_RSAGeneratePublicToken() generates an RSA key token from n and e
+ */
+
+static TPM_RESULT TPM_RSAGeneratePublicToken(RSAPublicKey *rsaPublicKey,
+ unsigned char *narr, /* public modulus */
+ uint32_t nbytes,
+ unsigned char *earr, /* public exponent */
+ uint32_t ebytes)
+{
+ TPM_RESULT rc = 0;
+
+ /* simply assign the arrays to the key token */
+ if (rc == 0) {
+ printf(" TPM_RSAGeneratePublicToken: nbytes %u ebytes %u\n", nbytes, ebytes);
+ rsaPublicKey->arena = NULL;
+ /* public modulus */
+ rsaPublicKey->modulus.type = siBuffer;
+ rsaPublicKey->modulus.data = narr;
+ rsaPublicKey->modulus.len = nbytes;
+ /* public exponent */
+ rsaPublicKey->publicExponent.type = siBuffer;
+ rsaPublicKey->publicExponent.data = earr;
+ rsaPublicKey->publicExponent.len = ebytes;
+ }
+ return rc;
+}
+
+/* TPM_RSAGeneratePrivateToken() generates an RSA key token from n, e, d
+ */
+
+static TPM_RESULT TPM_RSAGeneratePrivateToken(RSAPrivateKey *rsa_pri_key, /* freed by caller */
+ unsigned char *narr, /* public modulus */
+ uint32_t nbytes,
+ unsigned char *earr, /* public exponent */
+ uint32_t ebytes,
+ unsigned char *darr, /* private exponent */
+ uint32_t dbytes)
+{
+ TPM_RESULT rc = 0;
+ SECStatus rv = SECSuccess;
+
+ printf(" TPM_RSAGeneratePrivateToken:\n");
+ if (rc == 0) {
+ rsa_pri_key->arena = NULL;
+ /* public exponent */
+ rsa_pri_key->publicExponent.type = siBuffer;
+ rsa_pri_key->publicExponent.data = earr;
+ rsa_pri_key->publicExponent.len = ebytes;
+ /* public modulus */
+ rsa_pri_key->modulus.type = siBuffer;
+ rsa_pri_key->modulus.data = narr;
+ rsa_pri_key->modulus.len = nbytes;
+ /* private exponent */
+ rsa_pri_key->privateExponent.type = siBuffer;
+ rsa_pri_key->privateExponent.data = darr;
+ rsa_pri_key->privateExponent.len = dbytes;
+ /* given these key parameters (n,e,d), fill in the rest of the parameters */
+ rv = RSA_PopulatePrivateKey(rsa_pri_key); /* freed by caller */
+ if (rv != SECSuccess) {
+ printf("TPM_RSAGeneratePrivateToken: Error, RSA_PopulatePrivateKey rv %d\n", rv);
+ rc = TPM_BAD_PARAMETER;
+ }
+ }
+ return rc;
+}
+
+/* TPM_RSAPrivateDecrypt() decrypts 'encrypt_data' using the private key 'n, e, d'. The OAEP
+ padding is removed and 'decrypt_data_length' bytes are moved to 'decrypt_data'.
+
+ 'decrypt_data_length' is at most 'decrypt_data_size'.
+*/
+
+TPM_RESULT TPM_RSAPrivateDecrypt(unsigned char *decrypt_data, /* decrypted data */
+ uint32_t *decrypt_data_length, /* length of data put into
+ decrypt_data */
+ size_t decrypt_data_size, /* size of decrypt_data buffer */
+ TPM_ENC_SCHEME encScheme, /* encryption scheme */
+ unsigned char* encrypt_data, /* encrypted data */
+ uint32_t encrypt_data_size,
+ unsigned char *narr, /* public modulus */
+ uint32_t nbytes,
+ unsigned char *earr, /* public exponent */
+ uint32_t ebytes,
+ unsigned char *darr, /* private exponent */
+ uint32_t dbytes)
+{
+ TPM_RESULT rc = 0;
+ SECStatus rv = SECSuccess;
+ RSAPrivateKey rsa_pri_key;
+ unsigned char *padded_data = NULL; /* freed @2 */
+ int padded_data_size = 0;
+
+ printf(" TPM_RSAPrivateDecrypt: Input data size %u\n", encrypt_data_size);
+ TPM_RSAPrivateKeyInit(&rsa_pri_key); /* freed @1 */
+ /* the encrypted data size must equal the public key size */
+ if (rc == 0) {
+ if (encrypt_data_size != nbytes) {
+ printf("TPM_RSAPrivateDecrypt: Error, Encrypted data size is %u not %u\n",
+ encrypt_data_size, nbytes);
+ rc = TPM_DECRYPT_ERROR;
+ }
+ }
+ /* construct the freebl private key object from n,e,d */
+ if (rc == 0) {
+ rc = TPM_RSAGeneratePrivateToken(&rsa_pri_key, /* freed @1 */
+ narr, /* public modulus */
+ nbytes,
+ earr, /* public exponent */
+ ebytes,
+ darr, /* private exponent */
+ dbytes);
+ }
+ /* allocate intermediate buffer for the decrypted but still padded data */
+ if (rc == 0) {
+ /* the size of the decrypted data is guaranteed to be less than this */
+ padded_data_size = rsa_pri_key.modulus.len;
+ rc = TPM_Malloc(&padded_data, padded_data_size); /* freed @2 */
+ }
+ if (rc == 0) {
+ /* decrypt with private key. Must decrypt first and then remove padding because the decrypt
+ call cannot specify an encoding parameter */
+ rv = RSA_PrivateKeyOp(&rsa_pri_key, /* private key token */
+ padded_data, /* to - the decrypted but padded data */
+ encrypt_data); /* from - the encrypted data */
+ if (rv != SECSuccess) {
+ printf("TPM_RSAPrivateDecrypt: Error in RSA_PrivateKeyOp(), rv %d\n", rv);
+ rc = TPM_DECRYPT_ERROR;
+ }
+ }
+ if (rc == 0) {
+ printf(" TPM_RSAPrivateDecrypt: RSA_PrivateKeyOp() success\n");
+ printf(" TPM_RSAPrivateDecrypt: Padded data size %u\n", padded_data_size);
+ TPM_PrintFour(" TPM_RSAPrivateDecrypt: Decrypt padded data", padded_data);
+ /* check and remove the padding based on the TPM encryption scheme */
+ if (encScheme == TPM_ES_RSAESOAEP_SHA1_MGF1) {
+ /* recovered seed and pHash are not returned */
+ unsigned char seed[TPM_DIGEST_SIZE];
+ unsigned char pHash[TPM_DIGEST_SIZE];
+ if (rc == 0) {
+ /* the padded data skips the first 0x00 byte, since it expects the
+ padded data to come from a truncated bignum */
+ rc = TPM_RSA_padding_check_PKCS1_OAEP(decrypt_data, /* to */
+ decrypt_data_length, /* to length */
+ decrypt_data_size, /* to buffer size */
+ padded_data + 1, /* from */
+ padded_data_size - 1, /* from length */
+ pHash, /* 20 bytes */
+ seed); /* 20 bytes */
+ }
+ }
+ else if (encScheme == TPM_ES_RSAESPKCSv15) {
+ rc = TPM_PKCS1_PaddingType2Check(decrypt_data, /* to */
+ decrypt_data_length, /* to length */
+ decrypt_data_size, /* to buffer size*/
+ padded_data, /* from */
+ padded_data_size); /* from length */
+ }
+ else {
+ printf("TPM_RSAPrivateDecrypt: Error, unknown encryption scheme %04x\n", encScheme);
+ rc = TPM_INAPPROPRIATE_ENC;
+ }
+ }
+ if (rc == 0) {
+ printf(" TPM_RSAPrivateDecrypt: RSA_padding_check_PKCS1 recovered %d bytes\n",
+ *decrypt_data_length);
+ TPM_PrintFourLimit(" TPM_RSAPrivateDecrypt: Decrypt data", decrypt_data, decrypt_data_size);
+ }
+ PORT_FreeArena(rsa_pri_key.arena, PR_TRUE); /* @1 */
+ free(padded_data); /* @2 */
+ return rc;
+}
+
+/* TPM_RSAPublicEncrypt() PKCS1 pads 'decrypt_data' to 'encrypt_data_size' and encrypts using the
+ public key 'n, e'.
+*/
+
+TPM_RESULT TPM_RSAPublicEncrypt(unsigned char *encrypt_data, /* encrypted data */
+ size_t encrypt_data_size, /* size of encrypted data buffer */
+ TPM_ENC_SCHEME encScheme, /* padding type */
+ const unsigned char *decrypt_data, /* decrypted data */
+ size_t decrypt_data_size,
+ unsigned char *narr, /* public modulus */
+ uint32_t nbytes,
+ unsigned char *earr, /* public exponent */
+ uint32_t ebytes)
+{
+ TPM_RESULT rc = 0;
+ unsigned char *padded_data = NULL; /* freed @1 */
+
+ printf(" TPM_RSAPublicEncrypt: Input data size %lu\n", (unsigned long)decrypt_data_size);
+ /* intermediate buffer for the padded decrypted data */
+ if (rc == 0) {
+ rc = TPM_Malloc(&padded_data, encrypt_data_size); /* freed @1 */
+ }
+ /* pad the decrypted data */
+ if (rc == 0) {
+ /* based on the TPM encryption scheme */
+ if (encScheme == TPM_ES_RSAESOAEP_SHA1_MGF1) {
+ unsigned char seed[TPM_DIGEST_SIZE];
+ if (rc == 0) {
+ rc = TPM_Random(seed, TPM_DIGEST_SIZE);
+ }
+ if (rc == 0) {
+ padded_data[0] = 0x00;
+ rc = TPM_RSA_padding_add_PKCS1_OAEP(padded_data +1, /* to */
+ encrypt_data_size -1, /* to length */
+ decrypt_data, /* from */
+ decrypt_data_size, /* from length */
+ pHashConst, /* 20 bytes */
+ seed); /* 20 bytes */
+ }
+ }
+ else if (encScheme == TPM_ES_RSAESPKCSv15) {
+ rc = TPM_PKCS1_PaddingType2Add(padded_data, /* to */
+ encrypt_data_size, /* to length */
+ decrypt_data, /* from */
+ decrypt_data_size); /* from length */
+ }
+ else {
+ printf("TPM_RSAPublicEncrypt: Error, unknown encryption scheme %04x\n", encScheme);
+ rc = TPM_INAPPROPRIATE_ENC;
+ }
+ }
+ /* raw public key operation on the already padded input data */
+ if (rc == 0) {
+ rc = TPM_RSAPublicEncryptRaw(encrypt_data, /* output */
+ encrypt_data_size, /* input, size of enc buffer */
+ padded_data, /* input */
+ encrypt_data_size, /* input, size of dec buffer */
+ narr, /* public modulus */
+ nbytes,
+ earr, /* public exponent */
+ ebytes);
+ }
+ free(padded_data); /* @1 */
+ return rc;
+}
+
+/* TPM_RSAPublicEncryptRaw() does a raw public key operation without any padding.
+
+*/
+
+TPM_RESULT TPM_RSAPublicEncryptRaw(unsigned char *encrypt_data, /* output */
+ uint32_t encrypt_data_size, /* input, size of enc buffer */
+ unsigned char *decrypt_data, /* input */
+ uint32_t decrypt_data_size, /* input, size of dec buffer */
+ unsigned char *narr, /* public modulus */
+ uint32_t nbytes,
+ unsigned char *earr, /* public exponent */
+ uint32_t ebytes)
+{
+ TPM_RESULT rc = 0;
+ SECStatus rv = SECSuccess;
+ RSAPublicKey rsa_pub_key;
+
+ printf(" TPM_RSAPublicEncryptRaw:\n");
+ /* the input data size must equal the public key size (already padded) */
+ if (rc == 0) {
+ if (decrypt_data_size != nbytes) {
+ printf("TPM_RSAPublicEncryptRaw: Error, decrypt data size is %u not %u\n",
+ decrypt_data_size, nbytes);
+ rc = TPM_ENCRYPT_ERROR;
+ }
+ }
+ /* the output data size must equal the public key size */
+ if (rc == 0) {
+ if (encrypt_data_size != nbytes) {
+ printf("TPM_RSAPublicEncryptRaw: Error, Output data size is %u not %u\n",
+ encrypt_data_size, nbytes);
+ rc = TPM_ENCRYPT_ERROR;
+ }
+ }
+ /* construct the freebl public key object */
+ if (rc == 0) {
+ rc = TPM_RSAGeneratePublicToken(&rsa_pub_key, /* freebl public key token */
+ narr, /* public modulus */
+ nbytes,
+ earr, /* public exponent */
+ ebytes);
+ }
+ if (rc == 0) {
+ TPM_PrintFour(" TPM_RSAPublicEncryptRaw: Public modulus", narr);
+ TPM_PrintAll(" TPM_RSAPublicEncryptRaw: Public exponent", earr, ebytes);
+ TPM_PrintFourLimit(" TPM_RSAPublicEncryptRaw: Decrypt data", decrypt_data, decrypt_data_size);
+ /* raw public key operation, encrypt the decrypt_data */
+ rv = RSA_PublicKeyOp(&rsa_pub_key, /* freebl public key token */
+ encrypt_data, /* output - the encrypted data */
+ decrypt_data); /* input - the clear text data */
+ if (rv != SECSuccess) {
+ printf("TPM_RSAPublicEncrypt: Error in RSA_PublicKeyOp, rv %d\n", rv);
+ rc = TPM_ENCRYPT_ERROR;
+ }
+ }
+ if (rc == 0) {
+ TPM_PrintFour(" TPM_RSAPublicEncryptRaw: Encrypt data", encrypt_data);
+#if 0 /* NOTE: Uncomment as a debug aid for signature verification */
+ TPM_PrintAll(" TPM_RSAPublicEncryptRaw: Encrypt data",
+ encrypt_data, encrypt_data_size);
+#endif
+ }
+ return rc;
+}
+
+/* TPM_RSASign() signs 'message' of size 'message_size' using the private key n,e,d and the
+ signature scheme 'sigScheme' as specified in PKCS #1 v2.0.
+
+ 'signature_length' bytes are moved to 'signature'. 'signature_length' is at most
+ 'signature_size'. signature must point to bytes of memory equal to the public modulus size.
+*/
+
+TPM_RESULT TPM_RSASign(unsigned char *signature, /* output */
+ unsigned int *signature_length, /* output, size of signature */
+ unsigned int signature_size, /* input, size of signature buffer */
+ TPM_SIG_SCHEME sigScheme, /* input, type of signature */
+ const unsigned char *message, /* input */
+ size_t message_size, /* input */
+ unsigned char *narr, /* public modulus */
+ uint32_t nbytes,
+ unsigned char *earr, /* public exponent */
+ uint32_t ebytes,
+ unsigned char *darr, /* private exponent */
+ uint32_t dbytes)
+{
+ TPM_RESULT rc = 0;
+ RSAPrivateKey rsa_pri_key;
+
+ printf(" TPM_RSASign:\n");
+ TPM_RSAPrivateKeyInit(&rsa_pri_key); /* freed @1 */
+ /* construct the free private key object from n,e,d */
+ if (rc == 0) {
+ rc = TPM_RSAGeneratePrivateToken(&rsa_pri_key, /* freed @1 */
+ narr, /* public modulus */
+ nbytes,
+ earr, /* public exponent */
+ ebytes,
+ darr, /* private exponent */
+ dbytes);
+ }
+ /* sanity check the size of the output signature buffer */
+ if (rc == 0) {
+ if (signature_size < nbytes) {
+ printf("TPM_RSASign: Error (fatal), buffer %u too small for signature %u\n",
+ signature_size, nbytes);
+ rc = TPM_FAIL; /* internal error, should never occur */
+ }
+ }
+ /* determine the signature scheme for the key */
+ if (rc == 0) {
+ switch(sigScheme) {
+ case TPM_SS_NONE:
+ printf("TPM_RSASign: Error, sigScheme TPM_SS_NONE\n");
+ rc = TPM_INVALID_KEYUSAGE;
+ break;
+ case TPM_SS_RSASSAPKCS1v15_SHA1:
+ case TPM_SS_RSASSAPKCS1v15_INFO:
+ rc = TPM_RSASignSHA1(signature,
+ signature_length,
+ message,
+ message_size,
+ &rsa_pri_key);
+ break;
+ case TPM_SS_RSASSAPKCS1v15_DER:
+ rc = TPM_RSASignDER(signature,
+ signature_length,
+ message,
+ message_size,
+ &rsa_pri_key);
+ break;
+ default:
+ printf("TPM_RSASign: Error, sigScheme %04hx unknown\n", sigScheme);
+ rc = TPM_INVALID_KEYUSAGE;
+ break;
+ }
+ }
+ PORT_FreeArena(rsa_pri_key.arena, PR_TRUE); /* @1 */
+ return rc;
+}
+
+/* TPM_RSASignSHA1() performs the following:
+ prepend a DER encoded algorithm ID (SHA1 and RSA)
+ prepend a type 1 pad
+ encrypt with the private key
+*/
+
+static TPM_RESULT TPM_RSASignSHA1(unsigned char *signature, /* output */
+ unsigned int *signature_length, /* output, size of signature */
+ const unsigned char *message, /* input */
+ size_t message_size, /* input */
+ RSAPrivateKey *rsa_pri_key) /* signing private key */
+{
+ TPM_RESULT rc = 0;
+ unsigned char *message_der; /* DER padded message, freed @1 */
+
+ printf(" TPM_RSASignSHA1: key size %d\n", rsa_pri_key->modulus.len);
+ message_der = NULL; /* freed @1 */
+ /* sanity check, SHA1 messages must be 20 bytes */
+ if (rc == 0) {
+ if (message_size != TPM_DIGEST_SIZE) {
+ printf("TPM_RSASignSHA1: Error, message size %lu not TPM_DIGEST_SIZE\n",
+ (unsigned long)message_size );
+ rc = TPM_DECRYPT_ERROR;
+ }
+ }
+ /* allocate memory for the DER padded message */
+ if (rc == 0) {
+ rc = TPM_Malloc(&message_der, sizeof(sha1Oid) + message_size); /* freed @1 */
+ }
+ if (rc == 0) {
+ /* copy the OID */
+ memcpy(message_der, sha1Oid, sizeof(sha1Oid));
+ /* copy the message */
+ memcpy(message_der + sizeof(sha1Oid), message, message_size);
+ /* sign the DER padded message */
+ rc = TPM_RSASignDER(signature, /* output */
+ signature_length, /* output, size of signature */
+ message_der, /* input */
+ sizeof(sha1Oid) + message_size, /* input */
+ rsa_pri_key); /* signing private key */
+ }
+ free(message_der); /* @1 */
+ return rc;
+}
+
+/* TPM_RSASignDER() performs the following:
+
+ prepend a PKCS1 type 1 pad
+ encrypt with the private key
+
+ The caller must ensure that the signature buffer is >= the key size.
+*/
+
+static TPM_RESULT TPM_RSASignDER(unsigned char *signature, /* output */
+ unsigned int *signature_length, /* output, size of signature */
+ const unsigned char *message, /* input */
+ size_t message_size, /* input */
+ RSAPrivateKey *rsa_pri_key) /* signing private key */
+{
+ TPM_RESULT rc = 0;
+ SECStatus rv = SECSuccess;
+ unsigned char *message_pad; /* PKCS1 type 1 padded message, freed @1 */
+
+ printf(" TPM_RSASignDER: key size %d\n", rsa_pri_key->modulus.len);
+ message_pad = NULL; /* freed @1 */
+ /* the padded message size is the same as the key size */
+ /* allocate memory for the padded message */
+ if (rc == 0) {
+ rc = TPM_Malloc(&message_pad, rsa_pri_key->modulus.len); /* freed @1 */
+ }
+ /* PKCS1 type 1 pad the message */
+ if (rc == 0) {
+ printf(" TPM_RSASignDER: Applying PKCS1 type 1 padding, size from %lu to %u\n",
+ (unsigned long)message_size, rsa_pri_key->modulus.len);
+ TPM_PrintFourLimit(" TPM_RSASignDER: Input message", message, message_size);
+ /* This call checks that the message will fit with the padding */
+ rc = TPM_PKCS1_PaddingType1Add(message_pad, /* to */
+ rsa_pri_key->modulus.len, /* to length */
+ message, /* from */
+ message_size); /* from length */
+ }
+ /* raw sign with private key */
+ if (rc == 0) {
+ printf(" TPM_RSASignDER: Encrypting with private key, message size %d\n",
+ rsa_pri_key->modulus.len);
+ TPM_PrintFour(" TPM_RSASignDER: Padded message", message_pad);
+ /* sign with private key */
+ rv = RSA_PrivateKeyOp(rsa_pri_key, /* freebl key token */
+ signature, /* to - the decrypted but padded data */
+ message_pad); /* from - the encrypted data */
+ if (rv != SECSuccess) {
+ printf("TPM_RSASignDER: Error in RSA_PrivateKeyOp(), rv %d\n", rv);
+ rc = TPM_DECRYPT_ERROR;
+ }
+ }
+ if (rc == 0) {
+ TPM_PrintFour(" TPM_RSASignDER: signature", signature);
+ *signature_length = rsa_pri_key->modulus.len;
+ }
+ free(message_pad); /* @1 */
+ return rc;
+}
+
+/* TPM_RSAVerifySHA1() performs the following:
+ decrypt the signature
+ verify and remove type 1 pad
+ verify and remove DER encoded algorithm ID
+ verify the signature on the message
+*/
+
+TPM_RESULT TPM_RSAVerifySHA1(unsigned char *signature, /* input */
+ unsigned int signature_size, /* input, size of signature
+ buffer */
+ const unsigned char *message, /* input */
+ uint32_t message_size, /* input */
+ unsigned char *narr, /* public modulus */
+ uint32_t nbytes,
+ unsigned char *earr, /* public exponent */
+ uint32_t ebytes)
+{
+ TPM_RESULT rc = 0;
+ unsigned char *padded_data = NULL; /* decrypted signature, freed @1 */
+ uint32_t padLength;
+ int irc;
+
+ printf(" TPM_RSAVerifySHA1:\n");
+ /* allocate memory for the padded result of the public key operation */
+ if (rc == 0) {
+ rc = TPM_Malloc(&padded_data, nbytes); /* freed @1 */
+ }
+ /* do a raw encrypt of the signature */
+ if (rc == 0) {
+ rc = TPM_RSAPublicEncryptRaw(padded_data, /* output */
+ nbytes, /* input, size of message buffer */
+ signature, /* input */
+ signature_size, /* input, size of signature buffer */
+ narr, /* public modulus */
+ nbytes,
+ earr, /* public exponent */
+ ebytes);
+ }
+ /* check PKCS1 padding and OID */
+ if (rc == 0) {
+ rc = TPM_PKCS1_PaddingType1Check(&padLength, /* length of the PKCS1 padd and OID */
+ padded_data, /* input data */
+ nbytes); /* input data length */
+ }
+ /* check message length */
+ if (rc == 0) {
+ if (message_size != (nbytes - padLength)) {
+ printf("TPM_RSAVerifySHA1: Error, "
+ "message size %u not equal to size %u after padding removed\n",
+ message_size, nbytes - padLength);
+ rc = TPM_BAD_SIGNATURE;
+ }
+ }
+ /* check message */
+ if (rc == 0) {
+ irc = memcmp(message, padded_data + padLength, message_size);
+ if (irc != 0) {
+ printf("TPM_RSAVerifySHA1: Error, message mismatch\n");
+ TPM_PrintFourLimit(" TPM_RSAVerifySHA1: message", message, message_size);
+ TPM_PrintFourLimit(" TPM_RSAVerifySHA1: message from signature", padded_data + padLength, message_size);
+ rc = TPM_BAD_SIGNATURE;
+ }
+ }
+ /* public encrypt is general, here we're doing a signature check, so adjust the error message */
+ else {
+ rc = TPM_BAD_SIGNATURE;
+ }
+ free(padded_data); /* @1 */
+ return rc;
+}
+
+/* TPM_RSAGetPrivateKey calculates q (2nd prime factor) and d (private key) from n (public key), e
+ (public exponent), and p (1st prime factor)
+
+ 'qarr', darr' must be freed by the caller.
+*/
+
+TPM_RESULT TPM_RSAGetPrivateKey(uint32_t *qbytes, unsigned char **qarr,
+ uint32_t *dbytes, unsigned char **darr,
+ uint32_t nbytes, unsigned char *narr,
+ uint32_t ebytes, unsigned char *earr,
+ uint32_t pbytes, unsigned char *parr)
+{
+ TPM_RESULT rc = 0;
+ SECStatus rv = SECSuccess;
+ RSAPrivateKey rsa_pri_key;
+
+ /* set to NULL so caller can free after failure */
+ printf(" TPM_RSAGetPrivateKey:\n");
+ TPM_RSAPrivateKeyInit(&rsa_pri_key); /* freed @1 */
+ *qarr = NULL;
+ *darr = NULL;
+ /* check input parameters */
+ if (rc == 0) {
+ if ((narr == NULL) || (nbytes == 0)) {
+ printf("TPM_RSAGetPrivateKey: Error, missing n\n");
+ rc = TPM_BAD_PARAMETER;
+ }
+ }
+ /* check input parameters */
+ if (rc == 0) {
+ if ((earr == NULL) || (ebytes == 0)) {
+ printf("TPM_RSAGetPrivateKey: Error, missing e\n");
+ rc = TPM_BAD_PARAMETER;
+ }
+ }
+ /* check input parameters */
+ if (rc == 0) {
+ if ((parr == NULL) || (pbytes == 0)) {
+ printf("TPM_RSAGetPrivateKey: Error, missing p\n");
+ rc = TPM_BAD_PARAMETER;
+ }
+ }
+ /* populate the private key token with n, e, p */
+ if (rc == 0) {
+ rsa_pri_key.publicExponent.type = siBuffer;
+ rsa_pri_key.publicExponent.data = earr;
+ rsa_pri_key.publicExponent.len = ebytes;
+ rsa_pri_key.modulus.type = siBuffer;
+ rsa_pri_key.modulus.data = narr;
+ rsa_pri_key.modulus.len = nbytes;
+ rsa_pri_key.prime1.type = siBuffer;
+ rsa_pri_key.prime1.data = parr;
+ rsa_pri_key.prime1.len = pbytes;
+ /* fill in the rest of the freebl key token parameters. */
+ rv = RSA_PopulatePrivateKey(&rsa_pri_key); /* freed @1 */
+ if (rv != SECSuccess) {
+ printf("TPM_RSAGetPrivateKey: Error in RSA_PopulatePrivateKey rv %d\n", rv);
+ rc = TPM_BAD_PARAMETER;
+ }
+ }
+ /* extract and pad q */
+ if (rc == 0) {
+ rc = TPM_memcpyPad(qarr, /* freed by caller */
+ rsa_pri_key.prime2.data, rsa_pri_key.prime2.len,
+ pbytes); /* pad to p prime */
+ *qbytes = pbytes;
+ }
+ /* extract and pad d */
+ if (rc == 0) {
+ rc = TPM_memcpyPad(darr, /* freed by caller */
+ rsa_pri_key.privateExponent.data, rsa_pri_key.privateExponent.len,
+ nbytes); /* pad to public modulus */
+ *dbytes = nbytes;
+ }
+ if (rc == 0) {
+ TPM_PrintFour(" TPM_RSAGetPrivateKey: Calculated q", *qarr);
+ TPM_PrintFour(" TPM_RSAGetPrivateKey: Calculated d", *darr);
+ printf(" TPM_RSAGetPrivateKey: length of n,p,q,d = %u / %u / %u / %u\n",
+ nbytes, pbytes, *qbytes, *dbytes);
+ }
+ PORT_FreeArena(rsa_pri_key.arena, PR_TRUE); /* @1 */
+ return rc;
+}
+
+/*
+ PKCS1 Padding Functions
+*/
+
+/* TPM_PKCS1_PaddingType1Add() adds PKCS1 type 1 padding.
+
+ The output buffer is preallocated.
+*/
+
+static TPM_RESULT TPM_PKCS1_PaddingType1Add(unsigned char *output, /* to */
+ uint32_t outputLength,
+ const unsigned char *input, /* from */
+ uint32_t inputLength)
+{
+ TPM_RESULT rc = 0;
+ uint32_t psLength;
+ uint32_t index;
+
+ /* sanity check the length, this should never fail */
+ printf(" TPM_PKCS1_PaddingType1Add:\n");
+ if (rc == 0) {
+ if ((inputLength + 11) > outputLength) {
+ printf("TPM_PKCS1_PaddingType1Add: Error, input %u too big for output %u\n",
+ inputLength, outputLength);
+ rc = TPM_DECRYPT_ERROR;
+ }
+ }
+ if (rc == 0) {
+ index = 0;
+ /* psLength is the number of 0xff bytes, subtract 3 for the leading 00,01 and trailing 00 */
+ psLength = outputLength - inputLength - 3;
+
+ /* add the PKCS1 pad 01 || PS || 00 || T where PS is at least 8 0xff bytes */
+ /* PKCS1 pads to k-1 bytes, implies a leading 0 */
+ output[index] = 0x00;
+ index++;
+
+ output[index] = 0x01;
+ index++;
+
+ memset(output + index, 0xff, psLength);
+ index += psLength;
+
+ output[index] = 0x00;
+ index++;
+
+ /* add the input data */
+ memcpy(output + index, input, inputLength);
+ index += inputLength;
+ }
+ return rc;
+}
+
+/* TPM_PKCS1_PaddingType1Check() checks PKCS1 type 1 padding and the SHA1withRSA OID
+ and returns their length
+
+ Type 1 is: 00 01 FF's 00 OID message
+*/
+
+static TPM_RESULT TPM_PKCS1_PaddingType1Check(uint32_t *padLength,
+ unsigned char *input,
+ uint32_t inputLength)
+{
+ TPM_RESULT rc = 0;
+ int irc;
+
+ printf(" TPM_PKCS1_PaddingType1Check:\n");
+ /* sanity check the length */
+ if (rc == 0) {
+ if ((sizeof(sha1Oid) + 11) > inputLength) {
+ printf("TPM_PKCS1_PaddingType1Check: Error, "
+ "sizeof(sha1Oid) %lu + 11 > inputLength %u\n",
+ (unsigned long)sizeof(sha1Oid), inputLength);
+ rc = TPM_ENCRYPT_ERROR;
+ }
+ }
+ /* check byte 0 */
+ if (rc == 0) {
+ *padLength = 0;
+ if (input[*padLength] != 0x00) {
+ printf("TPM_PKCS1_PaddingType1Check: Error, byte %u %02x not 0x00\n",
+ *padLength, input[*padLength]);
+ rc = TPM_ENCRYPT_ERROR;
+ }
+ (*padLength)++;
+ }
+ /* check byte 1 */
+ if (rc == 0) {
+ if (input[*padLength] != 0x01) {
+ printf("TPM_PKCS1_PaddingType1Check: Error, byte %u %02x not 0x01\n",
+ *padLength, input[*padLength]);
+ rc = TPM_ENCRYPT_ERROR;
+ }
+ (*padLength)++;
+ }
+ /* check for at least 8 0xff bytes */
+ for ( ; (rc == 0) && (*padLength < 10) ; (*padLength)++) {
+ if (input[*padLength] != 0xff) {
+ printf("TPM_PKCS1_PaddingType1Check: Error, byte %u %02x not 0xff\n",
+ *padLength, input[*padLength]);
+ rc = TPM_ENCRYPT_ERROR;
+ }
+ }
+ /* check for more 0xff bytes */
+ for ( ; (rc == 0) && (*padLength < inputLength) ; (*padLength)++) {
+ if (input[*padLength] != 0xff) {
+ break;
+ }
+ }
+ /* check for 0x00 byte */
+ if (rc == 0) {
+ if (input[*padLength] != 0x00) {
+ printf("TPM_PKCS1_PaddingType1Check: Error, byte %u %02x not 0x00\n",
+ *padLength, input[*padLength]);
+ rc = TPM_ENCRYPT_ERROR;
+ }
+ (*padLength)++;
+ }
+ /* check length for OID */
+ if (rc == 0) {
+ if (*padLength + sizeof(sha1Oid) > inputLength) {
+ printf("TPM_PKCS1_PaddingType1Check: Error, "
+ "padLength %u + sizeof(sha1Oid) %lu > inputLength %u\n",
+ *padLength, (unsigned long)sizeof(sha1Oid), inputLength);
+ rc = TPM_ENCRYPT_ERROR;
+ }
+ }
+ /* check OID */
+ if (rc == 0) {
+ irc = memcmp(input + *padLength, sha1Oid, sizeof(sha1Oid));
+ if (irc != 0) {
+ printf("TPM_PKCS1_PaddingType1Check: Error, OID mismatch\n");
+ TPM_PrintAll(" TPM_PKCS1_PaddingType1Check: OID",
+ input + *padLength, sizeof(sha1Oid));
+ rc = TPM_ENCRYPT_ERROR;
+ }
+ *padLength += sizeof(sha1Oid);
+ }
+ return rc;
+}
+
+/* TPM_PKCS1_PaddingType2Add() adds the PKCS1 type 2 padding
+
+ The output buffer is preallocated.
+
+ See PKCS1 9.1.2.1 Encoding operation
+
+ This method cheats a bit by adding a leading 00 as well, which is needed for the RSA operation.
+
+ M message to be encoded, an octet string of length at most emLen-10
+ emLen intended length in octets of the encoded message
+
+ Output:
+ EM encoded message, an octet string of length emLen; or "message too long"
+*/
+
+static TPM_RESULT TPM_PKCS1_PaddingType2Add(unsigned char *encodedMessage, /* to */
+ uint32_t encodedMessageLength, /* to length */
+ const unsigned char *message, /* from */
+ uint32_t messageLength) /* from length */
+{
+ TPM_RESULT rc = 0;
+
+ printf(" TPM_PKCS1_PaddingType2Add: Message length %u padded length %u\n",
+ messageLength, encodedMessageLength);
+ /* 1. If the length of the message M is greater than emLen - 10 octets, output "message too
+ long" and stop. */
+ if (rc == 0) {
+ if ((messageLength + 11) > encodedMessageLength) {
+ printf("TPM_PKCS1_PaddingType2Add: Error, message length too big for padded length\n");
+ rc = TPM_ENCRYPT_ERROR;
+ }
+ }
+ /* 2. Generate an octet string PS of length emLen-||M||-2 consisting of pseudorandomly generated
+ nonzero octets. The length of PS will be at least 8 octets. */
+ if (rc == 0) {
+ rc = TPM_RandomNonZero(encodedMessage + 2, encodedMessageLength - messageLength - 3);
+ }
+ /* 3. Concatenate PS, the message M, and other padding to form the encoded message EM as: */
+ /* EM = 02 || PS || 00 || M */
+ if (rc == 0) {
+ encodedMessage[0] = 0x00;
+ encodedMessage[1] = 0x02;
+ encodedMessage[encodedMessageLength - messageLength - 1] = 0x00;
+ memcpy(encodedMessage + encodedMessageLength - messageLength, message, messageLength);
+ }
+ return rc;
+}
+
+/* TPM_PKCS1_Type2PaddingCheck checks the PKCS1 type 2 padding and recovers the message
+
+ The output buffer is preallocated.
+*/
+
+static
+TPM_RESULT TPM_PKCS1_PaddingType2Check(unsigned char *outputData, /* to */
+ uint32_t *outputDataLength, /* to length */
+ uint32_t outputDataSize, /* pre-allocated to length */
+ unsigned char *inputData, /* from - padded data */
+ uint32_t inputDataLength) /* from length */
+{
+ TPM_RESULT rc = 0;
+ size_t i;
+
+ printf(" TPM_PKCS1_PaddingType2Check:\n");
+ /* check the leading bytes for 0x00, 0x02 */
+ if (rc == 0) {
+ if ((inputData[0] != 0x00) ||
+ (inputData[1] != 0x02)) {
+ printf("TPM_PKCS1_PaddingType2Check: Error, bad leading bytes %02x %02x\n",
+ inputData[0], inputData[1]);
+ rc = TPM_DECRYPT_ERROR;
+ }
+ }
+ /* skip the non-zero random PS */
+ for (i = 2 ; (rc == 0) && (i < inputDataLength) ; i++) {
+ if (inputData[i] == 0x00) {
+ break;
+ }
+ }
+ /* check for the trailing 0x00 */
+ if (rc == 0) {
+ if (i == inputDataLength) {
+ printf("TPM_PKCS1_PaddingType2Check: Error, missing trailing 0x00\n");
+ rc = TPM_DECRYPT_ERROR;
+ }
+ }
+ /* check that PS was at least 8 bytes */
+ if (rc == 0) {
+ if (i < 10) {
+ printf("TPM_PKCS1_PaddingType2Check: Error, bad PS length %lu\n", (unsigned long)i-2);
+ rc = TPM_DECRYPT_ERROR;
+ }
+ }
+ /* check that the output can accommodate the message */
+ if (rc == 0) {
+ i++; /* index past the trailing 0x00 */
+ *outputDataLength = inputDataLength - i;
+ if (*outputDataLength > outputDataSize) {
+ printf("TPM_PKCS1_PaddingType2Check: Error, "
+ "message %u greater than output data size %u\n",
+ *outputDataLength, outputDataSize);
+ rc = TPM_DECRYPT_ERROR;
+ }
+ }
+ /* copy the message */
+ if (rc == 0) {
+ memcpy(outputData, inputData + inputDataLength - *outputDataLength, *outputDataLength);
+ }
+ return rc;
+}
+
+/*
+ GNU MP wrappers do error logging and transformation of errors to TPM type errors
+*/
+
+/* TPM_BN_num_bytes() wraps the gnump function in a TPM error handler
+
+ Returns number of bytes in the input
+*/
+
+TPM_RESULT TPM_BN_num_bytes(unsigned int *numBytes, TPM_BIGNUM bn_in)
+{
+ TPM_RESULT rc = 0;
+ mpz_t *bn = (mpz_t *)bn_in;
+
+ /* is the bignum zero */
+ int result = mpz_cmp_ui(*bn, 0);
+ /* mpz_sizeinbase() always returns at least one. If the value is zero, there should really be 0
+ bytes */
+ if (result == 0) {
+ *numBytes = 0;
+ }
+ /* take the base 2 number and round up to the next byte */
+ else {
+ *numBytes = (mpz_sizeinbase (*bn, 2) +7) / 8;
+ }
+ return rc;
+}
+
+/* TPM_BN_is_one() wraps the gnump function in a TPM error handler
+
+ Returns success if input is 1
+*/
+
+TPM_RESULT TPM_BN_is_one(TPM_BIGNUM bn_in)
+{
+ TPM_RESULT rc = 0;
+ mpz_t *bn = (mpz_t *)bn_in;
+ int irc;
+
+ irc = mpz_cmp_ui(*bn, 1);
+ if (irc != 0) {
+ printf("TPM_BN_is_one: Error, result is not 1\n");
+ rc = TPM_DAA_WRONG_W;
+ }
+ return rc;
+}
+
+
+/* TPM_BN_mod() wraps the gnump function in a TPM error handler
+
+ r = a mod m
+*/
+
+TPM_RESULT TPM_BN_mod(TPM_BIGNUM rem_in,
+ const TPM_BIGNUM a_in,
+ const TPM_BIGNUM m_in)
+{
+ TPM_RESULT rc = 0;
+ mpz_t *rBignum = (mpz_t *)rem_in;
+ mpz_t *aBignum = (mpz_t *)a_in;
+ mpz_t *mBignum = (mpz_t *)m_in;
+
+ /* set r to a mod m */
+ mpz_mod(*rBignum, *aBignum, *mBignum);
+ return rc;
+}
+
+/* TPM_BN_mask_bits() wraps the gnump function in a TPM error handler
+
+ erase all but the lowest n bits of bn
+ bn = bn mod 2^^n
+*/
+
+TPM_RESULT TPM_BN_mask_bits(TPM_BIGNUM bn_in, unsigned int n)
+{
+ TPM_RESULT rc = 0;
+ unsigned int numBytes;
+ mpz_t *bn = (mpz_t *)bn_in;
+
+ if (rc == 0) {
+ rc = TPM_BN_num_bytes(&numBytes, bn_in);
+ }
+ if (rc == 0) {
+ /* if the BIGNUM is already fewer bits, no need to mask */
+ if (numBytes > (n / 8)) {
+ /* divide and return remainder, divisor is 2^^n */
+ mpz_fdiv_r_2exp(*bn, *bn, n);
+ }
+ }
+ return rc;
+}
+
+/* TPM_BN_rshift() wraps the gnump function in a TPM error handler
+
+ Shift a right by n bits (discard the lowest n bits) and label the result r
+*/
+
+TPM_RESULT TPM_BN_rshift(TPM_BIGNUM *rBignum_in, /* freed by caller */
+ TPM_BIGNUM aBignum_in,
+ int n)
+{
+ TPM_RESULT rc = 0;
+ mpz_t **rBignum = (mpz_t **)rBignum_in;
+ mpz_t *aBignum = (mpz_t *)aBignum_in;
+
+ printf(" TPM_BN_rshift: n %d\n", n);
+ if (rc == 0) {
+ rc = TPM_BN_new(rBignum_in);
+ }
+ if (rc == 0) {
+ /* divide and return quotient, rounded down (floor) */
+ mpz_fdiv_q_2exp(**rBignum, *aBignum, n);
+ }
+ return rc;
+}
+
+/* TPM_BN_lshift() wraps the gnump function in a TPM error handler
+
+ Shift a left by n bits and label the result r
+*/
+
+TPM_RESULT TPM_BN_lshift(TPM_BIGNUM *rBignum_in, /* freed by caller */
+ TPM_BIGNUM aBignum_in,
+ int n)
+{
+ TPM_RESULT rc = 0;
+ mpz_t **rBignum = (mpz_t **)rBignum_in;
+ mpz_t *aBignum = (mpz_t *)aBignum_in;
+
+ printf(" TPM_BN_lshift: n %d\n", n);
+ if (rc == 0) {
+ rc = TPM_BN_new(rBignum_in);
+ }
+ if (rc == 0) {
+ /* multiply by 2^^n is is a left shift by n */
+ mpz_mul_2exp(**rBignum, *aBignum, n);
+ }
+ return rc;
+}
+
+/* TPM_BN_add() wraps the gnump function in a TPM error handler
+
+ r = a + b
+*/
+
+TPM_RESULT TPM_BN_add(TPM_BIGNUM rBignum_in,
+ TPM_BIGNUM aBignum_in,
+ TPM_BIGNUM bBignum_in)
+{
+ TPM_RESULT rc = 0;
+ mpz_t *rBignum = (mpz_t *)rBignum_in;
+ mpz_t *aBignum = (mpz_t *)aBignum_in;
+ mpz_t *bBignum = (mpz_t *)bBignum_in;
+
+ printf(" TPM_BN_add:\n");
+ /* result = a + b */
+ mpz_add(*rBignum, *aBignum, *bBignum);
+ return rc;
+}
+
+/* TPM_BN_mul() wraps the gnump function in a TPM error handler
+
+ r = a * b
+*/
+
+TPM_RESULT TPM_BN_mul(TPM_BIGNUM rBignum_in,
+ TPM_BIGNUM aBignum_in,
+ TPM_BIGNUM bBignum_in)
+{
+ TPM_RESULT rc = 0;
+ mpz_t *rBignum = (mpz_t *)rBignum_in;
+ mpz_t *aBignum = (mpz_t *)aBignum_in;
+ mpz_t *bBignum = (mpz_t *)bBignum_in;
+
+ printf(" TPM_BN_mul:\n");
+ /* r = a * b */
+ mpz_mul(*rBignum, *aBignum, *bBignum);
+ return rc;
+}
+
+/* TPM_BN_mod_exp() wraps the gnump function in a TPM error handler
+
+ computes a to the p-th power modulo m (r=a^p % n)
+*/
+
+TPM_RESULT TPM_BN_mod_exp(TPM_BIGNUM rBignum_in,
+ TPM_BIGNUM aBignum_in,
+ TPM_BIGNUM pBignum_in,
+ TPM_BIGNUM nBignum_in)
+{
+ TPM_RESULT rc = 0;
+ mpz_t *rBignum = (mpz_t *)rBignum_in;
+ mpz_t *aBignum = (mpz_t *)aBignum_in;
+ mpz_t *pBignum = (mpz_t *)pBignum_in;
+ mpz_t *nBignum = (mpz_t *)nBignum_in;
+
+ printf(" TPM_BN_mod_exp:\n");
+ mpz_powm(*rBignum, *aBignum, *pBignum, *nBignum);
+ return rc;
+}
+
+/* TPM_BN_Mod_add() wraps the gnump function in a TPM error handler
+
+ adds a to b modulo m
+*/
+
+TPM_RESULT TPM_BN_mod_add(TPM_BIGNUM rBignum_in,
+ TPM_BIGNUM aBignum_in,
+ TPM_BIGNUM bBignum_in,
+ TPM_BIGNUM mBignum_in)
+{
+ TPM_RESULT rc = 0;
+ mpz_t *rBignum = (mpz_t *)rBignum_in;
+ mpz_t *aBignum = (mpz_t *)aBignum_in;
+ mpz_t *bBignum = (mpz_t *)bBignum_in;
+ mpz_t *mBignum = (mpz_t *)mBignum_in;
+
+ printf(" TPM_BN_mod_add:\n");
+ /* r = a + b */
+ mpz_add(*rBignum, *aBignum, *bBignum);
+ /* set r to r mod m */
+ mpz_mod(*rBignum, *rBignum, *mBignum);
+ return rc;
+}
+
+/* TPM_BN_mod_mul() wraps the gnump function in a TPM error handler
+
+ r = (a * b) mod m
+*/
+
+TPM_RESULT TPM_BN_mod_mul(TPM_BIGNUM rBignum_in,
+ TPM_BIGNUM aBignum_in,
+ TPM_BIGNUM bBignum_in,
+ TPM_BIGNUM mBignum_in)
+{
+ TPM_RESULT rc = 0;
+ mpz_t *rBignum = (mpz_t *)rBignum_in;
+ mpz_t *aBignum = (mpz_t *)aBignum_in;
+ mpz_t *bBignum = (mpz_t *)bBignum_in;
+ mpz_t *mBignum = (mpz_t *)mBignum_in;
+
+ printf(" TPM_BN_mod_mul:\n");
+ /* r = a * b */
+ mpz_mul(*rBignum, *aBignum, *bBignum);
+ /* set r to r mod m */
+ mpz_mod(*rBignum, *rBignum, *mBignum);
+ return rc;
+}
+
+/* TPM_BN_new() wraps the gnump function in a TPM error handler
+
+ Allocates a new bignum
+*/
+
+TPM_RESULT TPM_BN_new(TPM_BIGNUM *bn_in) /* freed by caller */
+{
+ TPM_RESULT rc = 0;
+ mpz_t *bn;
+
+ if (rc== 0) {
+ rc = TPM_Malloc(bn_in, sizeof(mpz_t)); /* freed by caller */
+ }
+ if (rc== 0) {
+ bn = (mpz_t *)*bn_in;
+ mpz_init(*bn);
+ }
+ return rc;
+}
+
+/* TPM_BN_free() wraps the gnump function
+
+ Frees the bignum
+*/
+
+void TPM_BN_free(TPM_BIGNUM bn_in)
+{
+ mpz_t *bn = (mpz_t *)bn_in;
+ if (bn != NULL) {
+ mpz_clear(*bn);
+ free(bn_in);
+ }
+ return;
+}
+
+/* TPM_bn2bin wraps the function in gnump a TPM error handler.
+
+ Converts a bignum to char array
+
+ 'bin' must already be checked for sufficient size.
+*/
+
+TPM_RESULT TPM_bn2bin(unsigned char *bin,
+ TPM_BIGNUM bn_in)
+{
+ TPM_RESULT rc = 0;
+ mpz_t *bn = (mpz_t *)bn_in;
+
+ mpz_export(bin, /* output */
+ NULL, /* countp */
+ 1, /* order, MSB first */
+ 1, /* size, char */
+ 0, /* endian, native (unused) */
+ 0, /* nails, don't discard */
+ *bn); /* input */
+ return rc;
+}
+
+/* TPM_memcpyPad allocates a buffer 'bin_out' and loads it from 'bin_in'.
+
+ If padBytes is non-zero, 'bin_out' is padded with leading zeros if necessary, so that 'bytes'
+ will equal 'padBytes'. This is used when TPM data structures expect a fixed length while
+ the crypto library truncates leading zeros.
+
+ '*bin_out' must be freed by the caller
+*/
+
+static TPM_RESULT TPM_memcpyPad(unsigned char **bin_out,
+ unsigned char *bin_in,
+ uint32_t bin_in_length,
+ uint32_t padBytes)
+{
+ TPM_RESULT rc = 0;
+
+ printf(" TPM_memcpyPad: padBytes %u\n", padBytes);
+ if (rc == 0) {
+ /* padBytes 0 says that no padding is required */
+ if (padBytes == 0) {
+ padBytes = bin_in_length; /* setting equal yields no padding */
+ }
+ /* The required output should never be less than the supplied input. Sanity check and
+ return a fatal error. */
+ if (padBytes < bin_in_length) {
+ printf("TPM_memcpyPad: Error (fatal), "
+ "padBytes %u less than %u\n", padBytes, bin_in_length);
+ rc = TPM_FAIL;
+ }
+ if (padBytes != bin_in_length) {
+ printf(" TPM_memcpyPad: padBytes %u bytes %u\n", padBytes, bin_in_length);
+ }
+ }
+ /* allocate memory for the padded output */
+ if (rc == 0) {
+ rc = TPM_Malloc(bin_out, padBytes);
+ }
+ if (rc == 0) {
+ memset(*bin_out, 0, padBytes - bin_in_length); /* leading 0 padding */
+ memcpy((*bin_out) + padBytes - bin_in_length, /* start copy after padding */
+ bin_in, bin_in_length);
+ }
+ return rc;
+}
+
+/* TPM_bin2bn() wraps the gnump function in a TPM error handler
+
+ Converts a char array to bignum
+
+ bn must be freed by the caller.
+*/
+
+TPM_RESULT TPM_bin2bn(TPM_BIGNUM *bn_in, const unsigned char *bin, unsigned int bytes)
+{
+ TPM_RESULT rc = 0;
+
+ if (rc == 0) {
+ rc = TPM_BN_new(bn_in);
+ }
+ if (rc == 0) {
+ mpz_t *bn = (mpz_t *)*bn_in;
+ mpz_import(*bn, /* output */
+ bytes, /* count */
+ 1, /* order, MSB first */
+ 1, /* size, char */
+ 0, /* endian, native (unused) */
+ 0, /* nail, don't discard */
+ bin); /* input */
+ }
+ return rc;
+}
+
+/*
+ Hash Functions
+*/
+
+/* TPM_SHA1InitCmd() initializes a platform dependent TPM_SHA1Context structure.
+
+ The structure must be freed using TPM_SHA1FinalCmd()
+*/
+
+TPM_RESULT TPM_SHA1InitCmd(void **context)
+{
+ TPM_RESULT rc = 0;
+
+ printf(" TPM_SHA1InitCmd:\n");
+ if (rc == 0) {
+ /* create a new freebl SHA1 context */
+ *context = SHA1_NewContext();
+ if (*context == NULL) {
+ printf("TPM_SHA1InitCmd: Error allocating a new context\n");
+ rc = TPM_SIZE;
+ }
+ }
+ /* reset the SHA-1 context, preparing it for a fresh round of hashing */
+ if (rc== 0) {
+ SHA1_Begin(*context);
+ }
+ return rc;
+}
+
+/* TPM_SHA1UpdateCmd() adds 'data' of 'length' to the SHA-1 context
+ */
+
+TPM_RESULT TPM_SHA1UpdateCmd(void *context, const unsigned char *data, uint32_t length)
+{
+ TPM_RESULT rc = 0;
+
+ printf(" TPM_SHA1Update: length %u\n", length);
+ if (context != NULL) {
+ SHA1_Update(context, data, length);
+ }
+ else {
+ printf("TPM_SHA1Update: Error, no existing SHA1 thread\n");
+ rc = TPM_SHA_THREAD;
+ }
+ return rc;
+}
+
+/* TPM_SHA1FinalCmd() extracts the SHA-1 digest 'md' from the context
+ */
+
+TPM_RESULT TPM_SHA1FinalCmd(unsigned char *md, void *context)
+{
+ TPM_RESULT rc = 0;
+ unsigned int digestLen;
+
+ printf(" TPM_SHA1FinalCmd:\n");
+ if (rc== 0) {
+ if (context == NULL) {
+ printf("TPM_SHA1FinalCmd: Error, no existing SHA1 thread\n");
+ rc = TPM_SHA_THREAD;
+ }
+ }
+ if (rc== 0) {
+ SHA1_End(context, md, &digestLen, TPM_DIGEST_SIZE);
+ /* Sanity check. For SHA1 it should always be 20 bytes. */
+ if (digestLen != TPM_DIGEST_SIZE) {
+ printf("TPM_SHA1Final: Error (fatal), SHA1_End returned %u bytes\n", digestLen);
+ rc = TPM_FAIL;
+ }
+ }
+ return rc;
+}
+
+/* TPM_SHA1Delete() zeros and frees the SHA1 context */
+
+void TPM_SHA1Delete(void **context)
+{
+ if (*context != NULL) {
+ printf(" TPM_SHA1Delete:\n");
+ /* zero because the SHA1 context might have data left from an HMAC */
+ SHA1_DestroyContext(*context, PR_TRUE);
+ *context = NULL;
+ }
+ return;
+}
+
+#if defined (__x86_64__) || \
+ defined(__amd64__) || \
+ defined(__ia64__) || \
+ defined(__powerpc64__) || \
+ defined(__s390x__) || \
+ (defined(__sparc__) && defined(__arch64__)) || \
+ defined(__aarch64__)
+
+#define IS_64
+typedef PRUint64 SHA_HW_t;
+
+#elif defined (__i386__) || \
+ defined (__powerpc__) || \
+ defined (__s390__) || \
+ defined(__sparc__) || \
+ defined(__arm__)
+
+typedef PRUint32 SHA_HW_t;
+#undef IS_64
+
+#else
+#error "Cannot determine 32 or 64 bit platform"
+#endif
+
+/* The structure returned by the SHA1_Flatten() command and passed to SHA1_Resurrect()
+ */
+
+typedef struct SHA1SaveContextStrtd {
+ union {
+ PRUint32 w[16]; /* input buffer */
+ PRUint8 b[64];
+ } u;
+ PRUint64 size; /* count of hashed bytes. */
+ SHA_HW_t H[22]; /* 5 state variables, 16 tmp values, 1
+ extra */
+} SHA1SaveContextStr;
+
+
+/* TPM_Sha1Context_Load() is non-portable code to deserialize the FreeBL SHA1 context.
+
+ If the contextPresent prepended by TPM_Sha1Context_Store() is FALSE, context remains NULL. If
+ TRUE, context is allocated and loaded.
+*/
+
+TPM_RESULT TPM_Sha1Context_Load(void **context,
+ unsigned char **stream,
+ uint32_t *stream_size)
+{
+ TPM_RESULT rc = 0;
+ TPM_BOOL contextPresent; /* is there a context to be loaded */
+ uint32_t flattenSize; /* from the freebl library */
+ SHA1Context *tmpContext = NULL; /* temp to get flatten size, freed @1 */
+ uint32_t tmp32; /* temp to recreate 64-bit size */
+ SHA1SaveContextStr restoreContext;
+ size_t i;
+
+ printf(" TPM_Sha1Context_Load: FreeBL\n");
+ /* TPM_Sha1Context_Store() stored a flag to indicate whether a context was stored */
+ if (rc== 0) {
+ rc = TPM_LoadBool(&contextPresent, stream, stream_size);
+ printf(" TPM_Sha1Context_Load: contextPresent %u\n", contextPresent);
+ }
+ /* check format tag */
+ /* In the future, if multiple formats are supported, this check will be replaced by a 'switch'
+ on the tag */
+ if ((rc== 0) && contextPresent) {
+ rc = TPM_CheckTag(TPM_TAG_SHA1CONTEXT_FREEBL_V1, stream, stream_size);
+ }
+ /* check that context is NULL to detect memory leak */
+ if ((rc== 0) && contextPresent) {
+ if (*context != NULL) {
+ printf("TPM_Sha1Context_Load: Error (fatal), *context %p should be NULL\n", *context );
+ rc = TPM_FAIL;
+ }
+ }
+ /* create a temporary context just to get the freebl library size */
+ if ((rc== 0) && contextPresent) {
+ rc = TPM_SHA1InitCmd((void **)&tmpContext); /* freed @1 */
+ }
+ /* get the size of the FreeBL library SHA1 context */
+ if ((rc== 0) && contextPresent) {
+ flattenSize = SHA1_FlattenSize(tmpContext);
+ /* sanity check that the freebl library and TPM structure here are in sync */
+ if (flattenSize != sizeof(SHA1SaveContextStr)) {
+ printf("TPM_Sha1Context_Load: Error, "
+ "SHA1 context size %u from SHA1_FlattenSize not equal %lu from structure\n",
+ flattenSize, (unsigned long)sizeof(SHA1SaveContextStr));
+ rc = TPM_BAD_PARAM_SIZE;
+ }
+ }
+ /*
+ deserialization code to fill in restoreContext
+ */
+ /* b[0..63] <- u.b[0..63] (bytes only, no bytswapping) */
+ if ((rc== 0) && contextPresent) {
+ rc = TPM_Loadn(restoreContext.u.b, 64, stream, stream_size);
+ }
+ /* count <- size (this is 64 bits on all platforms) */
+ if ((rc== 0) && contextPresent) {
+ rc = TPM_Load32(&tmp32, stream, stream_size);
+ restoreContext.size = (uint64_t)tmp32 << 32; /* big endian */
+ }
+ if ((rc== 0) && contextPresent) {
+ rc = TPM_Load32(&tmp32, stream, stream_size);
+ restoreContext.size += (uint64_t)tmp32 & 0xffffffff; /* big endian */
+ }
+ for (i = 0 ; (rc == 0) && contextPresent && (i < 5) ; i++) {
+ rc = TPM_Load32(&tmp32, stream, stream_size);
+ restoreContext.H[i] = tmp32; /* H can be 32 or 64 bits */
+ }
+ /* load the context */
+ if ((rc== 0) && contextPresent) {
+ /* the size test above ensures that the cast here is safe */
+ *context = SHA1_Resurrect((unsigned char *)&restoreContext, NULL);
+ if (*context == NULL) {
+ printf("TPM_Sha1Context_Load: Error, could not SHA1_Resurrect\n");
+ rc = TPM_SIZE;
+ }
+ }
+ TPM_SHA1Delete((void *)&tmpContext); /* @1 */
+ return rc;
+}
+
+/* TPM_Sha1Context_Store() is non-portable code to serialize the FreeBL SHA1 context. context is
+ not altered.
+
+ It prepends a contextPresent flag to the stream, FALSE if context is NULL, TRUE if not.
+*/
+
+TPM_RESULT TPM_Sha1Context_Store(TPM_STORE_BUFFER *sbuffer,
+ void *context)
+{
+ TPM_RESULT rc = 0;
+ SECStatus rv = SECSuccess;
+ size_t i;
+ unsigned int flattenSize;
+ SHA1SaveContextStr saveContext;
+ TPM_BOOL contextPresent; /* is there a context to be stored */
+
+ printf(" TPM_Sha1Context_Store: FreeBL\n");
+ /* store contextPresent */
+ if (rc == 0) {
+ if (context != NULL) {
+ printf(" TPM_Sha1Context_Store: Storing context\n");
+ contextPresent = TRUE;
+ }
+ else {
+ printf(" TPM_Sha1Context_Store: No context to store\n");
+ contextPresent = FALSE;
+ }
+ printf(" TPM_Sha1Context_Store: contextPresent %u \n", contextPresent);
+ rc = TPM_Sbuffer_Append(sbuffer, &contextPresent, sizeof(TPM_BOOL));
+ }
+ /* overall format tag */
+ if ((rc== 0) && contextPresent) {
+ rc = TPM_Sbuffer_Append16(sbuffer, TPM_TAG_SHA1CONTEXT_FREEBL_V1);
+ }
+ if ((rc== 0) && contextPresent) {
+ /* get the size of the FreeBL SHA1 context */
+ flattenSize = SHA1_FlattenSize(context); /* it will not be NULL here */
+ /* sanity check that the freebl library and TPM structure here are in sync */
+ if (flattenSize != sizeof(SHA1SaveContextStr)) {
+ printf("TPM_Sha1Context_Store: Error (fatal), "
+ "SHA1 context size %u from SHA1_FlattenSize not equal %lu from structure\n",
+ flattenSize, (unsigned long)sizeof(SHA1SaveContextStr));
+ rc = TPM_FAIL;
+ }
+ }
+ /* store into the structure from the library */
+ if ((rc== 0) && contextPresent) {
+ /* the size test above ensures that the cast here is safe */
+ rv = SHA1_Flatten(context, (unsigned char *)&saveContext);
+ if (rv != SECSuccess) {
+ printf("TPM_Sha1Context_Store: Error (fatal), SHA1_Flatten rv %d\n", rv);
+ rc = TPM_FAIL;
+ }
+ }
+ /*
+ append the FreeBL SHA1 context to the stream
+ */
+ /* b[0..63] <- u.b[0..63] (bytes only, no byte swapping) */
+ if ((rc== 0) && contextPresent) {
+ rc = TPM_Sbuffer_Append(sbuffer, saveContext.u.b, 64);
+ }
+ /* count <- size (this is 64 bits on all platforms) */
+ if ((rc== 0) && contextPresent) {
+ rc = TPM_Sbuffer_Append32(sbuffer, saveContext.size >> 32); /* big endian */
+ }
+ if ((rc== 0) && contextPresent) {
+ rc = TPM_Sbuffer_Append32(sbuffer, saveContext.size & 0xffffffff);
+ }
+ /* SHA_HW_t - NSS uses 64 bits on 64 bit platforms for performance reasons only. The lower 32
+ bits are critical, so you can always serialize/deserialize just the lower 32 bits. */
+ /* The remainder of the H array is scratch memory and does not need to be preserved or
+ transmitted. */
+ for (i = 0 ; (rc == 0) && contextPresent && (i < 5) ; i++) {
+ rc = TPM_Sbuffer_Append32(sbuffer, saveContext.H[i] & 0xffffffff);
+ }
+ return rc;
+}
+
+/*
+ TPM_SYMMETRIC_KEY_DATA
+*/
+
+#ifdef TPM_AES
+
+/* TPM_SymmetricKeyData_New() allocates memory for and initializes a TPM_SYMMETRIC_KEY_DATA token.
+ */
+
+TPM_RESULT TPM_SymmetricKeyData_New(TPM_SYMMETRIC_KEY_TOKEN *tpm_symmetric_key_data)
+{
+ TPM_RESULT rc = 0;
+
+ printf(" TPM_SymmetricKeyData_New:\n");
+ if (rc == 0) {
+ rc = TPM_Malloc(tpm_symmetric_key_data, sizeof(TPM_SYMMETRIC_KEY_DATA));
+ }
+ if (rc == 0) {
+ TPM_SymmetricKeyData_Init(*tpm_symmetric_key_data);
+ }
+ return rc;
+}
+
+/* TPM_SymmetricKeyData_Free() initializes the key token to wipe secrets. It then frees the
+ TPM_SYMMETRIC_KEY_DATA token and sets it to NULL.
+*/
+
+void TPM_SymmetricKeyData_Free(TPM_SYMMETRIC_KEY_TOKEN *tpm_symmetric_key_data)
+{
+ printf(" TPM_SymmetricKeyData_Free:\n");
+ if (*tpm_symmetric_key_data != NULL) {
+ TPM_SymmetricKeyData_Init(*tpm_symmetric_key_data);
+ free(*tpm_symmetric_key_data);
+ *tpm_symmetric_key_data = NULL;
+ }
+ return;
+}
+
+/* TPM_SymmetricKeyData_Init() is AES non-portable code to initialize the TPM_SYMMETRIC_KEY_DATA
+
+ It depends on the TPM_SYMMETRIC_KEY_DATA declaration.
+*/
+
+void TPM_SymmetricKeyData_Init(TPM_SYMMETRIC_KEY_TOKEN tpm_symmetric_key_token)
+{
+ TPM_SYMMETRIC_KEY_DATA *tpm_symmetric_key_data =
+ (TPM_SYMMETRIC_KEY_DATA *)tpm_symmetric_key_token;
+
+ printf(" TPM_SymmetricKeyData_Init:\n");
+ tpm_symmetric_key_data->tag = TPM_TAG_KEY;
+ tpm_symmetric_key_data->valid = FALSE;
+ tpm_symmetric_key_data->fill = 0;
+ /* zero to wipe secrets */
+ memset(tpm_symmetric_key_data->userKey, 0, sizeof(tpm_symmetric_key_data->userKey));
+ return;
+}
+
+/* TPM_SymmetricKeyData_Load() is AES non-portable code to deserialize the TPM_SYMMETRIC_KEY_DATA
+
+ It depends on the above TPM_SYMMETRIC_KEY_DATA declaration.
+*/
+
+TPM_RESULT TPM_SymmetricKeyData_Load(TPM_SYMMETRIC_KEY_TOKEN tpm_symmetric_key_token,
+ unsigned char **stream,
+ uint32_t *stream_size)
+{
+ TPM_RESULT rc = 0;
+ TPM_SYMMETRIC_KEY_DATA *tpm_symmetric_key_data =
+ (TPM_SYMMETRIC_KEY_DATA *)tpm_symmetric_key_token;
+
+ printf(" TPM_SymmetricKeyData_Load:\n");
+ /* check tag */
+ if (rc == 0) {
+ rc = TPM_CheckTag(TPM_TAG_KEY, stream, stream_size);
+ }
+ /* load valid */
+ if (rc == 0) {
+ rc = TPM_LoadBool(&(tpm_symmetric_key_data->valid), stream, stream_size);
+ }
+ /* load fill */
+ if (rc == 0) {
+ rc = TPM_Load8(&(tpm_symmetric_key_data->fill), stream, stream_size);
+ }
+ /* The AES key is a simple array. */
+ if (rc == 0) {
+ rc = TPM_Loadn(tpm_symmetric_key_data->userKey, sizeof(tpm_symmetric_key_data->userKey),
+ stream, stream_size);
+ }
+ return rc;
+}
+
+/* TPM_SymmetricKeyData_Store() is AES non-portable code to serialize the TPM_SYMMETRIC_KEY_DATA
+
+ It depends on the above TPM_SYMMETRIC_KEY_DATA declaration.
+*/
+
+TPM_RESULT TPM_SymmetricKeyData_Store(TPM_STORE_BUFFER *sbuffer,
+ const TPM_SYMMETRIC_KEY_TOKEN tpm_symmetric_key_token)
+{
+ TPM_RESULT rc = 0;
+ TPM_SYMMETRIC_KEY_DATA *tpm_symmetric_key_data =
+ (TPM_SYMMETRIC_KEY_DATA *)tpm_symmetric_key_token;
+
+ printf(" TPM_SymmetricKeyData_Store:\n");
+ /* store tag */
+ if (rc == 0) {
+ rc = TPM_Sbuffer_Append16(sbuffer, tpm_symmetric_key_data->tag);
+ }
+ /* store valid */
+ if (rc == 0) {
+ rc = TPM_Sbuffer_Append(sbuffer, &(tpm_symmetric_key_data->valid), sizeof(TPM_BOOL));
+ }
+ /* store fill */
+ if (rc == 0) {
+ rc = TPM_Sbuffer_Append(sbuffer, &(tpm_symmetric_key_data->fill), sizeof(TPM_BOOL));
+ }
+ /* store AES key */
+ if (rc == 0) {
+ rc = TPM_Sbuffer_Append(sbuffer,
+ tpm_symmetric_key_data->userKey,
+ sizeof(tpm_symmetric_key_data->userKey));
+ }
+ return rc;
+}
+
+/* TPM_SymmetricKeyData_GenerateKey() is AES non-portable code to generate a random symmetric key
+
+ tpm_symmetric_key_data should be initialized before and after use
+*/
+
+TPM_RESULT TPM_SymmetricKeyData_GenerateKey(TPM_SYMMETRIC_KEY_TOKEN tpm_symmetric_key_token)
+{
+ TPM_RESULT rc = 0;
+ TPM_SYMMETRIC_KEY_DATA *tpm_symmetric_key_data =
+ (TPM_SYMMETRIC_KEY_DATA *)tpm_symmetric_key_token;
+
+ printf(" TPM_SymmetricKeyData_GenerateKey:\n");
+ /* generate a random key */
+ if (rc == 0) {
+ rc = TPM_Random(tpm_symmetric_key_data->userKey, sizeof(tpm_symmetric_key_data->userKey));
+ }
+ if (rc == 0) {
+ tpm_symmetric_key_data->valid = TRUE;
+ }
+ return rc;
+}
+
+/* TPM_SymmetricKeyData_Encrypt() is AES non-portable code to CBC encrypt 'decrypt_data' to
+ 'encrypt_data'
+
+ The stream is padded as per PKCS#7 / RFC2630
+
+ 'encrypt_data' must be free by the caller
+*/
+
+TPM_RESULT TPM_SymmetricKeyData_Encrypt(unsigned char **encrypt_data, /* output, caller frees */
+ uint32_t *encrypt_length, /* output */
+ const unsigned char *decrypt_data, /* input */
+ uint32_t decrypt_length, /* input */
+ const TPM_SYMMETRIC_KEY_TOKEN
+ tpm_symmetric_key_token) /* input */
+{
+ TPM_RESULT rc = 0;
+ SECStatus rv;
+ AESContext *cx;
+ uint32_t pad_length;
+ uint32_t output_length; /* dummy */
+ unsigned char *decrypt_data_pad;
+ unsigned char ivec[TPM_AES_BLOCK_SIZE]; /* initial chaining vector */
+ TPM_SYMMETRIC_KEY_DATA *tpm_symmetric_key_data =
+ (TPM_SYMMETRIC_KEY_DATA *)tpm_symmetric_key_token;
+
+ printf(" TPM_SymmetricKeyData_Encrypt: Length %u\n", decrypt_length);
+ decrypt_data_pad = NULL; /* freed @1 */
+ cx = NULL; /* freed @2 */
+
+ /* sanity check that the AES key has previously been generated */
+ if (rc == 0) {
+ if (!tpm_symmetric_key_data->valid) {
+ printf("TPM_SymmetricKeyData_Encrypt: Error (fatal), AES key not valid\n");
+ rc = TPM_FAIL;
+ }
+ }
+ if (rc == 0) {
+ /* calculate the PKCS#7 / RFC2630 pad length and padded data length */
+ pad_length = TPM_AES_BLOCK_SIZE - (decrypt_length % TPM_AES_BLOCK_SIZE);
+ *encrypt_length = decrypt_length + pad_length;
+ printf(" TPM_SymmetricKeyData_Encrypt: Padded length %u pad length %u\n",
+ *encrypt_length, pad_length);
+ /* allocate memory for the encrypted response */
+ rc = TPM_Malloc(encrypt_data, *encrypt_length);
+ }
+ /* allocate memory for the padded decrypted data */
+ if (rc == 0) {
+ rc = TPM_Malloc(&decrypt_data_pad, *encrypt_length);
+ }
+ if (rc == 0) {
+ /* set the IV */
+ memset(ivec, 0, sizeof(ivec));
+ /* create a new AES context */
+ cx = AES_CreateContext(tpm_symmetric_key_data->userKey,
+ ivec, /* CBC initialization vector */
+ NSS_AES_CBC, /* CBC mode */
+ TRUE, /* encrypt */
+ TPM_AES_BLOCK_SIZE, /* key length */
+ TPM_AES_BLOCK_SIZE); /* AES block length */
+ if (cx == NULL) {
+ printf("TPM_SymmetricKeyData_Encrypt: Error creating AES context\n");
+ rc = TPM_SIZE;
+ }
+ }
+ /* pad the decrypted clear text data */
+ if (rc == 0) {
+ /* unpadded original data */
+ memcpy(decrypt_data_pad, decrypt_data, decrypt_length);
+ /* last gets pad = pad length */
+ memset(decrypt_data_pad + decrypt_length, pad_length, pad_length);
+ /* encrypt the padded input to the output */
+ TPM_PrintFour(" TPM_SymmetricKeyData_Encrypt: Input", decrypt_data_pad);
+ /* perform the AES encryption */
+ rv = AES_Encrypt(cx,
+ *encrypt_data, &output_length, *encrypt_length, /* output */
+ decrypt_data_pad, *encrypt_length); /* input */
+
+ if (rv != SECSuccess) {
+ printf("TPM_SymmetricKeyData_Encrypt: Error, rv %d\n", rv);
+ rc = TPM_ENCRYPT_ERROR;
+ }
+ }
+ if (rc == 0) {
+ TPM_PrintFour(" TPM_SymmetricKeyData_Encrypt: Output", *encrypt_data);
+ }
+ free(decrypt_data_pad); /* @1 */
+ if (cx != NULL) {
+ /* due to a FreeBL bug, must zero the context before destroying it */
+ unsigned char dummy_key[TPM_AES_BLOCK_SIZE];
+ unsigned char dummy_ivec[TPM_AES_BLOCK_SIZE];
+ memset(dummy_key, 0x00, TPM_AES_BLOCK_SIZE);
+ memset(dummy_ivec, 0x00, TPM_AES_BLOCK_SIZE);
+ rv = AES_InitContext(cx, /* AES context */
+ dummy_key, /* AES key */
+ TPM_AES_BLOCK_SIZE, /* key length */
+ dummy_ivec, /* ivec */
+ NSS_AES_CBC, /* CBC mode */
+ TRUE, /* encrypt */
+ TPM_AES_BLOCK_SIZE); /* AES block length */
+ AES_DestroyContext(cx, PR_TRUE); /* @2 */
+ }
+ return rc;
+}
+
+/* TPM_SymmetricKeyData_Decrypt() is AES non-portable code to CBC decrypt 'encrypt_data' to
+ 'decrypt_data'
+
+ The stream must be padded as per PKCS#7 / RFC2630
+
+ decrypt_data must be free by the caller
+*/
+
+TPM_RESULT TPM_SymmetricKeyData_Decrypt(unsigned char **decrypt_data, /* output, caller frees */
+ uint32_t *decrypt_length, /* output */
+ const unsigned char *encrypt_data, /* input */
+ uint32_t encrypt_length, /* input */
+ const TPM_SYMMETRIC_KEY_TOKEN
+ tpm_symmetric_key_token) /* input */
+{
+ TPM_RESULT rc = 0;
+ SECStatus rv;
+ AESContext *cx;
+ uint32_t pad_length;
+ uint32_t output_length; /* dummy */
+ uint32_t i;
+ unsigned char *pad_data;
+ unsigned char ivec[TPM_AES_BLOCK_SIZE]; /* initial chaining vector */
+ TPM_SYMMETRIC_KEY_DATA *tpm_symmetric_key_data =
+ (TPM_SYMMETRIC_KEY_DATA *)tpm_symmetric_key_token;
+
+ printf(" TPM_SymmetricKeyData_Decrypt: Length %u\n", encrypt_length);
+ cx = NULL; /* freed @1 */
+
+ /* sanity check encrypted length */
+ if (rc == 0) {
+ if (encrypt_length < TPM_AES_BLOCK_SIZE) {
+ printf("TPM_SymmetricKeyData_Decrypt: Error, bad length\n");
+ rc = TPM_DECRYPT_ERROR;
+ }
+ }
+ /* sanity check that the AES key has previously been generated */
+ if (rc == 0) {
+ if (!tpm_symmetric_key_data->valid) {
+ printf("TPM_SymmetricKeyData_Decrypt: Error (fatal), AES key not valid\n");
+ rc = TPM_FAIL;
+ }
+ }
+ /* allocate memory for the PKCS#7 / RFC2630 padded decrypted data */
+ if (rc == 0) {
+ rc = TPM_Malloc(decrypt_data, encrypt_length);
+ }
+ if (rc == 0) {
+ /* set the IV */
+ memset(ivec, 0, sizeof(ivec));
+ /* create a new AES context */
+ cx = AES_CreateContext(tpm_symmetric_key_data->userKey,
+ ivec, /* CBC initialization vector */
+ NSS_AES_CBC, /* CBC mode */
+ FALSE, /* decrypt */
+ TPM_AES_BLOCK_SIZE, /* key length */
+ TPM_AES_BLOCK_SIZE); /* AES block length */
+ if (cx == NULL) {
+ printf("TPM_SymmetricKeyData_Decrypt: Error creating AES context\n");
+ rc = TPM_SIZE;
+ }
+ }
+ /* decrypt the input to the PKCS#7 / RFC2630 padded output */
+ if (rc == 0) {
+ TPM_PrintFour(" TPM_SymmetricKeyData_Decrypt: Input", encrypt_data);
+ /* perform the AES decryption */
+ rv = AES_Decrypt(cx,
+ *decrypt_data, &output_length, encrypt_length, /* output */
+ encrypt_data, encrypt_length); /* input */
+ if (rv != SECSuccess) {
+ printf("TPM_SymmetricKeyData_Decrypt: Error, rv %d\n", rv);
+ rc = TPM_DECRYPT_ERROR;
+ }
+ }
+ if (rc == 0) {
+ TPM_PrintFour(" TPM_SymmetricKeyData_Decrypt: Output", *decrypt_data);
+ }
+ /* get the pad length */
+ if (rc == 0) {
+ /* get the pad length from the last byte */
+ pad_length = (uint32_t)*(*decrypt_data + encrypt_length - 1);
+ /* sanity check the pad length */
+ printf(" TPM_SymmetricKeyData_Decrypt: Pad length %u\n", pad_length);
+ if ((pad_length == 0) ||
+ (pad_length > TPM_AES_BLOCK_SIZE)) {
+ printf("TPM_SymmetricKeyData_Decrypt: Error, illegal pad length\n");
+ rc = TPM_DECRYPT_ERROR;
+ }
+ }
+ if (rc == 0) {
+ /* get the unpadded length */
+ *decrypt_length = encrypt_length - pad_length;
+ /* pad starting point */
+ pad_data = *decrypt_data + *decrypt_length;
+ /* sanity check the pad */
+ for (i = 0 ; i < pad_length ; i++, pad_data++) {
+ if (*pad_data != pad_length) {
+ printf("TPM_SymmetricKeyData_Decrypt: Error, bad pad %02x at index %u\n",
+ *pad_data, i);
+ rc = TPM_DECRYPT_ERROR;
+ }
+ }
+ }
+ if (cx != NULL) {
+ /* due to a FreeBL bug, must zero the context before destroying it */
+ unsigned char dummy_key[TPM_AES_BLOCK_SIZE];
+ unsigned char dummy_ivec[TPM_AES_BLOCK_SIZE];
+ memset(dummy_key, 0x00, TPM_AES_BLOCK_SIZE);
+ memset(dummy_ivec, 0x00, TPM_AES_BLOCK_SIZE);
+ rv = AES_InitContext(cx, /* AES context */
+ dummy_key, /* AES key */
+ TPM_AES_BLOCK_SIZE, /* key length */
+ dummy_ivec, /* ivec */
+ NSS_AES_CBC, /* CBC mode */
+ TRUE, /* encrypt */
+ TPM_AES_BLOCK_SIZE); /* AES block length */
+ AES_DestroyContext(cx, PR_TRUE); /* @1 */
+ }
+ return rc;
+}
+
+/* TPM_SymmetricKeyData_CtrCrypt() does an encrypt or decrypt (they are the same XOR operation with
+ a CTR mode pad) of 'data_in' to 'data_out'.
+
+ TPM_SymmetricKeyData_CtrCrypt() is a TPM variant of the standard CTR encrypt function that
+ increments only the low 4 bytes of the counter.
+
+ NOTE: This function looks general, but is currently hard coded to AES128.
+
+ 'symmetric key' is the raw key, not converted to a non-portable form
+ 'ctr_in' is the initial CTR value before possible truncation
+*/
+
+TPM_RESULT TPM_SymmetricKeyData_CtrCrypt(unsigned char *data_out, /* output */
+ const unsigned char *data_in, /* input */
+ uint32_t data_size, /* input */
+ const unsigned char *symmetric_key, /* input */
+ uint32_t symmetric_key_size, /* input */
+ const unsigned char *ctr_in, /* input */
+ uint32_t ctr_in_size) /* input */
+{
+ TPM_RESULT rc = 0;
+ SECStatus rv;
+ AESContext *cx = NULL;
+ unsigned char ctr[TPM_AES_BLOCK_SIZE];
+ unsigned char pad_buffer[TPM_AES_BLOCK_SIZE]; /* the XOR pad */
+ uint32_t output_length; /* dummy */
+ uint32_t cint; /* counter as a 32-bit integer */
+
+ printf(" TPM_SymmetricKeyData_CtrCrypt: data_size %u\n", data_size);
+ symmetric_key_size = symmetric_key_size;
+ /* check the input CTR size, it can be truncated, but cannot be smaller than the AES key */
+ if (rc == 0) {
+ if (ctr_in_size < sizeof(ctr)) {
+ printf(" TPM_SymmetricKeyData_CtrCrypt: Error (fatal)"
+ ", CTR size %u too small for AES key\n", ctr_in_size);
+ rc = TPM_FAIL; /* should never occur */
+ }
+ }
+ if (rc == 0) {
+ /* make a truncated copy of CTR, since this function alters the value */
+ memcpy(ctr, ctr_in, sizeof(ctr));
+ TPM_PrintFour(" TPM_SymmetricKeyData_CtrCrypt: CTR", ctr);
+ }
+ /* create a new AES context */
+ if (rc == 0) {
+ cx = AES_CreateContext(symmetric_key, /* AES key */
+ NULL, /* ivec not used in NSS_AES */
+ NSS_AES, /* mode */
+ TRUE, /* encrypt */
+ TPM_AES_BLOCK_SIZE, /* key length */
+ TPM_AES_BLOCK_SIZE); /* AES block length */
+ if (cx == NULL) {
+ printf("TPM_SymmetricKeyData_CtrCrypt: Error creating AES context\n");
+ rc = TPM_SIZE;
+ }
+ }
+ while (data_size != 0) {
+ printf(" TPM_SymmetricKeyData_CtrCrypt : data_size remaining %u\n", data_size);
+ /* initialize the context each time through the loop */
+ if (rc == 0) {
+ rv = AES_InitContext(cx, /* AES context */
+ symmetric_key, /* AES key */
+ TPM_AES_BLOCK_SIZE, /* key length */
+ NULL, /* ivec not used in NSS_AES */
+ NSS_AES, /* mode */
+ TRUE, /* encrypt */
+ TPM_AES_BLOCK_SIZE); /* AES block length */
+ if (rv != SECSuccess) {
+ printf("TPM_SymmetricKeyData_CtrCrypt: Error, rv %d\n", rv);
+ rc = TPM_ENCRYPT_ERROR;
+ }
+ }
+ /* get an XOR pad array by encrypting the CTR with the AES key */
+ if (rc == 0) {
+ rv = AES_Encrypt(cx,
+ pad_buffer, &output_length, TPM_AES_BLOCK_SIZE, /* output */
+ ctr, TPM_AES_BLOCK_SIZE); /* input */
+
+ if (rv != SECSuccess) {
+ printf("TPM_SymmetricKeyData_CtrCrypt: Error, rv %d\n", rv);
+ rc = TPM_ENCRYPT_ERROR;
+ }
+ }
+ if (rc == 0) {
+ /* partial or full last data block */
+ if (data_size <= TPM_AES_BLOCK_SIZE) {
+ TPM_XOR(data_out, data_in, pad_buffer, data_size);
+ data_size = 0;
+ }
+ /* full block, not the last block */
+ else {
+ TPM_XOR(data_out, data_in, pad_buffer, TPM_AES_BLOCK_SIZE);
+ data_in += TPM_AES_BLOCK_SIZE;
+ data_out += TPM_AES_BLOCK_SIZE;
+ data_size -= TPM_AES_BLOCK_SIZE;
+ }
+ /* if not the last block, increment CTR, only the low 4 bytes */
+ if (data_size != 0) {
+ /* CTR is a big endian array, so the low 4 bytes are used */
+ cint = LOAD32(ctr, TPM_AES_BLOCK_SIZE-4); /* byte array to uint32_t */
+ cint++; /* increment */
+ STORE32(ctr, TPM_AES_BLOCK_SIZE-4, cint); /* uint32_t to byte array */
+ }
+ }
+ }
+ if (cx != NULL) {
+ /* due to a FreeBL bug, must zero the context before destroying it */
+ unsigned char dummy_key[TPM_AES_BLOCK_SIZE];
+ memset(dummy_key, 0x00, TPM_AES_BLOCK_SIZE);
+ rv = AES_InitContext(cx, /* AES context */
+ dummy_key, /* AES key */
+ TPM_AES_BLOCK_SIZE, /* key length */
+ NULL, /* ivec not used in NSS_AES */
+ NSS_AES, /* mode */
+ TRUE, /* encrypt */
+ TPM_AES_BLOCK_SIZE); /* AES block length */
+ AES_DestroyContext(cx, PR_TRUE); /* @2 */
+ }
+ return rc;
+}
+
+/* TPM_SymmetricKeyData_OfbCrypt() does an encrypt or decrypt (they are the same XOR operation with
+ a OFB mode pad) of 'data_in' to 'data_out'
+
+ NOTE: This function looks general, but is currently hard coded to AES128.
+
+ 'symmetric key' is the raw key, not converted to a non-portable form
+ 'ivec_in' is the initial IV value before possible truncation
+*/
+
+TPM_RESULT TPM_SymmetricKeyData_OfbCrypt(unsigned char *data_out, /* output */
+ const unsigned char *data_in, /* input */
+ uint32_t data_size, /* input */
+ const unsigned char *symmetric_key, /* in */
+ uint32_t symmetric_key_size, /* in */
+ unsigned char *ivec_in, /* input */
+ uint32_t ivec_in_size) /* input */
+{
+ TPM_RESULT rc = 0;
+ SECStatus rv;
+ AESContext *cx = NULL;
+ unsigned char ivec_loop[TPM_AES_BLOCK_SIZE]; /* ivec input to loop */
+ unsigned char pad_buffer[TPM_AES_BLOCK_SIZE]; /* the XOR pad */
+ uint32_t output_length; /* dummy */
+
+ printf(" TPM_SymmetricKeyData_OfbCrypt: data_size %u\n", data_size);
+ symmetric_key_size = symmetric_key_size;
+ /* check the input OFB size, it can be truncated, but cannot be smaller than the AES key */
+ if (rc == 0) {
+ if (ivec_in_size < TPM_AES_BLOCK_SIZE) {
+ printf(" TPM_SymmetricKeyData_OfbCrypt: Error (fatal),"
+ "IV size %u too small for AES key\n", ivec_in_size);
+ rc = TPM_FAIL; /* should never occur */
+ }
+ }
+ /* first time through, the ivec_loop will be the input ivec */
+ if (rc == 0) {
+ memcpy(ivec_loop, ivec_in, sizeof(ivec_loop));
+ TPM_PrintFour(" TPM_SymmetricKeyData_OfbCrypt: IV", ivec_loop);
+ }
+ /* create a new AES context */
+ if (rc == 0) {
+ cx = AES_CreateContext(symmetric_key,
+ NULL, /* ivec not used in NSS_AES */
+ NSS_AES, /* mode */
+ TRUE, /* encrypt */
+ TPM_AES_BLOCK_SIZE, /* key length */
+ TPM_AES_BLOCK_SIZE); /* AES block length */
+ if (cx == NULL) {
+ printf("TPM_SymmetricKeyData_OfbCrypt: Error creating AES context\n");
+ rc = TPM_SIZE;
+ }
+ }
+ while (data_size != 0) {
+ printf(" TPM_SymmetricKeyData_OfbCrypt: data_size remaining %u\n", data_size);
+ /* initialize the context each time through the loop */
+ if (rc == 0) {
+ rv = AES_InitContext(cx, /* AES context */
+ symmetric_key, /* AES key */
+ TPM_AES_BLOCK_SIZE, /* key length */
+ NULL, /* ivec not used in NSS_AES */
+ NSS_AES, /* mode */
+ TRUE, /* encrypt */
+ TPM_AES_BLOCK_SIZE); /* AES block length */
+ if (rv != SECSuccess) {
+ printf("TPM_SymmetricKeyData_OfbCrypt: Error, rv %d\n", rv);
+ rc = TPM_ENCRYPT_ERROR;
+ }
+ }
+ /* get an XOR pad array by encrypting the IV with the AES key */
+ if (rc == 0) {
+ TPM_PrintFour(" TPM_SymmetricKeyData_OfbCrypt: IV", ivec_loop);
+ rv = AES_Encrypt(cx,
+ pad_buffer, &output_length, TPM_AES_BLOCK_SIZE, /* output */
+ ivec_loop, TPM_AES_BLOCK_SIZE); /* input */
+
+ if (rv != SECSuccess) {
+ printf("TPM_SymmetricKeyData_OfbCrypt: Error, rv %d\n", rv);
+ rc = TPM_ENCRYPT_ERROR;
+ }
+ }
+ if (rc == 0) {
+ /* partial or full last data block */
+ if (data_size <= TPM_AES_BLOCK_SIZE) {
+ TPM_XOR(data_out, data_in, pad_buffer, data_size);
+ data_size = 0;
+ }
+ /* full block, not the last block */
+ else {
+ TPM_XOR(data_out, data_in, pad_buffer, TPM_AES_BLOCK_SIZE);
+ data_in += TPM_AES_BLOCK_SIZE;
+ data_out += TPM_AES_BLOCK_SIZE;
+ data_size -= TPM_AES_BLOCK_SIZE;
+ }
+ /* if not the last block, wrap the pad_buffer back to ivec_loop (output feed back) */
+ memcpy(ivec_loop, pad_buffer, TPM_AES_BLOCK_SIZE);
+ }
+ }
+ if (cx != NULL) {
+ /* due to a FreeBL bug, must zero the context before destroying it */
+ unsigned char dummy_key[TPM_AES_BLOCK_SIZE];
+ memset(dummy_key, 0x00, TPM_AES_BLOCK_SIZE);
+ rv = AES_InitContext(cx, /* AES context */
+ dummy_key, /* AES key */
+ TPM_AES_BLOCK_SIZE, /* key length */
+ NULL, /* ivec not used in NSS_AES */
+ NSS_AES, /* mode */
+ TRUE, /* encrypt */
+ TPM_AES_BLOCK_SIZE); /* AES block length */
+ AES_DestroyContext(cx, PR_TRUE); /* @2 */
+ }
+ return rc;
+}
+
+#endif /* TPM_AES */