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

tpm2: Implement TPM2_Import #341

Merged
merged 1 commit into from
Aug 7, 2023
Merged

Conversation

Foxboron
Copy link
Contributor

@Foxboron Foxboron commented Aug 4, 2023

This is an attempt from me to try and implement TPM2_Import, but I suspect I picked a non-obvious thing to implement.

Useage I have is to take a pre-existing EC key and import it into the TPM. But I suspect there is a bunch of wrapping with the duplicate argument I just don't understand. The legacy/tpm2 implementation seemed to just do things with the Private struct, but transferring that to the tpmdirect API is very non-obvious.

The outline for the test I have been trying to write for the implementation>

Any help our guidance would be appreicated.

package tpm2test

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/x509"
	"testing"

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

func TestImport(t *testing.T) {
	thetpm, err := simulator.OpenSimulator()
	if err != nil {
		t.Fatalf("could not connect to TPM simulator: %v", err)
	}
	defer thetpm.Close()

	srkCreate := CreatePrimary{
		PrimaryHandle: TPMRHOwner,
		InPublic:      New2B(ECCSRKTemplate),
	}

	srkCreateRsp, err := srkCreate.Execute(thetpm)
	if err != nil {
		t.Fatalf("could not generate SRK: %v", err)
	}
	defer func() {
		flush := FlushContext{
			FlushHandle: srkCreateRsp.ObjectHandle,
		}
		_, err := flush.Execute(thetpm)
		if err != nil {
			t.Fatalf("could not flush SRK: %v", err)
		}
	}()

	pk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	if err != nil {
		t.Fatalf("something")
	}
	b, err := x509.MarshalECPrivateKey(pk)
	if err != nil {
		t.Fatalf("failed DER encoding")
	}

	tpmdata := TPM2BPrivate{
		Buffer: b,
	}
	m := Marshal(tpmdata)

	_, err = Import{
		ParentHandle: &AuthHandle{
			Handle: srkCreateRsp.ObjectHandle,
			Name:   srkCreateRsp.Name,
			Auth:   PasswordAuth(nil),
		},
		Duplicate: TPM2BPrivate{Buffer: m},
		ObjectPublic: New2B(TPMTPublic{
			Type:    TPMAlgECC,
			NameAlg: TPMAlgSHA256,
			ObjectAttributes: TPMAObject{
				SignEncrypt:         true,
				SensitiveDataOrigin: true,
			},
			Parameters: NewTPMUPublicParms(
				TPMAlgECC,
				&TPMSECCParms{
					CurveID: TPMECCNistP256,
					Scheme: TPMTECCScheme{
						Scheme: TPMAlgECDSA,
						Details: NewTPMUAsymScheme(
							TPMAlgECDSA,
							&TPMSSigSchemeECDSA{
								HashAlg: TPMAlgSHA256,
							},
						),
					},
				},
			),
		}),
	}.Execute(thetpm)
	if err != nil {
		t.Fatalf("could not import: %v", err)
	}
}

@chrisfenner
Copy link
Member

chrisfenner commented Aug 4, 2023

pk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	if err != nil {
		t.Fatalf("something")
	}
	b, err := x509.MarshalECPrivateKey(pk)
	if err != nil {
		t.Fatalf("failed DER encoding")
	}

	tpmdata := TPM2BPrivate{
		Buffer: b,
	}
	m := Marshal(tpmdata)

This might cause a problem, because TPMs don't deal in DER-encoded data. Instead, you probably want to create a TPMT_SENSITIVE and then encrypt it (using the parent's public key) using a similar method to how go-tpm-tools does it. Heck, I'd probably just use go-tpm-tools for the test: let somebody else take up the separate work of dealing with import blobs.

@chrisfenner
Copy link
Member

TPM2_Import might be the hardest possible command to build a test for. So once you've cracked this, you should feel prepared to deal with anything. 😎

@Foxboron
Copy link
Contributor Author

Foxboron commented Aug 4, 2023

This might cause a problem, because TPMs don't deal in DER-encoded data.

I might have been tricked by the tpm2_tools implementation as they said it could be DER and I never found a place in the C code where they did anything with the bytes at all and assumed as a result it would be fine.

TPM2_Import might be the hardest possible command to build a test for. So once you've cracked this, you should feel prepared to deal with anything. sunglasses

I'm glad to see I involuntarily stumbled into the worst of them 🙃

@Foxboron
Copy link
Contributor Author

Foxboron commented Aug 4, 2023

Actually, with the links I was able to figure out how to pass the duplicate blob unencrypted.

I forgot filling out Unique with the public key parts and filling out TPM2BPrivate{} in the struct had me take a loop through Marshal() which seems a bit awkward.

If the outline for the test looks sane'ish I'll clean up this PR to undraft it.

Thanks for the help :)

package tpm2test

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"testing"

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

func TestImport(t *testing.T) {
	thetpm, err := simulator.OpenSimulator()
	if err != nil {
		t.Fatalf("could not connect to TPM simulator: %v", err)
	}
	defer thetpm.Close()

	srkCreate := CreatePrimary{
		PrimaryHandle: TPMRHOwner,
		InPublic:      New2B(ECCSRKTemplate),
	}

	srkCreateRsp, err := srkCreate.Execute(thetpm)
	if err != nil {
		t.Fatalf("could not generate SRK: %v", err)
	}
	defer func() {
		flush := FlushContext{
			FlushHandle: srkCreateRsp.ObjectHandle,
		}
		_, err := flush.Execute(thetpm)
		if err != nil {
			t.Fatalf("could not flush SRK: %v", err)
		}
	}()

	pk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	if err != nil {
		t.Fatalf("something")
	}

	sens2B := New2B(TPMTSensitive{
		SensitiveType: TPMAlgECC,
		Sensitive: NewTPMUSensitiveComposite(
			TPMAlgECC,
			&TPM2BECCParameter{Buffer: pk.D.FillBytes(make([]byte, 32))},
		),
	})

	l := Marshal(TPM2BPrivate{Buffer: sens2B.Bytes()})

	_, err = Import{
		ParentHandle: &AuthHandle{
			Handle: srkCreateRsp.ObjectHandle,
			Name:   srkCreateRsp.Name,
			Auth:   PasswordAuth(nil),
		},
		Duplicate: TPM2BPrivate{Buffer: l},
		ObjectPublic: New2B(TPMTPublic{
			Type:    TPMAlgECC,
			NameAlg: TPMAlgSHA256,
			ObjectAttributes: TPMAObject{
				SignEncrypt:          true,
				SensitiveDataOrigin:  false,
				EncryptedDuplication: false,
			},
			Parameters: NewTPMUPublicParms(
				TPMAlgECC,
				&TPMSECCParms{
					CurveID: TPMECCNistP256,
					Scheme: TPMTECCScheme{
						Scheme: TPMAlgECDSA,
						Details: NewTPMUAsymScheme(
							TPMAlgECDSA,
							&TPMSSigSchemeECDSA{
								HashAlg: TPMAlgSHA256,
							},
						),
					},
				},
			),
			Unique: NewTPMUPublicID(
				TPMAlgECC,
				&TPMSECCPoint{
					X: TPM2BECCParameter{
						Buffer: pk.X.FillBytes(make([]byte, 32)),
					},
					Y: TPM2BECCParameter{
						Buffer: pk.Y.FillBytes(make([]byte, 32)),
					},
				},
			),
		}),
	}.Execute(thetpm)
	if err != nil {
		t.Fatalf("could not import: %v", err)
	}
}

@chrisfenner
Copy link
Member

Feel free to test only based on unencrypted import; I have a hard time seeing a bug you could introduce here that would succeed for unencrypted import but fail for encrypted.

@chrisfenner
Copy link
Member

If your current plan for testing looks like the draft above (minus the DERification), seems totally reasonable!

Signed-off-by: Morten Linderud <morten@linderud.pw>
@Foxboron Foxboron force-pushed the morten/tpm_import branch from 0c543b8 to 549802b Compare August 5, 2023 09:29
@Foxboron Foxboron marked this pull request as ready for review August 5, 2023 09:29
@Foxboron Foxboron requested review from alexmwu, jkl73 and a team as code owners August 5, 2023 09:29
@Foxboron
Copy link
Contributor Author

Foxboron commented Aug 5, 2023

I've implemented a feature using this into ssh-tpm-agent with a integration test suite, and it seems to work :)

Foxboron/ssh-tpm-agent#16

@chrisfenner chrisfenner merged commit c49efc4 into google:main Aug 7, 2023
@chrisfenner
Copy link
Member

Thank you @Foxboron !!

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

Successfully merging this pull request may close these issues.

2 participants