Skip to content

Commit

Permalink
secboot: factor code for encrypted disk activation
Browse files Browse the repository at this point in the history
This factors and simplifies the TPM and FDE Hook code together. This
does not yet factor the key file base one.
  • Loading branch information
valentindavid committed Oct 3, 2024
1 parent 663bc3b commit 8bfb3b1
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 228 deletions.
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

0 comments on commit 8bfb3b1

Please sign in to comment.