@@ -10,6 +10,7 @@ import (
1010 "crypto/x509"
1111 "encoding/asn1"
1212 "encoding/gob"
13+ "encoding/json"
1314 "encoding/pem"
1415 "fmt"
1516 "io"
@@ -72,8 +73,17 @@ const (
7273 //EmptyPassword is an empty string
7374 EmptyPassword = ""
7475 vaultKeyLength = 32 //Bytes
76+
77+ // TODO : rename and move this to locations
78+ PcrPolicyIndexesFile = types .PersistStatusDir + "/sealingpcrs.json"
79+ PcrPolicyIndexesHashFile = types .PersistStatusDir + "/sealingpcrs.hash"
7580)
7681
82+ type SealingPcrPolicyIndexes struct {
83+ Pcrs []int
84+ Id int
85+ }
86+
7787// PCRBank256Status stores info about support for
7888// SHA256 PCR bank on this device
7989type PCRBank256Status uint32
98108 pcrBank256Status = PCRBank256StatusUnknown
99109
100110 //DiskKeySealingPCRs represents PCRs that we use for sealing
101- DiskKeySealingPCRs = tpm2.PCRSelection {Hash : tpm2 .AlgSHA256 , PCRs : []int {0 , 1 , 2 , 3 , 4 , 6 , 7 , 8 , 9 , 13 , 14 }}
111+ DefaultDiskKeySealingPCRs = tpm2.PCRSelection {Hash : tpm2 .AlgSHA256 , PCRs : []int {0 , 1 , 2 , 3 , 4 , 6 , 7 , 8 , 9 , 13 , 14 }}
102112
103113 // TpmDevicePath is the TPM device file path, it is not a constant due to
104114 // test usage.
@@ -259,6 +269,109 @@ var (
259269 }
260270)
261271
272+ // GetDiskKeySealingPCRs returns the PCR selection to use for sealing the disk key.
273+ // It reads from the saved sealing PCRs file if it exists, otherwise returns the default.
274+ func GetDiskKeySealingPCRs (log * base.LogObject , path string ) tpm2.PCRSelection {
275+ data , err := os .ReadFile (path )
276+ if err != nil {
277+ if log != nil {
278+ log .Warnf ("failed to read sealing PCRs file: %v, using default PCR list: %v" , err , DefaultDiskKeySealingPCRs .PCRs )
279+ }
280+ return DefaultDiskKeySealingPCRs
281+ }
282+
283+ var sp SealingPcrPolicyIndexes
284+ if err := json .Unmarshal (data , & sp ); err != nil {
285+ if log != nil {
286+ log .Warnf ("failed to unmarshal sealing PCRs: %v, using default PCR list: %v" , err , DefaultDiskKeySealingPCRs .PCRs )
287+ }
288+ return DefaultDiskKeySealingPCRs
289+ }
290+
291+ pcrs := make ([]int , len (sp .Pcrs ))
292+ for i , pcr := range sp .Pcrs {
293+ pcrs [i ] = int (pcr )
294+ }
295+
296+ return tpm2.PCRSelection {Hash : tpm2 .AlgSHA256 , PCRs : pcrs }
297+ }
298+
299+ // SaveDiskKeySealingPCRs saves the PCR policy indexes to a file.
300+ func SaveDiskKeySealingPCRs (sp SealingPcrPolicyIndexes , policyPath , policyHashPath string ) (tpm2.PCRSelection , bool , error ) {
301+ // Check if the list is empty
302+ if len (sp .Pcrs ) == 0 {
303+ return tpm2.PCRSelection {}, false , fmt .Errorf ("PCR list cannot be empty" )
304+ }
305+
306+ // Check for maximum count (reasonable limit for PCR selection)
307+ if len (sp .Pcrs ) > 16 {
308+ return tpm2.PCRSelection {}, false , fmt .Errorf ("too many PCRs in policy: maximum 16 allowed, got %d" , len (sp .Pcrs ))
309+ }
310+
311+ hasPCR0 := false
312+ seenPCRs := make (map [int ]bool )
313+ for _ , pcr := range sp .Pcrs {
314+ // Check for duplicate PCR indexes
315+ if seenPCRs [pcr ] {
316+ return tpm2.PCRSelection {}, false , fmt .Errorf ("duplicate PCR index %d in policy" , pcr )
317+ }
318+ seenPCRs [pcr ] = true
319+ // PCR indexes must be between 0 and 15 inclusive, otherwise reject the policy.
320+ if pcr < 0 || pcr > 15 {
321+ return tpm2.PCRSelection {}, false , fmt .Errorf ("invalid PCR index %d in policy: must be between 0 and 15" , pcr )
322+ }
323+ // PCR 5 is used for GPT partition table and boot manager configuration,
324+ // which can be volatile and unsuitable for sealing in many scenarios.
325+ if pcr == 5 {
326+ return tpm2.PCRSelection {}, false , fmt .Errorf ("invalid policy, PCR 5 is volatile (GPT/boot manager) and should not be included" )
327+ }
328+ // PCR 16 is resetable debug PCR, reject it.
329+ if pcr == 16 {
330+ return tpm2.PCRSelection {}, false , fmt .Errorf ("invalid policy, PCR 16 is a debug PCR and should not be included" )
331+ }
332+ // PCR 17-22 are used for DTRM, PCR 23 is resetable and also used for DTRM, reject them.
333+ if pcr >= 17 && pcr <= 23 {
334+ return tpm2.PCRSelection {}, false , fmt .Errorf ("invalid policy, PCR %d is used for DTRM and should not be included" , pcr )
335+ }
336+ // PCR 0 is static root of trust for measurement (SRTM), must be included.
337+ if pcr == 0 {
338+ hasPCR0 = true
339+ }
340+ }
341+ if ! hasPCR0 {
342+ return tpm2.PCRSelection {}, false , fmt .Errorf ("PCR 0 must be in the list" )
343+ }
344+
345+ data , err := json .Marshal (sp )
346+ if err != nil {
347+ return tpm2.PCRSelection {}, false , fmt .Errorf ("failed to marshal sealing PCR policy indexes: %w" , err )
348+ }
349+
350+ // Calculate hash of the policy
351+ hash := crypto .SHA256 .New ()
352+ hash .Write (data )
353+ policyHash := hash .Sum (nil )
354+
355+ // Check if policy has changed
356+ existingHash , err := os .ReadFile (policyHashPath )
357+ if err == nil && bytes .Equal (existingHash , policyHash ) {
358+ return tpm2.PCRSelection {Hash : tpm2 .AlgSHA256 , PCRs : sp .Pcrs }, false , nil
359+ }
360+
361+ // Save the policy file
362+ if err := fileutils .WriteRename (policyPath , data ); err != nil {
363+ return tpm2.PCRSelection {}, false , fmt .Errorf ("failed to write sealing PCRs policy (id=%d, pcrs=%v) to %s: %w" ,
364+ sp .Id , sp .Pcrs , policyPath , err )
365+ }
366+
367+ // Save the hash file
368+ if err := fileutils .WriteRename (policyHashPath , policyHash ); err != nil {
369+ return tpm2.PCRSelection {}, false , fmt .Errorf ("failed to write sealing PCRs hash file: %w" , err )
370+ }
371+
372+ return tpm2.PCRSelection {Hash : tpm2 .AlgSHA256 , PCRs : sp .Pcrs }, true , nil
373+ }
374+
262375// GetTpmLogFileNames returns paths to saved TPM logs
263376func GetTpmLogFileNames () (string , string ) {
264377 return measurementLogSealSuccess , measurementLogUnsealFail
@@ -699,6 +812,7 @@ func FetchSealedVaultKey(log *base.LogObject) ([]byte, error) {
699812 //gain some knowledge about existing environment
700813 sealedKeyPresent := isSealedKeyPresent ()
701814 legacyKeyPresent := isLegacyKeyPresent ()
815+ pcrSelection := GetDiskKeySealingPCRs (log , PcrPolicyIndexesFile )
702816
703817 if ! sealedKeyPresent && ! legacyKeyPresent {
704818 log .Noticef ("neither legacy nor sealed disk key present, generating a fresh key" )
@@ -723,7 +837,7 @@ func FetchSealedVaultKey(log *base.LogObject) ([]byte, error) {
723837 if err != nil {
724838 return nil , fmt .Errorf ("GetRandom failed: %w" , err )
725839 }
726- err = SealDiskKey (log , key , DiskKeySealingPCRs )
840+ err = SealDiskKey (log , key , pcrSelection )
727841 if err != nil {
728842 return nil , fmt .Errorf ("sealing the fresh disk key failed: %w" , err )
729843 }
@@ -750,7 +864,7 @@ func FetchSealedVaultKey(log *base.LogObject) ([]byte, error) {
750864
751865 log .Noticef ("try to convert the legacy key into a sealed key" )
752866
753- err = SealDiskKey (log , key , DiskKeySealingPCRs )
867+ err = SealDiskKey (log , key , pcrSelection )
754868 if err != nil {
755869 return nil , fmt .Errorf ("sealing the legacy disk key into TPM failed: %w" , err )
756870 }
@@ -761,7 +875,7 @@ func FetchSealedVaultKey(log *base.LogObject) ([]byte, error) {
761875 log .Noticef ("sealed disk key present int TPM, about to unseal it" )
762876 }
763877 //at this point, we have a key sealed into TPM
764- key , err := UnsealDiskKey (DiskKeySealingPCRs )
878+ key , err := UnsealDiskKey (pcrSelection )
765879 if err == nil {
766880 // be more verbose, lets celebrate
767881 log .Noticef ("successfully unsealed the disk key from TPM" )
@@ -1011,7 +1125,7 @@ func CompareLegacyandSealedKey() SealedKeyType {
10111125 //no cloning case, return SealedKeyTypeNew
10121126 return SealedKeyTypeNew
10131127 }
1014- unsealedKey , err := UnsealDiskKey (DiskKeySealingPCRs )
1128+ unsealedKey , err := UnsealDiskKey (GetDiskKeySealingPCRs ( nil , PcrPolicyIndexesFile ) )
10151129 if err != nil {
10161130 //key is present but can't unseal it
10171131 //but legacy key is present
@@ -1217,7 +1331,8 @@ func FindMismatchingPCRs() ([]int, error) {
12171331 // this should never happen, except when we update EVE and adding new
12181332 // indexes to the DiskKeySealingPCRs, anyways, better safe than sorry!
12191333 if ! ok {
1220- return nil , fmt .Errorf ("saved PCR index %d doesn't exist at run-time PCRs list %v" , i , DiskKeySealingPCRs .PCRs )
1334+ pcrSelection := GetDiskKeySealingPCRs (nil , PcrPolicyIndexesFile )
1335+ return nil , fmt .Errorf ("saved PCR index %d doesn't exist at run-time PCRs list %v" , i , pcrSelection .PCRs )
12211336 }
12221337
12231338 if ! bytes .Equal (readPCR , savedPCR ) {
@@ -1236,10 +1351,13 @@ func readDiskKeySealingPCRs() (map[int][]byte, error) {
12361351 }
12371352 defer rw .Close ()
12381353
1354+ // Get the PCRs used in sealing the disk key
1355+ pcrSelection := GetDiskKeySealingPCRs (nil , PcrPolicyIndexesFile )
1356+
12391357 // tpm2.ReadPCRs returns at most 8 PCRs, so loop over and read one by one
12401358 readPCRs := make (map [int ][]byte )
1241- for _ , v := range DiskKeySealingPCRs .PCRs {
1242- p , err := tpm2 .ReadPCR (rw , v , DiskKeySealingPCRs .Hash )
1359+ for _ , v := range pcrSelection .PCRs {
1360+ p , err := tpm2 .ReadPCR (rw , v , pcrSelection .Hash )
12431361 if err != nil {
12441362 return nil , err
12451363 }
0 commit comments