Giter Club home page Giter Club logo

go-tpm's Introduction

Go-TPM

Go-TPM is a Go library that communicates directly with a TPM device on Linux or Windows machines.

The libraries don't implement the entire spec for neither 1.2 nor 2.0. If you need a command that's missing, contributions are welcome!

Please note that this is not an official Google product.

Structure

The tpm directory contains TPM 1.2 client library. This library is in "maintenance mode".

The tpm2 directory contains TPM 2.0 client library.

The examples directory contains some simple examples for both versions of the spec.

The direct directory contains the prototype "TPMDirect" TPM 2.0 API, which is intended to (eventually) be 1:1 with the TPM 2.0 spec. Please report issues, complaints, or suggestions using the label tpmdirect Issues specific to the tpmdirect development work .

TPM 1.2

TPM 1.2 support currently has no maintainer. None of the TPM 2.0 maintainers have expertise on 1.2 either.

As such, TPM 1.2 library is in "maintenance" mode - all PRs with new functionality or non-critical fixes will be rejected.

If you'd like to volunteer to maintain the TPM 1.2 library, you can do so via an issue. You don't have to be a Googler to volunteer.

go-tpm's People

Contributors

akakou avatar alexmwu avatar bendhillier avatar brandonweeks avatar chrimarme avatar chrisfenner avatar deniskarch avatar elmostafaidrassi avatar ericchiang avatar flanfly avatar folbricht avatar foxboron avatar jkl73 avatar josephlr avatar lsiudut avatar matt-tsai avatar nathaniellehrer avatar nckrss avatar neeki-hushyar avatar pkern avatar quinnmagendanz avatar rautammi avatar rjoleary avatar rthellend avatar sambdavidson avatar shubhodeep9 avatar testwill avatar tmroeder avatar twitchy-jsonp avatar zaolin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

go-tpm's Issues

Question: Hiding or exporting structures

What is the policy for hiding or exporting structures?

In certain aspects im unsure if/when I'm allowed to export. Need some clarification here.

For example: tpm/GetPubKey returns a slice of bytes made from the pubkey structure.
To use this information externally I have to make a struct again myself and parse it again.
So we parse data back and forth?! Or is this a question of clear abstraction between TDD and TSS?

For clarification:
The question came up, because we want to use go-tpm in a low memory environment, so building a huge framework around, like TCG TSS spec suggests, is way out of our scope.

Thankful for input here :)

tpmutil does not compile with GOARCH=arm64

The error is undefined: syscall.SYS_POLL.

arm64 does not have syscall.SYS_POLL. Instead, the syscall.SYS_PPOLL syscall should be used. The functionality of the ppoll syscall is a superset of the poll syscall, so this should be relatively straightforward to fix.

tpm2: Sign with hash ticket

The TPM2_Sign command includes a validation hashcheck ticket parameter, but this isn't exposed via the tpm2.Sign function API. This is relevant to restricted keys (i.e., tpm2.FlagRestricted, which is included in tpm2.FlagSignerDefault), because they need to make sure the hashed data did not start with TPM_GENERATED_VALUE.

tpm2.Public cannot create default ECC Template

In go-tpm-tools, we want to be able to generate the default TPM2 EK/SRK for ECC like we are currently doing for RSA.

However, the TCG Credential Profile EK says the the buffers representing X, Y for ECC curves must be 16 bytes of all zeros each. This doesn't work for the current tpm2.Public as that's equivalent to the number 0, resulting in an incorrect encoding.

This is arguably a bug by the TCG (and they fixed it for later templates, see the templates in B.4 of the above linked doc). However, this "bug" is standardized so we need to support it. We solved a similar issue for RSAParams by using ModulusRaw, so we probably also need XRaw and YRaw parameters for ECPoint.

Create RSA with both Sign and Encrypt/Decrypt

Hey folks

I am trying to create an RSA key can both sign and encrypt/decrypt under SRK. However when I add both sign and decrypt like this:

defaultKeyParams = tpm2.Public{
		Type:       tpm2.AlgRSA,
		NameAlg:    tpm2.AlgSHA256,
		Attributes:  tpm2.FlagDecrypt| tpm2.FlagSign |  tpm2.FlagFixedTPM |
			tpm2.FlagFixedParent |  tpm2.FlagSensitiveDataOrigin |  tpm2.FlagUserWithAuth,
		RSAParameters: &tpm2.RSAParams{
			Sign: &tpm2.SigScheme{
				Alg:  tpm2.AlgRSASSA,
				Hash: tpm2.AlgSHA256,
			},
			KeyBits: 2048,
		},
	}

it fails saying parameter 2, error code 0x12 : unsupported or incompatible scheme
When I only use either FlagDecrypt or FlagSign it worked. Is there any constraint on creating key that an RSA key cannot both sign and encrypt?\

I think it is doable since I saw this:
https://github.com/tpm2-software/tpm2-tss-engine/blob/89327fa8b51962348c46ddc659fb8c3636336a60/test/rsasign_importtpm.sh#L21-L25

Thank you very much!

TPM2: DecodeSignature fails to decode CertifyCreation signature by an ECC key

I've added a test for this particular case, as I found TestCreateAndCertifyCreation only deals with RSA keys.

As described in the commit, the test fails because the signature does not seem to be valid, mainly because of its small length (32 bytes during my tests).
It should normally consist of both S and R which both need to be, in this test case, 32-bytes long.

This was tested using the simulator and a real TPM 2.0 device, under Linux.

I'm providing the test case here as well :

func TestCreateAndCertifyCreationECC(t *testing.T) {
	rw := openTPM(t)
	defer rw.Close()
	params := tpm2.Public{
		Type:       tpm2.AlgECC,
		NameAlg:    tpm2.AlgSHA256,
		Attributes: tpm2.FlagSignerDefault,
		ECCParameters: &tpm2.ECCParams{
			Sign: &tpm2.SigScheme{
				Alg:  tpm2.AlgECDSA,
				Hash: tpm2.AlgSHA256,
			},
			CurveID: tpm2.CurveNISTP256,
		},
	}
	keyHandle, pub, _, creationHash, tix, _, err := CreatePrimaryEx(rw, HandleEndorsement, pcrSelection7, emptyPassword, emptyPassword, params)
	if err != nil {
		t.Fatalf("CreatePrimaryEx failed: %s", err)
	}
	defer FlushContext(rw, keyHandle)

	scheme := SigScheme{Alg: AlgECDSA, Hash: AlgSHA256, Count: 0}
	attestation, signature, err := CertifyCreation(rw, emptyPassword, keyHandle, keyHandle, nil, creationHash, scheme, tix)
	if err != nil {
		t.Fatalf("CertifyCreation failed: %s", err)
	}
	att, err := DecodeAttestationData(attestation)
	if err != nil {
		t.Fatalf("DecodeAttestationData(%v) failed: %v", attestation, err)
	}
	if att.Type != TagAttestCreation {
		t.Errorf("Got att.Type = %v, want TagAttestCreation", att.Type)
	}
	p, err := DecodePublic(pub)
	if err != nil {
		t.Fatalf("DecodePublic failed: %v", err)
	}
	match, err := att.AttestedCreationInfo.Name.MatchesPublic(p)
	if err != nil {
		t.Fatalf("MatchesPublic failed: %v", err)
	}
	if !match {
		t.Error("Attested name does not match returned public key.")
		t.Logf("Name: %v", att.AttestedCreationInfo.Name)
		t.Logf("Public: %v", p)
	}

	sig, err := DecodeSignature(bytes.NewBuffer(signature))
	if err != nil {
		t.Fatalf("DecodeSignature failed: %v", err)
	}

	var pkEcdsa ecdsa.PublicKey
	var hsh hash.Hash
	pkEcdsa = ecdsa.PublicKey{Curve: elliptic.P256(), X: p.ECCParameters.Point.X(), Y: p.ECCParameters.Point.Y()}
	signHash, err := p.ECCParameters.Sign.Hash.Hash()
	if err != nil {
		t.Fatalf("Hash failed: %v", err)
	}
	hsh = signHash.New()
	hsh.Write(attestation)

	if !ecdsa.Verify(&pkEcdsa, hsh.Sum(nil), sig.ECC.R, sig.ECC.S) {
		t.Fatalf("Verify failed: %v", err)
	}
}

Tests fail on curent master

Right now, running go test -p 1 ./... results in the following failure (on go 1.16.2):

--- FAIL: TestEncodingPackTypeInvalid (0.00s)
    encoding_test.go:54: The packing function incorrectly succeeds for a non-packable value

This looks to be caused by ioutil.Discard being a valid value for packing. As of Go 1.16, this value was changed to be io.Discard which is just an empty struct (which is a valid value for packing).

Mark go.mod as requiring at least 1.13

[jsonp@redacted]:~/go-attestation> go get -u github.com/google/go-tpm/tpm2@latest
go: finding github.com/google/go-tpm/tpm2 latest
go: finding github.com/spf13/cobra v1.0.0
go: finding github.com/stretchr/testify v1.5.1
go: finding github.com/golang/protobuf v1.4.1
go: finding github.com/spf13/viper v1.6.3
go: finding github.com/google/go-tpm-tools v0.1.2
go: finding github.com/fsnotify/fsnotify v1.4.9
go: finding github.com/cpuguy83/go-md2man/v2 v2.0.0
go: finding github.com/pelletier/go-toml v1.7.0
go: finding github.com/armon/consul-api latest
go: finding gopkg.in/yaml.v2 v2.2.8
go: finding github.com/coreos/go-systemd latest
go: finding golang.org/x/crypto latest
go: finding gopkg.in/check.v1 latest
go: finding github.com/tmc/grpc-websocket-proxy latest
go: finding golang.org/x/sys latest
go: finding github.com/coreos/etcd v3.3.20+incompatible
go: finding github.com/russross/blackfriday/v2 v2.0.1
go: finding github.com/coreos/bbolt v1.3.4
go: github.com/coreos/[email protected]: go.mod has post-v0 module path "github.com/coreos/go-systemd/v22" at revision cb8b64719ae3
go: finding golang.org/x/net latest
go: finding github.com/shurcooL/sanitized_anchor_name v1.0.0
go: finding github.com/json-iterator/go v1.1.9
go: finding github.com/coreos/etcd v3.3.13+incompatible
go: finding google.golang.org/protobuf v1.22.0
go: github.com/coreos/[email protected]: parsing go.mod: unexpected module path "go.etcd.io/bbolt"
go get: error loading module requirements

I'm a little confuzzled, but I'll see what I can work out. If anyone has any ideas let me know.

poll timeout setting incorrect

The timeout value set here looks to be incorrect
https://github.com/google/go-tpm/blob/master/tpmutil/poll_unix.go#L19

instead of 0, it should read -1 or something negative to block indefinately (no timeout)?
http://man7.org/linux/man-pages/man2/poll.2.html


i'm writing a crypto.Signer for go-tpm and seeing tpm2.OpenTPM() fail very often which i ultimately traced back to the follwoing error in poll_unix.go:
```unexpected poll Revents ``

the fix was to set thetimeout l to -1

Add TPM2 unique field to Public type

The unique field in the Public template is used by TPM2_CreatePrimary in generating the Name and therefore indirectly incorporated into the RNG seed affecting the key generation. Without this, it is not possible to generate a unique primary, both in key and name, for any given algorithm. While the inSensitive->data is feed into RNG seed and thus affecting the key generation, it is not included in generating the name.

Make go-tpm a Go module

Now that go-tpm gets more dependents, it would be good to start versioning to enable them to vendor reliably.
As a bonus, we can start cleaning up the API from legacy cruft by bumping the major/minor versions.

tpm2/credactivation: Generate uses the wrong hash algorithm

credactivation.Generate uses the AK's NameAlg in a bunch of places, but it should be using the EK's.

I've experimentally verified that this causes problems using tpm2-tools's tpm2_activatecredential tool with an AK that uses SHA1 as its name algorithm.

Incidentally, I think having the API require the user to provide symBlockSize is unnecessarily awkward. Instead, just have them provide the EK as a tpm2.Public, and then you can pick out the NameAlg and symmetric cipher specs automatically.

Finally, I was interested in supporting ECC endorsement keys, so I implemented KDFe and support for generating an ECC seed.

Code is still hacky and proof-of-concept-y, but I've experimentally verified that it works with {RSA2048, ECC-P256} x {SHA1, SHA256} EKs on my workstation's TPM2. I plan to eventually try it against Microsoft's TPM simulator so I can test larger RSA/ECC keys.

func Protect(ek *tpm2.Public, obj *tpm2.HashValue, blob []byte, random io.Reader) ([]byte, []byte, error) {
	cv, err := tpmutil.Pack(tpmutil.U16Bytes(blob))
	if err != nil {
		return nil, nil, fmt.Errorf("generating cv (TPM2B_Digest): %v", err)
	}

	objName, err := obj.Encode()
	if err != nil {
		return nil, nil, fmt.Errorf("encoding objName: %v", err)
	}

	ekNameAlg := ek.NameAlg
	ekNameAlgHash, err := ekNameAlg.HashConstructor()
	if err != nil {
		return nil, nil, err
	}

	ekKey, err := ek.Key()
	if err != nil {
		return nil, nil, err
	}

	var seed, encSecret []byte
	var sym *tpm2.SymScheme
	switch ekKey := ekKey.(type) {
	case *rsa.PublicKey:
		sym = ek.RSAParameters.Symmetric
		seed, encSecret, err = generateRSA(ekNameAlgHash(), ekKey, random)
	case *ecdsa.PublicKey:
		sym = ek.ECCParameters.Symmetric
		seed, encSecret, err = generateECC(ekNameAlg, ekKey, random)
	default:
		err = errors.New("only RSA public keys are supported for credential activation")
	}
	if err != nil {
		return nil, nil, err
	}

	// TODO(mdempsky): Support additional algorithms/modes?
	if sym.Alg != tpm2.AlgAES {
		return nil, nil, fmt.Errorf("unsupported symmetric algorithm %v", sym.Alg)
	}
	if sym.Mode != tpm2.AlgCFB {
		return nil, nil, fmt.Errorf("unsupported symmetric mode %v", sym.Mode)
	}

	encKey, err := tpm2.KDFa(ekNameAlg, seed, labelStorage, objName, nil, int(sym.KeyBits))
	if err != nil {
		return nil, nil, fmt.Errorf("generating symmetric key: %v", err)
	}
	macKey, err := tpm2.KDFa(ekNameAlg, seed, labelIntegrity, nil, nil, ekNameAlgHash().Size()*8)
	if err != nil {
		return nil, nil, fmt.Errorf("generating HMAC key: %v", err)
	}

	c, err := aes.NewCipher(encKey)
	if err != nil {
		return nil, nil, fmt.Errorf("symmetric cipher setup: %v", err)
	}

	// IV is all null bytes. encIdentity represents the encrypted credential.
	encIdentity := make([]byte, len(cv))
	cipher.NewCFBEncrypter(c, make([]byte, len(encKey))).XORKeyStream(encIdentity, cv)

	mac := hmac.New(ekNameAlgHash, macKey)
	mac.Write(encIdentity)
	mac.Write(objName)
	integrityHMAC := mac.Sum(nil)

	idObject := &tpm2.IDObject{
		IntegrityHMAC: integrityHMAC,
		EncIdentity:   encIdentity,
	}
	id, err := tpmutil.Pack(idObject)
	if err != nil {
		return nil, nil, fmt.Errorf("encoding IDObject: %v", err)
	}

	packedID, err := tpmutil.Pack(tpmutil.U16Bytes(id))
	if err != nil {
		return nil, nil, fmt.Errorf("packing id: %v", err)
	}
	packedEncSecret, err := tpmutil.Pack(tpmutil.U16Bytes(encSecret))
	if err != nil {
		return nil, nil, fmt.Errorf("packing encSecret: %v", err)
	}

	return packedID, packedEncSecret, nil
}

func generateRSA(ekNameAlgHash hash.Hash, pub *rsa.PublicKey, random io.Reader) ([]byte, []byte, error) {
	// The seed length should match the keysize used by the EKs symmetric cipher.
	// For typical RSA EKs, this will be 128 bits (16 bytes).
	// Spec: TCG 2.0 EK Credential Profile revision 14, section 2.1.5.1.

	// TODO(mdempsky): The spec suggests the seed size should
	// match the hash function instead: "The seed size will be the
	// size of a digest produced by the OAEP hash algorithm of the
	// new parent." TPM 2.0, Part 1, Section B.10.3.
	//
	// Experimentally, any size seed seems to be accepted (even
	// empty!!).

	seed := make([]byte, 16)
	if _, err := io.ReadFull(random, seed); err != nil {
		return nil, nil, fmt.Errorf("generating seed: %v", err)
	}

	encSecret, err := rsa.EncryptOAEP(ekNameAlgHash, random, pub, seed, []byte(labelIdentity+"\x00"))
	if err != nil {
		return nil, nil, fmt.Errorf("generating encrypted seed: %v", err)
	}

	return seed, encSecret, err
}

func generateECC(ekNameAlg tpm2.Algorithm, pub *ecdsa.PublicKey, random io.Reader) ([]byte, []byte, error) {
	N := (pub.Curve.Params().BitSize + 7) / 8

	// TODO(mdempsky): Generalize.
	bits := 256
	if ekNameAlg == tpm2.AlgSHA1 {
		bits = 160
	}

	d, x, y, err := elliptic.GenerateKey(pub.Curve, random)
	if err != nil {
		return nil, nil, err
	}

	var encSecret []byte
	encSecret = append(encSecret, be16(uint16(N))...)
	encSecret = append(encSecret, padEC(x, N)...)
	encSecret = append(encSecret, be16(uint16(N))...)
	encSecret = append(encSecret, padEC(y, N)...)

	px, _ := pub.Curve.ScalarMult(pub.X, pub.Y, d)

	seed, err := tpm2.KDFe(ekNameAlg, padEC(px, N), labelIdentity, padEC(x, N), padEC(pub.X, N), bits)
	if err != nil {
		return nil, nil, err
	}

	return seed, encSecret, err
}

// padEC pads ECC coordinates according to TPM2.0, Part 1, C.8 "ECC Point Padding".
func padEC(x *big.Int, n int) []byte {
	b := x.Bytes()
	if len(b) >= n {
		return b
	}
	pad := make([]byte, n - len(b))
	return append(pad, b...)
}

In tpm2/kdf.go:

func KDFe(hashAlg Algorithm, Z []byte, label string, partyU, partyV []byte, bits int) ([]byte, error) {
	var h hash.Hash
	switch hashAlg {
	case AlgSHA1:
		h = sha1.New()
	case AlgSHA256:
		h = sha256.New()
	default:
		return nil, fmt.Errorf("hash algorithm 0x%x is not supported", hashAlg)
	}

	var out []byte
	var counter uint32
	for 8*len(out) < {
		counter++
		if err := binary.Write(h, binary.BigEndian, counter); err != nil {
			return nil, fmt.Errorf("pack counter: %v", err)
		}
		h.Write(Z)
		h.Write([]byte(label))
		h.Write([]byte{0}) // Terminating null character for C-string.
		h.Write(partyU)
		h.Write(partyV)

		out = h.Sum(out)
		h.Reset()
	}

	if partial := bits%8; partial != 0 {
		out[0] &= (1 << uint(partial)) - 1
	}
	return out[:(bits+7)/8], nil
}

Storing keys (private, public) and compatibility with tpm2-tss / tpm2-tools

I'm trying to stay compatible between tpm2-tss (resp. tpm2-tools) and my Golang implementation using go-tpm.

For example, I would like to create a key with go-tpm, and use it with tpm2-tools. I created a key with tpm2.CreateKey(), stored the resulting byte arrays in files, but using tpm2_load -u .. -r .. resulted in an error:

ERROR:esys:src/tss2-esys/api/Esys_Load.c:189:Esys_Load_Async() SAPI Prepare returned error. ErrorCode (0x0009000b) 
ERROR:esys:src/tss2-esys/api/Esys_Load.c:89:Esys_Load() Error in async function ErrorCode (0x0009000b) 
ERROR: Eys_Load(0x9000B) - mu:A parameter has a bad value
ERROR: Unable to run tpm2_load

As these files are two bytes smaller than those created with tpm2_create, I have the impression there are missing some prepending bytes in the blobs, giving the size or something. E.g. the public portion begins with:

0001 000b 0003 0432 0000 0006 0080 0043
0010 0800 0000 0000 0100 b7f7 2651 d736

Whereas another tpm2-tools generated public key begins with:

0116 0001 000b 0002 0032 0000 0010 0010
0800 0000 0000 0100 b093 8adc 6bae 05f9

What's the best practice for storing the key blobs, while staying compatible with tpm2-tools?

Add API to open TCP based TPM device

https://github.com/stefanberger/swtpm/ is a TPM software emulator. One of the modes it provides is a TCP communication channel. The advantage of using a TCP connection is that it does not require sudo permissions to create a new emulated device, and it is something important for unit testsing.

Some tools (e.g. IBM TSS) allow to use such socket connection by setting special envvar, see https://github.com/stefanberger/swtpm/wiki/Using-the-IBM-TSS-with-swtpm for example.

It might be handy if go-tpm in addition to API that deals with filesystem paths (tpm2.OpenTPM(filepath)) also had an ability to open a TCP-based communication channel.

ReadPCRs returns empty map without error when using SHA256

Hi Team

I was using tpm2.ReadPCRs on a hardware tpm with the following PCRSelection:
{11 [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23]}
This return an empty map without an error:
read PCRs: map[]
However when I using SHA1
{4 [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23]}
it works.

Can someone please help with some suggestions? Thanks!

I am using centos7 on Dell R640

TPM2 Quote takes an Algorithm where it should take a Scheme

func Quote(rw io.ReadWriter, signingHandle tpmutil.Handle, parentPassword, ownerPassword string, toQuote []byte, sel PCRSelection, sigAlg Algorithm) ([]byte, *Signature, error) {

The last parameter, sigAlg, is an Algorithm which corresponds to TPM_ALG in the spec, but it should be some type that corresponds to TPMT_SIG_SCHEME in the spec. As written, the only parameter that can be passed to Quote successfully is AlgNull (meaning you have to set the scheme on the AK when you create it).

This is what the test does:

go-tpm/tpm2/test/tpm2_test.go

Lines 1258 to 1279 in ac5b427

func TestQuote(t *testing.T) {
rw := openTPM(t)
defer rw.Close()
params := Public{
Type: AlgRSA,
NameAlg: AlgSHA256,
Attributes: FlagSignerDefault | FlagNoDA,
RSAParameters: &RSAParams{
Sign: &SigScheme{
Alg: AlgRSASSA,
Hash: AlgSHA256,
},
KeyBits: 2048,
},
}
keyHandle, pub, _, _, _, _, err := CreatePrimaryEx(rw, HandleEndorsement, pcrSelection7, emptyPassword, emptyPassword, params)
if err != nil {
t.Fatalf("CreatePrimaryEx failed: %s", err)
}
defer FlushContext(rw, keyHandle)
attestation, signature, err := Quote(rw, keyHandle, emptyPassword, emptyPassword, nil, pcrSelection7, AlgNull)

TakeOwnership in go-tpm/tpm2

Is there a go-tpm/tpm2 alternative to the TakeOwnership command from tpmv1.2?

It looks like the command code for TakeOwnership doesn't exist here and
tpm2-tools lib uses a combination of tpm2_clear and tpm2_changeauth to achieve the same functionality.

However, I don't see the command codes for either of those in go-tpm/tpm2/constants.go - are there some other go-tpm functions I'm overlooking?

TPM12: Does not read NVList in locked TPM

For some time now, I work with my locked tpm module and just realized, that it won't do some tests.
If I try to use GetNVList, the returned List is empty, but i know by using TPM_Tools that i have at least 4 indices set, which should be returned.

This is really unsettling. I still try to find out why this happens.

TPM2: Certify makes use of hardcoded signature scheme

Hello everyone,

The implementation of Certify does not give the caller the ability to set the signature scheme but instead, it calls encodeCertify which sets it to the hardcoded value AlgRSASSA, AlgSHA256.

This works if the signing key has a signature scheme set to exactly AlgRSASSA, AlgSHA256, but will fail if it is anything else (i.e.
AlgRSASSA, AlgSHA1, AlgECDSA, AlgSHA256...)

Here is a quick test that showcases this failure (it's the same as TestCertify, I just changed AlgSHA256 to AlgSHA1)

func TestCertifySHA1(t *testing.T) {
	rw := openTPM(t)
	defer rw.Close()

	params := Public{
		Type:       AlgRSA,
		NameAlg:    AlgSHA256,
		Attributes: FlagSignerDefault,
		RSAParameters: &RSAParams{
			Sign: &SigScheme{
				Alg:  AlgRSASSA,
				Hash: AlgSHA1,
			},
			KeyBits: 2048,
		},
	}
	signerHandle, signerPub, err := CreatePrimary(rw, HandleOwner, pcrSelection7, emptyPassword, defaultPassword, params)
	if err != nil {
		t.Fatalf("CreatePrimary(signer) failed: %s", err)
	}
	defer FlushContext(rw, signerHandle)

	subjectHandle, subjectPub, err := CreatePrimary(rw, HandlePlatform, pcrSelection7, emptyPassword, defaultPassword, params)
	if err != nil {
		t.Fatalf("CreatePrimary(subject) failed: %s", err)
	}
	defer FlushContext(rw, subjectHandle)

	attest, sig, err := Certify(rw, defaultPassword, defaultPassword, subjectHandle, signerHandle, nil)
	if err != nil {
		t.Errorf("Certify failed: %s", err)
		return
	}

	attestHash := sha256.Sum256(attest)
	if err := rsa.VerifyPKCS1v15(signerPub.(*rsa.PublicKey), crypto.SHA256, attestHash[:], sig); err != nil {
		t.Errorf("Signature verification failed: %v", err)
	}

	t.Run("DecodeAttestationData", func(t *testing.T) {
		ad, err := DecodeAttestationData(attest)
		if err != nil {
			t.Fatal("DecodeAttestationData:", err)
		}
		params := Public{
			Type:       AlgRSA,
			NameAlg:    AlgSHA256,
			Attributes: FlagSignerDefault,
			RSAParameters: &RSAParams{
				Sign: &SigScheme{
					Alg:  AlgRSASSA,
					Hash: AlgSHA1,
				},
				KeyBits: 2048,
				// Note: we don't include Exponent because CreatePrimary also
				// returns Public without it.
				ModulusRaw: subjectPub.(*rsa.PublicKey).N.Bytes(),
			},
		}
		matches, err := ad.AttestedCertifyInfo.Name.MatchesPublic(params)
		if err != nil {
			t.Fatalf("AttestedCertifyInfo.Name.MatchesPublic error: %v", err)
		}
		if !matches {
			t.Error("Name in AttestationData doesn't match Public structure of subject")
		}
	})
}

The test fails with the error parameter 2, error code 0x12 : unsupported or incompatible scheme

Can we modify Certify so that it takes the sigScheme as an argument and gives the caller the ability to set it accordingly rather than harcode it to AlgRSASSA, AlgSHA256 in encodeCertify ? I know this would break the API, but I can't think of anything better. I tried setting the hash alg of the sig scheme to AlgNull in vain, I kept getting parameter 2, error code 0x3 : hash algorithm not supported or not appropriate.

Cheers,

Getting error 0x12 with Quote

HI all,

Trying to correctly understand how Quote works. Infineon SLB9760 TPM 2.0, Raspberry Pi 3B+, Raspbian. Works fine with tpm2tools, AK loaded in 0x81010002

$ tpm2_getcap handles-persistent
- 0x81010001
- 0x81010002
$ tpm2_quote -c 0x81010002 -l sha256:0 -g sha256
quoted: ff54434780180022000ba45ba8b633e5da83b786edd2961c91817f64046ab2ef2e2b8f648973c924532a00000000000045568466d86d4057e767ba7101b6f4fbe4535c1b7600000001000b030100000020159ce676d07312874e98ef541bfb9e576929e8463313abb60cfe75d875d46ba6
signature:
  alg: rsassa
  sig: 4e57a34202d672765c8ce536da64f2d8c08052de6635ba4e81430b4c0d5715ff74c9c16c997142f1237090675c712664109fc0c80e3ea2cc7a0cfbd6692dbe4b3dcfad6ff6e7fd16f7917882254a0fd92efc246b80793490a686b045b8f9e563faecf53eca717b9051984260f7e1bce74251a7544cb24a6f7a2d958502e9c13860b28d09a52d8c12b4d4dbd49c4264d488964fd21226e1c295f6a00979402e657de334cefafebb4cc6d8fa2decc137ccc3eff69da6cd63da8fe5f3121a85ebf42bd7ca4fe732d9cc12e9a76ba60d6de5f6a94a5c7704c598d28ea11928ed8873fe63e2cc6f0ad2dfc2a114a4f394184fb870b479e5b907ea3d2d38ea9ac172c4

tpm2-abrmd has to be disabled so that go-tpm can see /dev/tpm0 without error.

func main() {
        rwc, err := tpm2.OpenTPM(*tpmPath)
        if err != nil {
                fmt.Errorf("can't open TPM at  %v", err)
        }
        att, sig, err := tpm2.Quote(
                 rwc,
                 tpmutil.Handle(0x81010002),
                "",
                "",
                nil,
                tpm2.PCRSelection{ tpm2.AlgSHA256 , []int{0} },
                tpm2.AlgSHA256)

        if err != nil {
                fmt.Errorf("Problem getting quote  %s", err)
                fmt.Println(err)
        }
        if sig != nil {
                fmt.Errorf("Sig is nil\n")
        }
        if att != nil {
                fmt.Errorf("Att is nil\n")
        }
        fmt.Println("Err is ",err)
        fmt.Println("length  ",len(att))
        fmt.Println("Att [% x] ",att)
}

I think from reading other examples that the Handle (and PCRSelection) is specified correctly. When run (or built) I get the error: parameter 2, error code 0x12 : unsupported or incompatible scheme which I assume has to do with the handle.

$ go run quote.go
Handle is 2164326402
Type: tpmutil.Handle
parameter 2, error code 0x12 : unsupported or incompatible scheme
Err is * parameter 2, error code 0x12 : unsupported or incompatible scheme *
length   0
Att [% x]  []
$ go build quote.go
$ ./quote 
Handle is 2164326402
Type: tpmutil.Handle
parameter 2, error code 0x12 : unsupported or incompatible scheme
Err is * parameter 2, error code 0x12 : unsupported or incompatible scheme *
length   0
Att [% x]  []

remote session: using go-tpm in a tpm-stack with tpm2-abrmd and swtpm

I have following setup:

swtpm socket --tpmstate dir=/some/dir --tpm2 --ctrl type=tcp,port=2322 --server type=tcp,port=2321 --flags startup-clear
tpm2-abrmd --allow-root --tcti=swtpm
socat unix-listen:/path/to/socket,reuseaddr,fork,mode=777 system:'tpm2_send --tcti=tabrmd'

Reasons for this setup:

  • Remote sessions to TPM (via ssh)
  • Avoiding dbus communication with abrmd, instead using a separate unix socket

Additionally, I already created a SRK:

tpm2_createprimary --hierarchy=owner --key-algorithm=rsa2048:aes128cfb --hash-algorithm=sha256 --key-context=prim.ctx -a 'restricted|decrypt|fixedtpm|fixedparent|sensitivedataorigin|userwithauth|noda'
tpm2_evictcontrol --object-context=prim.ctx --hierarchy=owner 0x81000001

I succesfully ran tpm2-tools in this setup:

tpm2_create --tcti="cmd:nc -U /path/to/socket" -C 0x81000001 -u tpm.pub -r tpm.priv
tpm2_load --tcti="cmd:nc -U /path/to/socket" -C 0x81000001 -u tpm.pub -r tpm.priv -c key.ctx

key.ctx has content! So the setup should generally work ...

Now to my problem. I have following demo code:

package main

import (
	"io/ioutil"

	"github.com/google/go-tpm/tpm2"
	"github.com/google/go-tpm/tpmutil"
)

var (
	tpmSrkHandle        = 0x81000001
	tpmPcrSelection     = tpm2.PCRSelection{Hash: tpm2.AlgSHA256, PCRs: []int{}}
	tpmDefaultKeyParams = tpm2.Public{
		Type:       tpm2.AlgRSA,
		NameAlg:    tpm2.AlgSHA256,
		Attributes: tpm2.FlagDecrypt | tpm2.FlagFixedTPM | tpm2.FlagFixedParent | tpm2.FlagSensitiveDataOrigin | tpm2.FlagNoDA,
		AuthPolicy: []byte{},
		RSAParameters: &tpm2.RSAParams{
			Symmetric: &tpm2.SymScheme{
				Alg:  tpm2.AlgNull,
				Mode: tpm2.AlgUnknown,
			},
			KeyBits: 2048,
		},
	}
)

func main() {
	rwc, tpmerr := tpm2.OpenTPM("/path/to/socket")
	if tpmerr != nil {
		return
	}
	defer rwc.Close()

	handle := tpmutil.Handle(tpmSrkHandle)
	privKey, pubKey, _, _, _, err := tpm2.CreateKey(rwc, handle, tpmPcrSelection, "", "", tpmDefaultKeyParams)
	tpmKeyHandle, _, err := tpm2.Load(rwc, handle, "", pubKey, privKey)
	if err != nil {
		return
	}
	defer tpm2.FlushContext(rwc, tpmKeyHandle)
	ekhBytes, err := tpm2.ContextSave(rwc, tpmKeyHandle)
	err = ioutil.WriteFile("key.ctx", ekhBytes, 0644)
}

Socket is opening, CreateKey gives reasonable return values, but on ContextSave abrmd (with G_MESSAGES_DEBUG=all) gives follwing warning:

** (tpm2-abrmd:19261): DEBUG: 16:43:58.362: resource_manager_load_handles: for 1 handles in command handle area
** (tpm2-abrmd:19261): DEBUG: 16:43:58.362: processing TPM2_HT_TRANSIENT: 0x800000ff
** (tpm2-abrmd:19261): DEBUG: 16:43:58.362: processing TPM2_HT_TRANSIENT: 0x800000ff
** (tpm2-abrmd:19261): DEBUG: 16:43:58.362: handle 0x800000ff is virtual TPM2_HT_TRANSIENT, loading
** (tpm2-abrmd:19261): WARNING **: 16:43:58.362: No HandleMapEntry for vhandle: 0x800000ff

key.ctx is empty in this case! This is observed with every "key-handle" function, e.g. ReadPublic ...

When I ommit abrmd, using swtpm directly with

swtpm socket --tpmstate dir=/some/dir --tpm2 --ctrl type=tcp,port=2322 --server type=unixio,path=/path/to/socket --flags startup-clear

it works without problems.

But as I ran tpm2-tools successfully on this setup, it should work.

Any ideas, suggestions? Am I missing something?

Thanks for your help.

Ticket.Hierarchy is a uint32 instead of a Handle

Here is the current definition of tpm2.Ticket:

// Ticket represents evidence the TPM previously processed
// information.
type Ticket struct {
	Type      tpmutil.Tag
	Hierarchy uint32
	Digest    tpmutil.U16Bytes
}

Hierarchy should really be a tpmutil.Handle instead of a raw uint32. Note that since Ticket is an exported type, this will be a breaking change.

Load should return TPM2B_NAME

I have some code that currently looks like this:

privBlob, pubBlob, _, _, _, err := tpm2.CreateKey(f, srk, tpm2.PCRSelection{}, "", "", tmpl)
if err != nil {
    log.Fatalf("create aik: %v", err)
}
aik, nameData, err := tpm2.Load(f, srk, "", pubBlob, privBlob)
if err != nil {
    log.Fatalf("load aik: %v", err)
}

I'm currently trying to parse nameData to pass to credactivation.Generate(). But the size prefix is lopped off.

if _, err := tpmutil.Unpack(in, &handle, &paramSize, &name); err != nil {

So parsing with DecodeName fails:

tpm2.DecodeName(nameData)

That returns

unpacking name: decoding Digest: hash algorithm not supported: 0x6e27

Okay if I send a fix?

Certify, CertifyEx, and CertifyCreation return partial signature data

Here is the response description from the TPM 2.0 spec for the parameters returned from Certify and CertifyCreation:

Type Name Description
TPM2B_ATTEST certifyInfo the structure that was signed
TPMT_SIGNATURE signature the asymmetric signature ...

Here is decodeCertify which is used by all 3 of Certify, CertifyEx, and CertifyCreation:

go-tpm/tpm2/tpm2.go

Lines 1747 to 1778 in 1ff48da

func decodeCertify(resp []byte) ([]byte, []byte, error) {
var paramSize uint32
var attest, signature tpmutil.U16Bytes
var sigAlg, hashAlg Algorithm
buf := bytes.NewBuffer(resp)
if err := tpmutil.UnpackBuf(buf, &paramSize); err != nil {
return nil, nil, err
}
buf.Truncate(int(paramSize))
if err := tpmutil.UnpackBuf(buf, &attest, &sigAlg); err != nil {
return nil, nil, err
}
// If sigAlg is AlgNull, there will be no hashAlg or signature.
// This will happen if AlgNull was passed in the Certify() as
// the signing key (no need to sign the response).
// See TPM2 spec part4 pg227 SignAttestInfo()
if sigAlg != AlgNull {
if sigAlg == AlgECDSA {
var r, s tpmutil.U16Bytes
if err := tpmutil.UnpackBuf(buf, &hashAlg, &r, &s); err != nil {
return nil, nil, err
}
signature = append(r, s...)
} else {
if err := tpmutil.UnpackBuf(buf, &hashAlg, &signature); err != nil {
return nil, nil, err
}
}
}
return attest, signature, nil
}

In particular, note that the second parameter is signature which is the parsed raw RSA signature (exponentiated plaintext modulo key's modulus) or ECDSA signature (r, s pair). This is a loss of the following data:

  • sigAlg from TPMT_SIGNATURE
  • hash from TPMS_SIGNATURE_ECC if sigAlg indicated an ECC signature, or from TPMS_SIGNATURE_RSA if sigAlg indicated an RSA signature.

This means that a consumer of the output of the Certify command (via Certify or CertifyEx) or CertifyCreation command has to remember or guess the signature algorithm, as well as the hash algorithm that was used.

  • Guessing the sigAlg parameter is not trivially "if it's small, it's ECC, else RSA" because TPM supports multiple schemes per algorithm family (e.g., either PSS or PKCS1.5 for RSA). So throwing this data away harms potential users of the API.
  • Guessing the hash is reasonably done in 2021 by assuming it's SHA2-256, but this is subject to change and should be expected to as more TPMs support SHA2-384 and users become interested in that algorithm.

tpm2: CreateKey should not return a tpmutil.Handle

The TPM2_Create command does not return a handle, but tpm2.CreateKey tries to decode and return one anyway, resulting in a bogus tpmutil.Handle value.

Relatedly, the TPM2_CreateLoaded command doesn't seem available.

TPM2: PolicyOr is broken.

I know that I introduced tpm2.PolicyOr myself and I just realized that the library can't encode []tpmutil.U16Bytes. So the function is not working. In any case.

Expose/use sequence variants of TPM2_Hash

The one-shot TPM2_Hash command is implementation-dependent, depending on MAX_DIGEST_BUFFER which is only guaranteed by the spec to be up to 1,024 bytes. Some TPMs may support larger buffers, leading to code which works on some TPMs (like the simulator) but not others (like a real discrete TPM with limited RAM). For more than 1024 bytes of data, the sequence commands should be used, but these aren't yet implemented by go-tpm.

We have a couple of options here:

  • Change Hash* under-the-hood to use the sequence commands automatically. This will cause an overhead of one additional command compared to TPM2_Hash
  • Add new variant(s) of Hash* to use the sequence commands on an arbitrarily large byte slice
  • Add all the sequence commands in a 1:1 relationship with the TPM 2.0 API and make callers call functions for each of Hash_Start/SequenceUpdate/SequenceComplete.

ReadPCRs returns a maximum of 8 results

Here is the code snipped

    resp,err := tpm2.ReadPCRs(rwc,tpm2.PCRSelection{ tpm2.AlgSHA256 , []int{10,11,12,13,14,15,16,17,18,19,20} })

        fmt.Println("Err is *",err,"*")
        fmt.Println("length  ",len(resp))
        fmt.Println("resp  ",resp)

The result is:

Err is * <nil> *
length   8
resp   map[10:[233 78 123 248 213 96 73 135 44 139 238 32 208 223 134 159 56 215 159 209 85 238 17 216 216 249 145 194 75 85 102 54] 11:[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] 12:[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] 13:[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] 14:[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] 15:[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] 16:[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] 17:[255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255]]

Same result, ie maximum of 8 PCRs returned for any combination of PCRs and PCR bank.

Getting error "open /dev/tpmrm0: device is not a TPM 2.0" on first 2-3 tries to open tpm connection.

Hello everyone.

I'm working on Embedded device with Infineon SLB9670 TPM2 module.
Trying to open tpm device by using tpm2.OpenTPM call.
During the first 2-3 retries, getting the following error:
open /dev/tpmrm0: device is not a TPM 2.0

After the 4th retry device opens successfully and I'm able to work with it.
I wrote simple program to reproduce an issue:

package main

import (
	"fmt"

	"github.com/google/go-tpm/tpm2"
)

func main() {
	_, err := tpm2.OpenTPM("/dev/tpmrm0")
	if err != nil {
		fmt.Printf("Err %s", err)
		return
	}
	return
}

Here is the output:

root@v1000:/var# ./1
Err open /dev/tpmrm0: device is not a TPM 2.0
root@v1000:/var# ./1
Err open /dev/tpmrm0: device is not a TPM 2.0
root@v1000:/var# ./1
root@v1000:/var# 

As can be seen from the output: 3rd try was successful.

NOTE Can't reproduce this issue on the simulator.

TPM1.2: DefineSpace generates false HMAC

I just realized that my swtpm can't authenticate the command because the HMAC doesn't match.
I'm not sure if this is a problem with the swtpm or something I messed up while implementing.

Implement tpm2_duplicate

FR to implement tpm2_duplicate capability which will allow sealed transfer of tpm based keys

Here is a sequence using tpm2_tools: Duplicating Objects

for ref, the

  -  TPM2_CC_Duplicate constant value is   0x14b from tpm2_tools command-code-,mappings for constants.go

but the larger thing that remains is implementing tpm2_duplicate

eg

tpm2_duplicate -C new_parent.ctx -c key.ctx -G null -p "session:session.dat" -r dup.dup -s dup.seed

TPM1.2: Some tests against a simulator fail erroneously

Failing tests:

  • TestMakeIdentity
  • TestReadEKCert
  • TestPcrExtend
  • TestSeal
  • TestOwnerReadSRK

Whats happening is the simulator is responding and closing the domain socket - tpmutil.RunCommand doesnt seem to like that.

Note that the command seems to be correct - the simulator responds correctly, but closes the socket quick enough that go-tpm gets confused.

Example trace - TestReadEKCert

jsonp@RedShell:~> TPM_PATH=/tmp/s12 go test -v -count=1 -run TestReadEKCert github.com/google/go-tpm/tpm

 TPM_IO_Connect: Accepting connection from port /tmp/s12 ...
  TPM_IO_ReadBytes: Reading 6 bytes
  TPM_IO_Read: through paramSize length 6
 00 C1 00 00 00 24 
  TPM_IO_ReadBytes: Reading 30 bytes
 TPM_IO_Read: length 36
 00 C1 00 00 00 24 00 00 00 0B 00 02 40 00 00 01 
 00 FD 95 84 70 DF 9C B1 87 3D 86 46 51 C0 C2 06 
 6B E0 24 32 
 TPM_Process_GetCommandParams:
  TPM_Process_GetCommandParams: tag 00c1 paramSize 36 ordinal 0000000b
 TPM_Process_Preprocess: Ordinal 0000000b
  TPM_IO_GetLocality: localityModifier 0
 TPM_LocalityModifier_CheckLegal: TPM_MODIFIER_INDICATOR 00000000
TPM_KeyHandleEntries_Trace: 0 handle 00000000 tpm_key (nil)
TPM_KeyHandleEntries_Trace: 1 handle 00000000 tpm_key (nil)
TPM_KeyHandleEntries_Trace: 2 handle 00000000 tpm_key (nil)
 TPM_OrdinalTable_GetProcessFunction: Ordinal 0000000b
TPM_Process_OSAP: Ordinal Entry
TPM_Process_OSAP: entityType 0002
TPM_Process_OSAP: entityValue 40000001
  TPM_Nonce_Load:
 TPM_GetInParamDigest:
  TPM_GetInParamDigest: transportEncrypt 00
 TPM_CheckState: Check map 00000031
 TPM_AuthSessions_GetNewHandle:
 TPM_AuthSessions_IsSpace:
  TPM_AuthSessions_IsSpace: Found space at 1
 TPM_Handle_GenerateHandle: handle 00000000, keepHandle 0
 TPM_Random: Requesting 4 bytes
 TPM_AuthSessions_GetEntry: authHandle 2ea33cd9
  TPM_AuthSessions_GetEntry: session handle 2ea33cd9 not found
  TPM_Handle_GenerateHandle: Assigned Handle 2ea33cd9
  TPM_AuthSessions_GetNewHandle: Assigned handle 2ea33cd9
TPM_Process_OSAP: Using authHandle 2ea33cd9
  TPM_Nonce_Generate:
 TPM_Random: Requesting 20 bytes
  TPM_Nonce_Generate:
 TPM_Random: Requesting 20 bytes
 TPM_AuthSessionData_CheckEncScheme: adipEncScheme 00
TPM_Process_OSAP: entityType TPM_ET_OWNER, ownerReference 40000001
  TPM_Digest_Copy:
TPM_Process_OSAP: entityDigest 00 00 00 00
TPM_Process_OSAP: authData 00 00 00 00
TPM_Process_OSAP: nonceEvenOSAP 0c fc bd 49
TPM_Process_OSAP: nonceOddOSAP 00 fd 95 84
 TPM_HMAC_Generate:
 TPM_HMAC_Generatevalist:
 TPM_SHA1_valist:
 TPM_SHA1InitCmd:
  TPM_SHA1_valist: Digesting 64 bytes
 TPM_SHA1Update: length 64
  TPM_SHA1_valist: Digesting 20 bytes
 TPM_SHA1Update: length 20
  TPM_SHA1_valist: Digesting 20 bytes
 TPM_SHA1Update: length 20
 TPM_SHA1FinalCmd:
  TPM_SHA1_valist: Digest d6 6a 08 40
 TPM_SHA1Delete:
 TPM_SHA1:
 TPM_SHA1_valist:
 TPM_SHA1InitCmd:
  TPM_SHA1_valist: Digesting 64 bytes
 TPM_SHA1Update: length 64
  TPM_SHA1_valist: Digesting 20 bytes
 TPM_SHA1Update: length 20
 TPM_SHA1FinalCmd:
  TPM_SHA1_valist: Digest 21 ac 7a f8
 TPM_SHA1Delete:
 TPM_HMAC_Generatevalist: HMAC 21 ac 7a f8
TPM_Process_OSAP: sharedSecret 21 ac 7a f8
TPM_Process_OSAP: Ordinal returnCode 00000000 0
 TPM_Sbuffer_StoreInitialResponse: returnCode 00000000
  TPM_Nonce_Store:
  TPM_Nonce_Store:
 TPM_GetOutParamDigest:
 TPM_Sbuffer_StoreFinalResponse: returnCode 00000000
TPM_KeyHandleEntries_Trace: 0 handle 00000000 tpm_key (nil)
TPM_KeyHandleEntries_Trace: 1 handle 00000000 tpm_key (nil)
TPM_KeyHandleEntries_Trace: 2 handle 00000000 tpm_key (nil)
TPM_State_Trace: disable 0 p_deactive 0 v_deactive 0 owned 1 state 2
 TPM_IO_Write: length 54
 00 C4 00 00 00 36 00 00 00 00 2E A3 3C D9 DE 7F 
 8E BA 1C 1C 5E 06 A2 5D 9A E0 A1 F1 37 52 80 D4 
 71 9A 0C FC BD 49 3A 6B 65 CB 49 14 79 15 BE BF 
 80 E3 A9 22 4D 53 
TPM_IO_Connect: Waiting for connections on port /tmp/s12
=== RUN   TestReadEKCert
--- FAIL: TestReadEKCert (0.00s)
    tpm_test.go:107: Unable to read EKCert from NVRAM: failed to read from NVRAM: write unix @->/tmp/s12: write: broken pipe
FAIL
FAIL	github.com/google/go-tpm/tpm	0.027s

Example trace - TestOwnerReadSRK

jsonp@RedShell:~> TPM_PATH=/tmp/s12 go test -v -count=1 -run TestOwnerReadSRK github.com/google/go-tpm/tpm
=== RUN   TestOwnerReadSRK

 TPM_IO_Connect: Accepting connection from port /tmp/s12 ...
  TPM_IO_ReadBytes: Reading 6 bytes
  TPM_IO_Read: through paramSize length 6
 00 C1 00 00 00 24 
  TPM_IO_ReadBytes: Reading 30 bytes
 TPM_IO_Read: length 36
 00 C1 00 00 00 24 00 00 00 0B 00 02 40 00 00 01 
 D4 2B 2F A9 37 4F 0C F5 A2 E5 92 CE 19 F7 57 2B 
 2D F2 95 55 
 TPM_Process_GetCommandParams:
  TPM_Process_GetCommandParams: tag 00c1 paramSize 36 ordinal 0000000b
 TPM_Process_Preprocess: Ordinal 0000000b
  TPM_IO_GetLocality: localityModifier 0
 TPM_LocalityModifier_CheckLegal: TPM_MODIFIER_INDICATOR 00000000
TPM_KeyHandleEntries_Trace: 0 handle 00000000 tpm_key (nil)
TPM_KeyHandleEntries_Trace: 1 handle 00000000 tpm_key (nil)
TPM_KeyHandleEntries_Trace: 2 handle 00000000 tpm_key (nil)
 TPM_OrdinalTable_GetProcessFunction: Ordinal 0000000b
TPM_Process_OSAP: Ordinal Entry
TPM_Process_OSAP: entityType 0002
TPM_Process_OSAP: entityValue 40000001
  TPM_Nonce_Load:
 TPM_GetInParamDigest:
  TPM_GetInParamDigest: transportEncrypt 00
 TPM_CheckState: Check map 00000031
 TPM_AuthSessions_GetNewHandle:
 TPM_AuthSessions_IsSpace:
  TPM_AuthSessions_IsSpace: Found space at 2
 TPM_Handle_GenerateHandle: handle 00000000, keepHandle 0
 TPM_Random: Requesting 4 bytes
 TPM_AuthSessions_GetEntry: authHandle 3bc8c7e6
  TPM_AuthSessions_GetEntry: session handle 3bc8c7e6 not found
  TPM_Handle_GenerateHandle: Assigned Handle 3bc8c7e6
  TPM_AuthSessions_GetNewHandle: Assigned handle 3bc8c7e6
TPM_Process_OSAP: Using authHandle 3bc8c7e6
  TPM_Nonce_Generate:
 TPM_Random: Requesting 20 bytes
  TPM_Nonce_Generate:
 TPM_Random: Requesting 20 bytes
 TPM_AuthSessionData_CheckEncScheme: adipEncScheme 00
TPM_Process_OSAP: entityType TPM_ET_OWNER, ownerReference 40000001
  TPM_Digest_Copy:
TPM_Process_OSAP: entityDigest 00 00 00 00
TPM_Process_OSAP: authData 00 00 00 00
TPM_Process_OSAP: nonceEvenOSAP 75 71 3b bb
TPM_Process_OSAP: nonceOddOSAP d4 2b 2f a9
 TPM_HMAC_Generate:
 TPM_HMAC_Generatevalist:
 TPM_SHA1_valist:
 TPM_SHA1InitCmd:
  TPM_SHA1_valist: Digesting 64 bytes
 TPM_SHA1Update: length 64
  TPM_SHA1_valist: Digesting 20 bytes
 TPM_SHA1Update: length 20
  TPM_SHA1_valist: Digesting 20 bytes
 TPM_SHA1Update: length 20
 TPM_SHA1FinalCmd:
  TPM_SHA1_valist: Digest 86 43 81 70
 TPM_SHA1Delete:
 TPM_SHA1:
 TPM_SHA1_valist:
 TPM_SHA1InitCmd:
  TPM_SHA1_valist: Digesting 64 bytes
 TPM_SHA1Update: length 64
  TPM_SHA1_valist: Digesting 20 bytes
 TPM_SHA1Update: length 20
 TPM_SHA1FinalCmd:
  TPM_SHA1_valist: Digest 01 14 d5 d9
 TPM_SHA1Delete:
 TPM_HMAC_Generatevalist: HMAC 01 14 d5 d9
TPM_Process_OSAP: sharedSecret 01 14 d5 d9
TPM_Process_OSAP: Ordinal returnCode 00000000 0
 TPM_Sbuffer_StoreInitialResponse: returnCode 00000000
  TPM_Nonce_Store:
  TPM_Nonce_Store:
 TPM_GetOutParamDigest:
 TPM_Sbuffer_StoreFinalResponse: returnCode 00000000
TPM_KeyHandleEntries_Trace: 0 handle 00000000 tpm_key (nil)
TPM_KeyHandleEntries_Trace: 1 handle 00000000 tpm_key (nil)
TPM_KeyHandleEntries_Trace: 2 handle 00000000 tpm_key (nil)
TPM_State_Trace: disable 0 p_deactive 0 v_deactive 0 owned 1 state 2
 TPM_IO_Write: length 54
 00 C4 00 00 00 36 00 00 00 00 3B C8 C7 E6 AC C8 
 D8 48 6E C3 37 65 2C EC EC F8 13 0B 32 16 4F 5E 
 18 1A 75 71 3B BB 44 31 11 E4 6F 44 5E BE 3E 08 
 36 99 18 C1 E5 98 
TPM_IO_Connect: Waiting for connections on port /tmp/s12
--- FAIL: TestOwnerReadSRK (0.00s)
    tpm_test.go:483: Couldn't read the SRK using owner auth: write unix @->/tmp/s12: write: broken pipe
FAIL
FAIL	github.com/google/go-tpm/tpm	0.027s

TPM2 Seal command supporting NoDA attribute

The Seal function doesn't currently allow the caller to specify TPMA_OBJECT.NoDA to exempt the sealed blob from dictionary attacks. If the caller knows the auth value is not a low-entropy secret like a PIN or human-generated password, the caller should exempt their sealed data from dictionary attack prevention to improve reliability even if the TPM loses power (which counts as a dictionary attack attempt to the TPM)

A SealEx or similar command ought to be constructed, allowing the caller to pass the attributes to TPM2_Create.

tpm2: Problem with expectedDigest in PolicyPCR

When a key is sealed against some PCR values without providing expectedDigest in the PolicyPCR to get the policy to seal with, everything works fine. Below is the example of it. (I am creating a sessionHandle and Policy digest as mentioned in examples of go-tpm.)
err := tpm2.PolicyPCR(rwc, sessHandle, nil /expectedDigest/, pcrSelection)

But when expected/predicted value of PCR is provided in PolicyPCR to get the new policy to seal against expected PCR value, it gives me error. Below is the example of it. I am using tpm2.AlgSHA256.
err := tpm2.PolicyPCR(rwc, sessHandle, expectedDigest, pcrSelection)

Here expectedDigest is [ ] byte type and I am getting it as a value of command line flag/argument. (Also tried defining with direct value in the function).

Is there anything that I am missing here or doing incorrectly?

Below is a little code sample of what I am doing.

`
func seal (c *cli) {
pcrs_slice := c.IntSlice("pcrs")
expectedDigest := c.String("digest")

       pcrs := make([]int, len(pcrs_slice))
       copy(pcrs[:], pcrs_slice)
 
       digest := []byte(nil)
       if expectedDigest != noDigest {
               digest, _ = hex.DecodeString(expectedDigest)
       }

       fmt.Printf("Digest: ")
       fmt.Println(hex.EncodeToString(digest[:]))          
      
      rwc, err := tpm2.OpenTPM(device)

      sessHandle, policy, err := policyPCRPasswordSession(rwc, pcrs, digest, objectPassword)
      if err != nil {
            fmt.Fprintf(os.Stderr, "unable to get policy: %v", err)
            os.Exit(1)
      }

      /*** code for seal and load ***/

}

func policyPCRPasswordSession(rwc io.ReadWriteCloser, pcrs []int, expectedDigest []byte, password string) (sessHandle tpmutil.Handle, policy []byte, retErr error) {
// this is not a very secure session.
sessHandle, _, err := tpm2.StartAuthSession(
rwc,
tpm2.HandleNull, /tpmKey/
tpm2.HandleNull, /bindKey/
make([]byte, 16), /nonceCaller/
nil, /secret/
tpm2.SessionPolicy,
tpm2.AlgNull,
tpm2.AlgSHA256)
if err != nil {
return tpm2.HandleNull, nil, fmt.Errorf("unable to start session: %v", err)
}

    pcrSelection := tpm2.PCRSelection{
            Hash: tpm2.AlgSHA256,
            PCRs: pcrs,
    }

    // An empty expected digest means that digest verification is skipped.
    if err := tpm2.PolicyPCR(rwc, sessHandle, expectedDigest, pcrSelection); err != nil {
            return sessHandle, nil, fmt.Errorf("unable to bind PCRs to auth policy: %v", err)
    }

    if err := tpm2.PolicyPassword(rwc, sessHandle); err != nil {
            return sessHandle, nil, fmt.Errorf("unable to require password for auth policy: %v", err)
    }

    policy, err = tpm2.PolicyGetDigest(rwc, sessHandle)
    if err != nil {
            return sessHandle, nil, fmt.Errorf("unable to get policy digest: %v", err)
    }
    return sessHandle, policy, nil

}
`

And these are the results for both the cases (with and without expectedDigest).
`
cat tmp.key | tpm2 seal -p 9 -hd 0x80000000 -d 46fd082b60a0ffa92cb32663dc3f6c67a04eb7902f7153f2274b6e5cc6d26e3c
Digest is : 46fd082b60a0ffa92cb32663dc3f6c67a04eb7902f7153f2274b6e5cc6d26e3c
Digest size is : 32
Digest value: [70 253 8 43 96 160 255 169 44 179 38 99 220 63 108 103 160 78 183 144 47 113 83 242 39 75 110 92 198 210 110 60]
unable to get policy: unable to bind PCRs to auth policy: parameter 1, error code 0x4 : value is out of range or is not correct for the context

cat tmp.key | tpm2 seal -p 9 -hd 0x80000000
Digest is :
Digest size is : 0
Digest value: []
Loaded sealed data with handle: 0x80000001
`

I also tried wrapping expectedDigest with tpmutil.U16Bytes here https://github.com/google/go-tpm/blob/master/tpm2/tpm2.go#L728 .

Is there anything that I am missing here or doing incorrectly or do we need to change anything in tpm2 for this?

Consistancy between io.ReadWriter and io.ReadWriteCloser

In go-tpm, the actual TPM device is passed as a generic go interface. For most of the functions in go-tpm this is a io.ReadWriter, however for GetRandom() and ReadPCR(), the TPM is passed as a io.ReadWriteCloser despite the fact that neither of those methods call Close().

We should be consistent throughout go-tpm in use io.ReadWriter for our functions. Note that the signature of:

func OpenTPM(path string) (io.ReadWriteCloser, error)

is fine as we need a way for a caller to close the TPM.

Example for storing & fetching string/bytes ?

Hi,
I'd like to use this library to fetch a master key out of the TPM chip.
This master key should only be available to my Go program.

Is there a best practice example I could use for this use case?
Altough this seems like a typical scenario, I am having issues grasping the API.
Would I...
0. take ownership of the TPM using owner & srk secrets via TakeOwnership
https://github.com/google/go-tpm/blob/master/examples/tpm-takeownership/own.go

  1. first open TPM chip using OpenTPM
  2. clear an address space using NVDefineSpace
  3. write the master key using NVWrite
  4. read out the master key using NVRead

If so, what defines the owner, handle and authString?

Should unpack of U16/U32Bytes validate a max size?

Malicious attacker-controlled input could make its way to tpmutil.Unpack() of U32Bytes or U16Bytes. For instance, calling tpm.UnmarshalPubRSAPublicKey() with 20ff ffff 2020 20ff ffff 2020 will cause a massive allocation, crashing the machine.

Given that TPMUnmarshal takes a reader, theres no upfront knowledge of the size of the buffer being processed.

Anyone have an opinion on these options?

  1. Add a max size (maybe 1mb?) which is larger than all expected use-cases
  2. Refactor tpmutil.Unpack to pass the size of the buffer down the chain so size prefix fields can be properly validated.

WDYT?

TPM2: NVUndefineSpaceSpecial has wrong parameter type

And another mistake I made, because I didn't understood the spec properly.

NVUndefineSpaceSpecial gets two handles insteat of two AuthCommands. This is wrong. The function can't create AuthCommand structures without further information about the nvIndex.

Possible solution:
It is the users responsibility to create the right conditions for the authentication against the nvIndex authValue or policy, as well as supply the right values for platform authorization to delete the index.
-> change type handle to type AuthCommand

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.