Jean-Francois Roy fd54dc191d
feat(talosctl): append microsoft secure boot certs
This patch adds a flag to `secureboot.database.Generate` to append the
Microsoft UEFI secure boot DB and KEK certificates to the appropriate
ESLs, in addition to complimentary command line flags.

This patch also includes a copy of said Microsoft certificates. The
certificates are downloaded from an official Microsoft repo.

Signed-off-by: Jean-Francois Roy <jf@devklog.net>
Signed-off-by: Andrey Smirnov <andrey.smirnov@siderolabs.com>
2024-07-22 14:15:42 +04:00

176 lines
4.4 KiB
Go

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Package database generates SecureBoot auto-enrollment database.
package database
import (
"crypto/sha256"
"crypto/x509"
"embed"
"path/filepath"
"sync"
"github.com/foxboron/go-uefi/efi/signature"
"github.com/foxboron/go-uefi/efi/util"
"github.com/foxboron/go-uefi/efivar"
"github.com/google/uuid"
"github.com/siderolabs/talos/internal/pkg/secureboot/pesign"
"github.com/siderolabs/talos/pkg/machinery/constants"
)
// Entry is a UEFI database entry.
type Entry struct {
Name string
Contents []byte
}
const (
microsoftSignatureOwnerGUID = "77fa9abd-0359-4d32-bd60-28f4e78f784b"
)
// Well-known UEFI DB certificates (DER data).
//
//go:embed certs/db/*.der
var wellKnownDB embed.FS
// Well-known UEFI KEK certificates (PEM data).
//
//go:embed certs/kek/*.der
var wellKnownKEK embed.FS
func loadWellKnownCertificates(fs embed.FS, path string) ([]*x509.Certificate, error) {
certs := []*x509.Certificate{}
files, err := fs.ReadDir(path)
if err != nil {
return nil, err
}
for _, file := range files {
data, err := fs.ReadFile(filepath.Join(path, file.Name()))
if err != nil {
return nil, err
}
cert, err := x509.ParseCertificate(data)
if err != nil {
return nil, err
}
certs = append(certs, cert)
}
return certs, nil
}
var wellKnownDBCertificates = sync.OnceValue(func() []*x509.Certificate {
certs, err := loadWellKnownCertificates(wellKnownDB, "certs/db")
if err != nil {
panic(err)
}
return certs
})
var wellKnownKEKCertificates = sync.OnceValue(func() []*x509.Certificate {
certs, err := loadWellKnownCertificates(wellKnownKEK, "certs/kek")
if err != nil {
panic(err)
}
return certs
})
// Options for Generate.
type Options struct {
IncludeWellKnownCertificates bool
}
// Option is a functional option for Generate.
type Option func(*Options)
// IncludeWellKnownCertificates is an option to include well-known certificates.
func IncludeWellKnownCertificates(v bool) Option {
return func(o *Options) {
o.IncludeWellKnownCertificates = v
}
}
// Generate generates a UEFI database to enroll the signing certificate.
//
// ref: https://blog.hansenpartnership.com/the-meaning-of-all-the-uefi-keys/
//
//nolint:gocyclo
func Generate(enrolledCertificate []byte, signer pesign.CertificateSigner, opts ...Option) ([]Entry, error) {
var options Options
for _, opt := range opts {
opt(&options)
}
// derive UUID from enrolled certificate
uuid := uuid.NewHash(sha256.New(), uuid.NameSpaceX500, enrolledCertificate, 4)
efiGUID := util.StringToGUID(uuid.String())
// Create PK ESL
pk := signature.NewSignatureDatabase()
if err := pk.Append(signature.CERT_X509_GUID, *efiGUID, enrolledCertificate); err != nil {
return nil, err
}
_, signedPK, err := signature.SignEFIVariable(efivar.PK, pk, signer.Signer(), signer.Certificate())
if err != nil {
return nil, err
}
// Create KEK ESL
kek := signature.NewSignatureDatabase()
if err := kek.Append(signature.CERT_X509_GUID, *efiGUID, enrolledCertificate); err != nil {
return nil, err
}
if options.IncludeWellKnownCertificates {
owner := util.StringToGUID(microsoftSignatureOwnerGUID)
for _, cert := range wellKnownKEKCertificates() {
if err := kek.Append(signature.CERT_X509_GUID, *owner, cert.Raw); err != nil {
return nil, err
}
}
}
_, signedKEK, err := signature.SignEFIVariable(efivar.KEK, kek, signer.Signer(), signer.Certificate())
if err != nil {
return nil, err
}
// Create db ESL
db := signature.NewSignatureDatabase()
if err := db.Append(signature.CERT_X509_GUID, *efiGUID, enrolledCertificate); err != nil {
return nil, err
}
if options.IncludeWellKnownCertificates {
owner := util.StringToGUID(microsoftSignatureOwnerGUID)
for _, cert := range wellKnownDBCertificates() {
if err := db.Append(signature.CERT_X509_GUID, *owner, cert.Raw); err != nil {
return nil, err
}
}
}
_, signedDB, err := signature.SignEFIVariable(efivar.Db, db, signer.Signer(), signer.Certificate())
if err != nil {
return nil, err
}
return []Entry{
{Name: constants.SignatureKeyAsset, Contents: signedDB.Bytes()},
{Name: constants.KeyExchangeKeyAsset, Contents: signedKEK.Bytes()},
{Name: constants.PlatformKeyAsset, Contents: signedPK.Bytes()},
}, nil
}