Foxboron / go-uefi

Linux UEFI library written in pure Go.
MIT License
133 stars 13 forks source link

Support for signing EFI executable with certificate chain #19

Open edgrz opened 5 months ago

edgrz commented 5 months ago

Hello,

We are using go-uefi library to generate UEFI executables in order to achieve Secure Boot on our systems. However, our UKI certificate we want to provide for creating the Signature is a certificate bundle/chain. However, as it is now it only supports to pass a single certificate object:

SignEFIExecutable

func CreateSignature(ctx *PECOFFSigningContext, Cert *x509.Certificate, Key crypto.Signer) ([]byte, error) {
    // Tianocore demands that we pad to 8 bytes
    // They also need to be added to the checksum file
    // We move this out of the checksum function since this padding is
    // only applied to the checksums used in the signature.
    paddingBytes, _ := PaddingBytes(len(ctx.PEFile), 8)
    ctx.PEFile = append(ctx.PEFile, paddingBytes...)
    ctx.SigData.Write(paddingBytes)

    sigCtx := &pkcs7.SigningContext{
        Cert:      Cert,
        KeySigner: Key,
        SigData:   ctx.SigData.Bytes(),
        Indirect:  true,
    }

    sd, err := pkcs7.SignData(sigCtx)
    if err != nil {
        return nil, err
    }
    return sd, nil
}

Is there a way we could provide a certificate chain?

Foxboron commented 5 months ago

I've been reworking the signing code recently so the linked code is soon obsolete.

https://github.com/Foxboron/go-uefi/blob/master/authenticode/authenticode.go#L128

If you give a complete example on how you would like this to work, I can try and work something out. But I would need help to validate the behaviour.

edgrz commented 5 months ago

Hi @Foxboron,

Thanks for the quick response. So basically, we want to generate an EFI executable providing the private key of a certificate for signing it and the certificate that needs to be embedded into the executable is not a self-signed one, so it means we need to provide the whole certificate chain (in my case just the cA and the certificate). So that, what we want is that the method for signing EFI executables (SignEFIExecutable function in the past and now SignAuthenticode) allow us to pass not a single cert *x509.Certificate but a slice of certs (or any other way you think fits here to support certificate chain).

I see you even have already parseCertificates function, so I think that if pkcs7.SignPKCS7 could support a slice or the bytes, would be already supporting it.

Regarding testing I do have baremetal hardware where i'm testing running Secure Boot system by generating my own ISOs, so I should be able to test it.

Foxboron commented 5 months ago

Are you expecting go-uefi to validate the certificate chain, or is this an external process?

edgrz commented 5 months ago

Not needed.

So here a useful schema of what we want to achieve (not UEFI): https://www.qualcomm.com/content/dam/qcomm-martech/dm-assets/documents/secure-boot-image-authentication_11.30.16.pdf (page 7)

We want to sign the UEFI executables with a certificate chain, where only the root cA is embedded into the signatureDB. So this would mean for validation that the other certificates of the chain must be embedded in the binary, so that UEFI can iterate over the chain to reach the root cA.

Foxboron commented 5 months ago

Unless there is an example of a properly signed binary I can compare against, or another form of example, I'm can't promise I'll look at it within any reasonable timeframe.

defreng commented 5 months ago

Hi @Foxboron

I just learned that sbsign does support embedding multiple certificates into a signed binary... Would this help as a reference?

Usage: sbsign [options] --key <keyfile> --cert <certfile> <efi-boot-image>
Sign an EFI boot image for use with secure boot.

Options:
    --engine <eng>     use the specified engine to load the key
    --key <keyfile>    signing key (PEM-encoded RSA private key)
    --cert <certfile>  certificate (x509 certificate)
    --addcert <addcertfile> additional intermediate certificates in a file
    --detached         write a detached signature, instead of
                        a signed binary
    --output <file>    write signed data to <file>
                        (default <efi-boot-image>.signed,
                        or <efi-boot-image>.pk7 for detached
                        signatures
Foxboron commented 5 months ago

Ah, yes. That makes things a lot easier. Should probably have checked that myself :)