Repository: google/go-tpm
Pull request #129 (2019-09-12 08:59:37)

TPM1.2: Add NVWriteValue
Add function to write NVRAM

ChriMarMe: @tmroeder and here
ChriMarMe: Really need some review here please
ChriMarMe: Can't write a test which test this function unless GetNVList of #128 is merged. 
I need to read some proper addresses first where i can try to write to.

Otherwise I need to implement DefineSpace at first, so I can test all the together.
-define space at NVRAM 
-write a defined value
-read the value and compare to defined value
-release space at NVRAM for clean up


twitchy-jsonp: @ChriMarMe you might need to merge in the latest master from go-tpm before it will let us merge this.

@tmroeder Github has you are marked as a reviewer, do you have outstanding comments / review?

Patch:
diff --git a/tpm/commands.go b/tpm/commands.go
index 90cd014..7b1f30a 100644
--- a/tpm/commands.go
+++ b/tpm/commands.go
@@ -193,6 +193,24 @@ func nvReadValue(rw io.ReadWriter, index, offset, len uint32, ca *commandAuth) (
 	return b, &ra, ret, nil
 }
 
+// nvWriteValue writes to the NVRAM
+// If TPM isn't locked, no authentication is needed.
+// See TPM-Main-Part-3-Commands-20.2
+func nvWriteValue(rw io.ReadWriter, index, offset, len uint32, data []byte, ca *commandAuth) ([]byte, *responseAuth, uint32, error) {
+	var b tpmutil.U32Bytes
+	var ra responseAuth
+	in := []interface{}{index, offset, len, data}
+	if ca != nil {
+		in = append(in, ca)
+	}
+	out := []interface{}{&b, &ra}
+	ret, err := submitTPMRequest(rw, tagRQUAuth1Command, ordNVWriteValue, in, out)
+	if err != nil {
+		return nil, nil, 0, err
+	}
+	return b, &ra, ret, nil
+}
+
 // quote2 signs arbitrary data under a given set of PCRs and using a key
 // specified by keyHandle. It returns information about the PCRs it signed
 // under, the signature, auth information, and optionally information about the
diff --git a/tpm/constants.go b/tpm/constants.go
index bc17071..7845ea9 100644
--- a/tpm/constants.go
+++ b/tpm/constants.go
@@ -60,6 +60,7 @@ const (
 	ordFlushSpecific        uint32 = 0x000000BA
 	ordNVDefineSpace        uint32 = 0x000000CC
 	ordPcrReset             uint32 = 0x000000C8
+	ordNVWriteValue         uint32 = 0x000000CD
 	ordNVReadValue          uint32 = 0x000000CF
 )
 
diff --git a/tpm/tpm.go b/tpm/tpm.go
index 55cf5c3..1bcc9d3 100644
--- a/tpm/tpm.go
+++ b/tpm/tpm.go
@@ -981,6 +981,38 @@ func NVReadValue(rw io.ReadWriter, index, offset, len uint32, ownAuth []byte) ([
 	return data, nil
 }
 
+// NVWriteValue for writing to the NVRAM. Needs a index for a defined space in NVRAM.
+// See TPM-Main-Part-3-Commands_v1.2_rev116_01032011, P216
+func NVWriteValue(rw io.ReadWriter, index, offset uint32, data []byte, ownAuth []byte) error {
+	if ownAuth == nil {
+		if _, _, _, err := nvWriteValue(rw, index, offset, uint32(len(data)), data, nil); err != nil {
+			return fmt.Errorf("failed to write to NVRAM: %v", err)
+		}
+		return nil
+	}
+	sharedSecretOwn, osaprOwn, err := newOSAPSession(rw, etOwner, khOwner, ownAuth[:])
+	if err != nil {
+		return fmt.Errorf("failed to start new auth session: %v", err)
+	}
+	defer osaprOwn.Close(rw)
+	defer zeroBytes(sharedSecretOwn[:])
+	authIn := []interface{}{ordNVWriteValue, index, offset, len(data), data}
+	ca, err := newCommandAuth(osaprOwn.AuthHandle, osaprOwn.NonceEven, sharedSecretOwn[:], authIn)
+	if err != nil {
+		return fmt.Errorf("failed to construct owner auth fields: %v", err)
+	}
+	data, ra, ret, err := nvWriteValue(rw, index, offset, uint32(len(data)), data, ca)
+	if err != nil {
+		return fmt.Errorf("failed to write to NVRAM: %v", err)
+	}
+	raIn := []interface{}{ret, ordNVReadValue, tpmutil.U32Bytes(data)}
+	if err := ra.verify(ca.NonceOdd, sharedSecretOwn[:], raIn); err != nil {
+		return fmt.Errorf("failed to verify authenticity of response: %v", err)
+	}
+
+	return nil
+}
+
 // OwnerReadPubEK uses owner auth to get a blob representing the public part of the
 // endorsement key.
 func OwnerReadPubEK(rw io.ReadWriter, ownerAuth digest) ([]byte, error) {


Test patch:
diff --git a/tpm/tpm_test.go b/tpm/tpm_test.go
index dae62f8..4b64bec 100644
--- a/tpm/tpm_test.go
+++ b/tpm/tpm_test.go
@@ -20,6 +20,7 @@ import (
 	"crypto/sha1"
 	"crypto/x509"
 	"io/ioutil"
+	mathrand "math/rand"
 	"os"
 	"testing"
 
@@ -631,3 +632,33 @@ func TestForceClear(t *testing.T) {
 		t.Fatal("Couldn't clear the TPM without owner auth in physical presence mode:", err)
 	}
 }
+
+func TestNVWriteValue(t *testing.T) {
+	mathrand.Seed(42)
+	rwc := openTPMOrSkip(t)
+	nvList, err := GetNVList(rwc)
+	if err != nil {
+		t.Fatalf("Couldn't open TPM connection: %v", err)
+	}
+	// If no indices are set, skip the test.
+	// Need DefineSpace-Function for implementing the other case
+	if len(nvList) == 0 {
+		t.Skip()
+	}
+	rndIndex := nvList[mathrand.Intn(len(nvList))]
+	rndIndexInfo, err := GetNVIndex(rwc, rndIndex)
+	if err != nil {
+		t.Fatalf("Couldn't retrieve index information: %v", err)
+	}
+
+	indexValue, err := NVReadValue(rwc, rndIndex, 0, rndIndexInfo.Size, nil)
+	if err != nil {
+		t.Fatalf("Couldn't read index value @ index: %v with error: %v", rndIndex, err)
+	}
+
+	err = NVWriteValue(rwc, rndIndex, 0, indexValue, nil)
+	if err != nil {
+		t.Errorf("Couldn't write to index: %v with error: %v", rndIndex, err)
+	}
+
+}