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

secboot: factor code for encrypted disk activation #14570

Draft
wants to merge 1 commit into
base: fde-manager-features
Choose a base branch
from
Draft
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
97 changes: 0 additions & 97 deletions secboot/secboot_hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import (
sb "github.com/snapcore/secboot"
sb_scope "github.com/snapcore/secboot/bootscope"
sb_hooks "github.com/snapcore/secboot/hooks"
"golang.org/x/xerrors"

"github.com/snapcore/snapd/kernel/fde"
"github.com/snapcore/snapd/logger"
Expand Down Expand Up @@ -136,102 +135,6 @@ func isV1EncryptedKeyFile(p string) bool {
return bytes.HasPrefix(buf, v1KeyPrefix)
}

// We have to deal with the following cases:
// 1. Key created with v1 data-format on disk (raw encrypted key), v1 hook reads the data
// 2. Key created with v2 data-format on disk (json), v1 hook created the data (no handle) and reads the data (hook output not json but raw binary data)
// 3. Key created with v1 data-format on disk (raw), v2 hook
// 4. Key created with v2 data-format on disk (json), v2 hook [easy]
func unlockVolumeUsingSealedKeyFDERevealKey(sealedEncryptionKeyFile, sourceDevice, targetDevice, mapperName string, opts *UnlockVolumeUsingSealedKeyOptions) (UnlockResult, error) {
// deal with v1 keys
if isV1EncryptedKeyFile(sealedEncryptionKeyFile) {
return unlockVolumeUsingSealedKeyFDERevealKeyV1(sealedEncryptionKeyFile, sourceDevice, targetDevice, mapperName)
}
return unlockVolumeUsingSealedKeyFDERevealKeyV2(sealedEncryptionKeyFile, sourceDevice, targetDevice, mapperName, opts)
}

func unlockVolumeUsingSealedKeyFDERevealKeyV1(sealedEncryptionKeyFile, sourceDevice, targetDevice, mapperName string) (UnlockResult, error) {
res := UnlockResult{IsEncrypted: true, PartDevice: sourceDevice}

sealedKey, err := os.ReadFile(sealedEncryptionKeyFile)
if err != nil {
return res, fmt.Errorf("cannot read sealed key file: %v", err)
}

p := fde.RevealParams{
SealedKey: sealedKey,
}
output, err := fde.Reveal(&p)
if err != nil {
return res, err
}

// the output of fde-reveal-key is the unsealed key
unsealedKey := output
if err := unlockEncryptedPartitionWithKey(mapperName, sourceDevice, unsealedKey); err != nil {
return res, fmt.Errorf("cannot unlock encrypted partition: %v", err)
}
res.FsDevice = targetDevice
res.UnlockMethod = UnlockedWithSealedKey
return res, nil
}

func unlockVolumeUsingSealedKeyFDERevealKeyV2(sealedEncryptionKeyFile, sourceDevice, targetDevice, mapperName string, opts *UnlockVolumeUsingSealedKeyOptions) (res UnlockResult, err error) {
res = UnlockResult{IsEncrypted: true, PartDevice: sourceDevice}

f, err := sb.NewFileKeyDataReader(sealedEncryptionKeyFile)
if err != nil {
return res, err
}
keyData, err := sb.ReadKeyData(f)
if err != nil {
fmt := "cannot read key data: %w"
return res, xerrors.Errorf(fmt, err)
}

model, err := opts.WhichModel()
if err != nil {
return res, fmt.Errorf("cannot retrieve which model to unlock for: %v", err)
}

// the output of fde-reveal-key is the unsealed key
options := activateVolOpts(opts.AllowRecoveryKey)
// FIXME: consider setting it in activateVolOpts if we keep
// this function separate from the tpm one for key data in
// files. Otherwise we should only set it if we provide key
// data generation 1.
options.Model = model

sbSetModel(model)
// This does not seem to work:
//defer sbSetModel(nil)
// TODO: set boot mode
//sbSetBootMode("run")
//defer sbSetBootMode("")
sbSetKeyRevealer(&keyRevealerV3{})
defer sbSetKeyRevealer(nil)

authRequestor, err := newAuthRequestor()
if err != nil {
return res, fmt.Errorf("internal error: cannot build an auth requestor: %v", err)
}

err = sbActivateVolumeWithKeyData(mapperName, sourceDevice, authRequestor, options, keyData)
if err == sb.ErrRecoveryKeyUsed {
logger.Noticef("successfully activated encrypted device %q using a fallback activation method", sourceDevice)
res.FsDevice = targetDevice
res.UnlockMethod = UnlockedWithRecoveryKey
return res, nil
}
if err != nil {
return res, fmt.Errorf("cannot unlock encrypted partition: %v", err)
}

logger.Noticef("successfully activated encrypted device %q using FDE kernel hooks", sourceDevice)
res.FsDevice = targetDevice
res.UnlockMethod = UnlockedWithSealedKey
return res, nil
}

type fdeHookV2DataHandler struct{}

func (fh *fdeHookV2DataHandler) RecoverKeys(data *sb.PlatformKeyData, encryptedPayload []byte) ([]byte, error) {
Expand Down
54 changes: 49 additions & 5 deletions secboot/secboot_sb.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,13 +120,57 @@ func UnlockVolumeUsingSealedKeyIfEncrypted(disk disks.Disk, name string, sealedE
sourceDevice := fmt.Sprintf("/dev/disk/by-uuid/%s", part.FilesystemUUID)
targetDevice := filepath.Join("/dev/mapper", mapperName)

if fdeHasRevealKey() {
res, err = unlockVolumeUsingSealedKeyFDERevealKey(sealedEncryptionKeyFile, sourceDevice, targetDevice, mapperName, opts)
res.PartDevice = partDevice

keyData, _, err := readKeyFile(sealedEncryptionKeyFile)
if err != nil {
return res, err
}

var keys []*sb.KeyData

if keyData != nil {
keys = append(keys, keyData)
}

if opts.WhichModel != nil {
model, err := opts.WhichModel()
if err != nil {
return res, fmt.Errorf("cannot retrieve which model to unlock for: %v", err)
}
sbSetModel(model)
// This does not seem to work:
//defer sbSetModel(nil)
}
// TODO: set boot mode
//sbSetBootMode("run")
//defer sbSetBootMode("")
sbSetKeyRevealer(&keyRevealerV3{})
defer sbSetKeyRevealer(nil)

options := activateVolOpts(opts.AllowRecoveryKey)
// TODO: remove this
options.Model = sb.SkipSnapModelCheck
authRequestor, err := newAuthRequestor()
if err != nil {
res.UnlockMethod = NotUnlocked
return res, fmt.Errorf("internal error: cannot build an auth requestor: %v", err)
}

err = sbActivateVolumeWithKeyData(mapperName, sourceDevice, authRequestor, options, keys...)
if err == sb.ErrRecoveryKeyUsed {
logger.Noticef("successfully activated encrypted device %q using a fallback activation method", sourceDevice)
res.UnlockMethod = UnlockedWithRecoveryKey
} else if err != nil {
res.UnlockMethod = NotUnlocked
return res, fmt.Errorf("cannot activate encrypted device %q: %v", sourceDevice, err)
} else {
res, err = unlockVolumeUsingSealedKeyTPM(name, sealedEncryptionKeyFile, sourceDevice, targetDevice, mapperName, opts)
logger.Noticef("successfully activated encrypted device %q with TPM", sourceDevice)
res.UnlockMethod = UnlockedWithSealedKey
}
res.PartDevice = partDevice
return res, err

res.FsDevice = targetDevice
return res, nil
}

// UnlockEncryptedVolumeUsingKey unlocks an existing volume using the provided key.
Expand Down
Loading
Loading