Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 132 additions & 0 deletions examples/tpm-fakeekcert/tpm-fakeekcert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
// 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/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", "Fake EK", "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
}
}
74 changes: 74 additions & 0 deletions examples/tpm-nvwrite/tpm-nvwrite.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// 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"
"io/ioutil"
"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()
if flag.NArg() < 1 {
fmt.Fprintf(os.Stderr, "Error: Missing required argument.")
return
}

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[:])
}

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, uint32(*index), uint32(len(data)), 0, ownerAuth); err != nil {
fmt.Fprintf(os.Stderr, "NVDefineSpace failed: %v\n", err)
return
}
}

if err := tpm.NVWriteValue(rwc, uint32(*index), 0, data, ownerAuth); err != nil {
fmt.Fprintf(os.Stderr, "NVWriteValue failed: %v\n", err)
return
}
}
22 changes: 22 additions & 0 deletions tpm/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -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, tpmutil.U32Bytes(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
Expand Down
21 changes: 21 additions & 0 deletions tpm/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ const (
ordOwnerReadInternalPub uint32 = 0x00000081
ordFlushSpecific uint32 = 0x000000BA
ordPcrReset uint32 = 0x000000C8
ordNVDefineSpace uint32 = 0x000000CC
ordNVWriteValue uint32 = 0x000000CD
ordNVReadValue uint32 = 0x000000CF
)

Expand Down Expand Up @@ -170,3 +172,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
48 changes: 47 additions & 1 deletion tpm/structures.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ type pcrInfoLong struct {

// pcrInfoShort stores detailed information about PCRs.
type pcrInfoShort struct {
LocAtRelease byte
PCRsAtRelease pcrSelection
LocAtRelease byte
DigestAtRelease digest
}

Expand Down Expand Up @@ -314,3 +314,49 @@ 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
}

// 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
}
Loading