Support importing HMAC keys and operations
salrashid123 opened this issue · 1 comments
salrashid123 commented
FR to support
- importing external HMAC key
- hmac some data with it
to quote offline thread
go-tpm does not support tpm2_hmac_start
yet, but it does support hash sequences (so the only command that needs to be added is tpm2_hmac_start).
With tpm2_hmac_start in hand, you could import a key and use it for hmacs through the tpm2_hmac_start/tpm2_sequenceupdate/tpm2_sequencecomplete
flows. Implementing this should be pretty similar to this PR.
tpm2_tools
currently does not support importing hmac but does support ::
salrashid123 commented
maybe something like this
tpm2/tpm2.go
:
// CmdHmacStart tpmutil.Command = 0x0000015B
func HmacStart(rw io.ReadWriter, sequenceAuth string, handle tpmutil.Handle, hashAlg Algorithm) (seqHandle tpmutil.Handle, err error) {
auth, err := encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(sequenceAuth)})
if err != nil {
return 0, err
}
out, err := tpmutil.Pack(handle)
if err != nil {
return 0, err
}
Cmd, err := concat(out, auth)
if err != nil {
return 0, err
}
resp, err := runCommand(rw, TagSessions, CmdHmacStart, tpmutil.RawBytes(Cmd), tpmutil.U16Bytes(sequenceAuth), hashAlg)
if err != nil {
return 0, err
}
var rhandle tpmutil.Handle
_, err = tpmutil.Unpack(resp, &rhandle)
return rhandle, err
}
then load an hmac key (the following loads and signs one
package main
import (
"crypto"
"crypto/rand"
"encoding/hex"
"flag"
"fmt"
"io"
"os"
"github.com/google/go-tpm-tools/client"
"github.com/google/go-tpm/tpm2"
"github.com/google/go-tpm/tpmutil"
)
const (
emptyPassword = ""
)
var (
tpmPath = flag.String("tpm-path", "/dev/tpm0", "Path to the TPM device (character device or a Unix socket).")
handleNames = map[string][]tpm2.HandleType{
"all": []tpm2.HandleType{tpm2.HandleTypeLoadedSession, tpm2.HandleTypeSavedSession, tpm2.HandleTypeTransient},
"loaded": []tpm2.HandleType{tpm2.HandleTypeLoadedSession},
"saved": []tpm2.HandleType{tpm2.HandleTypeSavedSession},
"transient": []tpm2.HandleType{tpm2.HandleTypeTransient},
}
defaultKeyParams = tpm2.Public{
Type: tpm2.AlgRSA,
NameAlg: tpm2.AlgSHA256,
Attributes: tpm2.FlagDecrypt | tpm2.FlagRestricted | tpm2.FlagFixedTPM |
tpm2.FlagFixedParent | tpm2.FlagSensitiveDataOrigin | tpm2.FlagUserWithAuth,
AuthPolicy: []byte{},
RSAParameters: &tpm2.RSAParams{
Symmetric: &tpm2.SymScheme{
Alg: tpm2.AlgAES,
KeyBits: 128,
Mode: tpm2.AlgCFB,
},
KeyBits: 2048,
},
}
secret = []byte("change this password to a secret")
data = []byte("foo")
)
func main() {
flag.Parse()
rwc, err := tpm2.OpenTPM(*tpmPath)
if err != nil {
fmt.Fprintf(os.Stderr, "Can't open TPM %s: %v", *tpmPath, err)
os.Exit(1)
}
defer func() {
if err := rwc.Close(); err != nil {
fmt.Fprintf(os.Stderr, "can't close TPM %s: %v", *tpmPath, err)
os.Exit(1)
}
}()
totalHandles := 0
for _, handleType := range handleNames["all"] {
handles, err := client.Handles(rwc, handleType)
if err != nil {
fmt.Fprintf(os.Stderr, "error getting handles", *tpmPath, err)
os.Exit(1)
}
for _, handle := range handles {
if err = tpm2.FlushContext(rwc, handle); err != nil {
fmt.Fprintf(os.Stderr, "Error flushing handle 0x%x: %v\n", handle, err)
os.Exit(1)
}
fmt.Printf("Handle 0x%x flushed\n", handle)
totalHandles++
}
}
pcrList := []int{}
pcrSelection := tpm2.PCRSelection{Hash: tpm2.AlgSHA256, PCRs: pcrList}
pkh, _, err := tpm2.CreatePrimary(rwc, tpm2.HandleOwner, pcrSelection, emptyPassword, emptyPassword, defaultKeyParams)
if err != nil {
fmt.Fprintf(os.Stderr, "Error creating Primary %v\n", err)
os.Exit(1)
}
defer tpm2.FlushContext(rwc, pkh)
inBuff := []byte(secret)
// Create a private area containing the input
private := tpm2.Private{
Type: tpm2.AlgKeyedHash,
AuthValue: nil,
SeedValue: make([]byte, 32),
Sensitive: inBuff,
}
io.ReadFull(rand.Reader, private.SeedValue)
privArea, err := private.Encode()
if err != nil {
fmt.Fprintf(os.Stderr, "Error encoding private %v\n", err)
os.Exit(1)
}
duplicate, err := tpmutil.Pack(tpmutil.U16Bytes(privArea))
if err != nil {
fmt.Fprintf(os.Stderr, "Error encoding dulicate %v\n", err)
os.Exit(1)
}
privHash := crypto.SHA256.New()
privHash.Write(private.SeedValue)
privHash.Write(private.Sensitive)
public := tpm2.Public{
Type: tpm2.AlgKeyedHash,
NameAlg: tpm2.AlgSHA256,
// Attributes: tpm2.FlagFixedTPM | tpm2.FlagFixedParent | tpm2.FlagSensitiveDataOrigin | tpm2.FlagUserWithAuth | tpm2.FlagSign, // gives "parameter 2, error code 0x2 : inconsistent attributes"
Attributes: tpm2.FlagSensitiveDataOrigin | tpm2.FlagUserWithAuth | tpm2.FlagSign, // works
KeyedHashParameters: &tpm2.KeyedHashParams{
Alg: tpm2.AlgHMAC,
Hash: tpm2.AlgSHA256,
Unique: privHash.Sum(nil),
},
}
pubArea, err := public.Encode()
if err != nil {
fmt.Fprintf(os.Stderr, "Error encoding public %v\n", err)
os.Exit(1)
}
emptyAuth := tpm2.AuthCommand{Session: tpm2.HandlePasswordSession, Attributes: tpm2.AttrContinueSession}
privInternal, err := tpm2.Import(rwc, pkh, emptyAuth, pubArea, duplicate, nil, nil, nil)
if err != nil {
fmt.Fprintf(os.Stderr, "Error Importing hash key %v\n", err)
os.Exit(1)
}
newHandle, _, err := tpm2.Load(rwc, pkh, "", pubArea, privInternal)
if err != nil {
fmt.Fprintf(os.Stderr, "Error loading hash key %v\n", err)
os.Exit(1)
}
defer tpm2.FlushContext(rwc, newHandle)
// pHandle := tpmutil.Handle(0x81010002)
// err = tpm2.EvictControl(rwc, emptyPassword, tpm2.HandleOwner, newHandle, pHandle)
// if err != nil {
// fmt.Fprintf(os.Stderr,"Error persisting hash key %v\n", err)
// os.Exit(1)
// }
// defer tpm2.FlushContext(rwc, pHandle)
maxDigestBuffer := 1024
seqAuth := ""
seq, err := tpm2.HmacStart(rwc, seqAuth, newHandle, tpm2.AlgSHA256)
if err != nil {
fmt.Fprintf(os.Stderr, "Error starting hash sequence %v\n", err)
os.Exit(1)
}
defer tpm2.FlushContext(rwc, seq)
for len(data) > maxDigestBuffer {
if err = tpm2.SequenceUpdate(rwc, seqAuth, seq, data[:maxDigestBuffer]); err != nil {
fmt.Fprintf(os.Stderr, "Error updating hash sequence %v\n", err)
os.Exit(1)
}
data = data[maxDigestBuffer:]
}
digest, _, err := tpm2.SequenceComplete(rwc, seqAuth, seq, tpm2.HandleNull, data)
if err != nil {
fmt.Fprintf(os.Stderr, "Error completing hash sequence %v\n", err)
os.Exit(1)
}
fmt.Printf("digest %s\n", hex.EncodeToString(digest))
}