Skip to content

efi: add CheckImageSignatureIsValidForHost function#532

Open
zyga wants to merge 1 commit intocanonical:masterfrom
zyga:feature/check-image-sig-for-host
Open

efi: add CheckImageSignatureIsValidForHost function#532
zyga wants to merge 1 commit intocanonical:masterfrom
zyga:feature/check-image-sig-for-host

Conversation

@zyga
Copy link
Copy Markdown

@zyga zyga commented Apr 1, 2026

The new function allows a update manager to look if the given image is signed with keys that are allowed by the host secure boot stack. This way an update manager can detect that an asset signature is unlikely to pass verification by the boot firmware before writing or updating the asset in persistent storage.

Copilot AI review requested due to automatic review settings April 1, 2026 15:06
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds an EFI helper to preflight whether a PE image’s Authenticode signing chain appears compatible with the host’s Secure Boot trust anchors, enabling update managers to detect likely-to-fail boot assets before writing them to persistent storage.

Changes:

  • Introduces CheckImageSignatureIsValidForHost to validate that an image has at least one signature chaining to a CA in the host’s UEFI db.
  • Adds a comprehensive unit test suite covering success and common failure modes.
  • Adds a test helper to mock CheckImageSignatureIsValidForHost.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
efi/image_trust.go Implements the new host-db trust check for image signatures.
efi/image_trust_test.go Adds unit tests for the new trust-check behavior.
efi/export_test.go Adds a mock helper for the new trust-check entry point.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread efi/image_trust.go Outdated
Comment thread efi/image_trust.go Outdated
Comment thread efi/image_trust.go Outdated
Comment thread efi/export_test.go Outdated
@zyga zyga force-pushed the feature/check-image-sig-for-host branch from f720b7f to 82d16e2 Compare April 1, 2026 15:23
@zyga zyga requested a review from Copilot April 1, 2026 15:24
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread efi/image_trust.go
Comment thread efi/image_trust.go Outdated
Comment thread efi/image_trust_test.go
@zyga zyga force-pushed the feature/check-image-sig-for-host branch from 82d16e2 to f15a594 Compare April 1, 2026 16:00
@zyga zyga requested a review from Copilot April 1, 2026 16:00
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread efi/image_trust_test.go Outdated
Comment thread efi/image_trust.go Outdated
Comment thread efi/image_trust.go
@zyga zyga force-pushed the feature/check-image-sig-for-host branch from f15a594 to 44a831a Compare April 1, 2026 16:15
@zyga zyga requested a review from Copilot April 1, 2026 16:15
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread efi/image_trust.go Outdated
Comment thread efi/image_trust.go Outdated
@zyga zyga force-pushed the feature/check-image-sig-for-host branch from 44a831a to c75ef5c Compare April 1, 2026 16:48
@zyga zyga requested a review from Copilot April 1, 2026 16:48
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread efi/image_trust.go
@zyga zyga force-pushed the feature/check-image-sig-for-host branch from c75ef5c to f6fd49e Compare April 1, 2026 16:56
@zyga zyga requested a review from Copilot April 1, 2026 17:00
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread efi/image_trust.go
zyga added a commit to zyga/snapd that referenced this pull request Apr 1, 2026
This should prevent writing boot shim that is only signed by the new
Microsoft key, on a machine that doesn't have that key in their UEFI DB.
By the nature of the check we also prevent writing things that have
entries in DBX that would equally fail to boot.

This requires canonical/secboot#532

Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
@zyga zyga force-pushed the feature/check-image-sig-for-host branch from f6fd49e to 203bc51 Compare April 1, 2026 17:08
@zyga zyga requested a review from Copilot April 1, 2026 17:08
zyga added a commit to zyga/snapd that referenced this pull request Apr 1, 2026
This should prevent writing boot shim that is only signed by the new
Microsoft key, on a machine that doesn't have that key in their UEFI DB.
By the nature of the check we also prevent writing things that have
entries in DBX that would equally fail to boot.

This requires canonical/secboot#532

Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread efi/image_trust.go Outdated
@zyga zyga force-pushed the feature/check-image-sig-for-host branch from 203bc51 to 2c37d78 Compare April 1, 2026 17:32
@zyga zyga requested a review from Copilot April 1, 2026 17:33
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread efi/image_trust.go Outdated
@zyga zyga force-pushed the feature/check-image-sig-for-host branch from 2c37d78 to 4349f60 Compare April 2, 2026 07:16
@zyga zyga requested a review from Copilot April 2, 2026 07:16
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread efi/image_trust.go Outdated
@zyga zyga force-pushed the feature/check-image-sig-for-host branch from 4349f60 to 4c7706c Compare April 2, 2026 08:00
@zyga zyga requested a review from Copilot April 2, 2026 08:01
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread efi/image_trust.go Outdated
Comment thread efi/image_trust.go Outdated
@zyga zyga force-pushed the feature/check-image-sig-for-host branch from 4c7706c to 93f2763 Compare April 2, 2026 08:58
@zyga zyga requested a review from Copilot April 2, 2026 09:11
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread efi/image_trust.go Outdated
Comment thread efi/image_trust.go Outdated
@zyga zyga requested a review from Copilot April 2, 2026 09:48
The new function allows a update manager to look if the given image
is signed with keys that are allowed by the host secure boot stack.
This way an update manager can detect that an asset signature is unlikely
to pass verification by the boot firmware before writing or updating
the asset in persistent storage.

Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
@zyga zyga force-pushed the feature/check-image-sig-for-host branch from 93f2763 to 282ccb2 Compare April 2, 2026 09:49
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread efi/image_trust.go
Comment on lines +35 to +44
// CheckImageSignatureIsValidForHost checks whether the supplied image has at
// least one Authenticode signature that is authorized by the host's authorized
// signature database (the UEFI "db" variable).
//
// The image must have at least one Authenticode signature. It is not possible
// to authorize an unsigned image based solely on its digest being present in db.
//
// The image is authorized if:
// - At least one of its Authenticode signatures chains to an X.509 certificate
// authority that is enrolled in db, OR
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

The function doc comment is out of sync with the current behavior: (1) the implementation rejects unsigned images (len(sigs)==0) even if a digest exists in db, but the comment doesn’t mention this requirement; and (2) digest-based authorization compares the PE image Authenticode digest computed using the hash implied by the EFI_SIGNATURE_LIST type (e.g. SHA384 list uses SHA384 digest), not “the digest of one of its Authenticode signatures”. Please update the comment so callers don’t rely on incorrect semantics.

Copilot uses AI. Check for mistakes.
Comment thread efi/image_trust.go
Comment on lines +73 to +76
// - The host's dbx variable cannot be read (eg, if EFI variables are unavailable).
// - The image is revoked by a certificate or digest entry in the host's dbx.
// - No signature on the image is authorized by the host's db.
func CheckImageSignatureIsValidForHost(ctx context.Context, image Image) error {
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

The error wrapping here can produce duplicated context because openPeImage already returns errors prefixed with "cannot open image" (see efi/pe.go). Consider either returning the error as-is, or wrapping with a more specific message (e.g. "cannot open PE image") to avoid messages like "cannot open image: cannot open image: ...".

Copilot uses AI. Check for mistakes.
Comment thread efi/image_trust.go
}
}
} else {
// Skip unrecognized signature list types since we cannot use them for verification
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

This inline comment is misleading: the continue happens for any non-matching digest as well as unrecognized signature list types. Please reword it to reflect the actual condition (e.g. “no digest match (or unrecognized type)”).

Suggested change
// Skip unrecognized signature list types since we cannot use them for verification
// Skip signature lists with no digest match (or unrecognized type)

Copilot uses AI. Check for mistakes.
Comment thread efi/image_trust.go
Comment on lines +135 to +144
for _, sig := range sigs {
if certIsForbiddenByDbx(sig, dbx) {
return errors.New("secure boot signature is forbidden by the current host's signature databases")
}
}

return nil
}

var errNoTrustedSignature = errors.New("cannot find any secure boot signature that is trusted by the current host's authorized signature database")
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

There’s no test exercising the error path where db exists but cannot be read (the cannot read authorized signature database: %w branch in checkDbAuthorization). Since this function is intended for update preflight decisions, it would be good to add a unit test that injects a vars backend read error for db and asserts the returned error matches that message.

Copilot uses AI. Check for mistakes.
Comment thread efi/image_trust.go

digests := newImageDigestCache(pei)

for _, sigList := range db {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Iterating through db then through signatures seems wrong. I think the spec specifies the way the loop is and it is the opposite. We should check. While the result will be the same, the certificate that will used to validate might be different, and if this function evolves for example to return which certificate would be measured... that will be wrong.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I have been looking through the multiple specifications. And I actually do not find anything about it. I really thought measurements in PCR 7 had to be predictable. But I am not so sure anymore.

Anyway, I do think we should have the same kind of loop as in secureBootPolicyMixin.DetermineAuthority

This is also what edk2 does. And it seems that edk2 also verifies first signatures, then the digest.

Comment thread efi/image_trust.go
return nil
}
}
} else {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Something I would like to check the specs. Is something signed never checked for its digest?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Images are tested against enrolled digests whether they are signed or not.

Copy link
Copy Markdown
Collaborator

@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.

Thanks for working on this. I've done an initial pass and left some comments.

Comment thread efi/image_trust.go

// Check for a trusted signature in DB.
return checkDbAuthorization(ctx, pei, sigs)
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should this function return true if secure boot is disabled?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Probably yes

Comment thread efi/image_trust.go
}
// If DB doesn't exist, then there are no authorized signatures, so the image cannot be
// authorized.
return errNoTrustedSignature
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I don't know whether I would handle the efi.ErrVarNotExist error case explicitly here - the variable not existing is unexpected.

Comment thread efi/image_trust.go

if len(sigs) == 0 {
return errors.New("image has no secure boot signatures")
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

It's odd that an image is tested against enrolled digests only if it is signed. An unsigned image would still authenticate ok if it matched an enrolled digest, even if there are no signatures.

Comment thread efi/image_trust.go
}

for _, imageSig := range imageSigs {
if !imageSig.CertWithIDLikelyTrustAnchor(efi.NewX509CertIDFromCertificate(cert)) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Would

if imageSig.CertWithIDLikelyTrustAnchor(efi.NewX509CertIDFromCertificate(cert)) {
    return nil
}

be neater here?

Comment thread efi/image_trust.go
alg := efiSignatureListTypeToDigestAlg(sigList.Type)
if alg == crypto.Hash(0) {
return false, nil
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Does this make the earlier call to efiSignatureListTypeToDigestAlg in checkDbAuthorizations redundant?

Comment thread efi/image_trust.go
return false, nil
}

func efiSignatureListTypeToDigestAlg(guid efi.GUID) crypto.Hash {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This function doesn't take a signature list, so I wonder if there could be a better name for it.

Comment thread efi/image_trust.go
}

return false
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

There are some cert types that this doesn't handle - EFI_CERT_X509_SHA{256,384,512}_GUID. These contain TBS digests of revoked certificates and a revocation time, and a match of any certificate in the signing chain marks a signature as forbidden if the image signature has no timestamp signature, or if there is a timestamp signature but the timestamp is before the revocation time (on systems that support timestamp revocation).

Adding this adds a lot of complexity but without it, the result could indicate an image. I'm not sure whether checking revocations is worth the complexity, given the case we most care about is upgrades where we're not expected to upgrade from an image that is not revoked to one that is.

Comment thread efi/export_test.go
}
}

func MockCheckImageSignatureIsValidForHost(fn func(context.Context, Image) error) (restore func()) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This isn't used anywhere

Comment thread efi/image_trust.go
// signature database (the UEFI "db" variable).
//
// The image must have at least one Authenticode signature. It is not possible
// to authorize an unsigned image based solely on its digest being present in db.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The firmware will authorize an image in this way regardless of the presence of a signature.

Comment thread efi/image_trust.go
return nil
}
}
} else {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Images are tested against enrolled digests whether they are signed or not.

@zyga
Copy link
Copy Markdown
Author

zyga commented Apr 8, 2026

@chrisccoulson thank you for the review. I need to spend a moment to iterate on the changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants