Repository: google/go-tpm
Pull request #63 (2019-01-06 15:53:56)

Named attributes for NV indexes and add NVReadPublic()
Supercedes https://github.com/google/go-tpm/pull/54

- Adds named attributes for NV indexes in NVDefineSpace()
- Exposes NVReadPublic()
- Expands NV read and write test 


Patch:
diff --git a/examples/tpm-takeownership/own.go b/examples/tpm-takeownership/own.go
index 9d92b4b..911d378 100644
--- a/examples/tpm-takeownership/own.go
+++ b/examples/tpm-takeownership/own.go
@@ -53,13 +53,13 @@ func main() {
 		copy(srkAuth[:], sa[:])
 	}
 
-	pubek, err := tpm.ReadPubEK(rwc)
+	pubEK, err := tpm.ReadPubEK(rwc)
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "Couldn't read the endorsement key: %s\n", err)
 		return
 	}
 
-	if err := tpm.TakeOwnership(rwc, ownerAuth, srkAuth, pubek); err != nil {
+	if err := tpm.TakeOwnership(rwc, ownerAuth, srkAuth, pubEK); err != nil {
 		fmt.Fprintf(os.Stderr, "Couldn't take ownership of the TPM: %s\n", err)
 		return
 	}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..0d7776a
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,1 @@
+module github.com/google/go-tpm
diff --git a/tpm/commands.go b/tpm/commands.go
index cbf6b1c..1a942fe 100644
--- a/tpm/commands.go
+++ b/tpm/commands.go
@@ -178,7 +178,7 @@ func quote2(rw io.ReadWriter, keyHandle tpmutil.Handle, hash [20]byte, pcrs *pcr
 }
 
 // quote performs a TPM 1.1 quote operation: it signs data using the
-// TPM_QUOTE_INFO structure for the current values of a selectied set of PCRs.
+// TPM_QUOTE_INFO structure for the current values of a selected set of PCRs.
 func quote(rw io.ReadWriter, keyHandle tpmutil.Handle, hash [20]byte, pcrs *pcrSelection, ca *commandAuth) (*pcrComposite, []byte, *responseAuth, uint32, error) {
 	in := []interface{}{keyHandle, hash, pcrs, ca}
 	var pcrc pcrComposite
diff --git a/tpm/errors.go b/tpm/errors.go
index 18b7a9f..c03a6ed 100644
--- a/tpm/errors.go
+++ b/tpm/errors.go
@@ -82,7 +82,7 @@ const (
 	errNoWrapTransport
 	errAuditFailUnsuccessful
 	errAuditFailSuccessful
-	errNotResetable
+	errNotResettable
 	errNotLocal
 	errBadType
 	errInvalidResource
@@ -108,7 +108,7 @@ const (
 	errNoOperator
 	errResourceMissing
 	errDelegateLock
-	errDelegateFamliy
+	errDelegateFamily
 	errDelegateAdmin
 	errTransportNotExclusive
 	errOwnerControl
@@ -118,7 +118,7 @@ const (
 	errDAAIssuerSettings
 	errDAASettings
 	errDAAState
-	errDAAIssuerVailidity
+	errDAAIssuerValidity
 	errDAAWrongW
 	errBadHandle
 	errBadDelegate
@@ -184,7 +184,7 @@ var tpmErrMsgs = map[tpmError]string{
 	errNoWrapTransport:       "the TPM does not allow for wrapped transport sessions",
 	errAuditFailUnsuccessful: "TPM audit construction failed and the underlying command was returning a failure code also",
 	errAuditFailSuccessful:   "TPM audit construction failed and the underlying command was returning success",
-	errNotResetable:          "attempt to reset a PCR register that does not have the resettable attribute",
+	errNotResettable:          "attempt to reset a PCR register that does not have the resettable attribute",
 	errNotLocal:              "attempt to reset a PCR register that requires locality and locality modifier not part of command transport",
 	errBadType:               "make identity blob not properly typed",
 	errInvalidResource:       "when saving context identified resource type does not match actual resource",
@@ -210,7 +210,7 @@ var tpmErrMsgs = map[tpmError]string{
 	errNoOperator:            "no operator AuthData value is set",
 	errResourceMissing:       "the resource pointed to by context is not loaded",
 	errDelegateLock:          "the delegate administration is locked",
-	errDelegateFamliy:        "attempt to manage a family other than the delegated family",
+	errDelegateFamily:        "attempt to manage a family other than the delegated family",
 	errDelegateAdmin:         "delegation table management not enabled",
 	errTransportNotExclusive: "there was a command executed outside of an exclusive transport session",
 	errOwnerControl:          "attempt to context save a owner evict controlled key",
@@ -220,7 +220,7 @@ var tpmErrMsgs = map[tpmError]string{
 	errDAAIssuerSettings:     "the consistency check on DAA_issuerSettings has failed",
 	errDAASettings:           "the consistency check on DAA_tpmSpecific has failed",
 	errDAAState:              "the atomic process indicated by the submitted DAA command is not the expected process",
-	errDAAIssuerVailidity:    "the issuer's validity check has detected an inconsistency",
+	errDAAIssuerValidity:    "the issuer's validity check has detected an inconsistency",
 	errDAAWrongW:             "the consistency check on w has failed",
 	errBadHandle:             "the handle is incorrect",
 	errBadDelegate:           "delegation is not correct",
diff --git a/tpm/structures.go b/tpm/structures.go
index 2944c92..fce6f85 100644
--- a/tpm/structures.go
+++ b/tpm/structures.go
@@ -69,7 +69,7 @@ type pcrInfo struct {
 
 // A capVersionInfo contains information about the TPM itself. Note that this
 // is deserialized specially, since it has a variable-length byte array but no
-// length. It is preceeded with a length in the response to the Quote2 command.
+// length. It is preceded with a length in the response to the Quote2 command.
 type capVersionInfo struct {
 	CapVersionFixed capVersionInfoFixed
 	VendorSpecific  []byte
@@ -188,23 +188,23 @@ func (ra responseAuth) String() string {
 }
 
 // These are the parameters of a TPM key.
-type keyParms struct {
+type keyParams struct {
 	AlgID     uint32
 	EncScheme uint16
 	SigScheme uint16
-	Parms     []byte // Serialized rsaKeyParms or symmetricKeyParms.
+	Params     []byte // Serialized rsaKeyParams or symmetricKeyParams.
 }
 
-// An rsaKeyParms encodes the length of the RSA prime in bits, the number of
+// An rsaKeyParams encodes the length of the RSA prime in bits, the number of
 // primes in its factored form, and the exponent used for public-key
 // encryption.
-type rsaKeyParms struct {
+type rsaKeyParams struct {
 	KeyLength uint32
 	NumPrimes uint32
 	Exponent  []byte
 }
 
-type symmetricKeyParms struct {
+type symmetricKeyParams struct {
 	KeyLength uint32
 	BlockSize uint32
 	IV        []byte
@@ -216,7 +216,7 @@ type key struct {
 	KeyUsage       uint16
 	KeyFlags       uint32
 	AuthDataUsage  byte
-	AlgorithmParms keyParms
+	AlgorithmParams keyParams
 	PCRInfo        []byte
 	PubKey         []byte
 	EncData        []byte
@@ -229,7 +229,7 @@ type key12 struct {
 	KeyUsage       uint16
 	KeyFlags       uint32
 	AuthDataUsage  byte
-	AlgorithmParms keyParms
+	AlgorithmParams keyParams
 	PCRInfo        []byte // This must be a serialization of a pcrInfoLong.
 	PubKey         []byte
 	EncData        []byte
@@ -237,7 +237,7 @@ type key12 struct {
 
 // A pubKey represents a public key known to the TPM.
 type pubKey struct {
-	AlgorithmParms keyParms
+	AlgorithmParams keyParams
 	Key            []byte
 }
 
@@ -285,7 +285,7 @@ func convertPubKey(pk crypto.PublicKey) (*pubKey, error) {
 		return nil, errors.New("The provided Privacy CA RSA public key was not a 2048-bit key")
 	}
 
-	rsakp := rsaKeyParms{
+	rsakp := rsaKeyParams{
 		KeyLength: 2048,
 		NumPrimes: 2,
 		Exponent:  big.NewInt(int64(pkRSA.E)).Bytes(),
@@ -294,16 +294,16 @@ func convertPubKey(pk crypto.PublicKey) (*pubKey, error) {
 	if err != nil {
 		return nil, err
 	}
-	kp := keyParms{
+	kp := keyParams{
 		AlgID:     algRSA,
 		EncScheme: esNone,
 		SigScheme: ssRSASaPKCS1v15SHA1,
-		Parms:     rsakpb,
+		Params:     rsakpb,
 	}
-	pubk := pubKey{
-		AlgorithmParms: kp,
+	pubKey := pubKey{
+		AlgorithmParams: kp,
 		Key:            pkRSA.N.Bytes(),
 	}
 
-	return &pubk, nil
+	return &pubKey, nil
 }
diff --git a/tpm/tpm.go b/tpm/tpm.go
index a6bb12a..73e8e3d 100644
--- a/tpm/tpm.go
+++ b/tpm/tpm.go
@@ -257,7 +257,7 @@ func newOSAPSession(rw io.ReadWriter, entityType uint16, entityValue tpmutil.Han
 	hm.Write(osapData)
 	// Note that crypto/hash.Sum returns a slice rather than an array, so we
 	// have to copy this into an array to make sure that serialization doesn't
-	// preprend a length in tpmutil.Pack().
+	// prepend a length in tpmutil.Pack().
 	sharedSecretBytes := hm.Sum(nil)
 	copy(sharedSecret[:], sharedSecretBytes)
 	return sharedSecret, osapr, nil
@@ -548,14 +548,14 @@ func MakeIdentity(rw io.ReadWriter, srkAuth []byte, ownerAuth []byte, aikAuth []
 	}
 
 	if pk != nil {
-		pubk, err := convertPubKey(pk)
+		pubKey, err := convertPubKey(pk)
 		if err != nil {
 			return nil, err
 		}
 
 		// We can't pack the pair of values directly, since the label is
 		// included directly as bytes, without any length.
-		fullpkb, err := tpmutil.Pack(pubk)
+		fullpkb, err := tpmutil.Pack(pubKey)
 		if err != nil {
 			return nil, err
 		}
@@ -564,21 +564,21 @@ func MakeIdentity(rw io.ReadWriter, srkAuth []byte, ownerAuth []byte, aikAuth []
 		caDigest = sha1.Sum(caDigestBytes)
 	}
 
-	rsaAIKParms := rsaKeyParms{
+	rsaAIKParams := rsaKeyParams{
 		KeyLength: 2048,
 		NumPrimes: 2,
 		//Exponent:  big.NewInt(0x10001).Bytes(), // 65537. Implicit?
 	}
-	packedParms, err := tpmutil.Pack(rsaAIKParms)
+	packedParams, err := tpmutil.Pack(rsaAIKParams)
 	if err != nil {
 		return nil, err
 	}
 
-	aikParms := keyParms{
+	aikParams := keyParams{
 		AlgID:     algRSA,
 		EncScheme: esNone,
 		SigScheme: ssRSASaPKCS1v15SHA1,
-		Parms:     packedParms,
+		Params:     packedParams,
 	}
 
 	aik := &key{
@@ -586,7 +586,7 @@ func MakeIdentity(rw io.ReadWriter, srkAuth []byte, ownerAuth []byte, aikAuth []
 		KeyUsage:       keyIdentity,
 		KeyFlags:       0,
 		AuthDataUsage:  authAlways,
-		AlgorithmParms: aikParms,
+		AlgorithmParams: aikParams,
 	}
 
 	// The digest input for MakeIdentity authentication is
@@ -619,7 +619,7 @@ func MakeIdentity(rw io.ReadWriter, srkAuth []byte, ownerAuth []byte, aikAuth []
 		return nil, err
 	}
 
-	// TODO(tmroeder): check the signature against the pubek.
+	// TODO(tmroeder): check the signature against the pubEK.
 	blob, err := tpmutil.Pack(k)
 	if err != nil {
 		return nil, err
@@ -818,7 +818,7 @@ func TakeOwnership(rw io.ReadWriter, newOwnerAuth digest, newSRKAuth digest, pub
 	// - Sig must be None
 	// - Key usage must be Storage
 	// - Key must not be migratable
-	srkRSAParams := rsaKeyParms{
+	srkRSAParams := rsaKeyParams{
 		KeyLength: 2048,
 		NumPrimes: 2,
 	}
@@ -826,18 +826,18 @@ func TakeOwnership(rw io.ReadWriter, newOwnerAuth digest, newSRKAuth digest, pub
 	if err != nil {
 		return err
 	}
-	srkParams := keyParms{
+	srkParams := keyParams{
 		AlgID:     algRSA,
 		EncScheme: esRSAEsOAEPSHA1MGF1,
 		SigScheme: ssNone,
-		Parms:     srkpb,
+		Params:     srkpb,
 	}
 	srk := &key{
 		Version:        0x01010000,
 		KeyUsage:       keyStorage,
 		KeyFlags:       0,
 		AuthDataUsage:  authAlways,
-		AlgorithmParms: srkParams,
+		AlgorithmParams: srkParams,
 	}
 
 	// Get command auth using OIAP with the new owner auth.
@@ -899,7 +899,7 @@ func CreateWrapKey(rw io.ReadWriter, srkAuth []byte, usageAuth digest, migration
 		encMigrationAuth[i] = encAuthDataKey[i] ^ migrationAuth[i]
 	}
 
-	rParams := rsaKeyParms{
+	rParams := rsaKeyParams{
 		KeyLength: 2048,
 		NumPrimes: 2,
 	}
@@ -925,11 +925,11 @@ func CreateWrapKey(rw io.ReadWriter, srkAuth []byte, usageAuth digest, migration
 		KeyUsage:      keySigning,
 		KeyFlags:      0,
 		AuthDataUsage: authAlways,
-		AlgorithmParms: keyParms{
+		AlgorithmParams: keyParams{
 			AlgID:     algRSA,
 			EncScheme: esNone,
 			SigScheme: ssRSASaPKCS1v15DER,
-			Parms:     rParamsPacked,
+			Params:     rParamsPacked,
 		},
 		PCRInfo: pcrInfoBytes,
 	}
diff --git a/tpm/verify.go b/tpm/verify.go
index b8b5e1e..5ef2c10 100644
--- a/tpm/verify.go
+++ b/tpm/verify.go
@@ -43,14 +43,14 @@ func UnmarshalRSAPublicKey(keyBlob []byte) (*rsa.PublicKey, error) {
 // unmarshalRSAPublicKey unmarshals a TPM key into a crypto/rsa.PublicKey.
 func (k *key) unmarshalRSAPublicKey() (*rsa.PublicKey, error) {
 	// Currently, we only support algRSA
-	if k.AlgorithmParms.AlgID != algRSA {
+	if k.AlgorithmParams.AlgID != algRSA {
 		return nil, errors.New("only TPM_ALG_RSA is supported")
 	}
 
-	// This means that k.AlgorithmsParms.Parms is an rsaKeyParms, which is
+	// This means that k.AlgorithmsParams.Params is an rsaKeyParams, which is
 	// enough to create the exponent, and k.PubKey contains the key.
-	var rsakp rsaKeyParms
-	if _, err := tpmutil.Unpack(k.AlgorithmParms.Parms, &rsakp); err != nil {
+	var rsakp rsaKeyParams
+	if _, err := tpmutil.Unpack(k.AlgorithmParams.Params, &rsakp); err != nil {
 		return nil, err
 	}
 
@@ -82,14 +82,14 @@ func UnmarshalPubRSAPublicKey(keyBlob []byte) (*rsa.PublicKey, error) {
 // This is almost identical to the identically named function for a TPM key.
 func (pk *pubKey) unmarshalRSAPublicKey() (*rsa.PublicKey, error) {
 	// Currently, we only support algRSA
-	if pk.AlgorithmParms.AlgID != algRSA {
+	if pk.AlgorithmParams.AlgID != algRSA {
 		return nil, errors.New("only TPM_ALG_RSA is supported")
 	}
 
-	// This means that pk.AlgorithmsParms.Parms is an rsaKeyParms, which is
+	// This means that pk.AlgorithmsParams.Params is an rsaKeyParams, which is
 	// enough to create the exponent, and pk.Key contains the key.
-	var rsakp rsaKeyParms
-	if _, err := tpmutil.Unpack(pk.AlgorithmParms.Parms, &rsakp); err != nil {
+	var rsakp rsaKeyParams
+	if _, err := tpmutil.Unpack(pk.AlgorithmParams.Params, &rsakp); err != nil {
 		return nil, err
 	}
 
diff --git a/tpm2/constants.go b/tpm2/constants.go
index 043a655..5ceccd6 100644
--- a/tpm2/constants.go
+++ b/tpm2/constants.go
@@ -157,7 +157,7 @@ const (
 // Capability identifies some TPM property or state type.
 type Capability uint32
 
-// TPM Capabilies.
+// TPM Capabilities.
 const (
 	CapabilityAlgs Capability = iota
 	CapabilityHandles
@@ -178,6 +178,7 @@ const (
 	TagNoSessions     tpmutil.Tag = 0x8001
 	TagSessions       tpmutil.Tag = 0x8002
 	TagAttestCertify  tpmutil.Tag = 0x8017
+	TagAttestQuote    tpmutil.Tag = 0x8018
 	TagAttestCreation tpmutil.Tag = 0x801a
 	TagHashCheck      tpmutil.Tag = 0x8024
 )
@@ -287,3 +288,32 @@ var hashConstructors = map[Algorithm]func() hash.Hash{
 	AlgSHA384: sha512.New384,
 	AlgSHA512: sha512.New,
 }
+
+// NVAttr is a bitmask used in Attributes field of NV indexes. Individual
+// flags should be OR-ed to form a full mask.
+type NVAttr uint32
+
+// NV Attributes
+const (
+	AttrPPWrite        NVAttr = 0x00000001
+	AttrOwnerWrite     NVAttr = 0x00000002
+	AttrAuthWrite      NVAttr = 0x00000004
+	AttrPolicyWrite    NVAttr = 0x00000008
+	AttrPolicyDelete   NVAttr = 0x00000400
+	AttrWriteLocked    NVAttr = 0x00000800
+	AttrWriteAll       NVAttr = 0x00001000
+	AttrWriteDefine    NVAttr = 0x00002000
+	AttrWriteSTClear   NVAttr = 0x00004000
+	AttrGlobalLock     NVAttr = 0x00008000
+	AttrPPRead         NVAttr = 0x00010000
+	AttrOwnerRead      NVAttr = 0x00020000
+	AttrAuthRead       NVAttr = 0x00040000
+	AttrPolicyRead     NVAttr = 0x00080000
+	AttrNoDA           NVAttr = 0x02000000
+	AttrOrderly        NVAttr = 0x04000000
+	AttrClearSTClear   NVAttr = 0x08000000
+	AttrReadLocked     NVAttr = 0x10000000
+	AttrWritten        NVAttr = 0x20000000
+	AttrPlatformCreate NVAttr = 0x40000000
+	AttrReadSTClear    NVAttr = 0x80000000
+)
diff --git a/tpm2/kdf.go b/tpm2/kdf.go
index 040374b..4864461 100644
--- a/tpm2/kdf.go
+++ b/tpm2/kdf.go
@@ -50,7 +50,7 @@ func KDFa(hashAlg Algorithm, key []byte, label string, contextU, contextV []byte
 			return nil, fmt.Errorf("pack counter: %v", err)
 		}
 		mac.Write([]byte(label))
-		mac.Write([]byte{0}) // Terminating null chacter for C-string.
+		mac.Write([]byte{0}) // Terminating null character for C-string.
 		mac.Write(contextU)
 		mac.Write(contextV)
 		if err := binary.Write(mac, binary.BigEndian, uint32(bits)); err != nil {
diff --git a/tpm2/structures.go b/tpm2/structures.go
index d976df8..a05e1c3 100644
--- a/tpm2/structures.go
+++ b/tpm2/structures.go
@@ -443,6 +443,7 @@ type AttestationData struct {
 	ClockInfo            ClockInfo
 	FirmwareVersion      uint64
 	AttestedCertifyInfo  *CertifyInfo
+	AttestedQuoteInfo    *QuoteInfo
 	AttestedCreationInfo *CreationInfo
 }
 
@@ -476,6 +477,10 @@ func DecodeAttestationData(in []byte) (*AttestationData, error) {
 		if ad.AttestedCreationInfo, err = decodeCreationInfo(buf); err != nil {
 			return nil, fmt.Errorf("decoding AttestedCreationInfo: %v", err)
 		}
+	case TagAttestQuote:
+		if ad.AttestedQuoteInfo, err = decodeQuoteInfo(buf); err != nil {
+			return nil, fmt.Errorf("decoding AttestedQuoteInfo: %v", err)
+		}
 	default:
 		return nil, fmt.Errorf("only Certify & Creation attestation structures are supported, got type 0x%x", ad.Type)
 	}
@@ -590,6 +595,25 @@ func (ci CertifyInfo) encode() ([]byte, error) {
 	return concat(n, qn)
 }
 
+// QuoteInfo represents a TPMS_QUOTE_INFO structure.
+type QuoteInfo struct {
+	PCRSelection PCRSelection
+	PCRDigest    []byte
+}
+
+func decodeQuoteInfo(in *bytes.Buffer) (*QuoteInfo, error) {
+	var out QuoteInfo
+	sel, err := decodeTPMLPCRSelection(in)
+	if err != nil {
+		return nil, fmt.Errorf("decoding PCRSelection: %v", err)
+	}
+	out.PCRSelection = sel
+	if err := tpmutil.UnpackBuf(in, &out.PCRDigest); err != nil {
+		return nil, fmt.Errorf("decoding PCRDigest: %v", err)
+	}
+	return &out, nil
+}
+
 // IDObject represents an encrypted credential bound to a TPM object.
 type IDObject struct {
 	IntegrityHMAC []byte
diff --git a/tpm2/tpm2.go b/tpm2/tpm2.go
index 405bc96..dbf45f8 100644
--- a/tpm2/tpm2.go
+++ b/tpm2/tpm2.go
@@ -1010,7 +1010,7 @@ func NVUndefineSpace(rw io.ReadWriter, ownerAuth string, owner, index tpmutil.Ha
 	return err
 }
 
-func encodeDefineSpace(owner, handle tpmutil.Handle, ownerAuth, authVal string, attributes uint32, policy []byte, dataSize uint16) ([]byte, error) {
+func encodeDefineSpace(owner, handle tpmutil.Handle, ownerAuth, authVal string, attributes NVAttr, policy []byte, dataSize uint16) ([]byte, error) {
 	ha, err := tpmutil.Pack(owner)
 	if err != nil {
 		return nil, err
@@ -1031,7 +1031,7 @@ func encodeDefineSpace(owner, handle tpmutil.Handle, ownerAuth, authVal string,
 }
 
 // NVDefineSpace creates an index in TPM's NV storage.
-func NVDefineSpace(rw io.ReadWriter, owner, handle tpmutil.Handle, ownerAuth, authString string, policy []byte, attributes uint32, dataSize uint16) error {
+func NVDefineSpace(rw io.ReadWriter, owner, handle tpmutil.Handle, ownerAuth, authString string, policy []byte, attributes NVAttr, dataSize uint16) error {
 	cmd, err := encodeDefineSpace(owner, handle, ownerAuth, authString, attributes, policy, dataSize)
 	if err != nil {
 		return err
@@ -1076,6 +1076,16 @@ func decodeNVReadPublic(in []byte) (NVPublic, error) {
 	return pub, err
 }
 
+// NVReadPublic reads the public data of an NV index.
+func NVReadPublic(rw io.ReadWriter, index tpmutil.Handle) (NVPublic, error) {
+	// Read public area to determine data size.
+	resp, err := runCommand(rw, TagNoSessions, cmdReadPublicNV, index)
+	if err != nil {
+		return NVPublic{}, err
+	}
+	return decodeNVReadPublic(resp)
+}
+
 func decodeNVRead(in []byte) ([]byte, error) {
 	var sessionAttributes uint32
 	var data []byte
@@ -1130,11 +1140,7 @@ func NVReadEx(rw io.ReadWriter, index, authHandle tpmutil.Handle, password strin
 	}
 
 	// Read public area to determine data size.
-	resp, err := runCommand(rw, TagNoSessions, cmdReadPublicNV, index)
-	if err != nil {
-		return nil, fmt.Errorf("running NV_ReadPublic command: %v", err)
-	}
-	pub, err := decodeNVReadPublic(resp)
+	pub, err := NVReadPublic(rw, index)
 	if err != nil {
 		return nil, fmt.Errorf("decoding NV_ReadPublic response: %v", err)
 	}
@@ -1151,7 +1157,7 @@ func NVReadEx(rw io.ReadWriter, index, authHandle tpmutil.Handle, password strin
 		if err != nil {
 			return nil, fmt.Errorf("building NV_Read command: %v", err)
 		}
-		resp, err = runCommand(rw, TagSessions, cmdReadNV, tpmutil.RawBytes(cmd))
+		resp, err := runCommand(rw, TagSessions, cmdReadNV, tpmutil.RawBytes(cmd))
 		if err != nil {
 			return nil, fmt.Errorf("running NV_Read command (cursor=%d,size=%d): %v", len(outBuff), readSize, err)
 		}
diff --git a/tpmutil/mssim/mssim.go b/tpmutil/mssim/mssim.go
index 1ee5595..53602e2 100644
--- a/tpmutil/mssim/mssim.go
+++ b/tpmutil/mssim/mssim.go
@@ -43,7 +43,7 @@ const (
 	tpmSessionEnd     uint32 = 20
 )
 
-// Config holds configuration parameters for connecting to the simluator.
+// Config holds configuration parameters for connecting to the simulator.
 type Config struct {
 	// Addresses of the command and platform handlers.
 	//
@@ -128,7 +128,7 @@ type Conn struct {
 }
 
 // Read a response from the simulator. If the response is longer than the provided
-// buffer, the remainer will be cached for the next read.
+// buffer, the remainder will be cached for the next read.
 func (c *Conn) Read(b []byte) (int, error) {
 	if c.prevRead != nil && c.prevRead.Len() > 0 {
 		return c.prevRead.Read(b)
@@ -150,7 +150,7 @@ func (c *Conn) Read(b []byte) (int, error) {
 
 	var rc uint32
 	if err := binary.Read(c.conn, binary.BigEndian, &rc); err != nil {
-		return 0, fmt.Errorf("read MS simualtor return code: %v", err)
+		return 0, fmt.Errorf("read MS simulator return code: %v", err)
 	}
 	if rc != 0 {
 		return 0, fmt.Errorf("MS simulator returned invalid return code: 0x%x", rc)


Test patch:
diff --git a/tpm/tpm_test.go b/tpm/tpm_test.go
index 1184af6..4ecfa3f 100644
--- a/tpm/tpm_test.go
+++ b/tpm/tpm_test.go
@@ -501,12 +501,12 @@ func TestTakeOwnership(t *testing.T) {
 	srkAuth := getAuth(srkAuthEnvVar)
 
 	// This test assumes that the TPM has been cleared using OwnerClear.
-	pubek, err := ReadPubEK(rwc)
+	pubEK, err := ReadPubEK(rwc)
 	if err != nil {
 		t.Fatal("Couldn't read the public endorsement key from the TPM:", err)
 	}
 
-	if err := TakeOwnership(rwc, ownerAuth, srkAuth, pubek); err != nil {
+	if err := TakeOwnership(rwc, ownerAuth, srkAuth, pubEK); err != nil {
 		t.Fatal("Couldn't take ownership of the TPM:", err)
 	}
 }
diff --git a/tpm2/tpm2_test.go b/tpm2/tpm2_test.go
index f9f8daa..a386589 100644
--- a/tpm2/tpm2_test.go
+++ b/tpm2/tpm2_test.go
@@ -979,14 +979,14 @@ func TestNVRead(t *testing.T) {
 	}
 }
 
-func TestNVWrite(t *testing.T) {
+func TestNVReadWrite(t *testing.T) {
 	rw := openTPM(t)
 	defer rw.Close()
 
 	var (
 		idx  tpmutil.Handle = 0x1500000
 		data                = []byte("testdata")
-		attr uint32         = 0x20002 // TODO: change to named attributes like NVAttrOwnerWrite|NVAttrOwnerRead
+		attr                = AttrOwnerWrite | AttrOwnerRead
 	)
 
 	// Define space in NV storage and clean up afterwards or subsequent runs will fail.
@@ -1007,6 +1007,24 @@ func TestNVWrite(t *testing.T) {
 	if err := NVWrite(rw, HandleOwner, idx, emptyPassword, data, 0); err != nil {
 		t.Fatalf("NVWrite failed: %v", err)
 	}
+
+	// Make sure the public area of the index can be read
+	pub, err := NVReadPublic(rw, idx)
+	if err != nil {
+		t.Fatalf("NVReadPublic failed: %v", err)
+	}
+	if int(pub.DataSize) != len(data) {
+		t.Fatalf("public NV data size mismatch, got %d, want %d, ", pub.DataSize, len(data))
+	}
+
+	// Read all of the data with NVReadEx and compare to what was written
+	outdata, err := NVReadEx(rw, idx, HandleOwner, emptyPassword, 0)
+	if err != nil {
+		t.Fatalf("NVReadEx failed: %v", err)
+	}
+	if !bytes.Equal(data, outdata) {
+		t.Fatalf("data read from NV index does not match, got %x, want %x", outdata, data)
+	}
 }
 
 func TestPolicySecret(t *testing.T) {
@@ -1023,3 +1041,52 @@ func TestPolicySecret(t *testing.T) {
 		t.Fatalf("PolicySecret() failed: %v", err)
 	}
 }
+
+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,
+			Modulus: big.NewInt(0),
+		},
+	}
+	keyHandle, pub, _, _, _, _, err := CreatePrimaryEx(rw, HandleEndorsement, pcrSelection, 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, pcrSelection, AlgNull)
+	if err != nil {
+		t.Fatalf("Quote failed: %v", err)
+	}
+
+	att, err := DecodeAttestationData(attestation)
+	if err != nil {
+		t.Fatalf("DecodeAttestationData(%v) failed: %v", attestation, err)
+	}
+	if att.Type != TagAttestQuote {
+		t.Errorf("Got att.Type = %v, want TagAttestQuote", att.Type)
+	}
+	if att.AttestedQuoteInfo == nil {
+		t.Error("AttestedQuoteInfo = nil, want non-nil")
+	}
+	p, err := DecodePublic(pub)
+	if err != nil {
+		t.Fatalf("DecodePublic failed: %v", err)
+	}
+	rsaPub := rsa.PublicKey{E: int(p.RSAParameters.Exponent), N: p.RSAParameters.Modulus}
+	hsh := crypto.SHA256.New()
+	hsh.Write(attestation)
+	if err := rsa.VerifyPKCS1v15(&rsaPub, crypto.SHA256, hsh.Sum(nil), signature.RSA.Signature); err != nil {
+		t.Errorf("VerifyPKCS1v15 failed: %v", err)
+	}
+}