Skip to content

Commit

Permalink
Add certification to Seal (#60)
Browse files Browse the repository at this point in the history
* added several PCR types and SealOpt/CertifyOpt in pcr.go to support new features in Seal() and Unseal()

* change Seal() to take in a SealOpt instead of a tpm2.PCRSelection

* Seal() will now output the creation data, certified PCR values and ticket info in the sealed blob to allow certification later

* added CertifyOpt as a parameter of Unseal() to allow user to certify the sealed blob (whether the blob is sealed under a certain PCR state)
  • Loading branch information
jkl73 committed Nov 26, 2019
1 parent a84e465 commit b2d92bf
Show file tree
Hide file tree
Showing 9 changed files with 365 additions and 109 deletions.
2 changes: 1 addition & 1 deletion go.mod
Expand Up @@ -4,6 +4,6 @@ go 1.12

require (
github.com/golang/protobuf v1.3.2
github.com/google/go-tpm v0.2.1-0.20191022013559-8c7ffca061af
github.com/google/go-tpm v0.2.1-0.20191106030929-f0607eac7f8a
github.com/spf13/cobra v0.0.5
)
2 changes: 2 additions & 0 deletions go.sum
Expand Up @@ -17,6 +17,8 @@ github.com/google/go-tpm v0.2.1-0.20190926184827-9f71d680f4ab h1:n0a1xd4IaX8uK/H
github.com/google/go-tpm v0.2.1-0.20190926184827-9f71d680f4ab/go.mod h1:gTv8GNuqS7CI+tQWrpt5BMMaD5W3G+dZULQLhhAKT5c=
github.com/google/go-tpm v0.2.1-0.20191022013559-8c7ffca061af h1:v5VgdlsBInaJ/LlEKLVKlHM1YxEvPjhgRB5PV2x/s0I=
github.com/google/go-tpm v0.2.1-0.20191022013559-8c7ffca061af/go.mod h1:iVLWvrPp/bHeEkxTFi9WG6K9w0iy2yIszHwZGHPbzAw=
github.com/google/go-tpm v0.2.1-0.20191106030929-f0607eac7f8a h1:Fy+pbfu/xFbY/PAyZBMSyh6ph6HiuOZ1ry+KBwu9no0=
github.com/google/go-tpm v0.2.1-0.20191106030929-f0607eac7f8a/go.mod h1:iVLWvrPp/bHeEkxTFi9WG6K9w0iy2yIszHwZGHPbzAw=
github.com/google/go-tpm-tools v0.0.0-20190906225433-1614c142f845/go.mod h1:AVfHadzbdzHo54inR2x1v640jdi1YSi3NauM2DUsxk0=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
Expand Down
10 changes: 8 additions & 2 deletions gotpm/cmd/seal.go
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/spf13/cobra"

"github.com/google/go-tpm-tools/proto"
"github.com/google/go-tpm-tools/tpm2tools"
"github.com/google/go-tpm/tpm2"
)

Expand Down Expand Up @@ -51,7 +52,12 @@ state (like Secure Boot).`,
}

fmt.Fprintf(debugOutput(), "Sealing to PCRs: %v\n", sel.PCRs)
sealed, err := srk.Seal(secret, sel)
var sealed *proto.SealedBytes
if len(sel.PCRs) > 0 {
sealed, err = srk.Seal(secret, tpm2tools.SealCurrent{PCRSelection: sel})
} else {
sealed, err = srk.Seal(secret, nil)
}
if err != nil {
return fmt.Errorf("sealing data: %v", err)
}
Expand Down Expand Up @@ -102,7 +108,7 @@ Thus, algorithm and PCR options are not needed for the unseal command.`,
defer srk.Close()

fmt.Fprintln(debugOutput(), "Unsealing data")
secret, err := srk.Unseal(&sealed)
secret, err := srk.Unseal(&sealed, nil)
if err != nil {
return fmt.Errorf("unsealing data: %v", err)
}
Expand Down
77 changes: 52 additions & 25 deletions proto/tpm.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions proto/tpm.proto
Expand Up @@ -24,6 +24,9 @@ message SealedBytes {
repeated int32 pcrs = 3;
HashAlgo hash = 4;
ObjectType srk = 5;
Pcrs certified_pcrs = 6;
bytes creation_data = 7;
bytes ticket = 8;
}

message ImportBlob {
Expand Down
130 changes: 82 additions & 48 deletions tpm2tools/keys.go
Expand Up @@ -2,6 +2,7 @@ package tpm2tools

import (
"crypto"
"crypto/subtle"
"fmt"
"io"

Expand Down Expand Up @@ -168,35 +169,46 @@ func (k *Key) Close() {
tpm2.FlushContext(k.rw, k.handle)
}

// Seal seals the sensitive byte buffer to the provided PCRs under the owner
// hierarchy using the SHA256 versions of the provided PCRs.
// The Key k is used as the parent key.
func (k *Key) Seal(sensitive []byte, sel tpm2.PCRSelection) (*proto.SealedBytes, error) {
// Seal seals the sensitive byte buffer to the PCRs specified by SealOpt (the
// SealOpt can be nil, which means seal to an none PCRs. However the PCR
// selection in SealOpt cannot be emtpy). The sealing is done under Owner
// Hierarchy. During the sealing process, certification data will be created
// allowing Unseal() to validate the state of the TPM during the sealing process.
func (k *Key) Seal(sensitive []byte, sOpt SealOpt) (*proto.SealedBytes, error) {
var pcrs *proto.Pcrs
var err error
var auth []byte
if len(sel.PCRs) > 0 {
pcrs, err := ReadPCRs(k.rw, sel)
if sOpt != nil {
pcrs, err = sOpt.PCRsForSealing(k.rw)
if err != nil {
return nil, fmt.Errorf("could not read pcrs for sealing: %v", err)
return nil, err
}
}
if len(pcrs.GetPcrs()) > 0 {
auth = ComputePCRSessionAuth(pcrs)
}

sb, err := sealHelper(k.rw, k.Handle(), auth, sensitive)
// use the session hash algo as SealOpt, because the certification PCRs could be nil
certifySel, err := FullPcrSel(sessionHashAlgTpm, k.rw)
if err != nil {
return nil, err
}
for _, pcr := range sel.PCRs {
sb.Pcrs = append(sb.Pcrs, int32(pcr))
sb, err := sealHelper(k.rw, k.Handle(), auth, sensitive, certifySel)
if err != nil {
return nil, err
}
sb.Hash = proto.HashAlgo(sel.Hash)

for pcrNum := range pcrs.GetPcrs() {
sb.Pcrs = append(sb.Pcrs, int32(pcrNum))
}
sb.Hash = pcrs.GetHash()
sb.Srk = proto.ObjectType(k.pubArea.Type)
return sb, nil
}

func sealHelper(rw io.ReadWriter, parentHandle tpmutil.Handle, auth []byte, sensitive []byte) (*proto.SealedBytes, error) {
func sealHelper(rw io.ReadWriter, parentHandle tpmutil.Handle, auth []byte, sensitive []byte, certifyPCRsSel tpm2.PCRSelection) (*proto.SealedBytes, error) {
inPublic := tpm2.Public{
Type: tpm2.AlgKeyedHash,
NameAlg: tpm2.AlgSHA256,
NameAlg: sessionHashAlgTpm,
Attributes: tpm2.FlagFixedTPM | tpm2.FlagFixedParent,
AuthPolicy: auth,
}
Expand All @@ -205,30 +217,46 @@ func sealHelper(rw io.ReadWriter, parentHandle tpmutil.Handle, auth []byte, sens
} else {
inPublic.Attributes |= tpm2.FlagAdminWithPolicy
}
priv, pub, _, _, _, err := tpm2.CreateKeyWithSensitive(rw, parentHandle, tpm2.PCRSelection{}, "", "", inPublic, sensitive)

priv, pub, creationData, _, ticket, err := tpm2.CreateKeyWithSensitive(rw, parentHandle, certifyPCRsSel, "", "", inPublic, sensitive)
if err != nil {
return nil, fmt.Errorf("Failed to create key: %v", err)
}
certifiedPcr, err := ReadPCRs(rw, certifyPCRsSel)
if err != nil {
return nil, fmt.Errorf("failed to seal data: %v", err)
return nil, fmt.Errorf("Failed to read PCRs: %v", err)
}
computedDigest := computePCRDigest(certifiedPcr)

decodedCreationData, err := tpm2.DecodeCreationData(creationData)
if err != nil {
return nil, fmt.Errorf("Failed to decode creation data: %v", err)
}

// make sure PCRs haven't being altered after sealing
if subtle.ConstantTimeCompare(computedDigest, decodedCreationData.PCRDigest) == 0 {
return nil, fmt.Errorf("PCRs have been modified after sealing")
}

sb := proto.SealedBytes{}
sb.CertifiedPcrs = certifiedPcr
sb.Priv = priv
sb.Pub = pub
sb.CreationData = creationData
if sb.Ticket, err = tpmutil.Pack(ticket); err != nil {
return nil, err
}
return &sb, nil
}

// Unseal takes a private/public pair of buffers and attempts to reverse the
// sealing process under the owner hierarchy using the SHA256 versions of the
// provided PCRs.
// The Key k is used as the parent key.
func (k *Key) Unseal(in *proto.SealedBytes) ([]byte, error) {
// Unseal attempts to reverse the process of Seal(), using the PCRs, public, and
// private data in proto.SealedBytes. Optionally, a CertifyOpt can be
// passed, to verify the state of the TPM when the data was sealed. A nil value
// can be passed to skip certification.
func (k *Key) Unseal(in *proto.SealedBytes, cOpt CertifyOpt) ([]byte, error) {
if in.Srk != proto.ObjectType(k.pubArea.Type) {
return nil, fmt.Errorf("Expected key of type %v, got %v", in.Srk, k.pubArea.Type)
}
sel := tpm2.PCRSelection{Hash: tpm2.Algorithm(in.Hash)}
for _, pcr := range in.Pcrs {
sel.PCRs = append(sel.PCRs, int(pcr))
}

sealed, _, err := tpm2.Load(
k.rw,
k.Handle(),
Expand All @@ -240,41 +268,47 @@ func (k *Key) Unseal(in *proto.SealedBytes) ([]byte, error) {
}
defer tpm2.FlushContext(k.rw, sealed)

if len(sel.PCRs) == 0 {
return tpm2.Unseal(k.rw, sealed, "")
if cOpt != nil {
var ticket tpm2.Ticket
if _, err = tpmutil.Unpack(in.GetTicket(), &ticket); err != nil {
return nil, fmt.Errorf("Ticket unpack failed: %v", err)
}
creationHash := sessionHashAlg.New()
creationHash.Write(in.GetCreationData())
if _, _, err = tpm2.CertifyCreation(k.rw, "", sealed, tpm2.HandleNull, nil, creationHash.Sum(nil), tpm2.SigScheme{}, ticket); err != nil {
return nil, fmt.Errorf("failed to certify creation: %v", err)
}
if err := cOpt.CertifyPCRs(k.rw, in.GetCertifiedPcrs()); err != nil {
return nil, fmt.Errorf("failed to certify PCRs: %v", err)
}
}

sel := tpm2.PCRSelection{Hash: tpm2.Algorithm(in.Hash)}
for _, pcr := range in.Pcrs {
sel.PCRs = append(sel.PCRs, int(pcr))
}
session, err := createPCRSession(k.rw, sel)
if err != nil {
return nil, fmt.Errorf("failed to unseal: %v", err)
return nil, fmt.Errorf("failed to create session: %v", err)
}
defer tpm2.FlushContext(k.rw, session)

if len(sel.PCRs) == 0 {
return tpm2.Unseal(k.rw, sealed, "")
}

return tpm2.UnsealWithSession(k.rw, session, sealed, "")
}

// Reseal unwraps a secret and rewraps it under the auth value that would be
// produced by the PCR state in pcrs. Similar to seal and unseal, this acts on
// the SHA256 PCRs and uses the owner hierarchy.
// The Key k is used as the parent key.
func (k *Key) Reseal(in *proto.SealedBytes, pcrs *proto.Pcrs) (*proto.SealedBytes, error) {
sensitive, err := k.Unseal(in)
// Reseal is a shortcut to call Unseal() followed by Seal().
// CertifyOpt(nillable) will be used in Unseal(), and SealOpt(nillable)
// will be used in Seal()
func (k *Key) Reseal(in *proto.SealedBytes, cOpt CertifyOpt, sOpt SealOpt) (*proto.SealedBytes, error) {
sensitive, err := k.Unseal(in, cOpt)
if err != nil {
return nil, fmt.Errorf("failed to unseal: %v", err)
}

auth := ComputePCRSessionAuth(pcrs)

sb, err := sealHelper(k.rw, k.Handle(), auth, sensitive)
if err != nil {
return nil, err
}
for pcr := range pcrs.Pcrs {
sb.Pcrs = append(sb.Pcrs, int32(pcr))
}
sb.Hash = in.Hash
sb.Srk = in.Srk
return sb, nil
return k.Seal(sensitive, sOpt)
}

func (k *Key) hasAttribute(attr tpm2.KeyProp) bool {
Expand Down

0 comments on commit b2d92bf

Please sign in to comment.