google / google/go-tpm

Using TPM key ctx file created with TPM2 Tools to sign data

Open
#378 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
Go
Stars
673
Forks
188
PR merge metrics
No merged PRs in 30d

Description

Is it possible to use a primary key's ctx file (created with tpm2-tools) to sign some data using the ["github.com/google/go-tpm/tpm2" package](https://pkg.go.dev/github.com/google/go-tpm/tpm2). Im using tpm2 and am on an ubuntu linux VM.

I'm using these commands to create the key in the terminal. Im creating the key as a transient object, rather than saving it to a persistent handle.
```
echo -n -e "\x00\x01\x49\x44\x45\x56\x49\x44" > idevid.dat
tpm2_createprimary --hierarchy e --key-algorithm rsa2048 --attributes "fixedtpm|fixedparent|sensitivedataorigin|userwithauth|sign|noda" --key-context idevid.ctx --unique-data idevid.dat --output idevid.pub
```
I am able to sign data with idevid.ctx key created above with the following tpm2 tools commands
```
echo "my message" > message.dat
tpm2_sign --key-context idevid.ctx --hash-algorithm sha256 --signature sig.rssa message.dat
tpm2_verifysignature --key-context idevid.ctx --hash-algorithm sha256 --signature sig.rssa --message message.dat
```

Im making a connection to the TPM using the ["github.com/google/go-tpm/tpm2/transport" package](https://pkg.go.dev/github.com/google/go-tpm/tpm2/transport#OpenTPM)
```go
tpmDevice := "/dev/tpmrm0"
tpm, err := transport.OpenTPM(tpmDevice)
if err != nil {
fmt.Printf("Could not connect to TPM: %v", err)
}
```

Im currently having an issue trying to load the key handle of the idevid.ctx file into the KeyHandle field in the [Sign struct](https://pkg.go.dev/github.com/google/go-tpm/tpm2#Sign). The examples I have seen in the go-tpm repo seem to make the keys using go-tpm with functions like CreatePrimary and then pass the key handle returned by CreatePrimary into other functions like Sign. In my case I need to get the key handle object by loading an external idevid.ctx file. Below an example of code that im building off of to sign data.
```go
sign := Sign{
KeyHandle: NamedHandle{
Handle: handle,
Name: name,
},
Digest: TPM2BDigest{
Buffer: digest[:],
},
InScheme: TPMTSigScheme{
Scheme: TPMAlgRSASSA,
Details: NewTPMUSigScheme(
TPMAlgRSASSA,
&TPMSSchemeHash{
HashAlg: TPMAlgSHA256,
},
),
},
Validation: TPMTTKHashCheck{
Tag: TPMSTHashCheck,
},
}
rspSign, err := sign.Execute(tpm)
if err != nil {
fmt.Printf("Failed to Sign Digest: %v", err)
}
fmt.Println(rspSign)
```

I wrote the following code to decode the ctx file and load its contents into a [TPMSContext](https://pkg.go.dev/github.com/google/go-tpm/tpm2#TPMSContext) object. Using the tpm2-tools [tpm2_print](https://tpm2-tools.readthedocs.io/en/latest/man/tpm2_print.1/#options) command, Ive been able to confirm that the value of the savehandle, hierarchy, sequence and contextblob size are correct and that they are the correct type. I have not been able to verify if the value of contextblob is correct.
```go
var ctx TPMSContext
reader := bytes.NewReader(primaryCtx)
var extra uint64
if err := binary.Read(reader, binary.BigEndian, &extra); err != nil {
fmt.Printf("Failed to parse SavedHandle: %v", err)
}
if err := binary.Read(reader, binary.BigEndian, &ctx.Hierarchy); err != nil {
fmt.Printf("Failed to parse SavedHandle: %v", err)
}
if err := binary.Read(reader, binary.BigEndian, &ctx.SavedHandle); err != nil {
fmt.Printf("Failed to parse Hierarchy: %v", err)
}
if err := binary.Read(reader, binary.BigEndian, &ctx.Sequence); err != nil {
fmt.Printf("Failed to parse size: %v", err)
}
var size uint16
if err := binary.Read(reader, binary.BigEndian, &size); err != nil {
fmt.Printf("Failed to parse size: %v", err)
}
contextData := make([]byte, size)
if err := binary.Read(reader, binary.BigEndian, &contextData); err != nil {
fmt.Printf("Failed to read context data buffer: %v", err)
return
}
ctx.ContextBlob = TPM2BContextData{
Buffer: contextData,
}
```

I have tried directly loading the ctx.SavedHandle variable (which is type [TPMHandle](https://pkg.go.dev/github.com/google/go-tpm/tpm2#TPMHandle)) and I tried using the [KnownName](https://github.com/google/go-tpm/blob/v0.9.1/tpm2/structures.go#L124) function on it to get the name and then pass the handle and name value into the NamedHandle object.
```go
name := ctx.SavedHandle.KnownName()

digest := sha256.Sum256([]byte("migrationpains"))
sign := Sign{
KeyHandle: NamedHandle{
Handle: ctx.SavedHandle,
Name: *name,
},
Digest: TPM2BDigest{
Buffer: digest[:],
},
InScheme: TPMTSigScheme{
Scheme: TPMAlgRSASSA,
Details: NewTPMUSigScheme(
TPMAlgRSASSA,
&TPMSSchemeHash{
HashAlg: TPMAlgSHA256,
},
),
},
Validation: TPMTTKHashCheck{
Tag: TPMSTHashCheck,
},
}
rspSign, err := sign.Execute(tpm)
if err != nil {
fmt.Printf("Failed to Sign Digest: %v", err)
}
}
```
It seems that the value returned by ctx.SaveHandle.KnownName() is nil. So when I run the code above I get the following error
```
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x525ac8]
```

I have also tried to use the [ContextLoad](https://pkg.go.dev/github.com/google/go-tpm/tpm2#ContextLoad) function to get the key handle from the idevid.ctx file to pass into the Sign struct
```go
var ctx TPMSContext
reader := bytes.NewReader(primaryCtx)
var extra uint64
if err := binary.Read(reader, binary.BigEndian, &extra); err != nil {
fmt.Printf("Failed to parse SavedHandle: %v", err)
}
if err := binary.Read(reader, binary.BigEndian, &ctx.Hierarchy); err != nil {
fmt.Printf("Failed to parse SavedHandle: %v", err)
}
if err := binary.Read(reader, binary.BigEndian, &ctx.SavedHandle); err != nil {
fmt.Printf("Failed to parse Hierarchy: %v", err)
}
if err := binary.Read(reader, binary.BigEndian, &ctx.Sequence); err != nil {
fmt.Printf("Failed to parse size: %v", err)
}
var size uint16
if err := binary.Read(reader, binary.BigEndian, &size); err != nil {
fmt.Printf("Failed to parse size: %v", err)
}
contextData := make([]byte, size)
if err := binary.Read(reader, binary.BigEndian, &contextData); err != nil {
fmt.Printf("Failed to read context data buffer: %v", err)
return
}
ctx.ContextBlob = TPM2BContextData{
Buffer: contextData,
}
contextLoad := ContextLoad{
Context: ctx,
}

rspCL, err := contextLoad.Execute(tpm)
if err != nil {
fmt.Printf("ContextLoad failed: %v", err)
}
fmt.Println(rspCL)
}
```
When I run the code above to load the context I get the following error.
```
ContextLoad failed: TPM_RC_SIZE (parameter 1): structure is the wrong size
```

Is there a way that I can load the idevid.ctx file and use go-tpm to sign data?

Contributor guide

Open the contributing guide

Research direction

Start with the tpm2-tools ctx format and the TPMSContext parsing shown in the report, then inspect go-tpm's ContextLoad and Sign entry points. Reproduce the TPM_RC_SIZE error using /dev/tpmrm0 and determine whether the idevid.ctx key can be loaded and used for signing, or whether the limitation needs to be documented.

Written by the indexing model from the issue text.

Assessment

Tech stack
go, linux
Domain
cryptography, security
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.