Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dbx Key Management #236

Merged
merged 5 commits into from
Aug 10, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat(reset): enable partial resets of secureboot keys
Signed-off-by: Fabian Wienand <fabian.wienand@9elements.com>
  • Loading branch information
Fabian Wienand committed Aug 3, 2023
commit eb4219c0cbfe99a6affc33731490996fc415102d
46 changes: 5 additions & 41 deletions cmd/sbctl/enroll-keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,46 +13,10 @@ import (
"github.com/foxboron/sbctl/certs"
"github.com/foxboron/sbctl/fs"
"github.com/foxboron/sbctl/logging"
"github.com/foxboron/sbctl/stringset"
"github.com/spf13/cobra"
"golang.org/x/exp/slices"
)

type StringSet struct {
Allowed []string
Value string
}

func NewStringSet(allowed []string, d string) *StringSet {
return &StringSet{
Allowed: allowed,
Value: d,
}
}

func (s StringSet) String() string {
return s.Value
}

func (s *StringSet) Set(p string) error {
if !slices.Contains(s.Allowed, p) {
return fmt.Errorf("%s is not included in %s", p, strings.Join(s.Allowed, ","))
}
s.Value = p
return nil
}

func (s *StringSet) Type() string {
var allowedValues string

for _, allowedValue := range s.Allowed {
allowedValues += fmt.Sprintf("%v,", allowedValue)
}

allowedValues = strings.TrimRight(allowedValues, ",")

return fmt.Sprintf("[%v]", allowedValues)
}

type FirmwareBuiltinFlags []string

func (f *FirmwareBuiltinFlags) String() string {
Expand All @@ -77,16 +41,16 @@ type EnrollKeysCmdOptions struct {
Force bool
TPMEventlogChecksums bool
Custom bool
Partial StringSet
Partial stringset.StringSet
BuiltinFirmwareCerts FirmwareBuiltinFlags
Export StringSet
Export stringset.StringSet
}

var (
systemEventlog = "/sys/kernel/security/tpm0/binary_bios_measurements"
enrollKeysCmdOptions = EnrollKeysCmdOptions{
Partial: StringSet{Allowed: []string{"PK", "KEK", "db"}},
Export: StringSet{Allowed: []string{"esl", "auth"}},
Partial: stringset.StringSet{Allowed: []string{"PK", "KEK", "db"}},
Export: stringset.StringSet{Allowed: []string{"esl", "auth"}},
}
enrollKeysCmd = &cobra.Command{
Use: "enroll-keys",
Expand Down
110 changes: 97 additions & 13 deletions cmd/sbctl/reset.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,49 +10,133 @@ import (
"github.com/foxboron/go-uefi/efi/util"
"github.com/foxboron/sbctl"
"github.com/foxboron/sbctl/logging"
"github.com/foxboron/sbctl/stringset"
"github.com/spf13/cobra"
)

var resetCmd = &cobra.Command{
Use: "reset",
Short: "Reset Platform Key (PK)",
RunE: RunReset,
type resetCmdOptions struct {
Partial stringset.StringSet
}

func resetKey() error {
var (
resetCmdOpts = resetCmdOptions{
Partial: stringset.StringSet{Allowed: []string{"PK", "KEK", "db"}},
}
resetCmd = &cobra.Command{
Use: "reset",
Short: "Reset Secure Boot Keys",
RunE: RunReset,
}
)

func resetKeys() error {
if resetCmdOpts.Partial.Value == "" {
if err := resetPK(); err != nil {
return fmt.Errorf("could not reset PK: %v", err)
}

return nil
}

switch partial := resetCmdOpts.Partial.Value; partial {
case "db":
if err := resetDB(); err != nil {
return err
}
case "KEK":
if err := resetKEK(); err != nil {
return err
}
case "PK":
if err := resetPK(); err != nil {
return err
}
default:
return fmt.Errorf("unsupported type to reset: %s, allowed values are: %s", partial, enrollKeysCmdOptions.Partial.Type())
}

return nil
}

func resetDB() error {
KEKKey := filepath.Join(sbctl.KeysPath, "KEK", "KEK.key")
KEKPem := filepath.Join(sbctl.KeysPath, "KEK", "KEK.pem")

if err := resetDatabase(KEKKey, KEKPem, "db"); err != nil {
return err
}

logging.Ok("Removed Signature Database!")
logging.Println("Use `sbctl enroll-keys` to enroll the Signature Database again.")
return nil
}

func resetKEK() error {
PKKey := filepath.Join(sbctl.KeysPath, "PK", "PK.key")
PKPem := filepath.Join(sbctl.KeysPath, "PK", "PK.pem")
key, err := util.ReadKeyFromFile(PKKey)

if err := resetDatabase(PKKey, PKPem, "KEK"); err != nil {
return err
}

logging.Ok("Removed Key Exchange Keys!")
logging.Println("Use `sbctl enroll-keys` to enroll a Key Exchange Key again.")
return nil
}

func resetPK() error {
PKKey := filepath.Join(sbctl.KeysPath, "PK", "PK.key")
PKPem := filepath.Join(sbctl.KeysPath, "PK", "PK.pem")

if err := resetDatabase(PKKey, PKPem, "PK"); err != nil {
return err
}

logging.Ok("Removed Platform Key!")
logging.Println("Use `sbctl enroll-keys` to enroll the Platform Key again.")
return nil
}

func resetDatabase(signerKey, signerPem string, efivar string) error {
key, err := util.ReadKeyFromFile(signerKey)
if err != nil {
return err
}
crt, err := util.ReadCertFromFile(PKPem)

crt, err := util.ReadCertFromFile(signerPem)
if err != nil {
return err
}
signedBuf, err := efi.SignEFIVariable(key, crt, "PK", []byte{})

signedBuf, err := efi.SignEFIVariable(key, crt, efivar, []byte{})
if err != nil {
return err
}
if err := efi.WriteEFIVariable("PK", signedBuf); err != nil {

if err := efi.WriteEFIVariable(efivar, signedBuf); err != nil {
if errors.Is(err, syscall.EIO) {
return fmt.Errorf("platform key already reset or not enrolled")
return fmt.Errorf("%s already reset or not enrolled", efivar)
}
return err
}

return nil
}

func RunReset(cmd *cobra.Command, args []string) error {
if err := resetKey(); err != nil {
if err := resetKeys(); err != nil {
return err
}
logging.Ok("Removed Platform Key!")
logging.Println("Use `sbctl enroll-keys` to enroll the Platform Key again.")
return nil
}

func resetKeysCmdFlags(cmd *cobra.Command) {
f := cmd.Flags()
f.VarPF(&resetCmdOpts.Partial, "partial", "p", "enroll a partial set of keys")
}

func init() {
resetKeysCmdFlags(resetCmd)
CliCommands = append(CliCommands, cliCommand{
Cmd: resetCmd,
})
Expand Down
2 changes: 1 addition & 1 deletion cmd/sbctl/rotate-keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ func rotatePK(oldKeys *Keys, newKeys *Keys) error {
}

func RunRotateKeys(cmd *cobra.Command, args []string) error {
if err := resetKey(); err != nil {
if err := resetPK(); err != nil {
fmt.Println(err)
}

Expand Down
5 changes: 3 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ require (
github.com/fatih/color v1.13.0
github.com/foxboron/go-uefi v0.0.0-20230217160721-375279dfc837
github.com/google/go-attestation v0.3.3-0.20210901195502-505680f536da
github.com/google/go-cmp v0.5.8
github.com/google/uuid v1.3.0
github.com/spf13/afero v1.9.3
github.com/spf13/cobra v1.1.3
golang.org/x/crypto v0.6.0
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1
golang.org/x/sys v0.5.0
)

Expand All @@ -21,8 +24,6 @@ require (
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/spf13/afero v1.9.3 // indirect
github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect
golang.org/x/text v0.7.0 // indirect
)
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -318,8 +318,9 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv38lDZ8kPZcQVoM=
github.com/google/go-licenses v0.0.0-20210329231322-ce1d9163b77d/go.mod h1:+TYOmkVoJOpwnS0wfdsJCV9CoD5nJYsHoFk/0CrTK4M=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
Expand Down Expand Up @@ -1111,7 +1112,6 @@ golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
Expand Down
44 changes: 44 additions & 0 deletions stringset/stringset.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package stringset

import (
"fmt"
"strings"

"golang.org/x/exp/slices"
)

type StringSet struct {
Allowed []string
Value string
}

func NewStringSet(allowed []string, d string) *StringSet {
return &StringSet{
Allowed: allowed,
Value: d,
}
}

func (s StringSet) String() string {
return s.Value
}

func (s *StringSet) Set(p string) error {
if !slices.Contains(s.Allowed, p) {
return fmt.Errorf("%s is not included in %s", p, strings.Join(s.Allowed, ","))
}
s.Value = p
return nil
}

func (s *StringSet) Type() string {
var allowedValues string

for _, allowedValue := range s.Allowed {
allowedValues += fmt.Sprintf("%v,", allowedValue)
}

allowedValues = strings.TrimRight(allowedValues, ",")

return fmt.Sprintf("[%v]", allowedValues)
}
69 changes: 69 additions & 0 deletions stringset/stringset_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package stringset

import (
"testing"
)

func TestStringSet(t *testing.T) {
for _, tt := range []struct {
name string
allowed []string
value string
wantType string
wantError bool
wantString string
}{
{
name: "no string allowed",
value: "abc",
wantType: "[]",
wantError: true,
},
{
name: "set value",
allowed: []string{"pk"},
value: "pk",
wantType: "[pk]",
wantError: false,
wantString: "pk",
},
{
name: "set wrong value",
allowed: []string{"pk"},
value: "pj",
wantType: "[pk]",
wantError: true,
},
{
name: "multiple allowed",
allowed: []string{"pk", "kek", "db"},
value: "db",
wantType: "[pk,kek,db]",
wantString: "db",
},
{
name: "fail on multiple allowed",
allowed: []string{"pk", "kek", "db"},
value: "da",
wantType: "[pk,kek,db]",
wantError: true,
},
} {
t.Run(tt.name, func(t *testing.T) {
stringSet := NewStringSet(tt.allowed, "")

if stringSet.Type() != tt.wantType {
t.Errorf("got type of stringSet: %v, but want: %v", stringSet.Type(), tt.wantType)
}

err := stringSet.Set(tt.value)
if (err != nil && !tt.wantError) || (err == nil && tt.wantError) {
t.Fatalf("expected error: %v, but got %v", tt.wantError, err)
}

if stringSet.String() != tt.wantString {
t.Errorf("expected stringSet value %v, but got %v", tt.wantString, stringSet.String())
}
})
}
}