Skip to content

Commit a91053e

Browse files
shjalaeriknordmark
authored andcommitted
tpm : more verbose error reporting when policy check failed
When EVE fails to unseal the vault due to policy check failure, it reports a generic error. This changes makes the error more verbose by finding the mismatching PCR(s) and report it. Signed-off-by: Shahriyar Jalayeri <[email protected]>
1 parent 89c70c1 commit a91053e

File tree

2 files changed

+150
-1
lines changed

2 files changed

+150
-1
lines changed

pkg/pillar/cmd/tpmmgr/tpmmgr_test.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,20 @@
66
package tpmmgr
77

88
import (
9+
"bytes"
10+
"crypto/sha256"
911
"crypto/x509"
1012
"encoding/pem"
1113
"fmt"
1214
"io/ioutil"
1315
"os"
1416
"reflect"
17+
"strings"
1518
"testing"
1619
"time"
1720

21+
"github.com/google/go-tpm/tpm2"
22+
"github.com/google/go-tpm/tpmutil"
1823
etpm "github.com/lf-edge/eve/pkg/pillar/evetpm"
1924
)
2025

@@ -223,3 +228,43 @@ func TestSealUnseal(t *testing.T) {
223228
t.Errorf("Seal/Unseal operation failed, want %v, but got %v", dataToSeal, unsealedData)
224229
}
225230
}
231+
232+
func TestSealUnsealMismatchReport(t *testing.T) {
233+
_, err := os.Stat(etpm.TpmDevicePath)
234+
if err != nil {
235+
t.Skip("TPM is not available, skipping the test.")
236+
}
237+
238+
rw, err := tpm2.OpenTPM(etpm.TpmDevicePath)
239+
if err != nil {
240+
t.Errorf("OpenTPM failed with err: %v", err)
241+
return
242+
}
243+
defer rw.Close()
244+
245+
dataToSeal := []byte("secret")
246+
if err := etpm.SealDiskKey(dataToSeal, etpm.DiskKeySealingPCRs); err != nil {
247+
t.Errorf("Seal operation failed with err: %v", err)
248+
return
249+
}
250+
251+
pcrIndexes := [3]int{1, 7, 8}
252+
pcrValue := bytes.Repeat([]byte{0xF}, sha256.Size)
253+
for _, pcr := range pcrIndexes {
254+
if err = tpm2.PCRExtend(rw, tpmutil.Handle(pcr), tpm2.AlgSHA256, pcrValue, ""); err != nil {
255+
t.Errorf("Failed to extend PCR %d: %s", pcr, err)
256+
return
257+
}
258+
}
259+
260+
_, err = etpm.UnsealDiskKey(etpm.DiskKeySealingPCRs)
261+
if err == nil {
262+
t.Errorf("Expected error from UnsealDiskKey, got nil")
263+
return
264+
}
265+
266+
if !strings.Contains(err.Error(), "[1 7 8]") {
267+
t.Errorf("UnsealDiskKey expected to report mismatching PCR indexes, got : %v", err)
268+
return
269+
}
270+
}

pkg/pillar/evetpm/tpm.go

Lines changed: 105 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@ import (
99
"crypto/ecdsa"
1010
"crypto/x509"
1111
"encoding/asn1"
12+
"encoding/gob"
1213
"encoding/pem"
1314
"fmt"
1415
"io"
1516
"io/ioutil"
1617
"math/big"
1718
"os"
19+
"sort"
1820
"unsafe"
1921

2022
"github.com/google/go-tpm/tpm2"
@@ -71,6 +73,10 @@ const (
7173
//EmptyPassword is an empty string
7274
EmptyPassword = ""
7375
vaultKeyLength = 32 //Bytes
76+
77+
// TpmSavedDiskSealingPcrs is the file that holds a copy of PCR values
78+
// at the time of generating and sealing the disk key into the TPM.
79+
TpmSavedDiskSealingPcrs = types.PersistStatusDir + "/sealingpcrs"
7480
)
7581

7682
// PCRBank256Status stores info about support for
@@ -612,6 +618,12 @@ func SealDiskKey(key []byte, pcrSel tpm2.PCRSelection) error {
612618
EmptyPassword, public, 0); err != nil {
613619
return fmt.Errorf("NVWrite %v failed: %v", TpmSealedDiskPubHdl, err)
614620
}
621+
622+
// save a snapshot of PCR values
623+
if err := saveDiskKeySealingPCRs(TpmSavedDiskSealingPcrs); err != nil {
624+
return fmt.Errorf("saving snapshot of sealing PCRs failed: %v", err)
625+
}
626+
615627
return nil
616628
}
617629

@@ -667,7 +679,14 @@ func UnsealDiskKey(pcrSel tpm2.PCRSelection) ([]byte, error) {
667679

668680
key, err := tpm2.UnsealWithSession(rw, session, sealedObjHandle, EmptyPassword)
669681
if err != nil {
670-
return nil, fmt.Errorf("UnsealWithSession failed: %v", err)
682+
// We get here mostly because of RCPolicyFail error, so try to get more
683+
// information about the failure by finding the mismatching PCR index.
684+
mismatch, newErr := findMismatchingPCRs(TpmSavedDiskSealingPcrs)
685+
if newErr != nil {
686+
return nil, fmt.Errorf("UnsealWithSession failed: %v, failed to get more info: %v", err, newErr)
687+
}
688+
689+
return nil, fmt.Errorf("UnsealWithSession failed: %v, possibly mismatching PCR indexes %v", err, mismatch)
671690
}
672691
return key, nil
673692
}
@@ -776,3 +795,88 @@ func pcrBankSHA256EnabledHelper() bool {
776795
_, err = tpm2.ReadPCR(rw, 0, tpm2.AlgSHA256)
777796
return err == nil
778797
}
798+
799+
func saveDiskKeySealingPCRs(pcrsFile string) error {
800+
trw, err := tpm2.OpenTPM(TpmDevicePath)
801+
if err != nil {
802+
return err
803+
}
804+
defer trw.Close()
805+
806+
readPCRs, err := readDiskKeySealingPCRs()
807+
if err != nil {
808+
return err
809+
}
810+
811+
frw, err := os.Create(pcrsFile)
812+
if err != nil {
813+
return err
814+
}
815+
defer frw.Close()
816+
817+
e := gob.NewEncoder(frw)
818+
err = e.Encode(readPCRs)
819+
if err != nil {
820+
return err
821+
}
822+
823+
return nil
824+
}
825+
826+
func findMismatchingPCRs(savedPCRsFile string) ([]int, error) {
827+
frw, err := os.Open(savedPCRsFile)
828+
if err != nil {
829+
return nil, err
830+
}
831+
defer frw.Close()
832+
833+
var savedPCRs map[int][]byte
834+
d := gob.NewDecoder(frw)
835+
err = d.Decode(&savedPCRs)
836+
if err != nil {
837+
return nil, err
838+
}
839+
840+
readPCRs, err := readDiskKeySealingPCRs()
841+
if err != nil {
842+
return nil, err
843+
}
844+
845+
mismatch := make([]int, 0)
846+
for i, savedPCR := range savedPCRs {
847+
readPCR, ok := readPCRs[i]
848+
// this should never happen, except when we update EVE and adding new
849+
// indexes to the DiskKeySealingPCRs, anyways, better safe than sorry!
850+
if !ok {
851+
return nil, fmt.Errorf("saved PCR index %d doesn't exist at run-time PCRs list %v", i, DiskKeySealingPCRs.PCRs)
852+
}
853+
854+
if !bytes.Equal(readPCR, savedPCR) {
855+
mismatch = append(mismatch, i)
856+
}
857+
}
858+
859+
sort.Ints(mismatch)
860+
return mismatch, nil
861+
}
862+
863+
func readDiskKeySealingPCRs() (map[int][]byte, error) {
864+
rw, err := tpm2.OpenTPM(TpmDevicePath)
865+
if err != nil {
866+
return nil, err
867+
}
868+
defer rw.Close()
869+
870+
// tpm2.ReadPCRs returns at most 8 PCRs, so loop over and read one by one
871+
readPCRs := make(map[int][]byte)
872+
for _, v := range DiskKeySealingPCRs.PCRs {
873+
p, err := tpm2.ReadPCR(rw, v, DiskKeySealingPCRs.Hash)
874+
if err != nil {
875+
return nil, err
876+
}
877+
878+
readPCRs[v] = p
879+
}
880+
881+
return readPCRs, nil
882+
}

0 commit comments

Comments
 (0)