Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support importing HMAC keys and operations #249

Open
salrashid123 opened this issue Jun 7, 2021 · 1 comment
Open

Support importing HMAC keys and operations #249

salrashid123 opened this issue Jun 7, 2021 · 1 comment

Comments

@salrashid123
Copy link
Contributor

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
Copy link
Contributor Author

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))

}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant