diff --git a/cmd/trust/policygen.go b/cmd/trust/policygen.go index 062533b..ff6352b 100644 --- a/cmd/trust/policygen.go +++ b/cmd/trust/policygen.go @@ -1,54 +1,55 @@ package main import ( - "github.com/project-machine/trust/pkg/trust" + "errors" "github.com/urfave/cli" + "github.com/project-machine/trust/pkg/trust" ) var tpmPolicyGenCmd = cli.Command{ Name: "tpm-policy-gen", - Usage: "Generate tpm policy", + Usage: "Generate tpm policy for a keyset", Action: doTpmPolicygen, Flags: []cli.Flag{ cli.StringFlag{ - Name: "pf,passwd-policy-file", + Name: "pf, passwd-policy-file", Usage: "File to which to write password policy", Value: "passwd_policy.out", }, cli.StringFlag{ - Name: "lf,luks-policy-file", + Name: "lf, luks-policy-file", Usage: "File to which to write luks policy", Value: "luks_policy.out", }, cli.StringFlag{ - Name: "pp,passwd-pcr7-file", - Usage: "File from which to read password pcr7", - Value: "passwd_pcr7.bin", - }, - cli.StringFlag{ - Name: "lp,production-pcr7-file,luks-pcr7-file", - Usage: "File from which to read production pcr7", - Value: "luks_pcr7.bin", - }, - cli.IntFlag{ - Name: "pv,policy-version", - Usage: "Policy version", - Value: 1, + Name: "pcr7-tpm", + Usage: "File from which to read uki-tpm pcr7 value", }, cli.StringFlag{ - Name: "pk,passwd-pubkey-file", - Usage: "File from which to read password policy pubkey", - Value: "passwd_pubkey.pem", + Name: "pcr7-production", + Usage: "File from which to read uki-production pcr7 value", }, cli.StringFlag{ - Name: "lk,luks-pubkey-file", - Usage: "File from which read write luks policy pubkey", - Value: "luks_pubkey.pem", + Name: "pv, policy-version", + Usage: "A four digit policy version, i.e. 0001", + Value: "0001", }, }, } func doTpmPolicygen(ctx *cli.Context) error { - return trust.TpmGenPolicy(ctx) -} + args := ctx.Args() + if len(args) != 0 { + return errors.New("Usage: extra arguments") + } + pData := trust.PolicyData{ + Pcr7Prod: ctx.String("pcr7-production"), + Pcr7Tpm: ctx.String("pcr7-tpm"), + LuksOutFile: ctx.String("luks-policy-file"), + PasswdOutFile: ctx.String("passwd-policy-file"), + PolicyVersion: ctx.String("policy-version"), + } + + return trust.TpmGenPolicy(pData) +} diff --git a/go.mod b/go.mod index bc3f89d..1cd68b9 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ require ( github.com/anuvu/disko v0.0.11 github.com/apex/log v1.9.0 github.com/canonical/go-efilib v0.9.4 + github.com/canonical/go-tpm2 v1.0.0 github.com/canonical/tcglog-parser v0.0.0-20230429160108-0d6d239de69d github.com/fatih/color v1.15.0 github.com/foxboron/go-uefi v0.0.0-20230218004016-d1bb9a12f92c @@ -18,7 +19,6 @@ require ( github.com/Microsoft/go-winio v0.4.16 // indirect github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect github.com/acomagu/bufpipe v1.0.3 // indirect - github.com/canonical/go-tpm2 v0.1.0 // indirect github.com/emirpasic/gods v1.12.0 // indirect github.com/go-git/gcfg v1.5.0 // indirect github.com/go-git/go-billy/v5 v5.3.1 // indirect diff --git a/go.sum b/go.sum index ec4c0d4..4c3e16e 100644 --- a/go.sum +++ b/go.sum @@ -59,6 +59,7 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkY github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= github.com/bsiegert/ranges v0.0.0-20111221115336-19303dc7aa63/go.mod h1:8z71/aZjDHLs4ihK/5nD5wZVQxm/W4eRDnxQZcJmVD4= +github.com/canonical/go-efilib v0.3.0/go.mod h1:9b2PNAuPcZsB76x75/uwH99D8CyH/A2y4rq1/+bvplg= github.com/canonical/go-efilib v0.3.1-0.20220314143719-95d50e8afc82/go.mod h1:9b2PNAuPcZsB76x75/uwH99D8CyH/A2y4rq1/+bvplg= github.com/canonical/go-efilib v0.9.4 h1:cD6oNSWeQSgeSeJZMCxhGEW4GoLSxFhIJ12Hg3vFCtU= github.com/canonical/go-efilib v0.9.4/go.mod h1:tHjv3Mni7hEpNSUNd1KJEV/AZJsFSH6LX/EQ0I75AZE= @@ -66,8 +67,9 @@ github.com/canonical/go-sp800.108-kdf v0.0.0-20210314145419-a3359f2d21b9/go.mod github.com/canonical/go-sp800.108-kdf v0.0.0-20210315104021-ead800bbf9a0 h1:ZE2XMRFHcwlib3uU9is37+pKkkMloVoEPWmgQ6GK1yo= github.com/canonical/go-sp800.108-kdf v0.0.0-20210315104021-ead800bbf9a0/go.mod h1:Zrs3YjJr+w51u0R/dyLh/oWt/EcBVdLPCVFYC4daW5s= github.com/canonical/go-sp800.90a-drbg v0.0.0-20210314144037-6eeb1040d6c3/go.mod h1:qdP0gaj0QtgX2RUZhnlVrceJ+Qln8aSlDyJwelLLFeM= -github.com/canonical/go-tpm2 v0.1.0 h1:2mXU+Hy+zYSxmuYys2NtPEO6NwT3Qr9Sygwtops7NYk= github.com/canonical/go-tpm2 v0.1.0/go.mod h1:vG41hdbBjV4+/fkubTT1ENBBqSkLwLr7mCeW9Y6kpZY= +github.com/canonical/go-tpm2 v1.0.0 h1:g03/robj/c+98Hm2GEkkBnKkkhy0xgFZ7GWE/4HzbwM= +github.com/canonical/go-tpm2 v1.0.0/go.mod h1:zKG2ng7qzxp+siDIa1Scd3h/cXF5Di13Havf5ZwfaVY= github.com/canonical/tcglog-parser v0.0.0-20230429160108-0d6d239de69d h1:TAe+Vi2UduW6+Ie8SPJROhkCEPo15CLY/gZqrA9viwk= github.com/canonical/tcglog-parser v0.0.0-20230429160108-0d6d239de69d/go.mod h1:AoJVV7tUwDDGPZkKqwqAMGdPiH7x45JLNmxFrxfoxcs= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -222,6 +224,7 @@ github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyex github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mvo5/goconfigparser v0.0.0-20201015074339-50f22f44deb5/go.mod h1:xmt4k1xLDl8Tdan+0S/jmMK2uSUBSzTc18+5GN5Vea8= +github.com/mvo5/goconfigparser v0.0.0-20221018104758-434073381f37/go.mod h1:inxjKzuGbpMDmdoI7kogueqBVRdf6fPAG5dAsU3gu60= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= @@ -260,6 +263,7 @@ github.com/smartystreets/gunit v1.0.0/go.mod h1:qwPWnhz6pn0NnRBP++URONOVyNkPyr4S github.com/snapcore/go-gettext v0.0.0-20191107141714-82bbea49e785 h1:PaunR+BhraKSLxt2awQ42zofkP+NKh/VjQ0PjIMk/y4= github.com/snapcore/go-gettext v0.0.0-20191107141714-82bbea49e785/go.mod h1:D3SsWAXK7wCCBZu+Vk5hc1EuKj/L3XN1puEMXTU4LrQ= github.com/snapcore/secboot v0.0.0-20211207204151-239d06c34009/go.mod h1:72paVOkm4sJugXt+v9ItmnjXgO921D8xqsbH2OekouY= +github.com/snapcore/secboot v0.0.0-20221114180054-b4be60e68879/go.mod h1:72paVOkm4sJugXt+v9ItmnjXgO921D8xqsbH2OekouY= github.com/snapcore/snapd v0.0.0-20201005140838-501d14ac146e h1:vqDZWKPBL9RKPA8KyOuTaSuXXlmRwn/ndvOkZaAmLJs= github.com/snapcore/snapd v0.0.0-20201005140838-501d14ac146e/go.mod h1:3xrn7QDDKymcE5VO2rgWEQ5ZAUGb9htfwlXnoel6Io8= github.com/snapcore/squashfuse v0.0.0-20171220165323-319f6d41a041/go.mod h1:8loYitFPSdoeCXBs/XjO0fyGcpgLAybOHLUsGwgMq90= diff --git a/pkg/trust/tpmpolicy.go b/pkg/trust/tpmpolicy.go index 004cd31..e35413f 100644 --- a/pkg/trust/tpmpolicy.go +++ b/pkg/trust/tpmpolicy.go @@ -1,85 +1,108 @@ package trust import ( - "path/filepath" - - "github.com/pkg/errors" - "github.com/urfave/cli" "os" + "errors" + + "github.com/canonical/go-tpm2" + "github.com/canonical/go-tpm2/util" ) +type PolicyData struct { + Pcr7Prod string + Pcr7Tpm string + LuksOutFile string + PasswdOutFile string + PolicyVersion string +} + // genLuksPolicy creates a policy for reading the LUKS password // while booted under a production key. -func genLuksPolicy(ctx *cli.Context) error { - workd, err := os.MkdirTemp("", "lukspol") +func genLuksPolicy(pData PolicyData) error { + // Read the uki-production pcr7 value. + luksPcr7, err := os.ReadFile(pData.Pcr7Prod) if err != nil { - return errors.Wrapf(err, "Error creating tempdir") - } - defer os.RemoveAll(workd) - - sessName := filepath.Join(workd, "tpm_session_b") - - if err := RunCommand("tpm2_startauthsession", "-S", sessName); err != nil { - return errors.Wrapf(err, "Failed creating auth session"); + return err } - defer RunCommand("tpm2_flushcontext", sessName) - - cmd := []string{ - "tpm2_policypcr", - "-S", sessName, - "-l", "sha256:7", - "-f", ctx.String("passwd-pcr7-file"), + // Put the pcr7 value in a tpm2.PCRSValues structure so we can compute its digest. + values := make(tpm2.PCRValues) + err = values.SetValue(tpm2.HashAlgorithmSHA256, 7, luksPcr7) + if err != nil { + return err } - if err := RunCommand(cmd...); err != nil { - return errors.Wrapf(err, "Failed running policypcr") + pcrDigest, err := util.ComputePCRDigest(tpm2.HashAlgorithmSHA256, tpm2.PCRSelectionList{{Hash: tpm2.HashAlgorithmSHA256, Select: []int{7}}}, values) + if err != nil { + return err } - cmd = []string{ - "tpm2_policynv", "-i-", - "-S", sessName, - TPM2IndexEAVersion.String(), "eq", - "-L", ctx.String("luks-policy-file"), - } - if err := runWithStdin(PolicyVersion.String(), cmd...); err != nil { - return errors.Wrapf(err, "Failed running policynv") - } + // Create a tpm2.NVPublic structure that resembles what we would have + // done via an nvwrite of the policy version to the index. + // Include TPMA_NV_WRITTEN attribute indicating the index has been written to. + nvpub := tpm2.NVPublic{Index: tpm2.Handle(TPM2IndexEAVersion), NameAlg: tpm2.HashAlgorithmSHA256, Attrs: tpm2.NVTypeOrdinary.WithAttrs(tpm2.AttrNVOwnerWrite|tpm2.AttrNVOwnerRead|tpm2.AttrNVAuthRead|tpm2.AttrNVWritten), Size: 4} - return nil + trial := util.ComputeAuthPolicy(tpm2.HashAlgorithmSHA256) + trial.PolicyPCR(pcrDigest, tpm2.PCRSelectionList{{Hash: tpm2.HashAlgorithmSHA256, Select: []int{7}}}) + trial.PolicyNV(nvpub.Name(), []byte(pData.PolicyVersion), 0, tpm2.OpEq) + policyDigest := trial.GetDigest() + return os.WriteFile(pData.LuksOutFile, policyDigest, 0400) } -func genPasswdPolicy(ctx *cli.Context) error { - workd, err := os.MkdirTemp("", "passpol") +func genPasswdPolicy(pData PolicyData) error { + // Read the uki-tpm pcr7 value. + Pcr7Pwd, err := os.ReadFile(pData.Pcr7Tpm) + if err != nil { + return err + } + // Put the pcr7 value in a tpm2.PCRSValues structure so we can compute its digest. + values := make(tpm2.PCRValues) + err = values.SetValue(tpm2.HashAlgorithmSHA256, 7, Pcr7Pwd) + if err != nil { + return err + } + pcrDigest, err := util.ComputePCRDigest(tpm2.HashAlgorithmSHA256, tpm2.PCRSelectionList{{Hash: tpm2.HashAlgorithmSHA256, Select: []int{7}}}, values) if err != nil { - return errors.Wrapf(err, "Error creating tempdir") + return err } - defer os.RemoveAll(workd) - sessName := filepath.Join(workd, "tpm_session_a") + // Use a "trial" session to compute the policy digest. + trial := util.ComputeAuthPolicy(tpm2.HashAlgorithmSHA256) + trial.PolicyPCR(pcrDigest, tpm2.PCRSelectionList{{Hash: tpm2.HashAlgorithmSHA256, Select: []int{7}}}) + policyDigest := trial.GetDigest() + return os.WriteFile(pData.PasswdOutFile, policyDigest, 0400) +} - if err := RunCommand("tpm2_startauthsession", "-S", sessName); err != nil { - return errors.Wrapf(err, "Failed creating auth session"); +func TpmGenPolicy(pData PolicyData) error { + // Check inputs + if pData.Pcr7Prod == "" || pData.Pcr7Tpm == "" { + return errors.New("Missing pcr7 value(s).") } - defer RunCommand("tpm2_flushcontext", sessName) - cmd := []string{ - "tpm2_policypcr", - "-S", sessName, - "-l", "sha256:7", - "-f", ctx.String("passwd-pcr7-file"), - "-L", ctx.String("passwd-policy-file"), + if pData.LuksOutFile == "" { + pData.LuksOutFile = "luks_policy.out" } - if err := RunCommand(cmd...); err != nil { - return errors.Wrapf(err, "Failed running policypcr") + + if pData.PasswdOutFile == "" { + pData.PasswdOutFile = "passwd_policy.out" } - return nil -} + // Policy Version if given must be 4 digits. Otherwise use a default of "0001". + if pData.PolicyVersion == "" { + pData.PolicyVersion = PolicyVersion.String() + } else { + if len(pData.PolicyVersion) != 4 { + return errors.New("Policy version should be a four digit string. i.e. 0001") + } + for _, c := range pData.PolicyVersion { + if c < '0' || c > '9' { + return errors.New("Policy version should be a four digit string. i.e. 0001") + } + } + } -func TpmGenPolicy(ctx *cli.Context) error { - if err := genLuksPolicy(ctx); err != nil { + if err := genLuksPolicy(pData); err != nil { return err } - if err := genPasswdPolicy(ctx); err != nil { + if err := genPasswdPolicy(pData); err != nil { return err } return nil