Repository: google/go-tpm
Pull request #72 (2019-02-17 03:19:41)

Add method to get a crypto.PublicKey key from tpm2.Public
This change moves the code that extract the public key from tpm2.Public so it can be used to more generally. The reason is that `CreatePrimary()` already returns a crypto.PublicKey, while `ReadPublic()` does not.


Patch:
diff --git a/tpm2/constants.go b/tpm2/constants.go
index 9033755..9c8ccc1 100644
--- a/tpm2/constants.go
+++ b/tpm2/constants.go
@@ -151,7 +151,20 @@ type TPMProp uint32
 
 // TPM Capability Properties.
 const (
-	NVMaxBufferSize TPMProp = 0x100 + 44
+	NVMaxBufferSize    TPMProp = 0x100 + 44
+	PCRFirst           TPMProp = 0x00000000
+	HMACSessionFirst   TPMProp = 0x02000000
+	LoadedSessionFirst TPMProp = 0x02000000
+	PolicySessionFirst TPMProp = 0x03000000
+	ActiveSessionFirst TPMProp = 0x03000000
+	TransientFirst     TPMProp = 0x80000000
+	PersistentFirst    TPMProp = 0x81000000
+	PersistentLast     TPMProp = 0x81FFFFFF
+	PlatformPersistent TPMProp = 0x81800000
+	NVIndexFirst       TPMProp = 0x01000000
+	NVIndexLast        TPMProp = 0x01FFFFFF
+	PermanentFirst     TPMProp = 0x40000000
+	PermanentLast      TPMProp = 0x4000010F
 )
 
 // Reserved Handles.
diff --git a/tpm2/structures.go b/tpm2/structures.go
index a05e1c3..cb36e7c 100644
--- a/tpm2/structures.go
+++ b/tpm2/structures.go
@@ -16,6 +16,9 @@ package tpm2
 
 import (
 	"bytes"
+	"crypto"
+	"crypto/ecdsa"
+	"crypto/rsa"
 	"errors"
 	"fmt"
 	"math/big"
@@ -90,6 +93,30 @@ func (p Public) Encode() ([]byte, error) {
 	return concat(head, params)
 }
 
+// Key returns the (public) key from the public area of an object.
+func (p Public) Key() (crypto.PublicKey, error) {
+	var pubKey crypto.PublicKey
+	switch p.Type {
+	case AlgRSA:
+		// Endianness of big.Int.Bytes/SetBytes and modulus in the TPM is the same
+		// (big-endian).
+		pubKey = &rsa.PublicKey{N: p.RSAParameters.Modulus, E: int(p.RSAParameters.Exponent)}
+	case AlgECC:
+		curve, ok := toGoCurve[p.ECCParameters.CurveID]
+		if !ok {
+			return nil, fmt.Errorf("can't map TPM EC curve ID 0x%x to Go elliptic.Curve value", p.ECCParameters.CurveID)
+		}
+		pubKey = &ecdsa.PublicKey{
+			X:     p.ECCParameters.Point.X,
+			Y:     p.ECCParameters.Point.Y,
+			Curve: curve,
+		}
+	default:
+		return nil, fmt.Errorf("unsupported public key type 0x%x", p.Type)
+	}
+	return pubKey, nil
+}
+
 // DecodePublic decodes a TPMT_PUBLIC message. No error is returned if
 // the input has extra trailing data.
 func DecodePublic(buf []byte) (Public, error) {
diff --git a/tpm2/tpm2.go b/tpm2/tpm2.go
index 6c5a07d..1b9a2e6 100644
--- a/tpm2/tpm2.go
+++ b/tpm2/tpm2.go
@@ -18,8 +18,6 @@ package tpm2
 import (
 	"bytes"
 	"crypto"
-	"crypto/ecdsa"
-	"crypto/rsa"
 	"fmt"
 	"io"
 
@@ -408,24 +406,9 @@ func CreatePrimary(rw io.ReadWriter, owner tpmutil.Handle, sel PCRSelection, par
 		return 0, nil, fmt.Errorf("parsing public: %v", err)
 	}
 
-	var pubKey crypto.PublicKey
-	switch pub.Type {
-	case AlgRSA:
-		// Endianness of big.Int.Bytes/SetBytes and modulus in the TPM is the same
-		// (big-endian).
-		pubKey = &rsa.PublicKey{N: pub.RSAParameters.Modulus, E: int(pub.RSAParameters.Exponent)}
-	case AlgECC:
-		curve, ok := toGoCurve[pub.ECCParameters.CurveID]
-		if !ok {
-			return 0, nil, fmt.Errorf("can't map TPM EC curve ID 0x%x to Go elliptic.Curve value", pub.ECCParameters.CurveID)
-		}
-		pubKey = &ecdsa.PublicKey{
-			X:     pub.ECCParameters.Point.X,
-			Y:     pub.ECCParameters.Point.Y,
-			Curve: curve,
-		}
-	default:
-		return 0, nil, fmt.Errorf("unsupported primary key type 0x%x", pub.Type)
+	pubKey, err := pub.Key()
+	if err != nil {
+		return 0, nil, err
 	}
 
 	return hnd, pubKey, err


Test patch:
diff --git a/tpm2/tpm2_test.go b/tpm2/tpm2_test.go
index 64fd1bf..00cdbd0 100644
--- a/tpm2/tpm2_test.go
+++ b/tpm2/tpm2_test.go
@@ -1084,3 +1084,83 @@ func TestQuote(t *testing.T) {
 		t.Errorf("VerifyPKCS1v15 failed: %v", err)
 	}
 }
+
+func TestReadPublicKey(t *testing.T) {
+	rw := openTPM(t)
+	defer rw.Close()
+
+	run := func(t *testing.T, public Public, private Private, pubKeyIn crypto.PublicKey) {
+		t.Helper()
+
+		h, _, err := LoadExternal(rw, public, private, HandleNull)
+		if err != nil {
+			t.Fatal(err)
+		}
+		defer FlushContext(rw, h)
+
+		pub, _, _, err := ReadPublic(rw, h)
+		if err != nil {
+			t.Fatalf("ReadPublic failed: %s", err)
+		}
+
+		pubKeyOut, err := pub.Key()
+		if err != nil {
+			t.Fatalf("Public.Key() failed: %s", err)
+		}
+
+		if !reflect.DeepEqual(pubKeyIn, pubKeyOut) {
+			t.Fatalf("Public.Key() = %#v; want %#v", pubKeyOut, pubKeyIn)
+		}
+	}
+
+	t.Run("RSA", func(t *testing.T) {
+		pk, err := rsa.GenerateKey(rand.Reader, 2048)
+		if err != nil {
+			t.Fatal(err)
+		}
+		rp := Public{
+			Type:       AlgRSA,
+			NameAlg:    AlgSHA1,
+			Attributes: FlagSign | FlagSensitiveDataOrigin | FlagUserWithAuth,
+			RSAParameters: &RSAParams{
+				Sign: &SigScheme{
+					Alg:  AlgRSASSA,
+					Hash: AlgSHA1,
+				},
+				KeyBits:  2048,
+				Exponent: uint32(pk.PublicKey.E),
+				Modulus:  pk.PublicKey.N,
+			},
+		}
+		private := Private{
+			Type:      AlgRSA,
+			Sensitive: pk.Primes[0].Bytes(),
+		}
+		run(t, rp, private, &pk.PublicKey)
+	})
+	t.Run("ECC", func(t *testing.T) {
+		skipOnUnsupportedAlg(t, rw, AlgECC)
+		pk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+		if err != nil {
+			t.Fatal(err)
+		}
+		public := Public{
+			Type:       AlgECC,
+			NameAlg:    AlgSHA1,
+			Attributes: FlagSign | FlagSensitiveDataOrigin | FlagUserWithAuth,
+			ECCParameters: &ECCParams{
+				Sign: &SigScheme{
+					Alg:  AlgECDSA,
+					Hash: AlgSHA1,
+				},
+				CurveID: CurveNISTP256,
+				Point:   ECPoint{X: pk.PublicKey.X, Y: pk.PublicKey.Y},
+			},
+		}
+		private := Private{
+			Type:      AlgECC,
+			Sensitive: pk.D.Bytes(),
+		}
+		run(t, public, private, &pk.PublicKey)
+	})
+}