500 lines
12 KiB
C
500 lines
12 KiB
C
/*
|
|
* The Initial Developer of the Original Code is International
|
|
* Business Machines Corporation. Portions created by IBM
|
|
* Corporation are Copyright (C) 2005, 2006 International Business
|
|
* Machines Corporation. All Rights Reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the Common Public License as published by
|
|
* IBM Corporation; either version 1 of the License, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* Common Public License for more details.
|
|
*
|
|
* You should have received a copy of the Common Public License
|
|
* along with this program; if not, a copy can be viewed at
|
|
* http://www.opensource.org/licenses/cpl1.0.php.
|
|
*/
|
|
|
|
#include "tpm_tspi.h"
|
|
#include "tpm_seal.h"
|
|
#include "tpm_unseal.h"
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
#include <openssl/evp.h>
|
|
#include <trousers/tss.h>
|
|
#include <trousers/trousers.h>
|
|
|
|
enum tspi_errors {
|
|
ETSPICTXCREAT = 0,
|
|
ETSPICTXCNCT,
|
|
ETSPICTXCO,
|
|
ETSPICTXLKBU,
|
|
ETSPICTXLKBB,
|
|
ETSPISETAD,
|
|
ETSPIGETPO,
|
|
ETSPIPOLSS,
|
|
ETSPIDATU,
|
|
ETSPIPOLATO,
|
|
};
|
|
|
|
TSS_HCONTEXT hContext = 0;
|
|
#define TSPI_FUNCTION_NAME_MAX 30
|
|
char tspi_error_strings[][TSPI_FUNCTION_NAME_MAX]= {
|
|
"Tspi_Context_Create",
|
|
"Tspi_Context_Connect",
|
|
"Tspi_Context_CreateObject",
|
|
"Tspi_Context_LoadKeyByUUID",
|
|
"Tspi_Context_LoadKeyByBlob",
|
|
"Tspi_SetAttribData",
|
|
"Tspi_GetPolicyObject",
|
|
"Tspi_Policy_SetSecret",
|
|
"Tspi_Data_Unseal",
|
|
"Tspi_Policy_AssignToObject",
|
|
};
|
|
|
|
#define TSSKEY_DEFAULT_SIZE 768
|
|
#define EVPKEY_DEFAULT_SIZE 512
|
|
|
|
int tpm_errno;
|
|
|
|
int tpmUnsealFile( char* fname, unsigned char** tss_data, int* tss_size,
|
|
BOOL srkWellKnown ) {
|
|
|
|
int rc, rcLen=0, tssLen=0, evpLen=0;
|
|
BYTE* rcPtr;
|
|
char data[EVP_CIPHER_block_size(EVP_aes_256_cbc()) * 16];
|
|
BYTE *tssKeyData = NULL;
|
|
int tssKeyDataSize = 0;
|
|
BYTE *evpKeyData = NULL;
|
|
int evpKeyDataSize = 0;
|
|
struct stat stats;
|
|
TSS_HENCDATA hEncdata;
|
|
TSS_HKEY hSrk, hKey;
|
|
TSS_HPOLICY hPolicy;
|
|
UINT32 symKeyLen;
|
|
BYTE *symKey;
|
|
BYTE wellKnown[TCPA_SHA1_160_HASH_LEN] = TSS_WELL_KNOWN_SECRET;
|
|
char *srkSecret = NULL;
|
|
int srkSecretLen;
|
|
unsigned char* res_data = NULL;
|
|
int res_size = 0;
|
|
|
|
BIO *bdata = NULL, *b64 = NULL, *bmem = NULL;
|
|
int bioRc;
|
|
|
|
if ( tss_data == NULL || tss_size == NULL ) {
|
|
rc = TPMSEAL_STD_ERROR;
|
|
tpm_errno = EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
*tss_data = NULL;
|
|
*tss_size = 0;
|
|
|
|
/* Test for file existence */
|
|
if ((rc = stat(fname, &stats))) {
|
|
tpm_errno = errno;
|
|
goto out;
|
|
}
|
|
|
|
/* Create an input file BIO */
|
|
if((bdata = BIO_new_file(fname, "r")) == NULL ) {
|
|
tpm_errno = errno;
|
|
rc = TPMSEAL_STD_ERROR;
|
|
goto out;
|
|
}
|
|
|
|
/* Test file header for TSS */
|
|
BIO_gets(bdata, data, sizeof(data));
|
|
if (strncmp(data, TPMSEAL_HDR_STRING,
|
|
strlen(TPMSEAL_HDR_STRING)) != 0) {
|
|
rc = TPMSEAL_FILE_ERROR;
|
|
tpm_errno = ENOTSSHDR;
|
|
goto out;
|
|
}
|
|
|
|
/* Looking for TSS Key Header */
|
|
BIO_gets(bdata, data, sizeof(data));
|
|
if (strncmp(data, TPMSEAL_TSS_STRING,
|
|
strlen(TPMSEAL_TSS_STRING)) != 0) {
|
|
rc = TPMSEAL_FILE_ERROR;
|
|
tpm_errno = EWRONGTSSTAG;
|
|
goto out;
|
|
}
|
|
|
|
/* Create a memory BIO to hold the base64 TSS key */
|
|
if ((bmem = BIO_new(BIO_s_mem())) == NULL) {
|
|
tpm_errno = EAGAIN;
|
|
rc = TPMSEAL_STD_ERROR;
|
|
goto out;
|
|
}
|
|
BIO_set_mem_eof_return(bmem, 0);
|
|
|
|
/* Read the base64 TSS key into the memory BIO */
|
|
while ((rcLen = BIO_gets(bdata, data, sizeof(data))) > 0) {
|
|
/* Look for EVP Key Header (end of key) */
|
|
if (strncmp(data, TPMSEAL_EVP_STRING,
|
|
strlen(TPMSEAL_EVP_STRING)) == 0)
|
|
break;
|
|
|
|
if (BIO_write(bmem, data, rcLen) <= 0) {
|
|
tpm_errno = EIO;
|
|
rc = TPMSEAL_STD_ERROR;
|
|
goto out;
|
|
}
|
|
}
|
|
if (strncmp(data, TPMSEAL_EVP_STRING,
|
|
strlen(TPMSEAL_EVP_STRING)) != 0 ) {
|
|
tpm_errno = EWRONGEVPTAG;
|
|
rc = TPMSEAL_FILE_ERROR;
|
|
goto out;
|
|
}
|
|
|
|
/* Create a base64 BIO to decode the TSS key */
|
|
if ((b64 = BIO_new(BIO_f_base64())) == NULL) {
|
|
tpm_errno = EAGAIN;
|
|
rc = TPMSEAL_STD_ERROR;
|
|
goto out;
|
|
}
|
|
|
|
/* Decode the TSS key */
|
|
bmem = BIO_push( b64, bmem );
|
|
while ((rcLen = BIO_read(bmem, data, sizeof(data))) > 0) {
|
|
if ((tssLen + rcLen) > tssKeyDataSize) {
|
|
tssKeyDataSize += TSSKEY_DEFAULT_SIZE;
|
|
rcPtr = realloc( tssKeyData, tssKeyDataSize);
|
|
if ( rcPtr == NULL ) {
|
|
tpm_errno = ENOMEM;
|
|
rc = TPMSEAL_STD_ERROR;
|
|
goto out;
|
|
}
|
|
tssKeyData = rcPtr;
|
|
}
|
|
memcpy(tssKeyData + tssLen, data, rcLen);
|
|
tssLen += rcLen;
|
|
}
|
|
bmem = BIO_pop(b64);
|
|
BIO_free(b64);
|
|
b64 = NULL;
|
|
bioRc = BIO_reset(bmem);
|
|
|
|
/* Check for EVP Key Type Header */
|
|
BIO_gets(bdata, data, sizeof(data));
|
|
if (strncmp(data, TPMSEAL_KEYTYPE_SYM,
|
|
strlen(TPMSEAL_KEYTYPE_SYM)) != 0 ) {
|
|
rc = TPMSEAL_FILE_ERROR;
|
|
tpm_errno = EWRONGKEYTYPE;
|
|
goto out;
|
|
}
|
|
|
|
/* Make sure it's a supported cipher
|
|
(currently only AES 256 CBC) */
|
|
if (strncmp(data + strlen(TPMSEAL_KEYTYPE_SYM),
|
|
TPMSEAL_CIPHER_AES256CBC,
|
|
strlen(TPMSEAL_CIPHER_AES256CBC)) != 0) {
|
|
rc = TPMSEAL_FILE_ERROR;
|
|
tpm_errno = EWRONGKEYTYPE;
|
|
goto out;
|
|
}
|
|
|
|
/* Read the base64 Symmetric key into the memory BIO */
|
|
while ((rcLen = BIO_gets(bdata, data, sizeof(data))) > 0) {
|
|
/* Look for Encrypted Data Header (end of key) */
|
|
if (strncmp(data, TPMSEAL_ENC_STRING,
|
|
strlen(TPMSEAL_ENC_STRING)) == 0)
|
|
break;
|
|
|
|
if (BIO_write(bmem, data, rcLen) <= 0) {
|
|
tpm_errno = EIO;
|
|
rc = TPMSEAL_STD_ERROR;
|
|
goto out;
|
|
}
|
|
}
|
|
if (strncmp(data, TPMSEAL_ENC_STRING,
|
|
strlen(TPMSEAL_ENC_STRING)) != 0 ) {
|
|
tpm_errno = EWRONGDATTAG;
|
|
rc = TPMSEAL_FILE_ERROR;
|
|
goto out;
|
|
}
|
|
|
|
/* Create a base64 BIO to decode the Symmetric key */
|
|
if ((b64 = BIO_new(BIO_f_base64())) == NULL) {
|
|
tpm_errno = EAGAIN;
|
|
rc = TPMSEAL_STD_ERROR;
|
|
goto out;
|
|
}
|
|
|
|
/* Decode the Symmetric key */
|
|
bmem = BIO_push( b64, bmem );
|
|
while ((rcLen = BIO_read(bmem, data, sizeof(data))) > 0) {
|
|
if ((evpLen + rcLen) > evpKeyDataSize) {
|
|
evpKeyDataSize += EVPKEY_DEFAULT_SIZE;
|
|
rcPtr = realloc( evpKeyData, evpKeyDataSize);
|
|
if ( rcPtr == NULL ) {
|
|
tpm_errno = ENOMEM;
|
|
rc = TPMSEAL_STD_ERROR;
|
|
goto out;
|
|
}
|
|
evpKeyData = rcPtr;
|
|
}
|
|
memcpy(evpKeyData + evpLen, data, rcLen);
|
|
evpLen += rcLen;
|
|
}
|
|
bmem = BIO_pop(b64);
|
|
BIO_free(b64);
|
|
b64 = NULL;
|
|
bioRc = BIO_reset(bmem);
|
|
|
|
/* Read the base64 encrypted data into the memory BIO */
|
|
while ((rcLen = BIO_gets(bdata, data, sizeof(data))) > 0) {
|
|
/* Look for TSS Footer (end of data) */
|
|
if (strncmp(data, TPMSEAL_FTR_STRING,
|
|
strlen(TPMSEAL_FTR_STRING)) == 0)
|
|
break;
|
|
|
|
if (BIO_write(bmem, data, rcLen) <= 0) {
|
|
tpm_errno = EIO;
|
|
rc = TPMSEAL_STD_ERROR;
|
|
goto out;
|
|
}
|
|
}
|
|
if (strncmp(data, TPMSEAL_FTR_STRING,
|
|
strlen(TPMSEAL_FTR_STRING)) != 0 ) {
|
|
tpm_errno = ENOTSSFTR;
|
|
rc = TPMSEAL_FILE_ERROR;
|
|
goto out;
|
|
}
|
|
|
|
/* Unseal */
|
|
if ((rc=Tspi_Context_Create(&hContext)) != TSS_SUCCESS) {
|
|
tpm_errno = ETSPICTXCREAT;
|
|
goto out;
|
|
}
|
|
|
|
if (!srkWellKnown) {
|
|
/* Prompt for SRK password */
|
|
srkSecret = GETPASSWD(_("Enter SRK password: "), &srkSecretLen, FALSE);
|
|
if (!srkSecret)
|
|
goto out;
|
|
}
|
|
if ((rc=Tspi_Context_Connect(hContext, NULL)) != TSS_SUCCESS) {
|
|
tpm_errno = ETSPICTXCNCT;
|
|
goto tss_out;
|
|
}
|
|
|
|
if ((rc=Tspi_Context_CreateObject(hContext,
|
|
TSS_OBJECT_TYPE_ENCDATA,
|
|
TSS_ENCDATA_SEAL,
|
|
&hEncdata)) != TSS_SUCCESS) {
|
|
tpm_errno = ETSPICTXCO;
|
|
goto tss_out;
|
|
}
|
|
|
|
if ((rc=Tspi_SetAttribData(hEncdata,
|
|
TSS_TSPATTRIB_ENCDATA_BLOB,
|
|
TSS_TSPATTRIB_ENCDATABLOB_BLOB,
|
|
evpLen, evpKeyData)) != TSS_SUCCESS) {
|
|
tpm_errno = ETSPISETAD;
|
|
goto tss_out;
|
|
}
|
|
|
|
if ((rc=Tspi_Context_CreateObject(hContext,
|
|
TSS_OBJECT_TYPE_POLICY,
|
|
TSS_POLICY_USAGE,
|
|
&hPolicy)) != TSS_SUCCESS) {
|
|
tpm_errno = ETSPICTXCO;
|
|
goto tss_out;
|
|
}
|
|
|
|
if ((rc=Tspi_Policy_SetSecret(hPolicy, TSS_SECRET_MODE_PLAIN,
|
|
strlen(TPMSEAL_SECRET),
|
|
(BYTE *)TPMSEAL_SECRET)) != TSS_SUCCESS) {
|
|
tpm_errno = ETSPIPOLSS;
|
|
goto tss_out;
|
|
}
|
|
|
|
if ((rc=Tspi_Policy_AssignToObject(hPolicy, hEncdata)) != TSS_SUCCESS) {
|
|
tpm_errno = ETSPIPOLATO;
|
|
goto tss_out;
|
|
}
|
|
|
|
if ((rc=Tspi_Context_LoadKeyByUUID(hContext, TSS_PS_TYPE_SYSTEM,
|
|
SRK_UUID, &hSrk)) != TSS_SUCCESS) {
|
|
tpm_errno = ETSPICTXLKBU;
|
|
goto tss_out;
|
|
}
|
|
|
|
/* Don't create a new policy for the SRK's secret, just use the context's
|
|
* default policy */
|
|
if ((rc=Tspi_GetPolicyObject(hSrk, TSS_POLICY_USAGE,
|
|
&hPolicy)) != TSS_SUCCESS){
|
|
tpm_errno = ETSPIGETPO;
|
|
goto tss_out;
|
|
}
|
|
|
|
if (srkWellKnown)
|
|
rc = Tspi_Policy_SetSecret(hPolicy, TSS_SECRET_MODE_SHA1,
|
|
sizeof(wellKnown),
|
|
(BYTE *) wellKnown);
|
|
else
|
|
rc = Tspi_Policy_SetSecret(hPolicy,TSS_SECRET_MODE_PLAIN,
|
|
srkSecretLen,
|
|
(BYTE *) srkSecret);
|
|
|
|
if (rc != TSS_SUCCESS) {
|
|
tpm_errno = ETSPIPOLSS;
|
|
goto tss_out;
|
|
}
|
|
|
|
/* Failure point if trying to unseal data on a differnt TPM */
|
|
if ((rc=Tspi_Context_LoadKeyByBlob(hContext, hSrk, tssLen,
|
|
tssKeyData, &hKey)) != TSS_SUCCESS) {
|
|
tpm_errno = ETSPICTXLKBB;
|
|
goto tss_out;
|
|
}
|
|
|
|
if ((rc=Tspi_Context_CreateObject(hContext,
|
|
TSS_OBJECT_TYPE_POLICY,
|
|
TSS_POLICY_USAGE,
|
|
&hPolicy)) != TSS_SUCCESS) {
|
|
tpm_errno = ETSPICTXCO;
|
|
goto tss_out;
|
|
}
|
|
|
|
if ((rc=Tspi_Policy_SetSecret(hPolicy, TSS_SECRET_MODE_PLAIN,
|
|
strlen(TPMSEAL_SECRET),
|
|
(BYTE *)TPMSEAL_SECRET)) != TSS_SUCCESS) {
|
|
tpm_errno = ETSPIPOLSS;
|
|
goto tss_out;
|
|
}
|
|
|
|
if ((rc=Tspi_Policy_AssignToObject(hPolicy, hKey)) != TSS_SUCCESS) {
|
|
tpm_errno = ETSPIPOLATO;
|
|
goto tss_out;
|
|
}
|
|
|
|
if ((rc=Tspi_Data_Unseal(hEncdata, hKey, &symKeyLen,
|
|
&symKey)) != TSS_SUCCESS) {
|
|
tpm_errno = ETSPIDATU;
|
|
goto tss_out;
|
|
}
|
|
|
|
/* Malloc a block of storage to hold the decrypted data
|
|
Using the size of the mem BIO is more than enough
|
|
(plus an extra cipher block size) */
|
|
res_data = malloc(BIO_pending(bmem) + EVP_CIPHER_block_size(EVP_aes_256_cbc()));
|
|
if ( res_data == NULL ) {
|
|
rc = TPMSEAL_STD_ERROR;
|
|
tpm_errno = ENOMEM;
|
|
goto tss_out;
|
|
}
|
|
|
|
/* Decode and decrypt the encrypted data */
|
|
EVP_CIPHER_CTX ctx;
|
|
EVP_DecryptInit(&ctx, EVP_aes_256_cbc(), symKey, (unsigned char *)TPMSEAL_IV);
|
|
|
|
/* Create a base64 BIO to decode the encrypted data */
|
|
if ((b64 = BIO_new(BIO_f_base64())) == NULL) {
|
|
tpm_errno = EAGAIN;
|
|
rc = TPMSEAL_STD_ERROR;
|
|
goto tss_out;
|
|
}
|
|
|
|
bmem = BIO_push( b64, bmem );
|
|
while ((rcLen = BIO_read(bmem, data, sizeof(data))) > 0) {
|
|
EVP_DecryptUpdate(&ctx, res_data+res_size,
|
|
&rcLen, (unsigned char *)data, rcLen);
|
|
res_size += rcLen;
|
|
}
|
|
EVP_DecryptFinal(&ctx, res_data+res_size, &rcLen);
|
|
res_size += rcLen;
|
|
bmem = BIO_pop(b64);
|
|
BIO_free(b64);
|
|
b64 = NULL;
|
|
bioRc = BIO_reset(bmem);
|
|
|
|
tss_out:
|
|
Tspi_Context_Close(hContext);
|
|
out:
|
|
|
|
if (srkSecret)
|
|
shredPasswd(srkSecret);
|
|
|
|
if ( bdata )
|
|
BIO_free(bdata);
|
|
if ( b64 )
|
|
BIO_free(b64);
|
|
if ( bmem ) {
|
|
bioRc = BIO_set_close(bmem, BIO_CLOSE);
|
|
BIO_free(bmem);
|
|
}
|
|
|
|
if ( evpKeyData )
|
|
free(evpKeyData);
|
|
if ( tssKeyData )
|
|
free(tssKeyData);
|
|
|
|
if ( rc == 0 ) {
|
|
*tss_data = res_data;
|
|
*tss_size = res_size;
|
|
} else
|
|
free(res_data);
|
|
|
|
return rc;
|
|
}
|
|
|
|
void tpmUnsealShred(unsigned char* data, int size) {
|
|
|
|
if ( data != NULL ) {
|
|
memset( data, 0, size);
|
|
free(data);
|
|
}
|
|
|
|
}
|
|
|
|
char tpm_error_buf[512];
|
|
char * tpmUnsealStrerror(int rc) {
|
|
|
|
switch(rc) {
|
|
case 0:
|
|
return "Success";
|
|
case TPMSEAL_STD_ERROR:
|
|
return strerror(tpm_errno);
|
|
case TPMSEAL_FILE_ERROR:
|
|
switch(tpm_errno) {
|
|
case ENOTSSHDR:
|
|
return _("No TSS header present");
|
|
case ENOTSSFTR:
|
|
return _("No TSS footer present");
|
|
case EWRONGTSSTAG:
|
|
return _("Wrong TSS tag");
|
|
case EWRONGEVPTAG:
|
|
return _("Wrong EVP tag");
|
|
case EWRONGDATTAG:
|
|
return _("Wrong DATA tag");
|
|
case EWRONGKEYTYPE:
|
|
return _("Not a Symmetric EVP Key");
|
|
case EBADSEEK:
|
|
return _("Unable to move to desired file position");
|
|
}
|
|
default:
|
|
snprintf(tpm_error_buf, sizeof(tpm_error_buf),
|
|
"%s: 0x%08x - layer=%s, code=%04x (%d), %s",
|
|
tspi_error_strings[tpm_errno],
|
|
rc, Trspi_Error_Layer(rc),
|
|
Trspi_Error_Code(rc),
|
|
Trspi_Error_Code(rc),
|
|
Trspi_Error_String(rc));
|
|
return tpm_error_buf;
|
|
}
|
|
return "";
|
|
}
|