From f211d19b8df9239c264d801af5b58957c6997f09 Mon Sep 17 00:00:00 2001 From: Sergii Dmytruk Date: Sat, 24 Aug 2024 16:19:37 +0300 Subject: [PATCH 1/5] kb/edk2-capsule-updates.md: fix a typo GenerateCapsules -> GenerateCapsule Signed-off-by: Sergii Dmytruk --- docs/kb/edk2-capsule-updates.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/kb/edk2-capsule-updates.md b/docs/kb/edk2-capsule-updates.md index bf21c92629..21c42c8809 100644 --- a/docs/kb/edk2-capsule-updates.md +++ b/docs/kb/edk2-capsule-updates.md @@ -384,7 +384,7 @@ the EDK2-way of doing things via `SignedCapsulePkg` ([PDF][edk2-capsules]) but it appears to be less flexible due to relying on the build system of EDK2 which is quite rigid for a large firmware variance that can be found in Dasharo. -Because `GenerateCapsules` is in Python and at least part of the functionality +Because `GenerateCapsule` is in Python and at least part of the functionality is abstracted in form of modules, it's also possible to build custom tools on top of that. From 776deb2cce49bdf66e0fec325d31c18fdc69f85a Mon Sep 17 00:00:00 2001 From: Sergii Dmytruk Date: Sat, 24 Aug 2024 16:20:14 +0300 Subject: [PATCH 2/5] kb/edk2-capsule-updates.md: clarify use of shorter key chains Signed-off-by: Sergii Dmytruk --- docs/kb/edk2-capsule-updates.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/kb/edk2-capsule-updates.md b/docs/kb/edk2-capsule-updates.md index 21c42c8809..cbdd6bbf48 100644 --- a/docs/kb/edk2-capsule-updates.md +++ b/docs/kb/edk2-capsule-updates.md @@ -90,7 +90,10 @@ Signing takes three keys (root, its subkey and subkey's subkey for signing): This structure of signing keys is presupposed by `GenerateCapsule` and `Pkcs7Sign` but it seems to be a design decision of the tooling rather than a strict requirement on the length of a signing key chain by UEFI or cryptographic -implementation in EDK2. +implementation in EDK2. In particular, a chain of length 2 (without a subkey) +can be used by specifying any certificate in place of the subkey (root, signing +certificate or literally any other; it won't be used by the code but still needs +to be a valid certificate so the tools don't complain). ### Optional payload metadata From b8a1e310cb853e5c65c88fcd98cdeb6ca9d547d2 Mon Sep 17 00:00:00 2001 From: Sergii Dmytruk Date: Sat, 24 Aug 2024 16:21:53 +0300 Subject: [PATCH 3/5] kb/edk2-capsule-updates.md: explain key generation Signed-off-by: Sergii Dmytruk --- docs/kb/edk2-capsule-updates.md | 177 +++++++++++++++++++++++++++++++- 1 file changed, 173 insertions(+), 4 deletions(-) diff --git a/docs/kb/edk2-capsule-updates.md b/docs/kb/edk2-capsule-updates.md index cbdd6bbf48..89cd4d4c0a 100644 --- a/docs/kb/edk2-capsule-updates.md +++ b/docs/kb/edk2-capsule-updates.md @@ -328,6 +328,170 @@ interferes). [wiki-pkc]: https://en.wikipedia.org/wiki/Public-key_cryptography +## Generating signing keys with OpenSSL + +The process involves creation of 3 certificates, a local certificate +authority (CA) in a directory and 14 files. This warrants some overview of +what's going to be done. + +Keys making up the chain will assume the following roles: + +- _root_ — self-signed certificate acting as the basis of a CA +- _sub_ — second-level CA signed by _root_ key +- _sign_ — a signing key (not a CA) that is signed by _sub_ key + +A digital signature algorithm (DSA) consists of a private key (PK) algorithm and +a digest algorithm. Keys in a chain don't have to use the same DSA. Also, most +DSAs allow use of an arbitrary digest (not Ed25519 or Ed448 which prescribe the +use of SHA-512 and SHAKE-256 respectively). + +Keys exist independently of the chains in which they appear. When stored, +private keys are often encrypted. + +### Key generation by example + +It will be easier to go into finer details while looking at the actual commands. +The entire process can be broken into several stages: + +1. Creation of 3 certificates +2. Construction of a CA +3. Preparation of _root_ certificate for inclusion into EDK +4. Preparation of _sign_ certificate for signing + +#### Make certificates + +Using 4096-bit RSA keys as an example: + +```bash +openssl genrsa -aes256 -out root.p8e 4096 +openssl genrsa -aes256 -out sub.p8e 4096 +openssl genrsa -aes256 -out sign.p8e 4096 +``` + +Each of them is encrypted with AES-256. The password is queried interactively +for creation and each time the certificate is used to sign something. Drop +`-aes256` to not use encryption (for a test or if you consider access to the +files a complete compromise of the security). + +`.p8e` extension is for PKCS #8 format carrying an encrypted private key. + +#### Make a CA + +By default, directory for a CA is called `demoCA`, although it can be different +depending on the OS. It can be looked up in `/etc/ssl/openssl.cnf` as +`dir = ...` in `[ CA_default ]` section (and that section is in turn referenced +by `default_ca` in `[ ca ]` section). The directory needs to be set up prior +to using `openssl ca`: + +```bash +mkdir -p demoCA/newcerts +touch demoCA/index.txt +echo 01 > demoCA/serial +``` + +Initialize it with self-signed root certificate (will ask for a password +and certificate fields; country, state and organization fields must match _root_ +certificate, common name must be a unique non-empty value): + +```bash +openssl req -new -x509 -days 3650 -key root.p8e -out root.pub.pem +``` + +Create certificate signing requests (CSRs) (don't bother with +entering a challenge for CSRs, you won't be asked for it): + +```bash +openssl req -new -key sub.p8e -out sub.csr +openssl req -new -key sign.p8e -out sign.csr +``` + +Perform the signing (there will be password and confirmation prompts): + +```bash +openssl ca -extensions v3_ca \ + -in sub.csr \ + -days 3650 \ + -cert root.pub.pem \ + -keyfile root.p8e \ + -notext \ + -out sub.pub.pem +openssl ca -in sign.csr \ + -days 3650 \ + -cert sub.pub.pem \ + -keyfile sub.p8e \ + -notext \ + -out sign.crt +``` + +The `-days 3650` is something to be adjusted and is provided as an example that +certificate properties are set in a different command for _root_ compared to +other certificates. + +`-notext` avoids dumping certificate details in text form to the output file +thus making all certificates look consistent. The details are easy to obtain +by running `openssl x509 -in {cert-file} -text -noout` when needed. + +`*.csr` files aren't necessary after successful signing and can be removed. + +`.pub.pem` and `.crt` files contain essentially the same X.509 certificates, but +the former is used for CAs. There is little consistency or sense in extension +for these types of files in general, so don't read too much meaning into them. + +#### Prepare _root_ for EDK build system + +EDK gets _root_ certificate(s) in a PCD. The PCD name differ and support one +or many certificates, in this case it's +`gFmpDevicePkgTokenSpaceGuid.PcdFmpDevicePkcs7CertBufferXdr` which expects one +or more certificates in DER (binary) form combined via XDR (simple format where +big-endian 32-bit length is followed by that number of bytes). + +EDK provides `BinToPcd.py` that can generate a file for inclusion via `!include` +in some DSC-file of an EDK package. + +```bash +openssl x509 -in root.pub.pem -out root.cer -outform DER +python payloads/external/edk2/workspace/Dasharo/BaseTools/Scripts/BinToPcd.py \ + -p gFmpDevicePkgTokenSpaceGuid.PcdFmpDevicePkcs7CertBufferXdr \ + -i root.cer \ + -x \ + -o CapsuleRootKey.inc +``` + +`root.cer` can be removed afterward. + +#### Prepare _sign_ certificate + +`GenerateCapsule` and EDK will only ever need public parts of _root_ and _sub_ +certificates, but _sign_ certificate will have to be provided in combination +with the corresponding private key. This is achieved by packing the two parts +via PKCS #12 which is an archive file format. + +`openssl pkcs12` either creates a PKCS #12 file or converts it, thus requiring +two invocations for obtaining the result in PEM format. + +First, create binary PKCS #12 (certificate and corresponding private key): + +```bash +openssl pkcs12 -export -inkey sign.p8e -in sign.crt -out sign.pfx +``` + +Add `-passout pass:` to perform export (creation, that is) without encryption. + +Now convert binary PKCS #12 into PEM form: + +```bash +openssl pkcs12 -in sign.pfx -out sign.p12 +``` + +`-passin pass:` can be added if `sign.pfx` was created without a password to +skip the prompt. + +`-noenc` (`-nodec` is deprecated in OpenSSL v3) can be added to avoid +encrypting `sign.p12`. Without this option, there will be a password prompt +during the conversion and whenever a capsule is signed. + +`sign.pfx` can now be removed. + ## `capsule.sh` script ### Building a capsule @@ -365,12 +529,17 @@ suitable set of keys which can be done like this: ./capsule.sh keygen my-test-keys ``` -At the end the script prints a command to use the keys: +At the end the script prints commands to use the keys (wrapped manually here): ```bash -./capsule.sh make -t my-test-keys/root.pub.pem \ - -o my-test-keys/sub.pub.pem \ - -s my-test-keys/sign.p12 +Installing root certificate (before build): + cp my-test-keys/CapsuleRootKey.inc \ + payloads/external/edk2/workspace/Dasharo/DasharoPayloadPkg/ + +Signing a capsule (after build): + ./capsule.sh make -t my-test-keys/root.pub.pem \ + -o my-test-keys/sub.pub.pem \ + -s my-test-keys/sign.p12 ``` !!! warning From dbdeeecf8a9a68272d3ccf290d91a95d37376946 Mon Sep 17 00:00:00 2001 From: Sergii Dmytruk Date: Sat, 24 Aug 2024 16:22:13 +0300 Subject: [PATCH 4/5] kb/edk2-capsule-updates.md: provide analysis of supported DSAs Signed-off-by: Sergii Dmytruk --- docs/kb/edk2-capsule-updates.md | 163 ++++++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) diff --git a/docs/kb/edk2-capsule-updates.md b/docs/kb/edk2-capsule-updates.md index 89cd4d4c0a..d03745f823 100644 --- a/docs/kb/edk2-capsule-updates.md +++ b/docs/kb/edk2-capsule-updates.md @@ -492,6 +492,169 @@ during the conversion and whenever a capsule is signed. `sign.pfx` can now be removed. +### Supported algorithms and digests + +EDK has 2 libraries implementing `AuthenticateFmpImage()` which is responsible +for verifying an FMP image: + +- `SecurityPkg/Library/FmpAuthenticationLibRsa2048Sha256` +- `SecurityPkg/Library/FmpAuthenticationLibPkcs7` + +The first one supports only one kind of a key as indicated by its name. +Nowadays the second library is much more likely to be used. PKCS #7 is a +generic container for signature/signed data and the set of permitted algorithms +and digests is defined by the implementation handling it. There are two +options in Dasharo EDK based on top of `edk2-stable202402` upstream release: + +- OpenSSL v3.0.9 +- MbedTLS v3.3.0 + +However, because OpenSSL was never designed for embedded environments, it became +prohibitively large in v3 which introduced providers feature. This is why +Dasharo uses MbedTLS and it will be the focus of the below discussion. + +Because MbedTLS deals with certificates prepared by OpenSSL tools, both +constrain the set of what can be used. + +#### Build-time configuration + +What's actually supported by MbedTLS depends on a specific build and which +version of an EDK wrapper is being used. + +The library is configured via a set of defines in +`CryptoPkg/Library/MbedTlsLib/Include/mbedtls/mbedtls_config.h`. One of the +most important ones is: + +```c +#define MBEDTLS_MPI_MAX_SIZE 1024 +``` + +It specifies the maximum number of bytes available for multiple precision +integers and puts a limit on private key algorithms. `1024` is the default +value that prevents the use of RSA keys longer than 8192 bits. Elliptic +curve (EC) keys are much shorter in size, so the default doesn't affect them in +any way. + +Another important set of defines is the list of enabled curves: + +```c +/* Short Weierstrass curves (supporting ECP, ECDH, ECDSA) */ +// #define MBEDTLS_ECP_DP_SECP192R1_ENABLED +// #define MBEDTLS_ECP_DP_SECP224R1_ENABLED +#define MBEDTLS_ECP_DP_SECP256R1_ENABLED +#define MBEDTLS_ECP_DP_SECP384R1_ENABLED +#define MBEDTLS_ECP_DP_SECP521R1_ENABLED +// #define MBEDTLS_ECP_DP_SECP192K1_ENABLED +// #define MBEDTLS_ECP_DP_SECP224K1_ENABLED +// #define MBEDTLS_ECP_DP_SECP256K1_ENABLED +// #define MBEDTLS_ECP_DP_BP256R1_ENABLED +// #define MBEDTLS_ECP_DP_BP384R1_ENABLED +// #define MBEDTLS_ECP_DP_BP512R1_ENABLED +/* Montgomery curves (supporting ECP) */ +#define MBEDTLS_ECP_DP_CURVE25519_ENABLED +#define MBEDTLS_ECP_DP_CURVE448_ENABLED +``` + +Mind that using EC requires the use of `MbedTlsLibFull` instead of +`MbedTlsLib`, that's really the difference between the two wrappers. + +This define is supposed to enable RSASSA-PSS (see below though): + +```c +#define MBEDTLS_X509_RSASSA_PSS_SUPPORT +``` + +#### Digest types + +MbedTLS has the list of supported digests in `include/mbedtls/md.h`: + +```c +typedef enum { + MBEDTLS_MD_MD5, /**< The MD5 message digest. */ + MBEDTLS_MD_SHA1, /**< The SHA-1 message digest. */ + MBEDTLS_MD_SHA224, /**< The SHA-224 message digest. */ + MBEDTLS_MD_SHA256, /**< The SHA-256 message digest. */ + MBEDTLS_MD_SHA384, /**< The SHA-384 message digest. */ + MBEDTLS_MD_SHA512, /**< The SHA-512 message digest. */ + MBEDTLS_MD_RIPEMD160, /**< The RIPEMD-160 message digest. */ +}; +``` + +OpenSSL supports all of them (see `openssl list -digest-algorithms`, but not all +listed there work with X.509 certificates, not clear why). + +#### Private key algorithms + +These are listed in `include/mbedtls/pk.h` of MbedTLS: + +```c +typedef enum { + MBEDTLS_PK_NONE=0, + MBEDTLS_PK_RSA, + MBEDTLS_PK_ECKEY, + MBEDTLS_PK_ECKEY_DH, + MBEDTLS_PK_ECDSA, + MBEDTLS_PK_RSA_ALT, + MBEDTLS_PK_RSASSA_PSS, + MBEDTLS_PK_OPAQUE, +} mbedtls_pk_type_t; +``` + +OpenSSL supports the following ones (see `openssl list -signature-algorithms` +but only those which were accepted by OpenSSL in the commands above are listed +here): + +- `rsa` +- `rsa-pss` +- `ed25519` +- `ed448` +- `ec` + +`rsa` and `ec` are the only ones that actually worked. While Ed25519 and Ed448 +curves are there, that is far from a complete implementation of a corresponding +DSAs. + +`rsa-pss` is also explicitly enabled, but the following error reported at +run-time: + +```c +/** Key algorithm is unsupported (only RSA and EC are supported). */ +#define MBEDTLS_ERR_PK_UNKNOWN_PK_ALG -0x3C80 +``` + +Suggesting that it's not actually supported even when enabled. The situation +might be similar to the elliptic curves above. + +#### What's actually supported and how to use it + +Valid digest names for OpenSSL: + +- `md5` +- `ripemd160` +- `sha1` +- `sha224` +- `sha256` +- `sha384` +- `sha512` + +How a digest is to be specified depends on a particular subcommand: + +- `openssl ca` takes `-md {digest-name}` +- `openssl req` takes `-{digest-name}` (confusingly documented as `-digest`) + +`openssl genpkey` accepts `-algorithm {alg}` parameter and `rsa` or `ec` works. + +RSA key length's lower bound is 512 in OpenSSL and upper bound is 8192 in +MbedTLS by default. Key size can be configured with +`-pkeyopt rsa_keygen_bits:numbits` if `openssl genpkey` is used. + +Given that MbedTLS has only 3 standard curves enabled, there are only 3 +possibilities for an EC algorithm (use of `MbedTlsLibFull` is required): + +- `-pkeyopt ec_paramgen_curve:P-256` +- `-pkeyopt ec_paramgen_curve:P-384` +- `-pkeyopt ec_paramgen_curve:P-521` + ## `capsule.sh` script ### Building a capsule From ccd16f85cca0082c07da3e86bc4e32999603d3d7 Mon Sep 17 00:00:00 2001 From: Sergii Dmytruk Date: Fri, 30 Aug 2024 00:44:24 +0300 Subject: [PATCH 5/5] kb/edk2-capsule-updates.md: drop InitiateReset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Linux rejects capsules with this flag and requires a manual soft reset for persistent capsules (CapsuleApp.efi does reset for persistent capsules by default, so no difference in behavior for it). Signed-off-by: Sergii Dmytruk --- docs/kb/edk2-capsule-updates.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/kb/edk2-capsule-updates.md b/docs/kb/edk2-capsule-updates.md index d03745f823..c6f09952c6 100644 --- a/docs/kb/edk2-capsule-updates.md +++ b/docs/kb/edk2-capsule-updates.md @@ -189,12 +189,15 @@ EDK2 payload: cd payloads/external/edk2/workspace/Dasharo/ BaseTools/BinWrappers/PosixLike/GenerateCapsule --encode \ --capflag PersistAcrossReset \ - --capflag InitiateReset \ --json-file dasharo.json \ --output dasharo.cap ``` -Values from multiple `--capflag` options are combined together. +In case more than one flag needs to be specified, values from multiple +`--capflag` options are combined together. There is no `InitiateReset` because +Linux rejects capsules with this flag and requires a manual soft reset for +persistent capsules (`CapsuleApp.efi` does reset for persistent capsules by +default, so no difference in behaviour for it). ## Capsule introspection