From a5402837d0eeed1ae9a739af8a691509eda8707e Mon Sep 17 00:00:00 2001 From: Tom D'Netto Date: Mon, 22 Apr 2019 14:34:47 -0700 Subject: [PATCH 1/8] Implement example to generate a fake EK certificate. --- examples/tpm-fakeekcert/tpm-fakeekcert.go | 132 ++++++++++++++++++++++ tpm/structures.go | 25 ++++ tpm/tpm_test.go | 31 +++++ 3 files changed, 188 insertions(+) create mode 100644 examples/tpm-fakeekcert/tpm-fakeekcert.go diff --git a/examples/tpm-fakeekcert/tpm-fakeekcert.go b/examples/tpm-fakeekcert/tpm-fakeekcert.go new file mode 100644 index 00000000..cdf06d4c --- /dev/null +++ b/examples/tpm-fakeekcert/tpm-fakeekcert.go @@ -0,0 +1,132 @@ +// Copyright (c) 2014, Google LLC All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/sha1" + "crypto/x509" + "crypto/x509/pkix" + "encoding/binary" + "flag" + "fmt" + "io" + "math/big" + "os" + "time" + + "github.com/google/go-tpm/tpm" +) + +var ( + ownerAuthEnvVar = "TPM_OWNER_AUTH" + + tpmPath = flag.String("tpm", "/dev/tpm0", "The path to the TPM device to use") + certPath = flag.String("cert", "ek.der", "The path to write the EK out to") + certOrg = flag.String("cert_org", "Acme Co", "The organization string to use in the EKCert") +) + +func generateCertificate(pub *rsa.PublicKey) ([]byte, error) { + priv, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return nil, err + } + + serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) + serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) + if err != nil { + return nil, err + } + + template := x509.Certificate{ + SerialNumber: serialNumber, + Subject: pkix.Name{ + Organization: []string{*certOrg}, + }, + NotBefore: time.Now(), + NotAfter: time.Now().AddDate(1, 0, 0), + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + BasicConstraintsValid: true, + } + + return x509.CreateCertificate(rand.Reader, &template, &template, pub, priv) +} + +func writePCCert(f io.Writer, der []byte) error { + // Write the header as documented in: TCG PC Specific Implementation + // Specification, section 7.3.2. + if _, err := f.Write([]byte{0x10, 0x01, 0x00}); err != nil { + return err + } + certLength := make([]byte, 2) + binary.BigEndian.PutUint16(certLength, uint16(len(der))) + if _, err := f.Write(certLength); err != nil { + return err + } + + _, err := f.Write(der) + return err +} + +func main() { + flag.Parse() + + var ownerAuth [20]byte + ownerInput := os.Getenv(ownerAuthEnvVar) + if ownerInput != "" { + oa := sha1.Sum([]byte(ownerInput)) + copy(ownerAuth[:], oa[:]) + } + + rwc, err := tpm.OpenTPM(*tpmPath) + if err != nil { + fmt.Fprintf(os.Stderr, "Couldn't open the TPM at %q: %v\n", *tpmPath, err) + return + } + + pubEK, err := tpm.OwnerReadPubEK(rwc, ownerAuth) + if err != nil { + fmt.Fprintf(os.Stderr, "Couldn't read the endorsement key: %v\n", err) + return + } + pub, err := tpm.DecodePublic(pubEK) + if err != nil { + fmt.Fprintf(os.Stderr, "Couldn't decode the endorsement key: %v\n", err) + return + } + + der, err := generateCertificate(pub.(*rsa.PublicKey)) + if err != nil { + fmt.Fprintf(os.Stderr, "Couldn't generate a certificate: %v\n", err) + return + } + + f, err := os.OpenFile(*certPath, os.O_RDWR|os.O_TRUNC|os.O_CREATE, 0744) + if err != nil { + fmt.Fprintf(os.Stderr, "Could open certificate path %q: %v\n", *certPath, err) + return + } + defer func() { + if err := f.Close(); err != nil { + fmt.Fprintf(os.Stderr, "Failed to close %q: %v\n", *certPath, err) + } + }() + + if err := writePCCert(f, der); err != nil { + fmt.Fprintf(os.Stderr, "Failed to write certificate: %v\n", err) + return + } +} diff --git a/tpm/structures.go b/tpm/structures.go index 4bb21f17..e5d60949 100644 --- a/tpm/structures.go +++ b/tpm/structures.go @@ -359,3 +359,28 @@ func convertPubKey(pk crypto.PublicKey) (*pubKey, error) { return &pubKey, nil } + +// DecodePublic consumes bytes representing a TPM_PUBKEY, and returns a +// crypto.PublicKey representing the encoded public key. Currently, this +// function only supports 2048-bit RSA keys. +func DecodePublic(b []byte) (crypto.PublicKey, error) { + var pk pubKey + if _, err := tpmutil.Unpack(b, &pk); err != nil { + return nil, err + } + if pk.AlgorithmParams.AlgID != algRSA { + return nil, fmt.Errorf("expected RSA algorithm, got %v", pk.AlgorithmParams.AlgID) + } + var kp rsaKeyParams + if _, err := tpmutil.Unpack(pk.AlgorithmParams.Params, &kp); err != nil { + return nil, fmt.Errorf("unpacking rsaKeyParams: %v", err) + } + if kp.KeyLength != 2048 { + return nil, fmt.Errorf("only 2048-bit keys supported, got %d-bit", kp.KeyLength) + } + + return &rsa.PublicKey{ + E: int(big.NewInt(0).SetBytes(kp.Exponent).Int64()), + N: big.NewInt(0).SetBytes(pk.Key), + }, nil +} diff --git a/tpm/tpm_test.go b/tpm/tpm_test.go index 3ef08b4f..875c3686 100644 --- a/tpm/tpm_test.go +++ b/tpm/tpm_test.go @@ -17,6 +17,7 @@ package tpm import ( "bytes" "crypto/rand" + "crypto/rsa" "crypto/sha1" "crypto/x509" "io/ioutil" @@ -554,3 +555,33 @@ func TestForceClear(t *testing.T) { t.Fatal("Couldn't clear the TPM without owner auth in physical presence mode:", err) } } + +func TestEncodePubkey(t *testing.T) { + k, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + t.Fatal(err) + } + pk, err := convertPubKey(k.Public()) + if err != nil { + t.Fatalf("convertPubKey() failed: %v", err) + } + + packed, err := tpmutil.Pack(&pk) + if err != nil { + t.Fatalf("tpmutil.Pack(&pk) failed: %v", err) + } + pk2, err := DecodePublic(packed) + if err != nil { + t.Fatalf("DecodePublic() failed: %v", err) + } + decodedPK := pk2.(*rsa.PublicKey) + + if k.N.Cmp(decodedPK.N) != 0 { + t.Errorf("k.N != decodedPK.N") + t.Logf("Got: %v", k.N) + t.Logf("Want: %v", decodedPK.N) + } + if k.E != decodedPK.E { + t.Errorf("decodedPK.E = %v, want %v", decodedPK.E, k.E) + } +} From 6fc8e1dfdb656488e7807a8391e0effc4c290658 Mon Sep 17 00:00:00 2001 From: Tom D'Netto Date: Mon, 22 Apr 2019 14:37:46 -0700 Subject: [PATCH 2/8] Correct test name --- tpm/tpm_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tpm/tpm_test.go b/tpm/tpm_test.go index 875c3686..92904cbe 100644 --- a/tpm/tpm_test.go +++ b/tpm/tpm_test.go @@ -556,7 +556,7 @@ func TestForceClear(t *testing.T) { } } -func TestEncodePubkey(t *testing.T) { +func TestEncodeDecodePubkey(t *testing.T) { k, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { t.Fatal(err) From 4ec831d06ebac0ab27e7193cad2a2b70d8703eed Mon Sep 17 00:00:00 2001 From: Tom D'Netto Date: Mon, 22 Apr 2019 14:38:41 -0700 Subject: [PATCH 3/8] Fix year in license header --- examples/tpm-fakeekcert/tpm-fakeekcert.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/tpm-fakeekcert/tpm-fakeekcert.go b/examples/tpm-fakeekcert/tpm-fakeekcert.go index cdf06d4c..f89afa8c 100644 --- a/examples/tpm-fakeekcert/tpm-fakeekcert.go +++ b/examples/tpm-fakeekcert/tpm-fakeekcert.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014, Google LLC All rights reserved. +// Copyright (c) 2019, Google LLC All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. From b734523c810d5ecbb3302d905ee9e04b551e11fb Mon Sep 17 00:00:00 2001 From: Tom D'Netto Date: Mon, 22 Apr 2019 14:48:54 -0700 Subject: [PATCH 4/8] Use 'Fake EK' as the default certificate org value --- examples/tpm-fakeekcert/tpm-fakeekcert.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/tpm-fakeekcert/tpm-fakeekcert.go b/examples/tpm-fakeekcert/tpm-fakeekcert.go index f89afa8c..a6cfb8cb 100644 --- a/examples/tpm-fakeekcert/tpm-fakeekcert.go +++ b/examples/tpm-fakeekcert/tpm-fakeekcert.go @@ -36,7 +36,7 @@ var ( tpmPath = flag.String("tpm", "/dev/tpm0", "The path to the TPM device to use") certPath = flag.String("cert", "ek.der", "The path to write the EK out to") - certOrg = flag.String("cert_org", "Acme Co", "The organization string to use in the EKCert") + certOrg = flag.String("cert_org", "Fake EK", "The organization string to use in the EKCert") ) func generateCertificate(pub *rsa.PublicKey) ([]byte, error) { From 7a4dc54ec0761bde8c8876d77e7a33d557fa6623 Mon Sep 17 00:00:00 2001 From: Tom D'Netto Date: Thu, 25 Apr 2019 09:52:02 -0700 Subject: [PATCH 5/8] Implement NVDefineSpace, NVWriteValue --- examples/tpm-nvwrite/tpm-nvwrite.go | 58 +++++++++++++++++++++++++++++ tpm/commands.go | 22 +++++++++++ tpm/constants.go | 21 +++++++++++ tpm/structures.go | 21 +++++++++++ tpm/tpm.go | 51 +++++++++++++++++++++++++ 5 files changed, 173 insertions(+) create mode 100644 examples/tpm-nvwrite/tpm-nvwrite.go diff --git a/examples/tpm-nvwrite/tpm-nvwrite.go b/examples/tpm-nvwrite/tpm-nvwrite.go new file mode 100644 index 00000000..c8a64b61 --- /dev/null +++ b/examples/tpm-nvwrite/tpm-nvwrite.go @@ -0,0 +1,58 @@ +// Copyright (c) 2019, Google LLC All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "crypto/sha1" + "flag" + "fmt" + "os" + + "github.com/google/go-tpm/tpm" +) + +const ekCertIndex = 268496896 + +var ( + ownerAuthEnvVar = "TPM_OWNER_AUTH" + + tpmPath = flag.String("tpm", "/dev/tpm0", "The path to the TPM device to use") + index = flag.Uint("index", ekCertIndex, "NV index to write to") + defineSpace = flag.Bool("define_space", false, "Whether to define the region in NVRAM") +) + +func main() { + flag.Parse() + + rwc, err := tpm.OpenTPM(*tpmPath) + if err != nil { + fmt.Fprintf(os.Stderr, "Couldn't open the TPM file %s: %s\n", *tpmPath, err) + return + } + + var ownerAuth [20]byte + ownerInput := os.Getenv(ownerAuthEnvVar) + if ownerInput != "" { + oa := sha1.Sum([]byte(ownerInput)) + copy(ownerAuth[:], oa[:]) + } + + if *defineSpace { + if err := tpm.NVDefineSpace(rwc, &tpm.NVPublicDescription{Index: uint32(*index)}, ownerAuth); err != nil { + fmt.Fprintf(os.Stderr, "NVDefineSpace failed: %v\n", err) + return + } + } +} diff --git a/tpm/commands.go b/tpm/commands.go index 7177b7e8..e6e383fb 100644 --- a/tpm/commands.go +++ b/tpm/commands.go @@ -170,6 +170,28 @@ func nvReadValue(rw io.ReadWriter, index, offset, len uint32, ca *commandAuth) ( return b, &ra, ret, nil } +func nvWriteValue(rw io.ReadWriter, index, offset uint32, data []byte, ca *commandAuth) (*responseAuth, uint32, error) { + var ra responseAuth + in := []interface{}{index, offset, uint32(len(data)), tpmutil.RawBytes(data), ca} + out := []interface{}{&ra} + ret, err := submitTPMRequest(rw, tagRQUAuth1Command, ordNVWriteValue, in, out) + if err != nil { + return nil, 0, err + } + return &ra, ret, nil +} + +func nvDefineSpace(rw io.ReadWriter, pub *NVPublicDescription, ca *commandAuth) (*responseAuth, uint32, error) { + var ra responseAuth + in := []interface{}{pub, ca} + out := []interface{}{&ra} + ret, err := submitTPMRequest(rw, tagRQUAuth1Command, ordNVDefineSpace, in, out) + if err != nil { + return nil, 0, err + } + return &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 8dc8e4cf..ce066186 100644 --- a/tpm/constants.go +++ b/tpm/constants.go @@ -58,6 +58,8 @@ const ( ordOwnerReadInternalPub uint32 = 0x00000081 ordFlushSpecific uint32 = 0x000000BA ordPcrReset uint32 = 0x000000C8 + ordNVDefineSpace uint32 = 0x000000CC + ordNVWriteValue uint32 = 0x000000CD ordNVReadValue uint32 = 0x000000CF ) @@ -175,3 +177,22 @@ const quoteVersion uint32 = 0x01010000 // oaepLabel is the label used for OEAP encryption in esRSAEsOAEPSHA1MGF1 var oaepLabel = []byte{byte('T'), byte('C'), byte('P'), byte('A')} + +// NV Attributes, as defined by section 19.2 of +// the TPM 1.2 structures specification. +const ( + NVAttrPPWrite NVAttr = (1 << 0) + NVAttrOwnerWrite NVAttr = (1 << 1) + NVAttrAuthWrite NVAttr = (1 << 2) + NVAttrWriteAll NVAttr = (1 << 12) + NVAttrWriteDefine NVAttr = (1 << 13) + NVAttrWriteSTClear NVAttr = (1 << 14) + NVAttrGlobalLock NVAttr = (1 << 15) + NVAttrPPRead NVAttr = (1 << 16) + NVAttrOwnerRead NVAttr = (1 << 17) + NVAttrAuthRead NVAttr = (1 << 18) + NVAttrReadSTClear NVAttr = (1 << 31) +) + +// NVAttr represents a NV area attributes value. +type NVAttr uint32 diff --git a/tpm/structures.go b/tpm/structures.go index e5d60949..272bc144 100644 --- a/tpm/structures.go +++ b/tpm/structures.go @@ -384,3 +384,24 @@ func DecodePublic(b []byte) (crypto.PublicKey, error) { N: big.NewInt(0).SetBytes(pk.Key), }, nil } + +// NVAttributesArea describes the permissions set on an NV area. +type NVAttributesArea struct { + Tag uint16 + Attributes NVAttr +} + +// NVPublicDescription describes the public description and controls on +// a NV area. This struct mirrors the layout of the TPM_NV_DATA_PUBLIC +// structure in the TPM 1.2 specification. +type NVPublicDescription struct { + Tag uint16 + Index uint32 + ReadPCRState pcrInfoShort + WritePCRState pcrInfoShort + Attributes NVAttributesArea + ReadSTClear byte + WriteSTClear byte + WriteDefine byte + DataSize uint32 +} diff --git a/tpm/tpm.go b/tpm/tpm.go index 46ef7e70..794fc7cf 100644 --- a/tpm/tpm.go +++ b/tpm/tpm.go @@ -934,6 +934,57 @@ func NVReadValue(rw io.ReadWriter, index, offset, len uint32, ownAuth digest) ([ return data, nil } +// NVDefineSpace creates a new region in NVRAM at the specified index, and +// with the specified properties. See TPM-Main-Part-2-TPM-Structures 19.3. +func NVDefineSpace(rw io.ReadWriter, pub *NVPublicDescription, ownAuth digest) error { + pub.Tag = 0x0018 + 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{}{ordNVDefineSpace, pub} + ca, err := newCommandAuth(osaprOwn.AuthHandle, osaprOwn.NonceEven, sharedSecretOwn[:], authIn) + if err != nil { + return fmt.Errorf("failed to construct owner auth fields: %v", err) + } + ra, ret, err := nvDefineSpace(rw, pub, ca) + if err != nil { + return fmt.Errorf("failed to define space in NVRAM: %v", err) + } + raIn := []interface{}{ret, ordNVDefineSpace} + if err := ra.verify(ca.NonceOdd, sharedSecretOwn[:], raIn); err != nil { + return fmt.Errorf("failed to verify authenticity of response: %v", err) + } + return nil +} + +// NVWriteValue writes the value into a given index & offset in NVRAM. +// See TPM-Main-Part-2-TPM-Commands 20.2. +func NVWriteValue(rw io.ReadWriter, index, offset uint32, data []byte, ownAuth digest) error { + 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, uint32(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) + } + ra, ret, err := nvWriteValue(rw, index, offset, data, ca) + if err != nil { + return fmt.Errorf("failed to read from NVRAM: %v", err) + } + raIn := []interface{}{ret, ordNVWriteValue} + 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) { From 3e9dbb080d78faf724040814ec808544113a5681 Mon Sep 17 00:00:00 2001 From: Tom D'Netto Date: Thu, 25 Apr 2019 10:09:09 -0700 Subject: [PATCH 6/8] Use U32Bytes in new methods --- tpm/commands.go | 2 +- tpm/tpm.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tpm/commands.go b/tpm/commands.go index 402fbead..51897ba0 100644 --- a/tpm/commands.go +++ b/tpm/commands.go @@ -172,7 +172,7 @@ func nvReadValue(rw io.ReadWriter, index, offset, len uint32, ca *commandAuth) ( func nvWriteValue(rw io.ReadWriter, index, offset uint32, data []byte, ca *commandAuth) (*responseAuth, uint32, error) { var ra responseAuth - in := []interface{}{index, offset, uint32(len(data)), tpmutil.RawBytes(data), ca} + in := []interface{}{index, offset, tpmutil.U32Bytes(data), ca} out := []interface{}{&ra} ret, err := submitTPMRequest(rw, tagRQUAuth1Command, ordNVWriteValue, in, out) if err != nil { diff --git a/tpm/tpm.go b/tpm/tpm.go index 2d883c60..afa1f2b3 100644 --- a/tpm/tpm.go +++ b/tpm/tpm.go @@ -969,7 +969,7 @@ func NVWriteValue(rw io.ReadWriter, index, offset uint32, data []byte, ownAuth d } defer osaprOwn.Close(rw) defer zeroBytes(sharedSecretOwn[:]) - authIn := []interface{}{ordNVWriteValue, index, offset, uint32(len(data)), data} + authIn := []interface{}{ordNVWriteValue, index, offset, tpmutil.U32Bytes(data)} ca, err := newCommandAuth(osaprOwn.AuthHandle, osaprOwn.NonceEven, sharedSecretOwn[:], authIn) if err != nil { return fmt.Errorf("failed to construct owner auth fields: %v", err) From c26e619cb5b8424d419901ecb8045b6a3827378a Mon Sep 17 00:00:00 2001 From: Tom D'Netto Date: Thu, 25 Apr 2019 10:15:37 -0700 Subject: [PATCH 7/8] Finish implementing example nvwrite --- examples/tpm-nvwrite/tpm-nvwrite.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/examples/tpm-nvwrite/tpm-nvwrite.go b/examples/tpm-nvwrite/tpm-nvwrite.go index c8a64b61..368a1577 100644 --- a/examples/tpm-nvwrite/tpm-nvwrite.go +++ b/examples/tpm-nvwrite/tpm-nvwrite.go @@ -17,6 +17,7 @@ package main import ( "crypto/sha1" "flag" + "io/ioutil" "fmt" "os" @@ -35,6 +36,10 @@ var ( func main() { flag.Parse() + if flag.NArg() < 1 { + fmt.Fprintf(os.Stderr, "Error: Missing required argument.") + return + } rwc, err := tpm.OpenTPM(*tpmPath) if err != nil { @@ -55,4 +60,15 @@ func main() { return } } + + data, err := ioutil.ReadFile(flag.Arg(1)) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to read input data from %s: %s\n", flag.Arg(1), err) + return + } + + if err := tpm.NVWriteValue(rwc, uint32(*index), 0, data, ownerAuth); err != nil { + fmt.Fprintf(os.Stderr, "NVWriteValue failed: %v\n", err) + return + } } From 775541b9c865e377fef294bd11bb4a77390eabe1 Mon Sep 17 00:00:00 2001 From: Tom D'Netto Date: Thu, 25 Apr 2019 11:18:41 -0700 Subject: [PATCH 8/8] Refactoring pass 1 --- examples/tpm-nvwrite/tpm-nvwrite.go | 16 ++++++++-------- tpm/commands.go | 2 +- tpm/structures.go | 6 +++--- tpm/tpm.go | 28 ++++++++++++++++++++++++---- 4 files changed, 36 insertions(+), 16 deletions(-) diff --git a/examples/tpm-nvwrite/tpm-nvwrite.go b/examples/tpm-nvwrite/tpm-nvwrite.go index 368a1577..00344e56 100644 --- a/examples/tpm-nvwrite/tpm-nvwrite.go +++ b/examples/tpm-nvwrite/tpm-nvwrite.go @@ -17,8 +17,8 @@ package main import ( "crypto/sha1" "flag" - "io/ioutil" "fmt" + "io/ioutil" "os" "github.com/google/go-tpm/tpm" @@ -54,19 +54,19 @@ func main() { copy(ownerAuth[:], oa[:]) } + data, err := ioutil.ReadFile(flag.Arg(0)) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to read input data from %s: %s\n", flag.Arg(0), err) + return + } + if *defineSpace { - if err := tpm.NVDefineSpace(rwc, &tpm.NVPublicDescription{Index: uint32(*index)}, ownerAuth); err != nil { + if err := tpm.NVDefineSpace(rwc, uint32(*index), uint32(len(data)), 0, ownerAuth); err != nil { fmt.Fprintf(os.Stderr, "NVDefineSpace failed: %v\n", err) return } } - data, err := ioutil.ReadFile(flag.Arg(1)) - if err != nil { - fmt.Fprintf(os.Stderr, "Failed to read input data from %s: %s\n", flag.Arg(1), err) - return - } - if err := tpm.NVWriteValue(rwc, uint32(*index), 0, data, ownerAuth); err != nil { fmt.Fprintf(os.Stderr, "NVWriteValue failed: %v\n", err) return diff --git a/tpm/commands.go b/tpm/commands.go index 51897ba0..df8b5663 100644 --- a/tpm/commands.go +++ b/tpm/commands.go @@ -181,7 +181,7 @@ func nvWriteValue(rw io.ReadWriter, index, offset uint32, data []byte, ca *comma return &ra, ret, nil } -func nvDefineSpace(rw io.ReadWriter, pub *NVPublicDescription, ca *commandAuth) (*responseAuth, uint32, error) { +func nvDefineSpace(rw io.ReadWriter, pub *nvPublicDescription, ca *commandAuth) (*responseAuth, uint32, error) { var ra responseAuth in := []interface{}{pub, ca} out := []interface{}{&ra} diff --git a/tpm/structures.go b/tpm/structures.go index a2820b0a..45fffdd3 100644 --- a/tpm/structures.go +++ b/tpm/structures.go @@ -56,8 +56,8 @@ type pcrInfoLong struct { // pcrInfoShort stores detailed information about PCRs. type pcrInfoShort struct { - LocAtRelease byte PCRsAtRelease pcrSelection + LocAtRelease byte DigestAtRelease digest } @@ -346,10 +346,10 @@ type NVAttributesArea struct { Attributes NVAttr } -// NVPublicDescription describes the public description and controls on +// nvPublicDescription describes the public description and controls on // a NV area. This struct mirrors the layout of the TPM_NV_DATA_PUBLIC // structure in the TPM 1.2 specification. -type NVPublicDescription struct { +type nvPublicDescription struct { Tag uint16 Index uint32 ReadPCRState pcrInfoShort diff --git a/tpm/tpm.go b/tpm/tpm.go index afa1f2b3..61ff18e7 100644 --- a/tpm/tpm.go +++ b/tpm/tpm.go @@ -936,20 +936,40 @@ func NVReadValue(rw io.ReadWriter, index, offset, len uint32, ownAuth digest) ([ // NVDefineSpace creates a new region in NVRAM at the specified index, and // with the specified properties. See TPM-Main-Part-2-TPM-Structures 19.3. -func NVDefineSpace(rw io.ReadWriter, pub *NVPublicDescription, ownAuth digest) error { - pub.Tag = 0x0018 +func NVDefineSpace(rw io.ReadWriter, index, len uint32, attrs NVAttr, ownAuth digest) error { + pcrs, err := newPCRSelection([]int{}) + if err != nil { + return fmt.Errorf("newPCRSelection failed: %v", err) + } + pcrState := pcrInfoShort{ + LocAtRelease: 0x1f, + PCRsAtRelease: *pcrs, + } + + pub := nvPublicDescription{ + Tag: 0x0018, + Index: index, + ReadPCRState: pcrState, + WritePCRState: pcrState, + Attributes: NVAttributesArea{ + Tag: 0x0017, + Attributes: attrs, + }, + DataSize: len, + } + 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{}{ordNVDefineSpace, pub} + authIn := []interface{}{ordNVDefineSpace, &pub} ca, err := newCommandAuth(osaprOwn.AuthHandle, osaprOwn.NonceEven, sharedSecretOwn[:], authIn) if err != nil { return fmt.Errorf("failed to construct owner auth fields: %v", err) } - ra, ret, err := nvDefineSpace(rw, pub, ca) + ra, ret, err := nvDefineSpace(rw, &pub, ca) if err != nil { return fmt.Errorf("failed to define space in NVRAM: %v", err) }