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: re-introduce v1 fde hook format #14638

Conversation

valentindavid
Copy link
Contributor

No description provided.

@valentindavid valentindavid added Run nested The PR also runs tests inluded in nested suite FDE Manager Pull requests that target FDE manager branch labels Oct 17, 2024
bboozzoo
bboozzoo previously approved these changes Oct 17, 2024
Copy link
Contributor

@bboozzoo bboozzoo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

secboot/secboot_hooks.go Outdated Show resolved Hide resolved
secboot/secboot_hooks.go Outdated Show resolved Hide resolved
@valentindavid valentindavid marked this pull request as ready for review October 18, 2024 13:17
@valentindavid
Copy link
Contributor Author

This was manually tested with

  • snapd 2.49.2
  • removing the assume in the pc gadget from latest/edge
  • rebuild pc-kernel latest/edge with core-initrd core20 branch and snap-bootstrap from this PR
  • core20 from latest/edge

Copy link

codecov bot commented Oct 18, 2024

Codecov Report

Attention: Patch coverage is 76.14679% with 26 lines in your changes missing coverage. Please review.

Please upload report for BASE (fde-manager-features@cbf35eb). Learn more about missing BASE report.

Files with missing lines Patch % Lines
secboot/secboot_tpm.go 75.75% 13 Missing and 3 partials ⚠️
secboot/secboot_hooks.go 61.53% 5 Missing and 5 partials ⚠️
Additional details and impacted files
@@                   Coverage Diff                   @@
##             fde-manager-features   #14638   +/-   ##
=======================================================
  Coverage                        ?   78.83%           
=======================================================
  Files                           ?     1093           
  Lines                           ?   147894           
  Branches                        ?        0           
=======================================================
  Hits                            ?   116594           
  Misses                          ?    24022           
  Partials                        ?     7278           
Flag Coverage Δ
unittests 78.83% <76.14%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.


🚨 Try these New Features:

@@ -161,6 +161,22 @@ func UnlockVolumeUsingSealedKeyIfEncrypted(disk disks.Disk, name string, sealedE
return res, fmt.Errorf("internal error: cannot build an auth requestor: %v", err)
}

if loadedKey.SealedKeyV1 != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this feels like it's a leaky abstraction which only muddies the whole picture, could we do something more explicit, eg:

// legacy fde-hooks keys which did not carry any metadata
var legacyV1Key []byte
// more modern keys with metadata
var keys []*sb.KeyData

// isLegacyV1Key - simply checks the header

if fdeHasRevealKey() && isLegacyV1Key(sealedEncryptioKeyFile) {
	legacyV1Key, err = os.ReadFile()
	...
} else {
	if err := readKeyFile(sealedEncryptionKeyFile, loadedKey); err != nil {
		return res, err
	}
}

...
if len(legacyV1KeyFile) != 0 {

}
...
// continue with ActivateVolumeWithKeyData

and then simply drop the loader thingy

Copy link
Contributor

@bboozzoo bboozzoo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

(although I added some changes here, so it should likely count as +0.5)

@valentindavid
Copy link
Contributor Author

I have retested. And it is broken.

@valentindavid
Copy link
Contributor Author

It is working again.

bboozzoo
bboozzoo previously approved these changes Nov 4, 2024
Copy link
Contributor

@bboozzoo bboozzoo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, just some minor comments

Comment on lines 242 to 250
LoadKeyData(kd *sb.KeyData)
LoadSealedKeyObject(sko *sb_tpm2.SealedKeyObject)
LoadFDEHookKeyV1(sk []byte)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
LoadKeyData(kd *sb.KeyData)
LoadSealedKeyObject(sko *sb_tpm2.SealedKeyObject)
LoadFDEHookKeyV1(sk []byte)
// LoadKeyData is expected to keep track of key data wrappers
LoadKeyData(kd *sb.KeyData)
// LoadSealedKeyObject is expected to keep track of a TPM sealed key
LoadSealedKeyObject(sko *sb_tpm2.SealedKeyObject)
// LoadFDEHookV1Key is expected to keep track of a FDE hook v1 sealed key
LoadFDEHookKeyV1(sk []byte)

case oldSealedKey && hintExpectFDEHook:
// FDE hook key v1
//
// It has the same magic header has sealed key object,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// It has the same magic header has sealed key object,
// It has the same magic header as sealed key object,

secboot/secboot_tpm.go Show resolved Hide resolved
Copy link
Contributor

@ZeyadYasser ZeyadYasser left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small comments

sb.RegisterPlatformKeyDataHandler(legacyFdeHooksPlatformName, handler)
v2Handler := &fdeHookV2DataHandler{}
sb.RegisterPlatformKeyDataHandler(legacyFdeHooksPlatformName, v2Handler)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick

Suggested change

type defaultKeyLoader struct {
KeyData *sb.KeyData
SealedKeyObject *sb_tpm2.SealedKeyObject
FDEHookKeyV1 []byte
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick, a small doc comment explaining those fields and what the zero values mean would be helpful.

e.g.

Suggested change
FDEHookKeyV1 []byte
// Non-nil FDEHookKeyV1 indicates that V1 hook key is used
FDEHookKeyV1 []byte

@@ -238,14 +238,40 @@ func newAuthRequestor() (sb.AuthRequestor, error) {
)
}

// TODO: consider moving this to secboot
func readKeyFileImpl(keyfile string) (*sb.KeyData, *sb_tpm2.SealedKeyObject, error) {
type keyLoader interface {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tiny nitpick, feel free to ignore.

For simplicity, can't this just be a struct that is returned as output from readKeyFileImpl?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had in mind that in the future we just make callbacks that ignore some input. For instance the sealed key objects for tpm are useless for unlocking, only for resealing. Though in the end I have just used the same type.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will try it and see if the code looks better. Maybe it can be done in another PR.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wonder if the interface should be called loadedKey now?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also are we going to have other implementations of this interface in later PRs?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. But I am not sure how it should look at this point. But one thing is sure is that having an interface here seemed a bit useless.

// to use a hint we are using FDE hooks.
sealedKey, err := os.ReadFile(keyfile)
if err != nil {
return nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is ignoring the error here intentional?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch.

@valentindavid
Copy link
Contributor Author

I had to resolve conflicts, so I squashed and and rebase. I also added some code in the resealing to ignore v1 keys and a test for it. So that will need to be reviewed.

Copy link
Collaborator

@pedronis pedronis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some comments

secboot/secboot_hooks.go Show resolved Hide resolved
if loadedKey.FDEHookKeyV1 != nil {
// Special case for hook keys v1. They do not have
// primary keys. So we cannot wrap them in KeyData
err := unlockDiskWithHookV1Key(mapperName, sourceDevice, loadedKey.FDEHookKeyV1)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

they also don't reach here

func readKeyFileImpl(keyfile string) (*sb.KeyData, *sb_tpm2.SealedKeyObject, error) {
type keyLoader interface {
// LoadKeyData keeps track of keys in KeyData format.
LoadKeyData(kd *sb.KeyData)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wouldn't Loaded... be clearer? this just keep tracks at the comment says, the loading has already happened

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my mind originally, it was that call that was loading the read content into what ever thing needs to actually interpret it. But I think it makes sense to call it Loaded...

Copy link
Collaborator

@pedronis pedronis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks, couple naming comments and some more questions/comments

@@ -238,14 +238,40 @@ func newAuthRequestor() (sb.AuthRequestor, error) {
)
}

// TODO: consider moving this to secboot
func readKeyFileImpl(keyfile string) (*sb.KeyData, *sb_tpm2.SealedKeyObject, error) {
type keyLoader interface {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wonder if the interface should be called loadedKey now?

// with key data if there are some on the tokens.
// Also the request for recovery key will happen in
// ActivateVolumeWithKeyData
logger.Noticef("WARNING: attempting opening device %s with key file %s failed: %v", sourceDevice, sealedEncryptionKeyFile, err)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this fallback path is not reached

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added `TestUnlockVolumeUsingSealedKeyIfEncryptedFdeRevealKeyV1FallbackKeyData

LoadedFDEHookKeyV1(sk []byte)
}

type defaultKeyLoader struct {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

then this is also defaultLoadedKey?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have put a todo to the keyLoader interface to change it.

I am not sure that defaultLoadedKey makes total sense here since we create it empty. So it is not loaded yet. But it will make sense once we get rid of the interface and we return it.

@@ -238,14 +238,40 @@ func newAuthRequestor() (sb.AuthRequestor, error) {
)
}

// TODO: consider moving this to secboot
func readKeyFileImpl(keyfile string) (*sb.KeyData, *sb_tpm2.SealedKeyObject, error) {
type keyLoader interface {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also are we going to have other implementations of this interface in later PRs?

hasNewData = true
} else {
hasOldObject = true
if loadedKey.SealedKeyObject != nil {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should maybe leave a TODO about refactor some of these ifs into methods on the loadedKey interface, but also I as wrote elsewhere wondering if there will be other implementations?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added a TODO to the definition of keyLoader interface.

Copy link
Collaborator

@pedronis pedronis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks

@pedronis pedronis dismissed bboozzoo’s stale review November 7, 2024 15:55

this has changed enough that it needs a re-review

Copy link
Contributor

@ZeyadYasser ZeyadYasser left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, Thank you!

This could use a spread test in a follow up PR.

//
// It has the same magic header as sealed key object,
// but there is no version information. Thus we need
// to use a hint we are using FDE hooks.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// to use a hint we are using FDE hooks.
// to use a hint that we are using FDE hooks.

// to use a hint we are using FDE hooks.
sealedKey, err := os.ReadFile(keyfile)
if err != nil {
return fmt.Errorf("cannot FDE hook v1 key: %v", err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return fmt.Errorf("cannot FDE hook v1 key: %v", err)
return fmt.Errorf("cannot read FDE hook v1 key: %v", err)

Copy link
Contributor

@bboozzoo bboozzoo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. The cleanups can be done in a followup PR.

Copy link
Contributor

@chrisccoulson chrisccoulson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks ok to me, although I've not spent a lot of time looking at the test code.

@ernestl ernestl merged commit 4e36599 into canonical:fde-manager-features Nov 19, 2024
42 of 58 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
FDE Manager Pull requests that target FDE manager branch Run nested The PR also runs tests inluded in nested suite
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants