Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
87 changes: 71 additions & 16 deletions backend/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ package backend
import (
"crypto"
"crypto/x509"
"crypto/x509/pkix"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
"io"
"os"
"path/filepath"
"strings"

"github.com/foxboron/go-uefi/authenticode"
"github.com/foxboron/go-uefi/efivar"
Expand All @@ -33,7 +35,6 @@ type KeyBackend interface {
Signer() crypto.Signer
Certificate() *x509.Certificate
Type() BackendType
Description() string
}

type KeyHierarchy struct {
Expand All @@ -50,16 +51,19 @@ func (k *KeyHierarchy) GetConfig(keydir string) *config.Keys {
Privkey: filepath.Join(keydir, "PK/PK.key"),
Pubkey: filepath.Join(keydir, "PK/PK.pem"),
Type: string(k.PK.Type()),
Subject: k.PK.Certificate().Subject.String(),
},
KEK: &config.KeyConfig{
Privkey: filepath.Join(keydir, "KEK/KEK.key"),
Pubkey: filepath.Join(keydir, "KEK/KEK.pem"),
Type: string(k.KEK.Type()),
Subject: k.KEK.Certificate().Subject.String(),
},
Db: &config.KeyConfig{
Privkey: filepath.Join(keydir, "db/db.key"),
Pubkey: filepath.Join(keydir, "db/db.pem"),
Type: string(k.Db.Type()),
Subject: k.Db.Certificate().Subject.String(),
},
}
}
Expand Down Expand Up @@ -127,11 +131,11 @@ func (k *KeyHierarchy) RotateKeyWithBackend(hier hierarchy.Hierarchy, backend Ba
var err error
switch hier {
case hierarchy.PK:
k.PK, err = createKey(k.state, string(backend), hier, k.PK.Description())
k.PK, err = createKey(k.state, k.state.Config.Keys.PK, hier)
case hierarchy.KEK:
k.KEK, err = createKey(k.state, string(backend), hier, k.KEK.Description())
k.KEK, err = createKey(k.state, k.state.Config.Keys.KEK, hier)
case hierarchy.Db:
k.Db, err = createKey(k.state, string(backend), hier, k.Db.Description())
k.Db, err = createKey(k.state, k.state.Config.Keys.Db, hier)
}
return err
}
Expand Down Expand Up @@ -190,19 +194,18 @@ func (k *KeyHierarchy) SignFile(hier hierarchy.Hierarchy, peBinary *authenticode
return peBinary.Bytes(), nil
}

func createKey(state *config.State, backend string, hier hierarchy.Hierarchy, desc string) (KeyBackend, error) {
if desc == "" {
desc = hier.Description()
}
switch backend {
func createKey(state *config.State, key *config.KeyConfig, hier hierarchy.Hierarchy) (KeyBackend, error) {
subject := parseSubject(key.Subject, hier)

switch key.Type {
case "file", "":
return NewFileKey(hier, desc)
return NewFileKey(hier, subject)
case "tpm":
return NewTPMKey(state.TPM, desc)
return NewTPMKey(state.TPM, subject)
case "yubikey":
return NewYubikeyKey(state.Yubikey, hier)
return NewYubikeyKey(state.Yubikey, subject)
default:
return NewFileKey(hier, desc)
return NewFileKey(hier, subject)
}
}

Expand All @@ -211,17 +214,17 @@ func CreateKeys(state *config.State) (*KeyHierarchy, error) {
var err error

c := state.Config
hier.PK, err = createKey(state, c.Keys.PK.Type, hierarchy.PK, c.Keys.PK.Description)
hier.PK, err = createKey(state, c.Keys.PK, hierarchy.PK)
if err != nil {
return nil, err
}

hier.KEK, err = createKey(state, c.Keys.KEK.Type, hierarchy.KEK, c.Keys.KEK.Description)
hier.KEK, err = createKey(state, c.Keys.KEK, hierarchy.KEK)
if err != nil {
return nil, err
}

hier.Db, err = createKey(state, c.Keys.Db.Type, hierarchy.Db, c.Keys.Db.Description)
hier.Db, err = createKey(state, c.Keys.Db, hierarchy.Db)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -337,3 +340,55 @@ func InitBackendFromKeys(state *config.State, priv, pem []byte, hier hierarchy.H
return nil, fmt.Errorf("unknown key backend: %s", t)
}
}

func parseSubject(subj string, hier hierarchy.Hierarchy) pkix.Name {
var subject pkix.Name

if subj != "" {
subject = pkix.Name{}

fields := strings.SplitSeq(subj, "/")
for field := range fields {
if field == "" {
continue
}
kv := strings.SplitN(field, "=", 2)
if len(kv) != 2 {
continue
}
key := strings.ToUpper(strings.TrimSpace(kv[0]))
value := strings.TrimSpace(kv[1])

switch key {
case "C":
subject.Country = append(subject.Country, value)
case "O":
subject.Organization = append(subject.Organization, value)
case "OU":
subject.OrganizationalUnit = append(subject.OrganizationalUnit, value)
case "L":
subject.Locality = append(subject.Locality, value)
case "ST":
subject.Province = append(subject.Province, value)
case "CN":
subject.CommonName = value
case "SERIALNUMBER":
subject.SerialNumber = value
default:
}
}

// Basic sanity: CN must be supplied
if subject.CommonName == "" {
panic("subject has no common name")
}
} else {
// return default
subject = pkix.Name{
Country: []string{"WW"},
CommonName: hier.Description(),
}
}

return subject
}
7 changes: 2 additions & 5 deletions backend/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ type FileKey struct {
privkey *rsa.PrivateKey
}

func NewFileKey(_ hierarchy.Hierarchy, desc string) (*FileKey, error) {
func NewFileKey(_ hierarchy.Hierarchy, subject pkix.Name) (*FileKey, error) {
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
serialNumber, _ := rand.Int(rand.Reader, serialNumberLimit)
c := x509.Certificate{
Expand All @@ -36,9 +36,7 @@ func NewFileKey(_ hierarchy.Hierarchy, desc string) (*FileKey, error) {
SignatureAlgorithm: x509.SHA256WithRSA,
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(5, 0, 0),
Subject: pkix.Name{
CommonName: desc,
},
Subject: subject,
}
priv, err := rsa.GenerateKey(rand.Reader, RSAKeySize)
if err != nil {
Expand Down Expand Up @@ -116,7 +114,6 @@ func FileKeyFromBytes(keyb, pemb []byte) (*FileKey, error) {
func (f *FileKey) Type() BackendType { return f.keytype }
func (f *FileKey) Certificate() *x509.Certificate { return f.cert }
func (f *FileKey) Signer() crypto.Signer { return f.privkey }
func (f *FileKey) Description() string { return f.Certificate().Subject.SerialNumber }

func (f *FileKey) PrivateKeyBytes() []byte {
privateKeyBytes, err := x509.MarshalPKCS8PrivateKey(f.privkey)
Expand Down
9 changes: 3 additions & 6 deletions backend/tpm.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ type TPMKey struct {
tpm func() transport.TPMCloser
}

func NewTPMKey(tpmcb func() transport.TPMCloser, desc string) (*TPMKey, error) {
func NewTPMKey(tpmcb func() transport.TPMCloser, subject pkix.Name) (*TPMKey, error) {
rwc := tpmcb()
key, err := keyfile.NewLoadableKey(rwc, tpm2.TPMAlgRSA, 2048, []byte(nil),
keyfile.WithDescription(desc),
keyfile.WithDescription(subject.String()),
)
if err != nil {
return nil, err
Expand All @@ -44,9 +44,7 @@ func NewTPMKey(tpmcb func() transport.TPMCloser, desc string) (*TPMKey, error) {
SignatureAlgorithm: x509.SHA256WithRSA,
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(5, 0, 0),
Subject: pkix.Name{
CommonName: desc,
},
Subject: subject,
}

pubkey, err := key.PublicKey()
Expand Down Expand Up @@ -78,7 +76,6 @@ func NewTPMKey(tpmcb func() transport.TPMCloser, desc string) (*TPMKey, error) {

func (t *TPMKey) Type() BackendType { return t.keytype }
func (t *TPMKey) Certificate() *x509.Certificate { return t.cert }
func (t *TPMKey) Description() string { return t.TPMKey.Description }

func (t *TPMKey) Signer() crypto.Signer {
s, err := t.TPMKey.Signer(t.tpm(), []byte(nil), []byte(nil))
Expand Down
15 changes: 3 additions & 12 deletions backend/yubikey.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
"time"

"github.com/foxboron/sbctl/config"
"github.com/foxboron/sbctl/hierarchy"
"github.com/foxboron/sbctl/logging"

"github.com/go-piv/piv-go/v2/piv"
Expand All @@ -41,7 +40,7 @@ type Yubikey struct {
touchPolicy piv.TouchPolicy
}

func NewYubikeyKey(yubikeyReader *config.YubikeyReader, hier hierarchy.Hierarchy) (*Yubikey, error) {
func NewYubikeyKey(yubikeyReader *config.YubikeyReader, subject pkix.Name) (*Yubikey, error) {
cert, err := yubikeyReader.GetPIVKeyCert()
if err != nil {
if !errors.Is(err, piv.ErrNotFound) {
Expand Down Expand Up @@ -107,16 +106,10 @@ func NewYubikeyKey(yubikeyReader *config.YubikeyReader, hier hierarchy.Hierarchy
SignatureAlgorithm: x509.SHA256WithRSA,
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(20, 0, 0),
Subject: pkix.Name{
Country: []string{hier.Description()},
CommonName: hier.Description(),
},
Subject: subject,
}

logging.Println(fmt.Sprintf("Creating %s (%s) key...\nPlease press Yubikey to confirm presence for RSA4096 MD5: %x",
hier.Description(),
hier.String(),
md5sum(ykCert.PublicKey)))
logging.Println(fmt.Sprintf("Please press Yubikey to confirm presence for RSA4096 MD5: %x", md5sum(ykCert.PublicKey)))
derBytes, err := x509.CreateCertificate(rand.Reader, &c, &c, ykCert.PublicKey, priv)
if err != nil {
return nil, err
Expand Down Expand Up @@ -183,8 +176,6 @@ func (f *Yubikey) Signer() crypto.Signer {
return priv.(crypto.Signer)
}

func (f *Yubikey) Description() string { return f.Certificate().Subject.SerialNumber }

// save YubiKey data to file
func (f *Yubikey) PrivateKeyBytes() []byte {
yubiData := YubikeyData{
Expand Down
15 changes: 15 additions & 0 deletions cmd/sbctl/create-keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ var (
KEKKeytype string
DbKeytype string
PKKeytype string
PKSubject string
KEKSubject string
DbSubject string
OverwriteYubikey bool
)

Expand Down Expand Up @@ -80,6 +83,15 @@ func RunCreateKeys(state *config.State) error {
}
}

if PKSubject != "" {
state.Config.Keys.PK.Subject = PKSubject
}
if KEKSubject != "" {
state.Config.Keys.KEK.Subject = KEKSubject
}
if DbSubject != "" {
state.Config.Keys.Db.Subject = DbSubject
}
// if any keytype is yubikey close it appropriately at the end
if Keytype == "yubikey" || PKKeytype == "yubikey" || KEKKeytype == "yubikey" || DbKeytype == "yubikey" {
defer state.Yubikey.Close()
Expand Down Expand Up @@ -119,6 +131,9 @@ func createKeysCmdFlags(cmd *cobra.Command) {
f.StringVarP(&PKKeytype, "pk-keytype", "", "", "PK key type (default: file)")
f.StringVarP(&KEKKeytype, "kek-keytype", "", "", "KEK key type (default: file)")
f.StringVarP(&DbKeytype, "db-keytype", "", "", "db key type (default: file)")
f.StringVarP(&PKSubject, "pk-subj", "", "", "Distinguished name for Platform Key certificate (openssl style, e.g. /CN=Platform Key/C=WW/)")
f.StringVarP(&KEKSubject, "kek-subj", "", "", "Distinguished name for Key Exchange Key certificate (openssl style, e.g. /CN=Key Exchange Key/C=WW/)")
f.StringVarP(&DbSubject, "db-subj", "", "", "Distinguished name for Database Key certificate (openssl style, e.g. /CN=Database Key/C=WW/)")
}

func init() {
Expand Down
11 changes: 7 additions & 4 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ type FileConfig struct {
}

type KeyConfig struct {
Privkey string `json:"privkey"`
Pubkey string `json:"pubkey"`
Type string `json:"type"`
Description string `json:"description,omitempty"`
Privkey string `json:"privkey"`
Pubkey string `json:"pubkey"`
Type string `json:"type"`
Subject string `json:"subject,omitempty"`
}

type Keys struct {
Expand Down Expand Up @@ -88,16 +88,19 @@ func MkConfig(dir string) *Config {
Privkey: path.Join(conf.Keydir, "PK", "PK.key"),
Pubkey: path.Join(conf.Keydir, "PK", "PK.pem"),
Type: "file",
Subject: "/CN=Platform Key/C=WW/",
},
KEK: &KeyConfig{
Privkey: path.Join(conf.Keydir, "KEK", "KEK.key"),
Pubkey: path.Join(conf.Keydir, "KEK", "KEK.pem"),
Type: "file",
Subject: "/CN=Key Exchange Key/C=WW/",
},
Db: &KeyConfig{
Privkey: path.Join(conf.Keydir, "db", "db.key"),
Pubkey: path.Join(conf.Keydir, "db", "db.pem"),
Type: "file",
Subject: "/CN=Database Key/C=WW/",
},
}
return conf
Expand Down
13 changes: 13 additions & 0 deletions docs/sbctl.conf.5.txt
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,16 @@ Options
+
Default: file

*subject:* distinguished name ;;
Distinguished name to be used for the creation of certificates.
+
Needs to be given in openssl fashion.
+
Default:
* *pk:* /CN=Platform Key/C=WW/
* *kek:* /CN=Key Exchange Key/C=WW/
* *db*: /CN=Database Key/C=WW/


Example
-------
Expand All @@ -135,14 +145,17 @@ An example of a /etc/sbctl/sbctl.conf file with the default values.
privkey: /var/lib/sbctl/keys/PK/PK.key
pubkey: /var/lib/sbctl/keys/PK/PK.pem
type: file
subject: /CN=Platform Key/C=WW/
kek:
privkey: /var/lib/sbctl/keys/KEK/KEK.key
pubkey: /var/lib/sbctl/keys/KEK/KEK.pem
type: file
subject: /CN=Key Exchange Key/C=WW/
db:
privkey: /var/lib/sbctl/keys/db/db.key
pubkey: /var/lib/sbctl/keys/db/db.pem
type: file
subject: /CN=Database Key/C=WW/

See Also
--------
Expand Down
Loading