Utility which will convert a context file between tpm2_tools and the raw structure TPMS_CONTEXT.
TPM specifications describe TPM2 Structure Context Data (pg 146) structure as
which is used by go-tpm tpm2.ContextSave and tpm2.ContextLoad
However, tpm2_tools context file includes tool-specific headers and values here
/*
* Saving the TPMS_CONTEXT structure to disk, format:
* TPM2.0-TOOLS HEADER
* U32 hierarchy
* U32 savedHandle
* U64 sequence
* U16 contextBlobLength
* BYTE[] contextBlob
*/Which means is if you use tpm2_tools to save a context file, its not compatible with go-tpm.
This utility will help convert basic contexts back and forth
THe follwoing converts
-
tpm2_toolssaved context -->go-tpmload context i.,e you create a key with tpm2_tools, save it and then read with go-tpm -
go-tpmkey -->tpm2_toolscontext i.,e you create a key with go-tpm, save it and then read with tpm2_tools
Limitations:
- The contexts files currently only support single parent-child keys. A
todois to support context chains - Only supports keys (not session contexts)
also see see
The following uses a softwaretpm
cd example/
#rm -rf myvtpm && mkdir myvtpm && \
#swtpm_setup --tpmstate myvtpm --tpm2 --create-ek-cert && \
swtpm socket --tpmstate dir=myvtpm --tpm2 --server type=tcp,port=2321 --ctrl type=tcp,port=2322 --flags not-need-init,startup-clear
export TPM2TOOLS_TCTI="swtpm:port=2321"
export TPM="127.0.0.1:2341" # /dev/tpmrm0The follwoing will read a tpm2 generated key file key.ctx, parse it and load it for use with go-tpm.
From there it will use the key to encrypt some data
## first create a an aes key
printf '\x00\x00' > unique.dat
tpm2_createprimary -C o -G ecc -g sha256 -c primary.ctx -a "fixedtpm|fixedparent|sensitivedataorigin|userwithauth|noda|restricted|decrypt" -u unique.dat
tpm2_create -g sha256 -G aes -u key.pub -r key.priv -C primary.ctx
### save the key as `key.ctx`
tpm2_load -C primary.ctx -u key.pub -r key.priv -n key.name -c key.ctx
### now convert
go run cmd/main.go --mode=from-tpm2 \
--tpm-path="127.0.0.1:2321" \
--contextFileIn=example/keys/key.ctx \
--contextFileOut=example/keys/tpm_context.ctx
### now use the key
cd example/
go run from_tpm2_tools/main.go \
--tpm-path="127.0.0.1:2321" \
--contextFileIn="keys/tpm_context.ctx"
Recalled Name 000b7ab4bca0b83ff1627cd9ada2ff83772205d62f4afdc92232fa5cd998069d931d
IV: 04066b6065e8a2aaccefd921a80396f3
Encrypted 6fb5aaf543
Decrypted foooo
THe following will create an aes key under the H2 parent using go and save the context to go_context.ctx
From there the utility here will read that file and write a tpm2_tools compativle context to key2.ctx
## create a key and save the context to go_context
cd example/
go run to_tpm2_tools/main.go \
--tpm-path="127.0.0.1:2321" \
--contextFileOut="keys/go_context.ctx"
go run cmd/main.go --mode=to-tpm2 \
--tpm-path="127.0.0.1:2321" \
--parentType=h2 \
--contextFileIn=example/keys/go_context.ctx \
--contextFileOut=example/keys/key2.ctxThen encrpt/decrypt with both key context
cd keys/
echo -n "foo" > plain.out
openssl rand -out iv.bin 16
### encrypt with tpm2_tools genreated key.ctx
tpm2_encryptdecrypt --iv iv.bin -c key2.ctx -o encrypt.out plain.out
## decrypt with converted generated key2.ctx
tpm2_encryptdecrypt --iv iv.bin -c key2.ctx -d -o decrypt.out encrypt.outIn my case the key name had the following specs.
$ tpm2_readpublic -c key.ctx
name: 000b7ab4bca0b83ff1627cd9ada2ff83772205d62f4afdc92232fa5cd998069d931d
qualified name: 000b9194d774c62d5197dc9838a19897a71afee145bfec1b3899e3ac1e80062cc9d9
name-alg:
value: sha256
raw: 0xb
attributes:
value: fixedtpm|fixedparent|sensitivedataorigin|userwithauth|decrypt|sign
raw: 0x60072
type:
value: symcipher
raw: 0x25
sym-alg:
value: aes
raw: 0x6
sym-mode:
value: null
raw: 0x10
sym-keybits: 128
symcipher: b5094699172df59db8fe34e925b6935663cbc39c1517baa3e8e32137aac254f2
$ tpm2_print -t TPM2B_PUBLIC key.pub
name-alg:
value: sha256
raw: 0xb
attributes:
value: fixedtpm|fixedparent|sensitivedataorigin|userwithauth|decrypt|sign
raw: 0x60072
type:
value: symcipher
raw: 0x25
sym-alg:
value: aes
raw: 0x6
sym-mode:
value: null
raw: 0x10
sym-keybits: 128
symcipher: b5094699172df59db8fe34e925b6935663cbc39c1517baa3e8e32137aac254f2And notice the tpm and go generated keys are of the same size
$ tpm2_print -t TPMS_CONTEXT key.ctx
version: 1
hierarchy: owner
handle: 0x80000000 (2147483648)
sequence: 18
contextBlob:
size: 488
$ tpm2_print -t TPMS_CONTEXT key2.ctx
version: 1
hierarchy: owner
handle: 0x80000000 (2147483648)
sequence: 18
contextBlob:
size: 488The context file tpm2_tools uses basically uses this structure
/*
* Saving the TPMS_CONTEXT structure to disk, format:
* TPM2.0-TOOLS HEADER
* U32 hierarchy
* U32 savedHandle
* U64 sequence
* U16 contextBlobLength
* BYTE[] contextBlob
*/Critically, contextBlob is not what go-tpm uses in plain tpm2.ContextSave but a complex structure which includes the raw context info from go-tpm plus values for Esys_ContextSave described below
see the section below for links to tpm2_tools and ESYS_Context
For a specific run of the parsing, consider the tpm2_tools generated context here
$ xxd -p -c 100000 key.ctx
badcc0de000000014000000180000000000000000000000201e80000000001800040112315c7b03d188040c4f4c550072c62547e5dfbf9ef83ccbaa59c40ce39a175b1d4dd0db1c034041401d3e8a6144f9fc720b4569b9bdeca5c47921d213608658931209745c4e7a610e3d65b8298056c6e251b7f357b0eaaf6505b64d6b8b0271979222e7b00f66bb55c9d1f6b075826bcd20d6da3c25d16280bda5bd74da4d83f9ade38f0e59cc1194e449a9a9349b8e195131c344adf57316c283ca6db9542a7222486a62e6c86a7b8306b9586faadae8518d0bda91ef4dfe04c797dee22e82350cf9fce1527ea2df47fd815e719e16c46fae353783347ade5753b2d77bdb4809a2f839de6a40f8263c5ec6df3447752fcc2217734dc02590ce40a93323d26ffba614662111591efa338c449cdb2f552f70014bde332f06f1511cec75d7e335078802377d7d775f486089e154a61ca4b86afb3c137e09d1706637392401ec5eb4252d130e974f235e1ee4b2089ec997c343ab12895e731e469957d08637e0cabb919311ced2cc98f6dd72ffaaa214de944d0357e44176657daeb2ef2d70000800000010022000b03f4e82d95cc2f50f3afda79980356ca8ca9b83d47231c15d7571b4669f9e6770000000100320025000b0006007200000006008000100020dc24075e84cea2fbc7d5302b3719aff39412a4051cb55c44a69bacb03993cae9which you can breadk down as:
* magic: `badcc0de` // static const UINT32 MAGIC = 0xBADCC0DE;
* version: `00000001` // #define CONTEXT_VERSION 1
* hierarch: `40000001`
* savedHandle `80000000`
* sequence `0000000000000002`
* length (contextblob+metadata) `01e8` length 488
* context blob + metadata:
context struct
0000 // always 0
00000180 // size 384
0040112315c7b03d188040c4f4c550072c62547e5dfbf9ef83ccbaa59c40ce39a175b1d4dd0db1c034041401d3e8a6144f9fc720b4569b9bdeca5c47921d213608658931209745c4e7a610e3d65b8298056c6e251b7f357b0eaaf6505b64d6b8b0271979222e7b00f66bb55c9d1f6b075826bcd20d6da3c25d16280bda5bd74da4d83f9ade38f0e59cc1194e449a9a9349b8e195131c344adf57316c283ca6db9542a7222486a62e6c86a7b8306b9586faadae8518d0bda91ef4dfe04c797dee22e82350cf9fce1527ea2df47fd815e719e16c46fae353783347ade5753b2d77bdb4809a2f839de6a40f8263c5ec6df3447752fcc2217734dc02590ce40a93323d26ffba614662111591efa338c449cdb2f552f70014bde332f06f1511cec75d7e335078802377d7d775f486089e154a61ca4b86afb3c137e09d1706637392401ec5eb4252d130e974f235e1ee4b2089ec997c343ab12895e731e469957d08637e0cabb919311ced2cc98f6dd72ffaaa214de944d0357e44176657daeb2ef2d7 // contextblob of size 384
metadata
0000 /**< size of the operand buffer */
80000001 /**< Handle used by TPM */
0022 // length = 34
000b03f4e82d95cc2f50f3afda79980356ca8ca9b83d47231c15d7571b4669f9e677 /**< TPM name of the object */
00000001 /**< Selector for resource type */ #define IESYSC_KEY_RSRC 1 /**< Tag for key resource */'
0032 // len public
0025000b0006007200000006008000100020dc24075e84cea2fbc7d5302b3719aff39412a4051cb55c44a69bacb03993cae9 // public tpm2b
note the public key:
$ xxd -p -c 1000 key.pub
00320025000b0006007200000006008000100020dc24075e84cea2fbc7d5302b3719aff39412a4051cb55c44a69bacb03993cae910.13 Esys_ContextSave Commands
If the ESYS_TR object being saved refers to a session, the ESYS_TR object is invalidated.
This means that the ESYS_TR object cannot be used for any future operations and the variable can be
discarded.
Futhermore, the ESAPI implementation augments the data inside the saved context blob by the metadata
it requires for e.g. object names, if needed. This is done by augmenting the contents of context-
>contextBlob.buffer (and size). This data is used to restore the ESYS_TR object during ContextLoad.
NOTE: authorization values kept inside the ESYS_TR object metadata shall not be stored in the context
blobs.
The recommended implementation is:
typedef TPM2B_EVENT TSS2B_METADATA;
typedef struct {
UINT32 reserved; /* Must always be zero */
TPM2B_CONTEXT_DATA tpmContext;
TPM2B_METADATA esysMetadata;
} ESYS_CONTEXT_DATA;
if (e.g. type == RSA_KEY && type != HashSequence) {
ESYS_CONTEXT_DATA esyscontextData;
esyscontextData.reserved = 0;
esyscontextData.tpmContext.buffer = context->contextBlob.buffer;
esyscontextData.tpmContext.size = context->contextBlob.size;
esyscontextData.esysMetadata.buffer = metadata;
esyscontextData.esysMetadata.size = metadata_size;
context->contextBlob.buffer = esyscontextData;
context->contextBlob.size = sizeof(UINT32) +
sizeof(UINT16) + esyscontextData.tpmContext.size +
sizeof(UINT16) + esyscontextData.esysMetadata.size;
}
in tpm2_tools
/** Esys resource with size field
*/
typedef struct {
UINT16 size; /**< size of the operand buffer */
IESYS_RESOURCE data; /**< Esys resource data */
} IESYS_METADATA;
/** Type for representing TPM-Resource
*/
typedef struct {
TPM2_HANDLE handle; /**< Handle used by TPM */
TPM2B_NAME name; /**< TPM name of the object */
IESYSC_RESOURCE_TYPE rsrcType; /**< Selector for resource type */
IESYS_RSRC_UNION misc; /**< Resource specific information */
} IESYS_RESOURCE;
#define IESYSC_KEY_RSRC 1 /**< Tag for key resource */'