From 3883d14cb9cd50ffece93e123f95ef4d29c57830 Mon Sep 17 00:00:00 2001 From: Morten Linderud Date: Wed, 20 May 2026 17:00:54 +0200 Subject: [PATCH] policy: Implement Clone() for the PolicyCalculator When implementing nested policies, like systemd-pcrlock, it's practical to clone the current PolicyCalculator to transfer the state of the previous policy without mutation. Signed-off-by: Morten Linderud --- tpm2/policy.go | 9 ++++++ tpm2/test/policy_test.go | 62 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/tpm2/policy.go b/tpm2/policy.go index c64f17e4..0f3afede 100644 --- a/tpm2/policy.go +++ b/tpm2/policy.go @@ -27,6 +27,15 @@ func NewPolicyCalculator(alg TPMIAlgHash) (*PolicyCalculator, error) { }, nil } +// Clone copies the internal state of the policy to a new calculator +func (p *PolicyCalculator) Clone() *PolicyCalculator { + return &PolicyCalculator{ + alg: p.alg, + hash: p.hash, + state: bytes.Clone(p.state), + } +} + // Reset resets the internal state of the policy hash to all 0x00. func (p *PolicyCalculator) Reset() { p.state = make([]byte, p.hash.Size()) diff --git a/tpm2/test/policy_test.go b/tpm2/test/policy_test.go index 81a90890..50147407 100644 --- a/tpm2/test/policy_test.go +++ b/tpm2/test/policy_test.go @@ -4,6 +4,7 @@ import ( "bytes" "crypto/sha1" "crypto/sha256" + "encoding/hex" "testing" . "github.com/google/go-tpm/tpm2" @@ -1125,3 +1126,64 @@ func TestPolicyDuplicationSelectUpdate(t *testing.T) { }) } } + +func mustHexToBytes(t *testing.T, s string) []byte { + t.Helper() + bytes, err := hex.DecodeString(s) + if err != nil { + t.Fatal("failed decoding hex string to bytes") + } + return bytes +} + +func TestPolicyCalculatorClone(t *testing.T) { + pol, err := NewPolicyCalculator(TPMAlgSHA256) + if err != nil { + t.Fatalf("creating policy calculator: %v", err) + } + + err = PolicyPCR{ + Pcrs: TPMLPCRSelection{ + PCRSelections: []TPMSPCRSelection{ + { + Hash: TPMAlgSHA256, + PCRSelect: PCClientCompatible.PCRs(1), + }, + }, + }, + }.Update(pol) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(pol.Hash().Digest, mustHexToBytes(t, "9541398bf83ab69f0df3a54ed4dd51ce04a0497196696a32bb93da77a4545aa7")) { + t.Fatalf("PolicyCalculator does not match expected hash digest, got: %x", pol.Hash().Digest) + } + + // Clone the new policy and copy the state + newPol := pol.Clone() + + err = PolicyPCR{ + Pcrs: TPMLPCRSelection{ + PCRSelections: []TPMSPCRSelection{ + { + Hash: TPMAlgSHA256, + PCRSelect: PCClientCompatible.PCRs(2), + }, + }, + }, + }.Update(newPol) + if err != nil { + t.Fatal(err) + } + + // Check that the old PolicyCalculator is unchange + if !bytes.Equal(pol.Hash().Digest, mustHexToBytes(t, "9541398bf83ab69f0df3a54ed4dd51ce04a0497196696a32bb93da77a4545aa7")) { + t.Fatalf("PolicyCalculator does not match expected hash digest, got: %x", pol.Hash().Digest) + } + + // Check that the new instance has the updated checksum + if !bytes.Equal(newPol.Hash().Digest, mustHexToBytes(t, "20f15aec09d69e75070296a5a1b613502678a9f0e1c5615736414e99073807b0")) { + t.Fatalf("PolicyCalculator does not match expected hash digest, got: %x", newPol.Hash().Digest) + } +}