From 24db39fb2983ca83ab5c6ee37cb57a4f7f6f94e6 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 3 Dec 2024 13:55:18 +0000 Subject: [PATCH 01/50] [gve] Run startup process only while device is open The startup process is scheduled to run when the device is opened and terminated (if still running) when the device is closed. It assumes that the resource allocation performed in gve_open() has taken place, and that the admin and transmit/receive data structure pointers are therefore valid. The process initialisation in gve_probe() erroneously calls process_init() rather than process_init_stopped() and will therefore schedule the startup process immediately, before the relevant resources have been allocated. This bug is masked in the typical use case of a Google Cloud instance with a single NIC built with the config/cloud/gce.ipxe embedded script, since the embedded script will immediately open the NIC (and therefore allocate the required resources) before the scheduled process is allowed to run for the first time. In a multi-NIC instance, undefined behaviour will arise as soon as the startup process for the second NIC is allowed to run. Fix by using process_init_stopped() to avoid implicitly scheduling the startup process during gve_probe(). Originally-fixed-by: Kal Cutter Conley Signed-off-by: Michael Brown --- src/drivers/net/gve.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/drivers/net/gve.c b/src/drivers/net/gve.c index df10a94c6..efc38dd21 100644 --- a/src/drivers/net/gve.c +++ b/src/drivers/net/gve.c @@ -1543,7 +1543,8 @@ static int gve_probe ( struct pci_device *pci ) { gve->netdev = netdev; gve->tx.type = &gve_tx_type; gve->rx.type = &gve_rx_type; - process_init ( &gve->startup, &gve_startup_desc, &netdev->refcnt ); + process_init_stopped ( &gve->startup, &gve_startup_desc, + &netdev->refcnt ); timer_init ( &gve->watchdog, gve_watchdog, &netdev->refcnt ); /* Fix up PCI device */ From 97079553b66ea9036348543e2b92cbe29bfd2c6b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 16 Dec 2024 15:09:56 +0000 Subject: [PATCH 02/50] [crypto] Calculate inverse of modulus on demand in bigint_montgomery() Reduce the number of parameters passed to bigint_montgomery() by calculating the inverse of the modulus modulo the element size on demand. Cache the result, since Montgomery reduction will be used repeatedly with the same modulus value. In all currently supported algorithms, the modulus is a public value (or a fixed value defined by specification) and so this non-constant timing does not leak any private information. Signed-off-by: Michael Brown --- src/crypto/bigint.c | 40 ++++++++++++++++++--------------------- src/include/ipxe/bigint.h | 8 +++----- src/tests/bigint_test.c | 11 ++--------- 3 files changed, 23 insertions(+), 36 deletions(-) diff --git a/src/crypto/bigint.c b/src/crypto/bigint.c index b357ea29f..3ef96d13d 100644 --- a/src/crypto/bigint.c +++ b/src/crypto/bigint.c @@ -354,23 +354,17 @@ void bigint_mod_invert_raw ( const bigint_element_t *invertend0, * Perform Montgomery reduction (REDC) of a big integer product * * @v modulus0 Element 0 of big integer modulus - * @v modinv0 Element 0 of the inverse of the modulus modulo 2^k * @v mont0 Element 0 of big integer Montgomery product * @v result0 Element 0 of big integer to hold result * @v size Number of elements in modulus and result * - * Note that only the least significant element of the inverse modulo - * 2^k is required, and that the Montgomery product will be - * overwritten. + * Note that the Montgomery product will be overwritten. */ void bigint_montgomery_raw ( const bigint_element_t *modulus0, - const bigint_element_t *modinv0, bigint_element_t *mont0, bigint_element_t *result0, unsigned int size ) { const bigint_t ( size ) __attribute__ (( may_alias )) *modulus = ( ( const void * ) modulus0 ); - const bigint_t ( 1 ) __attribute__ (( may_alias )) - *modinv = ( ( const void * ) modinv0 ); union { bigint_t ( size * 2 ) full; struct { @@ -380,7 +374,8 @@ void bigint_montgomery_raw ( const bigint_element_t *modulus0, } __attribute__ (( may_alias )) *mont = ( ( void * ) mont0 ); bigint_t ( size ) __attribute__ (( may_alias )) *result = ( ( void * ) result0 ); - bigint_element_t negmodinv = -modinv->element[0]; + static bigint_t ( 1 ) cached; + static bigint_t ( 1 ) negmodinv; bigint_element_t multiple; bigint_element_t carry; unsigned int i; @@ -391,11 +386,18 @@ void bigint_montgomery_raw ( const bigint_element_t *modulus0, /* Sanity checks */ assert ( bigint_bit_is_set ( modulus, 0 ) ); + /* Calculate inverse (or use cached version) */ + if ( cached.element[0] != modulus->element[0] ) { + bigint_mod_invert ( modulus, &negmodinv ); + negmodinv.element[0] = -negmodinv.element[0]; + cached.element[0] = modulus->element[0]; + } + /* Perform multiprecision Montgomery reduction */ for ( i = 0 ; i < size ; i++ ) { /* Determine scalar multiple for this round */ - multiple = ( mont->low.element[i] * negmodinv ); + multiple = ( mont->low.element[i] * negmodinv.element[0] ); /* Multiply value to make it divisible by 2^(width*i) */ carry = 0; @@ -467,7 +469,6 @@ void bigint_mod_exp_raw ( const bigint_element_t *base0, } product; } *temp = tmp; const uint8_t one[1] = { 1 }; - bigint_t ( 1 ) modinv; bigint_element_t submask; unsigned int subsize; unsigned int scale; @@ -494,9 +495,6 @@ void bigint_mod_exp_raw ( const bigint_element_t *base0, if ( ! submask ) submask = ~submask; - /* Calculate inverse of (scaled) modulus N modulo element size */ - bigint_mod_invert ( &temp->modulus, &modinv ); - /* Calculate (R^2 mod N) via direct reduction of (R^2 - N) */ memset ( &temp->product.full, 0, sizeof ( temp->product.full ) ); bigint_subtract ( &temp->padded_modulus, &temp->product.full ); @@ -504,12 +502,11 @@ void bigint_mod_exp_raw ( const bigint_element_t *base0, bigint_copy ( &temp->product.low, &temp->stash ); /* Initialise result = Montgomery(1, R^2 mod N) */ - bigint_montgomery ( &temp->modulus, &modinv, - &temp->product.full, result ); + bigint_montgomery ( &temp->modulus, &temp->product.full, result ); /* Convert base into Montgomery form */ bigint_multiply ( base, &temp->stash, &temp->product.full ); - bigint_montgomery ( &temp->modulus, &modinv, &temp->product.full, + bigint_montgomery ( &temp->modulus, &temp->product.full, &temp->stash ); /* Calculate x1 = base^exponent modulo N */ @@ -518,13 +515,13 @@ void bigint_mod_exp_raw ( const bigint_element_t *base0, /* Square (and reduce) */ bigint_multiply ( result, result, &temp->product.full ); - bigint_montgomery ( &temp->modulus, &modinv, - &temp->product.full, result ); + bigint_montgomery ( &temp->modulus, &temp->product.full, + result ); /* Multiply (and reduce) */ bigint_multiply ( &temp->stash, result, &temp->product.full ); - bigint_montgomery ( &temp->modulus, &modinv, - &temp->product.full, &temp->product.low ); + bigint_montgomery ( &temp->modulus, &temp->product.full, + &temp->product.low ); /* Conditionally swap the multiplied result */ bigint_swap ( result, &temp->product.low, @@ -533,8 +530,7 @@ void bigint_mod_exp_raw ( const bigint_element_t *base0, /* Convert back out of Montgomery form */ bigint_grow ( result, &temp->product.full ); - bigint_montgomery ( &temp->modulus, &modinv, &temp->product.full, - result ); + bigint_montgomery ( &temp->modulus, &temp->product.full, result ); /* Handle even moduli via Garner's algorithm */ if ( subsize ) { diff --git a/src/include/ipxe/bigint.h b/src/include/ipxe/bigint.h index 3058547a6..90e212b54 100644 --- a/src/include/ipxe/bigint.h +++ b/src/include/ipxe/bigint.h @@ -257,16 +257,15 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * Perform Montgomery reduction (REDC) of a big integer product * * @v modulus Big integer modulus - * @v modinv Big integer inverse of the modulus modulo 2^k * @v mont Big integer Montgomery product * @v result Big integer to hold result * * Note that the Montgomery product will be overwritten. */ -#define bigint_montgomery( modulus, modinv, mont, result ) do { \ +#define bigint_montgomery( modulus, mont, result ) do { \ unsigned int size = bigint_size (modulus); \ - bigint_montgomery_raw ( (modulus)->element, (modinv)->element, \ - (mont)->element, (result)->element, \ + bigint_montgomery_raw ( (modulus)->element, (mont)->element, \ + (result)->element, \ size ); \ } while ( 0 ) @@ -377,7 +376,6 @@ void bigint_reduce_raw ( bigint_element_t *modulus0, bigint_element_t *value0, void bigint_mod_invert_raw ( const bigint_element_t *invertend0, bigint_element_t *inverse0, unsigned int size ); void bigint_montgomery_raw ( const bigint_element_t *modulus0, - const bigint_element_t *modinv0, bigint_element_t *mont0, bigint_element_t *result0, unsigned int size ); void bigint_mod_exp_raw ( const bigint_element_t *base0, diff --git a/src/tests/bigint_test.c b/src/tests/bigint_test.c index dc74740e6..07ba13bb4 100644 --- a/src/tests/bigint_test.c +++ b/src/tests/bigint_test.c @@ -207,20 +207,17 @@ void bigint_mod_invert_sample ( const bigint_element_t *invertend0, } void bigint_montgomery_sample ( const bigint_element_t *modulus0, - const bigint_element_t *modinv0, bigint_element_t *mont0, bigint_element_t *result0, unsigned int size ) { const bigint_t ( size ) __attribute__ (( may_alias )) *modulus = ( ( const void * ) modulus0 ); - const bigint_t ( 1 ) __attribute__ (( may_alias )) - *modinv = ( ( const void * ) modinv0 ); bigint_t ( 2 * size ) __attribute__ (( may_alias )) *mont = ( ( void * ) mont0 ); bigint_t ( size ) __attribute__ (( may_alias )) *result = ( ( void * ) result0 ); - bigint_montgomery ( modulus, modinv, mont, result ); + bigint_montgomery ( modulus, mont, result ); } void bigint_mod_exp_sample ( const bigint_element_t *base0, @@ -631,7 +628,6 @@ void bigint_mod_exp_sample ( const bigint_element_t *base0, unsigned int size = \ bigint_required_size ( sizeof ( modulus_raw ) ); \ bigint_t ( size ) modulus_temp; \ - bigint_t ( 1 ) modinv_temp; \ bigint_t ( 2 * size ) mont_temp; \ bigint_t ( size ) result_temp; \ {} /* Fix emacs alignment */ \ @@ -641,13 +637,10 @@ void bigint_mod_exp_sample ( const bigint_element_t *base0, bigint_init ( &modulus_temp, modulus_raw, \ sizeof ( modulus_raw ) ); \ bigint_init ( &mont_temp, mont_raw, sizeof ( mont_raw ) ); \ - bigint_mod_invert ( &modulus_temp, &modinv_temp ); \ DBG ( "Montgomery:\n" ); \ DBG_HDA ( 0, &modulus_temp, sizeof ( modulus_temp ) ); \ - DBG_HDA ( 0, &modinv_temp, sizeof ( modinv_temp ) ); \ DBG_HDA ( 0, &mont_temp, sizeof ( mont_temp ) ); \ - bigint_montgomery ( &modulus_temp, &modinv_temp, &mont_temp, \ - &result_temp ); \ + bigint_montgomery ( &modulus_temp, &mont_temp, &result_temp ); \ DBG_HDA ( 0, &result_temp, sizeof ( result_temp ) ); \ bigint_done ( &result_temp, result_raw, \ sizeof ( result_raw ) ); \ From 8816ddcd96d9831f83448eb579aceae36ca57daa Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 17 Dec 2024 13:30:16 +0000 Subject: [PATCH 03/50] [efi] Update to current EDK2 headers Signed-off-by: Michael Brown --- src/include/ipxe/efi/Base.h | 4 +- .../ipxe/efi/IndustryStandard/Acpi51.h | 1 + .../ipxe/efi/IndustryStandard/Acpi60.h | 1 + src/include/ipxe/efi/IndustryStandard/Tpm12.h | 4 +- src/include/ipxe/efi/IndustryStandard/Tpm20.h | 23 +- .../efi/IndustryStandard/UefiTcgPlatform.h | 236 +++++++++++++++++- src/include/ipxe/efi/IndustryStandard/Usb.h | 34 ++- src/include/ipxe/efi/Library/BaseLib.h | 147 ++++++++++- src/include/ipxe/efi/Pi/PiDxeCis.h | 19 +- src/include/ipxe/efi/Pi/PiHob.h | 14 +- src/include/ipxe/efi/Pi/PiMultiPhase.h | 9 +- src/include/ipxe/efi/Protocol/DebugSupport.h | 63 +++-- src/include/ipxe/efi/Protocol/DevicePath.h | 22 ++ src/include/ipxe/efi/Protocol/Http.h | 3 +- src/include/ipxe/efi/Protocol/SimpleTextIn.h | 1 + .../ipxe/efi/Protocol/SimpleTextInEx.h | 1 + .../efi/Uefi/UefiInternalFormRepresentation.h | 12 +- src/include/ipxe/efi/Uefi/UefiMultiPhase.h | 17 +- src/include/ipxe/efi/Uefi/UefiSpec.h | 10 +- 19 files changed, 536 insertions(+), 85 deletions(-) diff --git a/src/include/ipxe/efi/Base.h b/src/include/ipxe/efi/Base.h index 46c31a3b1..abc4e4627 100644 --- a/src/include/ipxe/efi/Base.h +++ b/src/include/ipxe/efi/Base.h @@ -61,7 +61,7 @@ FILE_LICENCE ( BSD2_PATENT ); /// up to the compiler to remove any code past that point. /// #define UNREACHABLE() __builtin_unreachable () - #elif defined (__has_feature) + #elif defined (__has_builtin) && defined (__has_feature) #if __has_builtin (__builtin_unreachable) /// /// Signal compilers and analyzers that this call is not reachable. It is @@ -1060,7 +1060,7 @@ typedef UINTN RETURN_STATUS; @retval FALSE The high bit of StatusCode is clear. **/ -#define RETURN_ERROR(StatusCode) (((INTN)(RETURN_STATUS)(StatusCode)) < 0) +#define RETURN_ERROR(StatusCode) (((RETURN_STATUS)(StatusCode)) >= MAX_BIT) /// /// The operation completed successfully. diff --git a/src/include/ipxe/efi/IndustryStandard/Acpi51.h b/src/include/ipxe/efi/IndustryStandard/Acpi51.h index a2079ecc5..101d7b5d1 100644 --- a/src/include/ipxe/efi/IndustryStandard/Acpi51.h +++ b/src/include/ipxe/efi/IndustryStandard/Acpi51.h @@ -1762,6 +1762,7 @@ typedef struct { #define EFI_ACPI_5_1_EINJ_EXECUTE_OPERATION 0x05 #define EFI_ACPI_5_1_EINJ_CHECK_BUSY_STATUS 0x06 #define EFI_ACPI_5_1_EINJ_GET_COMMAND_STATUS 0x07 +#define EFI_ACPI_5_1_EINJ_SET_ERROR_TYPE_WITH_ADDRESS 0x08 #define EFI_ACPI_5_1_EINJ_TRIGGER_ERROR 0xFF /// diff --git a/src/include/ipxe/efi/IndustryStandard/Acpi60.h b/src/include/ipxe/efi/IndustryStandard/Acpi60.h index c8d99214c..19c51df5c 100644 --- a/src/include/ipxe/efi/IndustryStandard/Acpi60.h +++ b/src/include/ipxe/efi/IndustryStandard/Acpi60.h @@ -1949,6 +1949,7 @@ typedef struct { #define EFI_ACPI_6_0_EINJ_EXECUTE_OPERATION 0x05 #define EFI_ACPI_6_0_EINJ_CHECK_BUSY_STATUS 0x06 #define EFI_ACPI_6_0_EINJ_GET_COMMAND_STATUS 0x07 +#define EFI_ACPI_6_0_EINJ_SET_ERROR_TYPE_WITH_ADDRESS 0x08 #define EFI_ACPI_6_0_EINJ_TRIGGER_ERROR 0xFF /// diff --git a/src/include/ipxe/efi/IndustryStandard/Tpm12.h b/src/include/ipxe/efi/IndustryStandard/Tpm12.h index 6bebcb7bd..6cb60dc3f 100644 --- a/src/include/ipxe/efi/IndustryStandard/Tpm12.h +++ b/src/include/ipxe/efi/IndustryStandard/Tpm12.h @@ -746,8 +746,8 @@ typedef struct tdTPM_PERMANENT_FLAGS { BOOLEAN TPMpost; BOOLEAN TPMpostLock; BOOLEAN FIPS; - BOOLEAN operator; - BOOLEAN enableRevokeEK; + BOOLEAN operator_; + BOOLEAN enableRevokeEK; BOOLEAN nvLocked; BOOLEAN readSRKPub; BOOLEAN tpmEstablished; diff --git a/src/include/ipxe/efi/IndustryStandard/Tpm20.h b/src/include/ipxe/efi/IndustryStandard/Tpm20.h index b314d6e91..7bcb55700 100644 --- a/src/include/ipxe/efi/IndustryStandard/Tpm20.h +++ b/src/include/ipxe/efi/IndustryStandard/Tpm20.h @@ -205,15 +205,16 @@ typedef UINT16 TPM_ALG_ID; // Table 8 - TPM_ECC_CURVE Constants typedef UINT16 TPM_ECC_CURVE; -#define TPM_ECC_NONE (TPM_ECC_CURVE)(0x0000) -#define TPM_ECC_NIST_P192 (TPM_ECC_CURVE)(0x0001) -#define TPM_ECC_NIST_P224 (TPM_ECC_CURVE)(0x0002) -#define TPM_ECC_NIST_P256 (TPM_ECC_CURVE)(0x0003) -#define TPM_ECC_NIST_P384 (TPM_ECC_CURVE)(0x0004) -#define TPM_ECC_NIST_P521 (TPM_ECC_CURVE)(0x0005) -#define TPM_ECC_BN_P256 (TPM_ECC_CURVE)(0x0010) -#define TPM_ECC_BN_P638 (TPM_ECC_CURVE)(0x0011) -#define TPM_ECC_SM2_P256 (TPM_ECC_CURVE)(0x0020) +#define TPM_ECC_NONE (TPM_ECC_CURVE)(0x0000) +#define TPM_ECC_NIST_P192 (TPM_ECC_CURVE)(0x0001) +#define TPM_ECC_NIST_P224 (TPM_ECC_CURVE)(0x0002) +#define TPM_ECC_NIST_P256 (TPM_ECC_CURVE)(0x0003) +#define TPM_ECC_NIST_P384 (TPM_ECC_CURVE)(0x0004) +#define TPM_ECC_NIST_P521 (TPM_ECC_CURVE)(0x0005) +#define TPM_ECC_BN_P256 (TPM_ECC_CURVE)(0x0010) +#define TPM_ECC_BN_P638 (TPM_ECC_CURVE)(0x0011) +#define TPM_ECC_SM2_P256 (TPM_ECC_CURVE)(0x0020) +#define TPM_ECC_BP_P512_R1 (TPM_ECC_CURVE)(0x0032) // Table 11 - TPM_CC Constants (Numeric Order) typedef UINT32 TPM_CC; @@ -1249,7 +1250,7 @@ typedef union { TPMI_AES_KEY_BITS aes; TPMI_SM4_KEY_BITS SM4; TPM_KEY_BITS sym; - TPMI_ALG_HASH xor; + TPMI_ALG_HASH xor_; } TPMU_SYM_KEY_BITS; // Table 123 - TPMU_SYM_MODE Union @@ -1322,7 +1323,7 @@ typedef struct { // Table 136 - TPMU_SCHEME_KEYEDHASH Union typedef union { TPMS_SCHEME_HMAC hmac; - TPMS_SCHEME_XOR xor; + TPMS_SCHEME_XOR xor_; } TPMU_SCHEME_KEYEDHASH; // Table 137 - TPMT_KEYEDHASH_SCHEME Structure diff --git a/src/include/ipxe/efi/IndustryStandard/UefiTcgPlatform.h b/src/include/ipxe/efi/IndustryStandard/UefiTcgPlatform.h index a89986712..5b02a10a7 100644 --- a/src/include/ipxe/efi/IndustryStandard/UefiTcgPlatform.h +++ b/src/include/ipxe/efi/IndustryStandard/UefiTcgPlatform.h @@ -1,8 +1,8 @@ /** @file TCG EFI Platform Definition in TCG_EFI_Platform_1_20_Final and - TCG PC Client Platform Firmware Profile Specification, Revision 1.05 + TCG PC Client Platform Firmware Profile Specification, Revision 1.06 - Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.
+ Copyright (c) 2006 - 2024, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ @@ -55,6 +55,18 @@ FILE_LICENCE ( BSD2_PATENT ); #define EV_EFI_VARIABLE_AUTHORITY (EV_EFI_EVENT_BASE + 0xE0) #define EV_EFI_SPDM_FIRMWARE_BLOB (EV_EFI_EVENT_BASE + 0xE1) #define EV_EFI_SPDM_FIRMWARE_CONFIG (EV_EFI_EVENT_BASE + 0xE2) +#define EV_EFI_SPDM_DEVICE_BLOB EV_EFI_SPDM_FIRMWARE_BLOB +#define EV_EFI_SPDM_DEVICE_CONFIG EV_EFI_SPDM_FIRMWARE_CONFIG +// +// The SPDM policy database for SPDM verification. +// It goes to PCR7 +// +#define EV_EFI_SPDM_DEVICE_POLICY (EV_EFI_EVENT_BASE + 0xE3) +// +// The SPDM policy authority for SPDM verification for the signature +// of GET_MEASUREMENT or CHALLENGE_AUTH. It goes to PCR7. +// +#define EV_EFI_SPDM_DEVICE_AUTHORITY (EV_EFI_EVENT_BASE + 0xE4) #define EFI_CALLING_EFI_APPLICATION \ "Calling EFI Application from Boot Option" @@ -376,6 +388,7 @@ typedef struct { #define TCG_EfiSpecIDEventStruct_SPEC_VERSION_MINOR_TPM2 0 #define TCG_EfiSpecIDEventStruct_SPEC_ERRATA_TPM2 0 #define TCG_EfiSpecIDEventStruct_SPEC_ERRATA_TPM2_REV_105 105 +#define TCG_EfiSpecIDEventStruct_SPEC_ERRATA_TPM2_REV_106 106 typedef struct { UINT8 signature[16]; @@ -440,6 +453,7 @@ typedef struct tdTCG_PCClientTaggedEvent { #define TCG_Sp800_155_PlatformId_Event_SIGNATURE "SP800-155 Event" #define TCG_Sp800_155_PlatformId_Event2_SIGNATURE "SP800-155 Event2" +#define TCG_Sp800_155_PlatformId_Event3_SIGNATURE "SP800-155 Event3" typedef struct tdTCG_Sp800_155_PlatformId_Event2 { UINT8 Signature[16]; @@ -461,15 +475,58 @@ typedef struct tdTCG_Sp800_155_PlatformId_Event2 { // UINT8 PlatformModel[PlatformModelSize]; // UINT8 PlatformVersionSize; // UINT8 PlatformVersion[PlatformVersionSize]; - // UINT8 PlatformModelSize; - // UINT8 PlatformModel[PlatformModelSize]; // UINT8 FirmwareManufacturerStrSize; // UINT8 FirmwareManufacturerStr[FirmwareManufacturerStrSize]; // UINT32 FirmwareManufacturerId; // UINT8 FirmwareVersion; - // UINT8 FirmwareVersion[FirmwareVersionSize]]; + // UINT8 FirmwareVersion[FirmwareVersionSize]; } TCG_Sp800_155_PlatformId_Event2; +typedef struct tdTCG_Sp800_155_PlatformId_Event3 { + UINT8 Signature[16]; + // + // Where Vendor ID is an integer defined + // at http://www.iana.org/assignments/enterprisenumbers + // + UINT32 VendorId; + // + // 16-byte identifier of a given platform's static configuration of code + // + EFI_GUID ReferenceManifestGuid; + // UINT8 PlatformManufacturerStrSize; + // UINT8 PlatformManufacturerStr[PlatformManufacturerStrSize]; + // UINT8 PlatformModelSize; + // UINT8 PlatformModel[PlatformModelSize]; + // UINT8 PlatformVersionSize; + // UINT8 PlatformVersion[PlatformVersionSize]; + // UINT8 FirmwareManufacturerStrSize; + // UINT8 FirmwareManufacturerStr[FirmwareManufacturerStrSize]; + // UINT32 FirmwareManufacturerId; + // UINT8 FirmwareVersion; + // UINT8 FirmwareVersion[FirmwareVersionSize]; + // + // Below structure is newly added in TCG_Sp800_155_PlatformId_Event3 + // + // UINT32 RimLocatorType; + // UINT32 RimLocatorLength; + // UINT8 RimLocator[RimLocatorLength]; + // UINT32 PlatformCertLocatorType; + // UINT32 PlatformCertLocatorLength; + // UINT8 PlatformCertLocator[PlatformCertLocatorLength]; +} TCG_Sp800_155_PlatformId_Event3; + +/** + * TCG specifies a locator type with the following values + * 0 - Raw data in the locator itself. + * 1 - URI in rtf2396 format. + * 2 - local device path in EFI_DEVICE_PATH_PROTOCOL format. + * 3 - UEFI variable (16 byte EFI_GUID, then 00-terminated UCS2 string) +**/ +#define TCG_LOCATOR_TYPE_RAW_DATA 0 +#define TCG_LOCATOR_TYPE_URI 1 +#define TCG_LOCATOR_TYPE_DEVICE_PATH 2 +#define TCG_LOCATOR_TYPE_UEFI_VARIABLE 3 + #define TCG_EfiStartupLocalityEvent_SIGNATURE "StartupLocality" // @@ -494,4 +551,173 @@ typedef struct tdTCG_EfiStartupLocalityEvent { // #pragma pack () +// +// ====================================================================================================================== +// Event Type PCR Event Log Usage +// ====================================================================================================================== +// EV_EFI_SPDM_DEVICE_BLOB 2 SPDM_MEASUREMENT_BLOCK (subtype) MEASUREMENT from device +// EV_EFI_SPDM_DEVICE_CONFIG 3 SPDM_MEASUREMENT_BLOCK (subtype) MEASUREMENT from device +// EV_EFI_SPDM_DEVICE_BLOB 2 SPDM_MEASUREMENT_SUMMARY_HASH.TCB (subtype) SUMMARY_HASH from device + +// EV_EFI_SPDM_DEVICE_POLICY 7 UEFI_VARIABLE_DATA with EFI_SIGNATURE_LIST Provisioned device public cert. +// EV_EFI_SPDM_DEVICE_AUTHORITY 7 UEFI_VARIABLE_DATA with EFI_SIGNATURE_DATA CHALLENGE_AUTH signature verification +// ====================================================================================================================== +// + +#define PCR_INDEX_FOR_SIGNATURE_DB 7 + +#pragma pack(1) + +#define TCG_DEVICE_SECURITY_EVENT_DATA_VERSION_1 1 +#define TCG_DEVICE_SECURITY_EVENT_DATA_VERSION_2 2 +#define TCG_DEVICE_SECURITY_EVENT_DATA_SIGNATURE_2 "SPDM Device Sec2" + +typedef struct { + UINT8 Signature[16]; + UINT16 Version; + UINT8 AuthState; + UINT8 Reserved; + UINT32 Length; // Length in bytes for all following structures. + UINT32 DeviceType; + UINT32 SubHeaderType; + UINT32 SubHeaderLength; // Length in bytes of the sub header followed by. + UINT64 SubHeaderUID; // Universal identifier assigned by the event log creator. It can be used to bind two sub header structure together. + // UINT64 DevicePathLength; + // UINT8 DevicePath[DevicePathLength]; +} TCG_DEVICE_SECURITY_EVENT_DATA_HEADER2; + +#define TCG_DEVICE_SECURITY_EVENT_DATA_DEVICE_AUTH_STATE_SUCCESS 0 +#define TCG_DEVICE_SECURITY_EVENT_DATA_DEVICE_AUTH_STATE_NO_AUTH 1 +#define TCG_DEVICE_SECURITY_EVENT_DATA_DEVICE_AUTH_STATE_NO_BINDING 2 +#define TCG_DEVICE_SECURITY_EVENT_DATA_DEVICE_AUTH_STATE_FAIL_NO_SIG 3 +#define TCG_DEVICE_SECURITY_EVENT_DATA_DEVICE_AUTH_STATE_FAIL_INVALID 4 +#define TCG_DEVICE_SECURITY_EVENT_DATA_DEVICE_AUTH_STATE_NO_SPDM 0xFF + +#define TCG_DEVICE_SECURITY_EVENT_DATA_DEVICE_SUB_HEADER_TYPE_SPDM_MEASUREMENT_BLOCK 0 +#define TCG_DEVICE_SECURITY_EVENT_DATA_DEVICE_SUB_HEADER_TYPE_SPDM_CERT_CHAIN 1 + +typedef struct { + UINT16 SpdmVersion; + UINT8 SpdmMeasurementBlockCount; + UINT8 Reserved; + UINT32 SpdmMeasurementHashAlgo; + // SPDM_MEASUREMENT_BLOCK SpdmMeasurementBlock; +} TCG_DEVICE_SECURITY_EVENT_DATA_SUB_HEADER_SPDM_MEASUREMENT_BLOCK; + +typedef struct { + UINT16 SpdmVersion; + UINT8 SpdmSlotId; + UINT8 Reserved; + UINT32 SpdmHashAlgo; + // SPDM_CERT_CHAIN SpdmCertChain; +} TCG_DEVICE_SECURITY_EVENT_DATA_SUB_HEADER_SPDM_CERT_CHAIN; + +typedef struct { + UINT32 Type; + UINT32 Length; + UINT8 Value[1]; +} TCG_DEVICE_SECURITY_EVENT_DATA_SUB_HEADER_OEM_MEASUREMENT; + +typedef union { + TCG_DEVICE_SECURITY_EVENT_DATA_SUB_HEADER_SPDM_MEASUREMENT_BLOCK SpdmMeasurementBlock; + TCG_DEVICE_SECURITY_EVENT_DATA_SUB_HEADER_SPDM_CERT_CHAIN SpdmCertChain; + TCG_DEVICE_SECURITY_EVENT_DATA_SUB_HEADER_OEM_MEASUREMENT OemMeasurement; +} TCG_DEVICE_SECURITY_EVENT_DATA_SUB_HEADER; + +typedef union { + TCG_DEVICE_SECURITY_EVENT_DATA_PCI_CONTEXT Pci; + TCG_DEVICE_SECURITY_EVENT_DATA_USB_CONTEXT Usb; +} TCG_DEVICE_SECURITY_EVENT_DATA_DEVICE_CONTEXT; + +typedef struct { + TCG_DEVICE_SECURITY_EVENT_DATA_HEADER2 EventDataHeader; + TCG_DEVICE_SECURITY_EVENT_DATA_SUB_HEADER EventDataSubHeader; + TCG_DEVICE_SECURITY_EVENT_DATA_DEVICE_CONTEXT DeviceContext; +} TCG_DEVICE_SECURITY_EVENT_DATA2; + +#pragma pack() + +// +// EventType:EV_NO_ACTION +// ====================================================================================================================== +// NVIndex Name PCR/NvIndex Event Log Usage +// ====================================================================================================================== +// NV_EXTEND_INDEX_FOR_INSTANCE 0x01C40200 NV_INDEX_INSTANCE_EVENT_LOG_STRUCT NV Extend Record for instance data (CertChain) +// NV_EXTEND_INDEX_FOR_DYNAMIC 0x01C40201 NV_INDEX_DYNAMIC_EVENT_LOG_STRUCT NV Extend Record for dynamic data (Nonce) + +// EVENT_LOG_INTEGRITY_NV_INDEX_EXIT_PM_AUTH 0x01C40202 EVENT_LOG_INTEGRITY_NV_INDEX_STRUCT Event Log Integrity for ExitPmAuth +// EVENT_LOG_INTEGRITY_NV_INDEX_READY_TO_BOOT 0x01C40203 EVENT_LOG_INTEGRITY_NV_INDEX_STRUCT Event Log Integrity for ReadyToBoot +// ====================================================================================================================== +// + +#define TCG_NV_EXTEND_INDEX_FOR_INSTANCE 0x01C40200 +#define TCG_NV_EXTEND_INDEX_FOR_DYNAMIC 0x01C40201 +#define TCG_EVENT_LOG_INTEGRITY_NV_INDEX_EXIT_PM_AUTH 0x01C40202 +#define TCG_EVENT_LOG_INTEGRITY_NV_INDEX_READY_TO_BOOT 0x01C40203 + +#pragma pack(1) + +#define TCG_NV_EXTEND_INDEX_FOR_INSTANCE_SIGNATURE "NvIndexInstance" +#define TCG_NV_INDEX_INSTANCE_EVENT_LOG_STRUCT_VERSION 1 + +typedef struct { + UINT8 Signature[16]; + UINT16 Version; + UINT8 Reserved[6]; + // TCG_DEVICE_SECURITY_EVENT_DATA2 Data; +} TCG_NV_INDEX_INSTANCE_EVENT_LOG_STRUCT; + +#define TCG_NV_EXTEND_INDEX_FOR_DYNAMIC_SIGNATURE "NvIndexDynamic " +#define TCG_NV_INDEX_DYNAMIC_EVENT_LOG_STRUCT_VERSION 1 + +#define TCG_SPDM_CHALLENGE_DESCRIPTION "SPDM CHALLENGE" +#define TCG_SPDM_CHALLENGE_AUTH_DESCRIPTION "SPDM CHALLENGE_AUTH" +#define TCG_SPDM_GET_MEASUREMENTS_DESCRIPTION "SPDM GET_MEASUREMENTS" +#define TCG_SPDM_MEASUREMENTS_DESCRIPTION "SPDM MEASUREMENTS" + +typedef struct { + UINT8 Signature[16]; + UINT16 Version; + UINT8 Reserved[6]; + UINT64 Uid; + // UINT16 DescriptionSize; + // UINT8 Description[DescriptionSize]; + // UINT16 DataSize; + // UINT8 Data[DataSize]; +} TCG_NV_INDEX_DYNAMIC_EVENT_LOG_STRUCT; + +typedef struct { + TCG_NV_INDEX_DYNAMIC_EVENT_LOG_STRUCT Header; + UINT16 DescriptionSize; + UINT8 Description[sizeof (TCG_SPDM_CHALLENGE_DESCRIPTION)]; + UINT16 DataSize; + UINT8 Data[32]; +} TCG_NV_INDEX_DYNAMIC_EVENT_LOG_STRUCT_SPDM_CHALLENGE; + +typedef struct { + TCG_NV_INDEX_DYNAMIC_EVENT_LOG_STRUCT Header; + UINT16 DescriptionSize; + UINT8 Description[sizeof (TCG_SPDM_CHALLENGE_AUTH_DESCRIPTION)]; + UINT16 DataSize; + UINT8 Data[32]; +} TCG_NV_INDEX_DYNAMIC_EVENT_LOG_STRUCT_SPDM_CHALLENGE_AUTH; + +typedef struct { + TCG_NV_INDEX_DYNAMIC_EVENT_LOG_STRUCT Header; + UINT16 DescriptionSize; + UINT8 Description[sizeof (TCG_SPDM_GET_MEASUREMENTS_DESCRIPTION)]; + UINT16 DataSize; + UINT8 Data[32]; +} TCG_NV_INDEX_DYNAMIC_EVENT_LOG_STRUCT_SPDM_GET_MEASUREMENTS; + +typedef struct { + TCG_NV_INDEX_DYNAMIC_EVENT_LOG_STRUCT Header; + UINT16 DescriptionSize; + UINT8 Description[sizeof (TCG_SPDM_MEASUREMENTS_DESCRIPTION)]; + UINT16 DataSize; + UINT8 Data[32]; +} TCG_NV_INDEX_DYNAMIC_EVENT_LOG_STRUCT_SPDM_MEASUREMENTS; + +#pragma pack() + #endif diff --git a/src/include/ipxe/efi/IndustryStandard/Usb.h b/src/include/ipxe/efi/IndustryStandard/Usb.h index 8c7fe834f..2e87e1632 100644 --- a/src/include/ipxe/efi/IndustryStandard/Usb.h +++ b/src/include/ipxe/efi/IndustryStandard/Usb.h @@ -2,6 +2,8 @@ Support for USB 2.0 standard. Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.
+ Copyright (c) 2024, American Megatrends International LLC. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent **/ @@ -133,6 +135,21 @@ typedef struct { UINT8 MaxPower; } USB_CONFIG_DESCRIPTOR; +/// +/// Standard Interface Association Descriptor +/// USB 3.0 spec, Section 9.6.4 +/// +typedef struct { + UINT8 Length; + UINT8 DescriptorType; + UINT8 FirstInterface; + UINT8 InterfaceCount; + UINT8 FunctionClass; + UINT8 FunctionSubclass; + UINT8 FunctionProtocol; + UINT8 FunctionDescriptionStringIndex; +} USB_INTERFACE_ASSOCIATION_DESCRIPTOR; + /// /// Standard Interface Descriptor /// USB 2.0 spec, Section 9.6.5 @@ -209,13 +226,16 @@ typedef enum { // // USB Descriptor types // - USB_DESC_TYPE_DEVICE = 0x01, - USB_DESC_TYPE_CONFIG = 0x02, - USB_DESC_TYPE_STRING = 0x03, - USB_DESC_TYPE_INTERFACE = 0x04, - USB_DESC_TYPE_ENDPOINT = 0x05, - USB_DESC_TYPE_HID = 0x21, - USB_DESC_TYPE_REPORT = 0x22, + USB_DESC_TYPE_DEVICE = 0x01, + USB_DESC_TYPE_CONFIG = 0x02, + USB_DESC_TYPE_STRING = 0x03, + USB_DESC_TYPE_INTERFACE = 0x04, + USB_DESC_TYPE_ENDPOINT = 0x05, + USB_DESC_TYPE_INTERFACE_ASSOCIATION = 0x0b, + USB_DESC_TYPE_HID = 0x21, + USB_DESC_TYPE_REPORT = 0x22, + USB_DESC_TYPE_CS_INTERFACE = 0x24, + USB_DESC_TYPE_CS_ENDPOINT = 0x25, // // Features to be cleared by CLEAR_FEATURE requests diff --git a/src/include/ipxe/efi/Library/BaseLib.h b/src/include/ipxe/efi/Library/BaseLib.h index 16ea35cd2..f1a8210c8 100644 --- a/src/include/ipxe/efi/Library/BaseLib.h +++ b/src/include/ipxe/efi/Library/BaseLib.h @@ -7,6 +7,7 @@ Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
Copyright (c) Microsoft Corporation.
Portions Copyright (c) 2020, Hewlett Packard Enterprise Development LP. All rights reserved.
Portions Copyright (c) 2022, Loongson Technology Corporation Limited. All rights reserved.
+Copyright (c) 2023 - 2024, Arm Limited. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent @@ -128,6 +129,92 @@ typedef struct { #define BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT 8 +/** + Reads the current value of CNTPCT_EL0 register. + + Reads and returns the current value of CNTPCT_EL0. + This function is only available on AARCH64. + + @return The current value of CNTPCT_EL0 +**/ +UINT64 +EFIAPI +ArmReadCntPctReg ( + VOID + ); + +// +// Bit shifts for the ID_AA64ISAR0_EL1 register. +// +#define ARM_ID_AA64ISAR0_EL1_AES_SHIFT (4U) +#define ARM_ID_AA64ISAR0_EL1_SHA1_SHIFT (8U) +#define ARM_ID_AA64ISAR0_EL1_SHA2_SHIFT (12U) +#define ARM_ID_AA64ISAR0_EL1_CRC32_SHIFT (16U) +#define ARM_ID_AA64ISAR0_EL1_ATOMIC_SHIFT (20U) +#define ARM_ID_AA64ISAR0_EL1_RDM_SHIFT (28U) +#define ARM_ID_AA64ISAR0_EL1_SHA3_SHIFT (32U) +#define ARM_ID_AA64ISAR0_EL1_SM3_SHIFT (36U) +#define ARM_ID_AA64ISAR0_EL1_SM4_SHIFT (40U) +#define ARM_ID_AA64ISAR0_EL1_DP_SHIFT (44U) +#define ARM_ID_AA64ISAR0_EL1_FHM_SHIFT (48U) +#define ARM_ID_AA64ISAR0_EL1_TS_SHIFT (52U) +#define ARM_ID_AA64ISAR0_EL1_TLB_SHIFT (56U) +#define ARM_ID_AA64ISAR0_EL1_RNDR_SHIFT (60U) + +// +// Bit masks for the ID_AA64ISAR0_EL1 fields. +// +#define ARM_ID_AA64ISAR0_EL1_AES_MASK (0xFU) +#define ARM_ID_AA64ISAR0_EL1_SHA1_MASK (0xFU) +#define ARM_ID_AA64ISAR0_EL1_SHA2_MASK (0xFU) +#define ARM_ID_AA64ISAR0_EL1_CRC32_MASK (0xFU) +#define ARM_ID_AA64ISAR0_EL1_ATOMIC_MASK (0xFU) +#define ARM_ID_AA64ISAR0_EL1_RDM_MASK (0xFU) +#define ARM_ID_AA64ISAR0_EL1_SHA3_MASK (0xFU) +#define ARM_ID_AA64ISAR0_EL1_SM3_MASK (0xFU) +#define ARM_ID_AA64ISAR0_EL1_SM4_MASK (0xFU) +#define ARM_ID_AA64ISAR0_EL1_DP_MASK (0xFU) +#define ARM_ID_AA64ISAR0_EL1_FHM_MASK (0xFU) +#define ARM_ID_AA64ISAR0_EL1_TS_MASK (0xFU) +#define ARM_ID_AA64ISAR0_EL1_TLB_MASK (0xFU) +#define ARM_ID_AA64ISAR0_EL1_RNDR_MASK (0xFU) + +// +// Bit masks for the ID_AA64ISAR0_EL1 field values. +// +#define ARM_ID_AA64ISAR0_EL1_AES_FEAT_AES_MASK (0x1U) +#define ARM_ID_AA64ISAR0_EL1_AES_FEAT_PMULL_MASK (0x2U) +#define ARM_ID_AA64ISAR0_EL1_SHA1_FEAT_SHA1_MASK (0x1U) +#define ARM_ID_AA64ISAR0_EL1_SHA2_FEAT_SHA256_MASK (0x1U) +#define ARM_ID_AA64ISAR0_EL1_SHA2_FEAT_SHA512_MASK (0x2U) +#define ARM_ID_AA64ISAR0_EL1_CRC32_HAVE_CRC32_MASK (0x1U) +#define ARM_ID_AA64ISAR0_EL1_ATOMIC_FEAT_LSE_MASK (0x2U) +#define ARM_ID_AA64ISAR0_EL1_RDM_FEAT_RDM_MASK (0x1U) +#define ARM_ID_AA64ISAR0_EL1_SHA3_FEAT_SHA3_MASK (0x1U) +#define ARM_ID_AA64ISAR0_EL1_SM3_FEAT_SM3_MASK (0x1U) +#define ARM_ID_AA64ISAR0_EL1_SM4_FEAT_SM4_MASK (0x1U) +#define ARM_ID_AA64ISAR0_EL1_DP_FEAT_DOTPROD_MASK (0x1U) +#define ARM_ID_AA64ISAR0_EL1_FHM_FEAT_FHM_MASK (0x1U) +#define ARM_ID_AA64ISAR0_EL1_TS_FEAT_FLAGM_MASK (0x1U) +#define ARM_ID_AA64ISAR0_EL1_TS_FEAT_FLAGM2_MASK (0x2U) +#define ARM_ID_AA64ISAR0_EL1_TLB_FEAT_TLBIOS_MASK (0x1U) +#define ARM_ID_AA64ISAR0_EL1_TLB_FEAT_TLBIRANGE_MASK (0x2U) +#define ARM_ID_AA64ISAR0_EL1_RNDR_FEAT_RNG_MASK (0x1U) + +/** + Reads the current value of ID_AA64ISAR0_EL1 register. + + Reads and returns the current value of ID_AA64ISAR0_EL1. + This function is only available on AARCH64. + + @return The current value of ID_AA64ISAR0_EL1 +**/ +UINT64 +EFIAPI +ArmReadIdAA64Isar0Reg ( + VOID + ); + #endif // defined (MDE_CPU_AARCH64) #if defined (MDE_CPU_RISCV64) @@ -4902,6 +4989,23 @@ CalculateCrc32c ( IN UINT32 InitialValue ); +/** + Calculates the CRC16-CCITT-FALSE checksum of the given buffer. + + @param[in] Buffer Pointer to the buffer. + @param[in] Length Length of the buffer, in bytes. + @param[in] InitialValue Initial value of the CRC. + + @return The CRC16-CCITT-FALSE checksum. +**/ +UINT16 +EFIAPI +CalculateCrc16CcittF ( + IN CONST VOID *Buffer, + IN UINTN Length, + IN UINT16 InitialValue + ); + // // Base Library CPU Functions // @@ -5157,8 +5261,6 @@ SpeculationBarrier ( VOID ); -#if defined (MDE_CPU_X64) || defined (MDE_CPU_IA32) - /** The TDCALL instruction causes a VM exit to the Intel TDX module. It is used to call guest-side Intel TDX functions, either local or a TD exit @@ -5221,8 +5323,6 @@ TdIsEnabled ( VOID ); -#endif - #if defined (MDE_CPU_X64) // // The page size for the PVALIDATE instruction @@ -7878,6 +7978,45 @@ AsmVmgExit ( VOID ); +/// +/// The structure used to supply and return data to and from the SVSM. +/// +typedef struct { + VOID *Caa; + UINT64 RaxIn; + UINT64 RcxIn; + UINT64 RdxIn; + UINT64 R8In; + UINT64 R9In; + UINT64 RaxOut; + UINT64 RcxOut; + UINT64 RdxOut; + UINT64 R8Out; + UINT64 R9Out; + UINT8 *CallPending; +} SVSM_CALL_DATA; + +/** + Executes a VMGEXIT instruction (VMMCALL with a REP prefix) with arguments + and return code + + Executes a VMGEXIT instruction placing the specified arguments in the + corresponding registers before invocation. Upon return an XCHG is done to + atomically clear and retrieve the SVSM call pending value. The returned RAX + register value becomes the function return code. This function is intended + for use with an SVSM. This function is only available on IA-32 and x64. + + @param[in,out] SvsmCallPending Pointer to the location of the SVSM call data + + @return Value of the RAX register on return + +**/ +UINT32 +EFIAPI +AsmVmgExitSvsm ( + IN OUT SVSM_CALL_DATA *SvsmCallData + ); + /** Patch the immediate operand of an IA32 or X64 instruction such that the byte, word, dword or qword operand is encoded at the end of the instruction's diff --git a/src/include/ipxe/efi/Pi/PiDxeCis.h b/src/include/ipxe/efi/Pi/PiDxeCis.h index 9b9254936..246b19dac 100644 --- a/src/include/ipxe/efi/Pi/PiDxeCis.h +++ b/src/include/ipxe/efi/Pi/PiDxeCis.h @@ -5,7 +5,7 @@ Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent @par Revision Reference: - PI Version 1.7 + PI Version 1.8.A **/ @@ -58,14 +58,11 @@ typedef enum { /// system. If all memory has the same reliability, then this bit is not used. /// EfiGcdMemoryTypeMoreReliable, - // /// - // /// A memory region that describes system memory that has not been accepted - // /// by a corresponding call to the underlying isolation architecture. - // /// - // /// Please be noted: - // /// EfiGcdMemoryTypeUnaccepted is defined in PrePiDxeCis.h because it has not been - // /// defined in PI spec. - // EfiGcdMemoryTypeUnaccepted, + /// + /// A memory region that describes system memory that has not been accepted + /// by a corresponding call to the underlying isolation architecture. + /// + EfiGcdMemoryTypeUnaccepted, EfiGcdMemoryTypeMaximum = 7 } EFI_GCD_MEMORY_TYPE; @@ -696,8 +693,8 @@ EFI_STATUS // DXE Services Table // #define DXE_SERVICES_SIGNATURE 0x565245535f455844ULL -#define DXE_SPECIFICATION_MAJOR_REVISION 1 -#define DXE_SPECIFICATION_MINOR_REVISION 70 +#define DXE_SPECIFICATION_MAJOR_REVISION PI_SPECIFICATION_MAJOR_REVISION +#define DXE_SPECIFICATION_MINOR_REVISION PI_SPECIFICATION_MINOR_REVISION #define DXE_SERVICES_REVISION ((DXE_SPECIFICATION_MAJOR_REVISION<<16) | (DXE_SPECIFICATION_MINOR_REVISION)) typedef struct { diff --git a/src/include/ipxe/efi/Pi/PiHob.h b/src/include/ipxe/efi/Pi/PiHob.h index 5ecdf321f..1a19c34a0 100644 --- a/src/include/ipxe/efi/Pi/PiHob.h +++ b/src/include/ipxe/efi/Pi/PiHob.h @@ -234,16 +234,8 @@ typedef UINT32 EFI_RESOURCE_TYPE; #define EFI_RESOURCE_MEMORY_MAPPED_IO_PORT 0x00000004 #define EFI_RESOURCE_MEMORY_RESERVED 0x00000005 #define EFI_RESOURCE_IO_RESERVED 0x00000006 -// -// BZ3937_EFI_RESOURCE_MEMORY_UNACCEPTED is defined for unaccepted memory. -// But this defitinion has not been officially in the PI spec. Base -// on the code-first we define BZ3937_EFI_RESOURCE_MEMORY_UNACCEPTED at -// MdeModulePkg/Include/Pi/PrePiHob.h and update EFI_RESOURCE_MAX_MEMORY_TYPE -// to 8. After BZ3937_EFI_RESOURCE_MEMORY_UNACCEPTED is officially published -// in PI spec, we will re-visit here. -// -// #define BZ3937_EFI_RESOURCE_MEMORY_UNACCEPTED 0x00000007 -#define EFI_RESOURCE_MAX_MEMORY_TYPE 0x00000008 +#define EFI_RESOURCE_MEMORY_UNACCEPTED 0x00000007 +#define EFI_RESOURCE_MAX_MEMORY_TYPE 0x00000008 /// /// A type of recount attribute type. @@ -299,6 +291,8 @@ typedef UINT32 EFI_RESOURCE_ATTRIBUTE_TYPE; #define EFI_RESOURCE_ATTRIBUTE_READ_ONLY_PROTECTED 0x00040000 #define EFI_RESOURCE_ATTRIBUTE_READ_ONLY_PROTECTABLE 0x00080000 +#define EFI_RESOURCE_ATTRIBUTE_ENCRYPTED 0x04000000 +#define EFI_RESOURCE_ATTRIBUTE_SPECIAL_PURPOSE 0x08000000 // // Physical memory relative reliability attribute. This // memory provides higher reliability relative to other diff --git a/src/include/ipxe/efi/Pi/PiMultiPhase.h b/src/include/ipxe/efi/Pi/PiMultiPhase.h index e631821ca..187b131bc 100644 --- a/src/include/ipxe/efi/Pi/PiMultiPhase.h +++ b/src/include/ipxe/efi/Pi/PiMultiPhase.h @@ -5,7 +5,7 @@ Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent @par Revision Reference: - These elements are defined in UEFI Platform Initialization Specification 1.2. + These elements are defined in UEFI Platform Initialization Specification 1.8.A **/ @@ -22,6 +22,13 @@ FILE_LICENCE ( BSD2_PATENT ); #include #include +// +// PI Specification Version Information +// +#define PI_SPECIFICATION_MAJOR_REVISION 1 +#define PI_SPECIFICATION_MINOR_REVISION 80 +#define PI_SPECIFICATION_VERSION ((PI_SPECIFICATION_MAJOR_REVISION << 16) | (PI_SPECIFICATION_MINOR_REVISION)) + /** Produces an error code in the range reserved for use by the Platform Initialization Architecture Specification. diff --git a/src/include/ipxe/efi/Protocol/DebugSupport.h b/src/include/ipxe/efi/Protocol/DebugSupport.h index 8f930e335..b17befbad 100644 --- a/src/include/ipxe/efi/Protocol/DebugSupport.h +++ b/src/include/ipxe/efi/Protocol/DebugSupport.h @@ -682,23 +682,23 @@ typedef struct { UINT32 STVAL; } EFI_SYSTEM_CONTEXT_RISCV64; -// -// LoongArch processor exception types. -// -// The exception types is located in the CSR ESTAT -// register offset 16 bits, width 6 bits. -// -// If you want to register an exception hook, you can -// shfit the number left by 16 bits, and the exception -// handler will know the types. -// -// For example: -// mCpu->CpuRegisterInterruptHandler ( -// mCpu, -// (EXCEPT_LOONGARCH_PPI << CSR_ESTAT_EXC_SHIFT), -// PpiExceptionHandler -// ); -// +/// +/// LoongArch processor exception types. +/// +/// The exception types is located in the CSR ESTAT +/// register offset 16 bits, width 6 bits. +/// +/// If you want to register an exception hook, you can +/// shfit the number left by 16 bits, and the exception +/// handler will know the types. +/// +/// For example: +/// mCpu->CpuRegisterInterruptHandler ( +/// mCpu, +/// (EXCEPT_LOONGARCH_PPI << CSR_ESTAT_EXC_SHIFT), +/// PpiExceptionHandler +/// ); +/// #define EXCEPT_LOONGARCH_INT 0 #define EXCEPT_LOONGARCH_PIL 1 #define EXCEPT_LOONGARCH_PIS 2 @@ -718,11 +718,22 @@ typedef struct { #define EXCEPT_LOONGARCH_SXD 16 #define EXCEPT_LOONGARCH_ASXD 17 #define EXCEPT_LOONGARCH_FPE 18 -#define EXCEPT_LOONGARCH_TBR 64 // For code only, there is no such type in the ISA spec, the TLB refill is defined for an independent exception. +#define EXCEPT_LOONGARCH_WPE 19 +#define EXCEPT_LOONGARCH_BTD 20 +#define EXCEPT_LOONGARCH_BTE 21 +#define EXCEPT_LOONGARCH_GSPR 22 +#define EXCEPT_LOONGARCH_HVC 23 +#define EXCEPT_LOONGARCH_GCXC 24 -// -// LoongArch processor Interrupt types. -// +/// +/// For coding convenience, define the maximum valid +/// LoongArch exception. +/// +#define MAX_LOONGARCH_EXCEPTION 64 + +/// +/// LoongArch processor Interrupt types. +/// #define EXCEPT_LOONGARCH_INT_SIP0 0 #define EXCEPT_LOONGARCH_INT_SIP1 1 #define EXCEPT_LOONGARCH_INT_IP0 2 @@ -737,11 +748,11 @@ typedef struct { #define EXCEPT_LOONGARCH_INT_TIMER 11 #define EXCEPT_LOONGARCH_INT_IPI 12 -// -// For coding convenience, define the maximum valid -// LoongArch interrupt. -// -#define MAX_LOONGARCH_INTERRUPT 14 +/// +/// For coding convenience, define the maximum valid +/// LoongArch interrupt. +/// +#define MAX_LOONGARCH_INTERRUPT 16 typedef struct { UINT64 R0; diff --git a/src/include/ipxe/efi/Protocol/DevicePath.h b/src/include/ipxe/efi/Protocol/DevicePath.h index 3256d5594..2bec5a6e5 100644 --- a/src/include/ipxe/efi/Protocol/DevicePath.h +++ b/src/include/ipxe/efi/Protocol/DevicePath.h @@ -839,6 +839,26 @@ typedef struct { UINT64 NamespaceUuid; } NVME_NAMESPACE_DEVICE_PATH; +/// +/// NVMe over Fabric (NVMe-oF) Namespace Device Path SubType. +/// +#define MSG_NVME_OF_NAMESPACE_DP 0x22 +typedef struct { + EFI_DEVICE_PATH_PROTOCOL Header; + /// + /// Namespace Identifier Type (NIDT) + /// + UINT8 NamespaceIdType; + /// + /// Namespace Identifier (NID) + /// + UINT8 NamespaceId[16]; + /// + /// Unique identifier of an NVM subsystem + /// + CHAR8 SubsystemNqn[]; +} NVME_OF_NAMESPACE_DEVICE_PATH; + /// /// DNS Device Path SubType /// @@ -1289,6 +1309,7 @@ typedef union { SAS_DEVICE_PATH Sas; SASEX_DEVICE_PATH SasEx; NVME_NAMESPACE_DEVICE_PATH NvmeNamespace; + NVME_OF_NAMESPACE_DEVICE_PATH NvmeOfNamespace; DNS_DEVICE_PATH Dns; URI_DEVICE_PATH Uri; BLUETOOTH_DEVICE_PATH Bluetooth; @@ -1345,6 +1366,7 @@ typedef union { SAS_DEVICE_PATH *Sas; SASEX_DEVICE_PATH *SasEx; NVME_NAMESPACE_DEVICE_PATH *NvmeNamespace; + NVME_OF_NAMESPACE_DEVICE_PATH *NvmeOfNamespace; DNS_DEVICE_PATH *Dns; URI_DEVICE_PATH *Uri; BLUETOOTH_DEVICE_PATH *Bluetooth; diff --git a/src/include/ipxe/efi/Protocol/Http.h b/src/include/ipxe/efi/Protocol/Http.h index d30a5aa46..d13b049ab 100644 --- a/src/include/ipxe/efi/Protocol/Http.h +++ b/src/include/ipxe/efi/Protocol/Http.h @@ -100,7 +100,8 @@ typedef enum { HTTP_STATUS_503_SERVICE_UNAVAILABLE, HTTP_STATUS_504_GATEWAY_TIME_OUT, HTTP_STATUS_505_HTTP_VERSION_NOT_SUPPORTED, - HTTP_STATUS_308_PERMANENT_REDIRECT + HTTP_STATUS_308_PERMANENT_REDIRECT, + HTTP_STATUS_429_TOO_MANY_REQUESTS } EFI_HTTP_STATUS_CODE; /// diff --git a/src/include/ipxe/efi/Protocol/SimpleTextIn.h b/src/include/ipxe/efi/Protocol/SimpleTextIn.h index 11daeb5bc..7e53d523c 100644 --- a/src/include/ipxe/efi/Protocol/SimpleTextIn.h +++ b/src/include/ipxe/efi/Protocol/SimpleTextIn.h @@ -102,6 +102,7 @@ EFI_STATUS @retval EFI_NOT_READY There was no keystroke data available. @retval EFI_DEVICE_ERROR The keystroke information was not returned due to hardware errors. + @retval EFI_UNSUPPORTED The device does not support the ability to read keystroke data. **/ typedef diff --git a/src/include/ipxe/efi/Protocol/SimpleTextInEx.h b/src/include/ipxe/efi/Protocol/SimpleTextInEx.h index 9a93da509..8971b4f18 100644 --- a/src/include/ipxe/efi/Protocol/SimpleTextInEx.h +++ b/src/include/ipxe/efi/Protocol/SimpleTextInEx.h @@ -188,6 +188,7 @@ typedef struct { @retval EFI_NOT_READY There was no keystroke data available. @retval EFI_DEVICE_ERROR The keystroke information was not returned due to hardware errors. + @retval EFI_UNSUPPORTED The device does not support the ability to read keystroke data. **/ diff --git a/src/include/ipxe/efi/Uefi/UefiInternalFormRepresentation.h b/src/include/ipxe/efi/Uefi/UefiInternalFormRepresentation.h index 36468e022..2d075aed2 100644 --- a/src/include/ipxe/efi/Uefi/UefiInternalFormRepresentation.h +++ b/src/include/ipxe/efi/Uefi/UefiInternalFormRepresentation.h @@ -1698,7 +1698,17 @@ typedef enum { EfiKeyF12, EfiKeyPrint, EfiKeySLck, - EfiKeyPause + EfiKeyPause, + EfiKeyIntl0, + EfiKeyIntl1, + EfiKeyIntl2, + EfiKeyIntl3, + EfiKeyIntl4, + EfiKeyIntl5, + EfiKeyIntl6, + EfiKeyIntl7, + EfiKeyIntl8, + EfiKeyIntl9 } EFI_KEY; typedef struct { diff --git a/src/include/ipxe/efi/Uefi/UefiMultiPhase.h b/src/include/ipxe/efi/Uefi/UefiMultiPhase.h index 4ac760469..bffd14ce1 100644 --- a/src/include/ipxe/efi/Uefi/UefiMultiPhase.h +++ b/src/include/ipxe/efi/Uefi/UefiMultiPhase.h @@ -110,7 +110,22 @@ typedef enum { /// by a corresponding call to the underlying isolation architecture. /// EfiUnacceptedMemoryType, - EfiMaxMemoryType + EfiMaxMemoryType, + // + // +---------------------------------------------------+ + // | 0..(EfiMaxMemoryType - 1) - Normal memory type | + // +---------------------------------------------------+ + // | EfiMaxMemoryType..0x6FFFFFFF - Invalid | + // +---------------------------------------------------+ + // | 0x70000000..0x7FFFFFFF - OEM reserved | + // +---------------------------------------------------+ + // | 0x80000000..0xFFFFFFFF - OS reserved | + // +---------------------------------------------------+ + // + MEMORY_TYPE_OEM_RESERVED_MIN = 0x70000000, + MEMORY_TYPE_OEM_RESERVED_MAX = 0x7FFFFFFF, + MEMORY_TYPE_OS_RESERVED_MIN = 0x80000000, + MEMORY_TYPE_OS_RESERVED_MAX = 0xFFFFFFFF } EFI_MEMORY_TYPE; /// diff --git a/src/include/ipxe/efi/Uefi/UefiSpec.h b/src/include/ipxe/efi/Uefi/UefiSpec.h index cc166fc34..4dfc346df 100644 --- a/src/include/ipxe/efi/Uefi/UefiSpec.h +++ b/src/include/ipxe/efi/Uefi/UefiSpec.h @@ -2044,7 +2044,8 @@ typedef struct { UINT32 FirmwareRevision; /// /// The handle for the active console input device. This handle must support - /// EFI_SIMPLE_TEXT_INPUT_PROTOCOL and EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL. + /// EFI_SIMPLE_TEXT_INPUT_PROTOCOL and EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL. If + /// there is no active console, these protocols must still be present. /// EFI_HANDLE ConsoleInHandle; /// @@ -2053,7 +2054,9 @@ typedef struct { /// EFI_SIMPLE_TEXT_INPUT_PROTOCOL *ConIn; /// - /// The handle for the active console output device. + /// The handle for the active console output device. This handle must support the + /// EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL. If there is no active console, these protocols + /// must still be present. /// EFI_HANDLE ConsoleOutHandle; /// @@ -2063,7 +2066,8 @@ typedef struct { EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ConOut; /// /// The handle for the active standard error console device. - /// This handle must support the EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL. + /// This handle must support the EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL. If there + /// is no active console, this protocol must still be present. /// EFI_HANDLE StandardErrorHandle; /// From c0cbe7c2e69185bad65344914e757fe1844ee962 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 17 Dec 2024 13:51:40 +0000 Subject: [PATCH 04/50] [efi] Add EFI_TCG2_PROTOCOL header and GUID definition Signed-off-by: Michael Brown --- src/include/ipxe/efi/Protocol/Tcg2Protocol.h | 337 +++++++++++++++++++ src/include/ipxe/efi/efi.h | 1 + src/interface/efi/efi_debug.c | 2 + src/interface/efi/efi_guid.c | 5 + 4 files changed, 345 insertions(+) create mode 100644 src/include/ipxe/efi/Protocol/Tcg2Protocol.h diff --git a/src/include/ipxe/efi/Protocol/Tcg2Protocol.h b/src/include/ipxe/efi/Protocol/Tcg2Protocol.h new file mode 100644 index 000000000..e6c2a728e --- /dev/null +++ b/src/include/ipxe/efi/Protocol/Tcg2Protocol.h @@ -0,0 +1,337 @@ +/** @file + TPM2 Protocol as defined in TCG PC Client Platform EFI Protocol Specification Family "2.0". + See http://trustedcomputinggroup.org for the latest specification + +Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __TCG2_PROTOCOL_H__ +#define __TCG2_PROTOCOL_H__ + +FILE_LICENCE ( BSD2_PATENT ); + +#include +#include + +#define EFI_TCG2_PROTOCOL_GUID \ + {0x607f766c, 0x7455, 0x42be, { 0x93, 0x0b, 0xe4, 0xd7, 0x6d, 0xb2, 0x72, 0x0f }} + +typedef struct tdEFI_TCG2_PROTOCOL EFI_TCG2_PROTOCOL; + +typedef struct tdEFI_TCG2_VERSION { + UINT8 Major; + UINT8 Minor; +} EFI_TCG2_VERSION; + +typedef UINT32 EFI_TCG2_EVENT_LOG_BITMAP; +typedef UINT32 EFI_TCG2_EVENT_LOG_FORMAT; +typedef UINT32 EFI_TCG2_EVENT_ALGORITHM_BITMAP; + +#define EFI_TCG2_EVENT_LOG_FORMAT_TCG_1_2 0x00000001 +#define EFI_TCG2_EVENT_LOG_FORMAT_TCG_2 0x00000002 + +typedef struct tdEFI_TCG2_BOOT_SERVICE_CAPABILITY { + // + // Allocated size of the structure + // + UINT8 Size; + // + // Version of the EFI_TCG2_BOOT_SERVICE_CAPABILITY structure itself. + // For this version of the protocol, the Major version shall be set to 1 + // and the Minor version shall be set to 1. + // + EFI_TCG2_VERSION StructureVersion; + // + // Version of the EFI TCG2 protocol. + // For this version of the protocol, the Major version shall be set to 1 + // and the Minor version shall be set to 1. + // + EFI_TCG2_VERSION ProtocolVersion; + // + // Supported hash algorithms (this bitmap is determined by the supported PCR + // banks in the TPM and the hashing algorithms supported by the firmware) + // + EFI_TCG2_EVENT_ALGORITHM_BITMAP HashAlgorithmBitmap; + // + // Bitmap of supported event log formats + // + EFI_TCG2_EVENT_LOG_BITMAP SupportedEventLogs; + // + // False = TPM not present + // + BOOLEAN TPMPresentFlag; + // + // Max size (in bytes) of a command that can be sent to the TPM + // + UINT16 MaxCommandSize; + // + // Max size (in bytes) of a response that can be provided by the TPM + // + UINT16 MaxResponseSize; + // + // 4-byte Vendor ID + // (see TCG Vendor ID registry, Section "TPM Capabilities Vendor ID") + // + UINT32 ManufacturerID; + // + // Maximum number of PCR banks (hashing algorithms) supported. + // No granularity is provided to support a specific set of algorithms. + // Minimum value is 1. + // + UINT32 NumberOfPCRBanks; + // + // A bitmap of currently active PCR banks (hashing algorithms). + // This is a subset of the supported hashing algorithms reported in HashAlgorithmBitMap. + // NumberOfPcrBanks defines the number of bits that are set. + // + EFI_TCG2_EVENT_ALGORITHM_BITMAP ActivePcrBanks; +} EFI_TCG2_BOOT_SERVICE_CAPABILITY; + +#define EFI_TCG2_BOOT_HASH_ALG_SHA1 0x00000001 +#define EFI_TCG2_BOOT_HASH_ALG_SHA256 0x00000002 +#define EFI_TCG2_BOOT_HASH_ALG_SHA384 0x00000004 +#define EFI_TCG2_BOOT_HASH_ALG_SHA512 0x00000008 +#define EFI_TCG2_BOOT_HASH_ALG_SM3_256 0x00000010 + +// +// This bit is shall be set when an event shall be extended but not logged. +// +#define EFI_TCG2_EXTEND_ONLY 0x0000000000000001 +// +// This bit shall be set when the intent is to measure a PE/COFF image. +// +#define PE_COFF_IMAGE 0x0000000000000010 + +#define MAX_PCR_INDEX 23 + +#pragma pack(1) + +#define EFI_TCG2_EVENT_HEADER_VERSION 1 + +typedef struct { + // + // Size of the event header itself (sizeof(EFI_TCG2_EVENT_HEADER)). + // + UINT32 HeaderSize; + // + // Header version. For this version of this specification, the value shall be 1. + // + UINT16 HeaderVersion; + // + // Index of the PCR that shall be extended (0 - 23). + // + TCG_PCRINDEX PCRIndex; + // + // Type of the event that shall be extended (and optionally logged). + // + TCG_EVENTTYPE EventType; +} EFI_TCG2_EVENT_HEADER; + +typedef struct tdEFI_TCG2_EVENT { + // + // Total size of the event including the Size component, the header and the Event data. + // + UINT32 Size; + EFI_TCG2_EVENT_HEADER Header; + UINT8 Event[1]; +} EFI_TCG2_EVENT; + +#pragma pack() + +/** + The EFI_TCG2_PROTOCOL GetCapability function call provides protocol + capability information and state information. + + @param[in] This Indicates the calling context + @param[in, out] ProtocolCapability The caller allocates memory for a EFI_TCG2_BOOT_SERVICE_CAPABILITY + structure and sets the size field to the size of the structure allocated. + The callee fills in the fields with the EFI protocol capability information + and the current EFI TCG2 state information up to the number of fields which + fit within the size of the structure passed in. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. + The ProtocolCapability variable will not be populated. + @retval EFI_INVALID_PARAMETER One or more of the parameters are incorrect. + The ProtocolCapability variable will not be populated. + @retval EFI_BUFFER_TOO_SMALL The ProtocolCapability variable is too small to hold the full response. + It will be partially populated (required Size field will be set). +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_TCG2_GET_CAPABILITY)( + IN EFI_TCG2_PROTOCOL *This, + IN OUT EFI_TCG2_BOOT_SERVICE_CAPABILITY *ProtocolCapability + ); + +/** + The EFI_TCG2_PROTOCOL Get Event Log function call allows a caller to + retrieve the address of a given event log and its last entry. + + @param[in] This Indicates the calling context + @param[in] EventLogFormat The type of the event log for which the information is requested. + @param[out] EventLogLocation A pointer to the memory address of the event log. + @param[out] EventLogLastEntry If the Event Log contains more than one entry, this is a pointer to the + address of the start of the last entry in the event log in memory. + @param[out] EventLogTruncated If the Event Log is missing at least one entry because an event would + have exceeded the area allocated for events, this value is set to TRUE. + Otherwise, the value will be FALSE and the Event Log will be complete. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_INVALID_PARAMETER One or more of the parameters are incorrect + (e.g. asking for an event log whose format is not supported). +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_TCG2_GET_EVENT_LOG)( + IN EFI_TCG2_PROTOCOL *This, + IN EFI_TCG2_EVENT_LOG_FORMAT EventLogFormat, + OUT EFI_PHYSICAL_ADDRESS *EventLogLocation, + OUT EFI_PHYSICAL_ADDRESS *EventLogLastEntry, + OUT BOOLEAN *EventLogTruncated + ); + +/** + The EFI_TCG2_PROTOCOL HashLogExtendEvent function call provides callers with + an opportunity to extend and optionally log events without requiring + knowledge of actual TPM commands. + The extend operation will occur even if this function cannot create an event + log entry (e.g. due to the event log being full). + + @param[in] This Indicates the calling context + @param[in] Flags Bitmap providing additional information. + @param[in] DataToHash Physical address of the start of the data buffer to be hashed. + @param[in] DataToHashLen The length in bytes of the buffer referenced by DataToHash. + @param[in] EfiTcgEvent Pointer to data buffer containing information about the event. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. + @retval EFI_VOLUME_FULL The extend operation occurred, but the event could not be written to one or more event logs. + @retval EFI_INVALID_PARAMETER One or more of the parameters are incorrect. + @retval EFI_UNSUPPORTED The PE/COFF image type is not supported. +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_TCG2_HASH_LOG_EXTEND_EVENT)( + IN EFI_TCG2_PROTOCOL *This, + IN UINT64 Flags, + IN EFI_PHYSICAL_ADDRESS DataToHash, + IN UINT64 DataToHashLen, + IN EFI_TCG2_EVENT *EfiTcgEvent + ); + +/** + This service enables the sending of commands to the TPM. + + @param[in] This Indicates the calling context + @param[in] InputParameterBlockSize Size of the TPM input parameter block. + @param[in] InputParameterBlock Pointer to the TPM input parameter block. + @param[in] OutputParameterBlockSize Size of the TPM output parameter block. + @param[in] OutputParameterBlock Pointer to the TPM output parameter block. + + @retval EFI_SUCCESS The command byte stream was successfully sent to the device and a response was successfully received. + @retval EFI_DEVICE_ERROR The command was not successfully sent to the device or a response was not successfully received from the device. + @retval EFI_INVALID_PARAMETER One or more of the parameters are incorrect. + @retval EFI_BUFFER_TOO_SMALL The output parameter block is too small. +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_TCG2_SUBMIT_COMMAND)( + IN EFI_TCG2_PROTOCOL *This, + IN UINT32 InputParameterBlockSize, + IN UINT8 *InputParameterBlock, + IN UINT32 OutputParameterBlockSize, + IN UINT8 *OutputParameterBlock + ); + +/** + This service returns the currently active PCR banks. + + @param[in] This Indicates the calling context + @param[out] ActivePcrBanks Pointer to the variable receiving the bitmap of currently active PCR banks. + + @retval EFI_SUCCESS The bitmap of active PCR banks was stored in the ActivePcrBanks parameter. + @retval EFI_INVALID_PARAMETER One or more of the parameters are incorrect. +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_TCG2_GET_ACTIVE_PCR_BANKS)( + IN EFI_TCG2_PROTOCOL *This, + OUT UINT32 *ActivePcrBanks + ); + +/** + This service sets the currently active PCR banks. + + @param[in] This Indicates the calling context + @param[in] ActivePcrBanks Bitmap of the requested active PCR banks. At least one bit SHALL be set. + + @retval EFI_SUCCESS The bitmap in ActivePcrBank parameter is already active. + @retval EFI_INVALID_PARAMETER One or more of the parameters are incorrect. +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_TCG2_SET_ACTIVE_PCR_BANKS)( + IN EFI_TCG2_PROTOCOL *This, + IN UINT32 ActivePcrBanks + ); + +/** + This service retrieves the result of a previous invocation of SetActivePcrBanks. + + @param[in] This Indicates the calling context + @param[out] OperationPresent Non-zero value to indicate a SetActivePcrBank operation was invoked during the last boot. + @param[out] Response The response from the SetActivePcrBank request. + + @retval EFI_SUCCESS The result value could be returned. + @retval EFI_INVALID_PARAMETER One or more of the parameters are incorrect. +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_TCG2_GET_RESULT_OF_SET_ACTIVE_PCR_BANKS)( + IN EFI_TCG2_PROTOCOL *This, + OUT UINT32 *OperationPresent, + OUT UINT32 *Response + ); + +struct tdEFI_TCG2_PROTOCOL { + EFI_TCG2_GET_CAPABILITY GetCapability; + EFI_TCG2_GET_EVENT_LOG GetEventLog; + EFI_TCG2_HASH_LOG_EXTEND_EVENT HashLogExtendEvent; + EFI_TCG2_SUBMIT_COMMAND SubmitCommand; + EFI_TCG2_GET_ACTIVE_PCR_BANKS GetActivePcrBanks; + EFI_TCG2_SET_ACTIVE_PCR_BANKS SetActivePcrBanks; + EFI_TCG2_GET_RESULT_OF_SET_ACTIVE_PCR_BANKS GetResultOfSetActivePcrBanks; +}; + +extern EFI_GUID gEfiTcg2ProtocolGuid; + +// +// Log entries after Get Event Log service +// + +#define EFI_TCG2_FINAL_EVENTS_TABLE_GUID \ + {0x1e2ed096, 0x30e2, 0x4254, { 0xbd, 0x89, 0x86, 0x3b, 0xbe, 0xf8, 0x23, 0x25 }} + +extern EFI_GUID gEfiTcg2FinalEventsTableGuid; + +typedef struct tdEFI_TCG2_FINAL_EVENTS_TABLE { + // + // The version of this structure. + // + UINT64 Version; + // + // Number of events recorded after invocation of GetEventLog API + // + UINT64 NumberOfEvents; + // + // List of events of type TCG_PCR_EVENT2. + // + // TCG_PCR_EVENT2 Event[1]; +} EFI_TCG2_FINAL_EVENTS_TABLE; + +#define EFI_TCG2_FINAL_EVENTS_TABLE_VERSION 1 + +#endif diff --git a/src/include/ipxe/efi/efi.h b/src/include/ipxe/efi/efi.h index 2137b824d..843c79e2e 100644 --- a/src/include/ipxe/efi/efi.h +++ b/src/include/ipxe/efi/efi.h @@ -228,6 +228,7 @@ extern EFI_GUID efi_simple_text_input_protocol_guid; extern EFI_GUID efi_simple_text_input_ex_protocol_guid; extern EFI_GUID efi_simple_text_output_protocol_guid; extern EFI_GUID efi_tcg_protocol_guid; +extern EFI_GUID efi_tcg2_protocol_guid; extern EFI_GUID efi_tcp4_protocol_guid; extern EFI_GUID efi_tcp4_service_binding_protocol_guid; extern EFI_GUID efi_tcp6_protocol_guid; diff --git a/src/interface/efi/efi_debug.c b/src/interface/efi/efi_debug.c index 52efebe5f..895a712bd 100644 --- a/src/interface/efi/efi_debug.c +++ b/src/interface/efi/efi_debug.c @@ -209,6 +209,8 @@ static struct efi_well_known_guid efi_well_known_guids[] = { "SimpleTextOutput" }, { &efi_tcg_protocol_guid, "Tcg" }, + { &efi_tcg2_protocol_guid, + "Tcg2" }, { &efi_tcp4_protocol_guid, "Tcp4" }, { &efi_tcp4_service_binding_protocol_guid, diff --git a/src/interface/efi/efi_guid.c b/src/interface/efi/efi_guid.c index f841448f1..16c1a5738 100644 --- a/src/interface/efi/efi_guid.c +++ b/src/interface/efi/efi_guid.c @@ -70,6 +70,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include #include @@ -326,6 +327,10 @@ EFI_GUID efi_simple_text_output_protocol_guid EFI_GUID efi_tcg_protocol_guid = EFI_TCG_PROTOCOL_GUID; +/** TCG2 protocol GUID */ +EFI_GUID efi_tcg2_protocol_guid + = EFI_TCG2_PROTOCOL_GUID; + /** TCPv4 protocol GUID */ EFI_GUID efi_tcp4_protocol_guid = EFI_TCP4_PROTOCOL_GUID; From 83ba34076ad4ca79be81a71f25303b340c60e7b8 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 18 Dec 2024 14:03:37 +0000 Subject: [PATCH 05/50] [crypto] Allow for relaxed Montgomery reduction Classic Montgomery reduction involves a single conditional subtraction to ensure that the result is strictly less than the modulus. When performing chains of Montgomery multiplications (potentially interspersed with additions and subtractions), it can be useful to work with values that are stored modulo some small multiple of the modulus, thereby allowing some reductions to be elided. Each addition and subtraction stage will increase this running multiple, and the following multiplication stages can be used to reduce the running multiple since the reduction carried out for multiplication products is generally strong enough to absorb some additional bits in the inputs. This approach is already used in the x25519 code, where multiplication takes two 258-bit inputs and produces a 257-bit output. Split out the conditional subtraction from bigint_montgomery() and provide a separate bigint_montgomery_relaxed() for callers who do not require immediate reduction to within the range of the modulus. Modular exponentiation could potentially make use of relaxed Montgomery multiplication, but this would require R>4N, i.e. that the two most significant bits of the modulus be zero. For both RSA and DHE, this would necessitate extending the modulus size by one element, which would negate any speed increase from omitting the conditional subtractions. We therefore retain the use of classic Montgomery reduction for modular exponentiation, apart from the final conversion out of Montgomery form. Signed-off-by: Michael Brown --- src/crypto/bigint.c | 175 +++++++++++++++++++++++++++++++++----- src/include/ipxe/bigint.h | 38 ++++++--- src/tests/bigint_test.c | 6 +- 3 files changed, 185 insertions(+), 34 deletions(-) diff --git a/src/crypto/bigint.c b/src/crypto/bigint.c index 3ef96d13d..92747982e 100644 --- a/src/crypto/bigint.c +++ b/src/crypto/bigint.c @@ -351,18 +351,113 @@ void bigint_mod_invert_raw ( const bigint_element_t *invertend0, } /** - * Perform Montgomery reduction (REDC) of a big integer product + * Perform relaxed Montgomery reduction (REDC) of a big integer * - * @v modulus0 Element 0 of big integer modulus - * @v mont0 Element 0 of big integer Montgomery product + * @v modulus0 Element 0 of big integer odd modulus + * @v value0 Element 0 of big integer to be reduced * @v result0 Element 0 of big integer to hold result * @v size Number of elements in modulus and result + * @ret carry Carry out * - * Note that the Montgomery product will be overwritten. + * The value to be reduced will be made divisible by the size of the + * modulus while retaining its residue class (i.e. multiples of the + * modulus will be added until the low half of the value is zero). + * + * The result may be expressed as + * + * tR = x + mN + * + * where x is the input value, N is the modulus, R=2^n (where n is the + * number of bits in the representation of the modulus, including any + * leading zero bits), and m is the number of multiples of the modulus + * added to make the result tR divisible by R. + * + * The maximum addend is mN <= (R-1)*N (and such an m can be proven to + * exist since N is limited to being odd and therefore coprime to R). + * + * Since the result of this addition is one bit larger than the input + * value, a carry out bit is also returned. The caller may be able to + * prove that the carry out is always zero, in which case it may be + * safely ignored. + * + * The upper half of the output value (i.e. t) will also be copied to + * the result pointer. It is permissible for the result pointer to + * overlap the lower half of the input value. + * + * External knowledge of constraints on the modulus and the input + * value may be used to prove constraints on the result. The + * constraint on the modulus may be generally expressed as + * + * R > kN + * + * for some positive integer k. The value k=1 is allowed, and simply + * expresses that the modulus fits within the number of bits in its + * own representation. + * + * For classic Montgomery reduction, we have k=1, i.e. R > N and a + * separate constraint that the input value is in the range x < RN. + * This gives the result constraint + * + * tR < RN + (R-1)N + * < 2RN - N + * < 2RN + * t < 2N + * + * A single subtraction of the modulus may therefore be required to + * bring it into the range t < N. + * + * When the input value is known to be a product of two integers A and + * B, with A < aN and B < bN, we get the result constraint + * + * tR < abN^2 + (R-1)N + * < (ab/k)RN + RN - N + * < (1 + ab/k)RN + * t < (1 + ab/k)N + * + * If we have k=a=b=1, i.e. R > N with A < N and B < N, then the + * result is in the range t < 2N and may require a single subtraction + * of the modulus to bring it into the range t < N so that it may be + * used as an input on a subsequent iteration. + * + * If we have k=4 and a=b=2, i.e. R > 4N with A < 2N and B < 2N, then + * the result is in the range t < 2N and may immediately be used as an + * input on a subsequent iteration, without requiring a subtraction. + * + * Larger values of k may be used to allow for larger values of a and + * b, which can be useful to elide intermediate reductions in a + * calculation chain that involves additions and subtractions between + * multiplications (as used in elliptic curve point addition, for + * example). As a general rule: each intermediate addition or + * subtraction will require k to be doubled. + * + * When the input value is known to be a single integer A, with A < aN + * (as used when converting out of Montgomery form), we get the result + * constraint + * + * tR < aN + (R-1)N + * < RN + (a-1)N + * + * If we have a=1, i.e. A < N, then the constraint becomes + * + * tR < RN + * t < N + * + * and so the result is immediately in the range t < N with no + * subtraction of the modulus required. + * + * For any larger value of a, the result value t=N becomes possible. + * Additional external knowledge may potentially be used to prove that + * t=N cannot occur. For example: if the caller is performing modular + * exponentiation with a prime modulus (or, more generally, a modulus + * that is coprime to the base), then there is no way for a non-zero + * base value to end up producing an exact multiple of the modulus. + * If t=N cannot be disproved, then conversion out of Montgomery form + * may require an additional subtraction of the modulus. */ -void bigint_montgomery_raw ( const bigint_element_t *modulus0, - bigint_element_t *mont0, - bigint_element_t *result0, unsigned int size ) { +int bigint_montgomery_relaxed_raw ( const bigint_element_t *modulus0, + bigint_element_t *value0, + bigint_element_t *result0, + unsigned int size ) { const bigint_t ( size ) __attribute__ (( may_alias )) *modulus = ( ( const void * ) modulus0 ); union { @@ -371,7 +466,7 @@ void bigint_montgomery_raw ( const bigint_element_t *modulus0, bigint_t ( size ) low; bigint_t ( size ) high; } __attribute__ (( packed )); - } __attribute__ (( may_alias )) *mont = ( ( void * ) mont0 ); + } __attribute__ (( may_alias )) *value = ( ( void * ) value0 ); bigint_t ( size ) __attribute__ (( may_alias )) *result = ( ( void * ) result0 ); static bigint_t ( 1 ) cached; @@ -381,7 +476,6 @@ void bigint_montgomery_raw ( const bigint_element_t *modulus0, unsigned int i; unsigned int j; int overflow; - int underflow; /* Sanity checks */ assert ( bigint_bit_is_set ( modulus, 0 ) ); @@ -397,33 +491,73 @@ void bigint_montgomery_raw ( const bigint_element_t *modulus0, for ( i = 0 ; i < size ; i++ ) { /* Determine scalar multiple for this round */ - multiple = ( mont->low.element[i] * negmodinv.element[0] ); + multiple = ( value->low.element[i] * negmodinv.element[0] ); /* Multiply value to make it divisible by 2^(width*i) */ carry = 0; for ( j = 0 ; j < size ; j++ ) { bigint_multiply_one ( multiple, modulus->element[j], - &mont->full.element[ i + j ], + &value->full.element[ i + j ], &carry ); } /* Since value is now divisible by 2^(width*i), we * know that the current low element must have been - * zeroed. We can store the multiplication carry out - * in this element, avoiding the need to immediately - * propagate the carry through the remaining elements. + * zeroed. */ - assert ( mont->low.element[i] == 0 ); - mont->low.element[i] = carry; + assert ( value->low.element[i] == 0 ); + + /* Store the multiplication carry out in the result, + * avoiding the need to immediately propagate the + * carry through the remaining elements. + */ + result->element[i] = carry; } /* Add the accumulated carries */ - overflow = bigint_add ( &mont->low, &mont->high ); + overflow = bigint_add ( result, &value->high ); + + /* Copy to result buffer */ + bigint_copy ( &value->high, result ); + + return overflow; +} + +/** + * Perform classic Montgomery reduction (REDC) of a big integer + * + * @v modulus0 Element 0 of big integer odd modulus + * @v value0 Element 0 of big integer to be reduced + * @v result0 Element 0 of big integer to hold result + * @v size Number of elements in modulus and result + */ +void bigint_montgomery_raw ( const bigint_element_t *modulus0, + bigint_element_t *value0, + bigint_element_t *result0, + unsigned int size ) { + const bigint_t ( size ) __attribute__ (( may_alias )) + *modulus = ( ( const void * ) modulus0 ); + union { + bigint_t ( size * 2 ) full; + struct { + bigint_t ( size ) low; + bigint_t ( size ) high; + } __attribute__ (( packed )); + } __attribute__ (( may_alias )) *value = ( ( void * ) value0 ); + bigint_t ( size ) __attribute__ (( may_alias )) + *result = ( ( void * ) result0 ); + int overflow; + int underflow; + + /* Sanity check */ + assert ( ! bigint_is_geq ( &value->high, modulus ) ); + + /* Perform relaxed Montgomery reduction */ + overflow = bigint_montgomery_relaxed ( modulus, &value->full, result ); /* Conditionally subtract the modulus once */ - memcpy ( result, &mont->high, sizeof ( *result ) ); underflow = bigint_subtract ( modulus, result ); - bigint_swap ( result, &mont->high, ( underflow & ~overflow ) ); + bigint_swap ( result, &value->high, ( underflow & ~overflow ) ); /* Sanity check */ assert ( ! bigint_is_geq ( result, modulus ) ); @@ -530,7 +664,8 @@ void bigint_mod_exp_raw ( const bigint_element_t *base0, /* Convert back out of Montgomery form */ bigint_grow ( result, &temp->product.full ); - bigint_montgomery ( &temp->modulus, &temp->product.full, result ); + bigint_montgomery_relaxed ( &temp->modulus, &temp->product.full, + result ); /* Handle even moduli via Garner's algorithm */ if ( subsize ) { diff --git a/src/include/ipxe/bigint.h b/src/include/ipxe/bigint.h index 90e212b54..db907f1cd 100644 --- a/src/include/ipxe/bigint.h +++ b/src/include/ipxe/bigint.h @@ -235,7 +235,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * @v modulus Big integer modulus * @v value Big integer to be reduced */ -#define bigint_reduce( modulus, value ) do { \ +#define bigint_reduce( modulus, value ) do { \ unsigned int size = bigint_size (modulus); \ bigint_reduce_raw ( (modulus)->element, \ (value)->element, size ); \ @@ -254,19 +254,31 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); } while ( 0 ) /** - * Perform Montgomery reduction (REDC) of a big integer product + * Perform relaxed Montgomery reduction (REDC) of a big integer * - * @v modulus Big integer modulus - * @v mont Big integer Montgomery product + * @v modulus Big integer odd modulus + * @v value Big integer to be reduced * @v result Big integer to hold result - * - * Note that the Montgomery product will be overwritten. + * @ret carry Carry out */ -#define bigint_montgomery( modulus, mont, result ) do { \ +#define bigint_montgomery_relaxed( modulus, value, result ) ( { \ unsigned int size = bigint_size (modulus); \ - bigint_montgomery_raw ( (modulus)->element, (mont)->element, \ - (result)->element, \ - size ); \ + bigint_montgomery_relaxed_raw ( (modulus)->element, \ + (value)->element, \ + (result)->element, size ); \ + } ) + +/** + * Perform classic Montgomery reduction (REDC) of a big integer + * + * @v modulus Big integer odd modulus + * @v value Big integer to be reduced + * @v result Big integer to hold result + */ +#define bigint_montgomery( modulus, value, result ) do { \ + unsigned int size = bigint_size (modulus); \ + bigint_montgomery_raw ( (modulus)->element, (value)->element, \ + (result)->element, size ); \ } while ( 0 ) /** @@ -375,8 +387,12 @@ void bigint_reduce_raw ( bigint_element_t *modulus0, bigint_element_t *value0, unsigned int size ); void bigint_mod_invert_raw ( const bigint_element_t *invertend0, bigint_element_t *inverse0, unsigned int size ); +int bigint_montgomery_relaxed_raw ( const bigint_element_t *modulus0, + bigint_element_t *value0, + bigint_element_t *result0, + unsigned int size ); void bigint_montgomery_raw ( const bigint_element_t *modulus0, - bigint_element_t *mont0, + bigint_element_t *value0, bigint_element_t *result0, unsigned int size ); void bigint_mod_exp_raw ( const bigint_element_t *base0, const bigint_element_t *modulus0, diff --git a/src/tests/bigint_test.c b/src/tests/bigint_test.c index 07ba13bb4..fce5f5ca3 100644 --- a/src/tests/bigint_test.c +++ b/src/tests/bigint_test.c @@ -207,17 +207,17 @@ void bigint_mod_invert_sample ( const bigint_element_t *invertend0, } void bigint_montgomery_sample ( const bigint_element_t *modulus0, - bigint_element_t *mont0, + bigint_element_t *value0, bigint_element_t *result0, unsigned int size ) { const bigint_t ( size ) __attribute__ (( may_alias )) *modulus = ( ( const void * ) modulus0 ); bigint_t ( 2 * size ) __attribute__ (( may_alias )) - *mont = ( ( void * ) mont0 ); + *value = ( ( void * ) value0 ); bigint_t ( size ) __attribute__ (( may_alias )) *result = ( ( void * ) result0 ); - bigint_montgomery ( modulus, mont, result ); + bigint_montgomery ( modulus, value, result ); } void bigint_mod_exp_sample ( const bigint_element_t *base0, From d88eb0a1935942cdeccd3efee38f9765d2f1c235 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 10 Jan 2025 13:44:13 +0000 Subject: [PATCH 06/50] [crypto] Extract bigint_reduce_supremum() from bigint_mod_exp() Calculating the Montgomery constant (R^2 mod N) is done in our implementation by zeroing the double-width representation of N, subtracting N once to give (R^2 - N) in order to obtain a positive value, then reducing this value modulo N. Extract this logic from bigint_mod_exp() to a separate function bigint_reduce_supremum(), to allow for reuse by other code. Signed-off-by: Michael Brown --- src/crypto/bigint.c | 30 ++++++++++++++++++++++++++---- src/include/ipxe/bigint.h | 21 ++++++++++++++++++--- 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/src/crypto/bigint.c b/src/crypto/bigint.c index 92747982e..e5e6e2f12 100644 --- a/src/crypto/bigint.c +++ b/src/crypto/bigint.c @@ -277,6 +277,30 @@ void bigint_reduce_raw ( bigint_element_t *modulus0, bigint_element_t *value0, profile_stop ( &bigint_mod_profiler ); } +/** + * Reduce supremum of big integer representation + * + * @v modulus0 Element 0 of big integer modulus + * @v result0 Element 0 of big integer to hold result + * @v size Number of elements in modulus and value + * + * Reduce the value 2^k (where k is the bit width of the big integer + * representation) modulo the specified modulus. + */ +void bigint_reduce_supremum_raw ( bigint_element_t *modulus0, + bigint_element_t *result0, + unsigned int size ) { + bigint_t ( size ) __attribute__ (( may_alias )) + *modulus = ( ( void * ) modulus0 ); + bigint_t ( size ) __attribute__ (( may_alias )) + *result = ( ( void * ) result0 ); + + /* Calculate (2^k) mod N via direct reduction of (2^k - N) mod N */ + memset ( result, 0, sizeof ( *result ) ); + bigint_subtract ( modulus, result ); + bigint_reduce ( modulus, result ); +} + /** * Compute inverse of odd big integer modulo any power of two * @@ -629,10 +653,8 @@ void bigint_mod_exp_raw ( const bigint_element_t *base0, if ( ! submask ) submask = ~submask; - /* Calculate (R^2 mod N) via direct reduction of (R^2 - N) */ - memset ( &temp->product.full, 0, sizeof ( temp->product.full ) ); - bigint_subtract ( &temp->padded_modulus, &temp->product.full ); - bigint_reduce ( &temp->padded_modulus, &temp->product.full ); + /* Calculate (R^2 mod N) */ + bigint_reduce_supremum ( &temp->padded_modulus, &temp->product.full ); bigint_copy ( &temp->product.low, &temp->stash ); /* Initialise result = Montgomery(1, R^2 mod N) */ diff --git a/src/include/ipxe/bigint.h b/src/include/ipxe/bigint.h index db907f1cd..2dd99380d 100644 --- a/src/include/ipxe/bigint.h +++ b/src/include/ipxe/bigint.h @@ -236,9 +236,21 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * @v value Big integer to be reduced */ #define bigint_reduce( modulus, value ) do { \ - unsigned int size = bigint_size (modulus); \ - bigint_reduce_raw ( (modulus)->element, \ - (value)->element, size ); \ + unsigned int size = bigint_size (modulus); \ + bigint_reduce_raw ( (modulus)->element, (value)->element, \ + size ); \ + } while ( 0 ) + +/** + * Reduce supremum of big integer representation + * + * @v modulus0 Big integer modulus + * @v result0 Big integer to hold result + */ +#define bigint_reduce_supremum( modulus, result ) do { \ + unsigned int size = bigint_size (modulus); \ + bigint_reduce_supremum_raw ( (modulus)->element, \ + (result)->element, size ); \ } while ( 0 ) /** @@ -385,6 +397,9 @@ void bigint_multiply_raw ( const bigint_element_t *multiplicand0, bigint_element_t *result0 ); void bigint_reduce_raw ( bigint_element_t *modulus0, bigint_element_t *value0, unsigned int size ); +void bigint_reduce_supremum_raw ( bigint_element_t *modulus0, + bigint_element_t *value0, + unsigned int size ); void bigint_mod_invert_raw ( const bigint_element_t *invertend0, bigint_element_t *inverse0, unsigned int size ); int bigint_montgomery_relaxed_raw ( const bigint_element_t *modulus0, From cc38d7dd3e379f3f93bcc97390137bbfd4049d60 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 20 Jan 2025 15:55:13 +0000 Subject: [PATCH 07/50] [crypto] Add bigint_ntoa() for transcribing big integers In debug messages, big integers are currently printed as hex dumps. This is quite verbose and cumbersome to check against external sources. Add bigint_ntoa() to transcribe big integers into a static buffer (following the model of inet_ntoa(), eth_ntoa(), uuid_ntoa(), etc). Abbreviate big integers that will not fit within the static buffer, showing both the most significant and least significant digits in the transcription. This is generally the most useful form when visually comparing against external sources (such as test vectors, or results produced by high-level languages). Signed-off-by: Michael Brown --- src/crypto/bigint.c | 47 +++++++++++++++++++++++++++++++++++++++ src/include/ipxe/bigint.h | 13 +++++++++++ 2 files changed, 60 insertions(+) diff --git a/src/crypto/bigint.c b/src/crypto/bigint.c index e5e6e2f12..dd75cd9d1 100644 --- a/src/crypto/bigint.c +++ b/src/crypto/bigint.c @@ -26,6 +26,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include @@ -38,6 +39,52 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); static struct profiler bigint_mod_profiler __profiler = { .name = "bigint_mod" }; +/** Minimum number of least significant bytes included in transcription */ +#define BIGINT_NTOA_LSB_MIN 16 + +/** + * Transcribe big integer (for debugging) + * + * @v value0 Element 0 of big integer to be transcribed + * @v size Number of elements + * @ret string Big integer in string form (may be abbreviated) + */ +const char * bigint_ntoa_raw ( const bigint_element_t *value0, + unsigned int size ) { + const bigint_t ( size ) __attribute__ (( may_alias )) + *value = ( ( const void * ) value0 ); + bigint_element_t element; + static char buf[256]; + unsigned int count; + int threshold; + uint8_t byte; + char *tmp; + int i; + + /* Calculate abbreviation threshold, if any */ + count = ( size * sizeof ( element ) ); + threshold = count; + if ( count >= ( ( sizeof ( buf ) - 1 /* NUL */ ) / 2 /* "xx" */ ) ) { + threshold -= ( ( sizeof ( buf ) - 3 /* "..." */ + - ( BIGINT_NTOA_LSB_MIN * 2 /* "xx" */ ) + - 1 /* NUL */ ) / 2 /* "xx" */ ); + } + + /* Transcribe bytes, abbreviating with "..." if necessary */ + for ( tmp = buf, i = ( count - 1 ) ; i >= 0 ; i-- ) { + element = value->element[ i / sizeof ( element ) ]; + byte = ( element >> ( 8 * ( i % sizeof ( element ) ) ) ); + tmp += sprintf ( tmp, "%02x", byte ); + if ( i == threshold ) { + tmp += sprintf ( tmp, "..." ); + i = BIGINT_NTOA_LSB_MIN; + } + } + assert ( tmp < ( buf + sizeof ( buf ) ) ); + + return buf; +} + /** * Conditionally swap big integers (in constant time) * diff --git a/src/include/ipxe/bigint.h b/src/include/ipxe/bigint.h index 2dd99380d..1cc606b89 100644 --- a/src/include/ipxe/bigint.h +++ b/src/include/ipxe/bigint.h @@ -40,6 +40,17 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define bigint_size( bigint ) \ ( sizeof ( *(bigint) ) / sizeof ( (bigint)->element[0] ) ) +/** + * Transcribe big integer (for debugging) + * + * @v value Big integer to be transcribed + * @ret string Big integer in string form (may be abbreviated) + */ +#define bigint_ntoa( value ) ( { \ + unsigned int size = bigint_size (value); \ + bigint_ntoa_raw ( (value)->element, size ); \ + } ) + /** * Initialise big integer * @@ -360,6 +371,8 @@ bigint_msb_is_set_raw ( const bigint_element_t *value0, unsigned int size ) { return ( !! ( value->element[index] & ( 1UL << subindex ) ) ); } +const char * bigint_ntoa_raw ( const bigint_element_t *value0, + unsigned int size ); void bigint_init_raw ( bigint_element_t *value0, unsigned int size, const void *data, size_t len ); void bigint_done_raw ( const bigint_element_t *value0, unsigned int size, From df7ec31766cd08eb1e01d59afc79198f5411517e Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 21 Jan 2025 15:13:20 +0000 Subject: [PATCH 08/50] [crypto] Generalise elliptic curve key exchange to ecdhe_key() Split out the portion of tls_send_client_key_exchange_ecdhe() that actually performs the elliptic curve key exchange into a separate function ecdhe_key(). Signed-off-by: Michael Brown --- src/crypto/ecdhe.c | 66 ++++++++++++++++++++++++++++++++++++++++ src/include/ipxe/ecdhe.h | 17 +++++++++++ src/net/tls.c | 13 +++----- 3 files changed, 87 insertions(+), 9 deletions(-) create mode 100644 src/crypto/ecdhe.c create mode 100644 src/include/ipxe/ecdhe.h diff --git a/src/crypto/ecdhe.c b/src/crypto/ecdhe.c new file mode 100644 index 000000000..5481b02eb --- /dev/null +++ b/src/crypto/ecdhe.c @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2025 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * Elliptic Curve Ephemeral Diffie-Hellman (ECDHE) key exchange + * + */ + +#include +#include + +/** + * Calculate ECDHE key + * + * @v curve Elliptic curve + * @v partner Partner public curve point + * @v private Private key + * @v public Public curve point to fill in (may overlap partner key) + * @v shared Shared secret curve point to fill in + * @ret rc Return status code + */ +int ecdhe_key ( struct elliptic_curve *curve, const void *partner, + const void *private, void *public, void *shared ) { + int rc; + + /* Construct shared key */ + if ( ( rc = elliptic_multiply ( curve, partner, private, + shared ) ) != 0 ) { + DBGC ( curve, "CURVE %s could not generate shared key: %s\n", + curve->name, strerror ( rc ) ); + return rc; + } + + /* Construct public key */ + if ( ( rc = elliptic_multiply ( curve, NULL, private, + public ) ) != 0 ) { + DBGC ( curve, "CURVE %s could not generate public key: %s\n", + curve->name, strerror ( rc ) ); + return rc; + } + + return 0; +} diff --git a/src/include/ipxe/ecdhe.h b/src/include/ipxe/ecdhe.h new file mode 100644 index 000000000..36fc0a1ee --- /dev/null +++ b/src/include/ipxe/ecdhe.h @@ -0,0 +1,17 @@ +#ifndef _IPXE_ECDHE_H +#define _IPXE_ECDHE_H + +/** @file + * + * Elliptic Curve Ephemeral Diffie-Hellman (ECDHE) key exchange + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +extern int ecdhe_key ( struct elliptic_curve *curve, const void *partner, + const void *private, void *public, void *shared ); + +#endif /* _IPXE_ECDHE_H */ diff --git a/src/net/tls.c b/src/net/tls.c index ded100d0e..286d2cc9f 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -50,6 +50,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include #include @@ -1733,9 +1734,9 @@ static int tls_send_client_key_exchange_ecdhe ( struct tls_connection *tls ) { } /* Calculate pre-master secret */ - if ( ( rc = elliptic_multiply ( curve->curve, - ecdh->public, private, - pre_master_secret ) ) != 0 ) { + if ( ( rc = ecdhe_key ( curve->curve, ecdh->public, + private, key_xchg.public, + pre_master_secret ) ) != 0 ) { DBGC ( tls, "TLS %p could not exchange ECDHE key: %s\n", tls, strerror ( rc ) ); return rc; @@ -1750,12 +1751,6 @@ static int tls_send_client_key_exchange_ecdhe ( struct tls_connection *tls ) { htonl ( sizeof ( key_xchg ) - sizeof ( key_xchg.type_length ) ) ); key_xchg.public_len = len; - if ( ( rc = elliptic_multiply ( curve->curve, NULL, private, - key_xchg.public ) ) != 0 ) { - DBGC ( tls, "TLS %p could not generate ECDHE key: %s\n", - tls, strerror ( rc ) ); - return rc; - } /* Transmit Client Key Exchange record */ if ( ( rc = tls_send_handshake ( tls, &key_xchg, From c9291bc5c7adfa9aa05e94aded90ba49d3dc8179 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 21 Jan 2025 15:29:05 +0000 Subject: [PATCH 09/50] [tls] Allow for NIST elliptic curve point formats The elliptic curve point representation for the x25519 curve includes only the X value, since the curve is designed such that the Montgomery ladder does not need to ever know or calculate a Y value. There is no curve point format byte: the public key data is simply the X value. The pre-master secret is also simply the X value of the shared secret curve point. The point representation for the NIST curves includes both X and Y values, and a single curve point format byte that must indicate that the format is uncompressed. The pre-master secret for the NIST curves does not include both X and Y values: only the X value is used. Extend the definition of an elliptic curve to allow the point size to be specified separately from the key size, and extend the definition of a TLS named curve to include an optional curve point format byte and a pre-master secret length. Signed-off-by: Michael Brown --- src/crypto/mishmash/oid_x25519.c | 1 + src/crypto/x25519.c | 1 + src/include/ipxe/crypto.h | 4 +++- src/include/ipxe/tls.h | 7 ++++++ src/net/tls.c | 38 +++++++++++++++++++++++--------- 5 files changed, 40 insertions(+), 11 deletions(-) diff --git a/src/crypto/mishmash/oid_x25519.c b/src/crypto/mishmash/oid_x25519.c index 2f8aa065b..30b7905ea 100644 --- a/src/crypto/mishmash/oid_x25519.c +++ b/src/crypto/mishmash/oid_x25519.c @@ -42,4 +42,5 @@ struct asn1_algorithm x25519_algorithm __asn1_algorithm = { struct tls_named_curve tls_x25519_named_curve __tls_named_curve ( 01 ) = { .curve = &x25519_curve, .code = htons ( TLS_NAMED_CURVE_X25519 ), + .pre_master_secret_len = sizeof ( struct x25519_value ), }; diff --git a/src/crypto/x25519.c b/src/crypto/x25519.c index ab5d2e8b0..995cfa352 100644 --- a/src/crypto/x25519.c +++ b/src/crypto/x25519.c @@ -839,6 +839,7 @@ static int x25519_curve_multiply ( const void *base, const void *scalar, /** X25519 elliptic curve */ struct elliptic_curve x25519_curve = { .name = "x25519", + .pointsize = sizeof ( struct x25519_value ), .keysize = sizeof ( struct x25519_value ), .multiply = x25519_curve_multiply, }; diff --git a/src/include/ipxe/crypto.h b/src/include/ipxe/crypto.h index dcc73f3ef..4bd543ae2 100644 --- a/src/include/ipxe/crypto.h +++ b/src/include/ipxe/crypto.h @@ -184,7 +184,9 @@ struct pubkey_algorithm { struct elliptic_curve { /** Curve name */ const char *name; - /** Key size */ + /** Point (and public key) size */ + size_t pointsize; + /** Scalar (and private key) size */ size_t keysize; /** Multiply scalar by curve point * diff --git a/src/include/ipxe/tls.h b/src/include/ipxe/tls.h index 08d58689e..bf9807230 100644 --- a/src/include/ipxe/tls.h +++ b/src/include/ipxe/tls.h @@ -218,12 +218,19 @@ struct tls_cipher_suite { /** TLS named curved type */ #define TLS_NAMED_CURVE_TYPE 3 +/** TLS uncompressed curve point format */ +#define TLS_POINT_FORMAT_UNCOMPRESSED 4 + /** A TLS named curve */ struct tls_named_curve { /** Elliptic curve */ struct elliptic_curve *curve; /** Numeric code (in network-endian order) */ uint16_t code; + /** Curve point format byte (if any) */ + uint8_t format; + /** Pre-master secret length */ + uint8_t pre_master_secret_len; }; /** TLS named curve table */ diff --git a/src/net/tls.c b/src/net/tls.c index 286d2cc9f..a94e71c8a 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -1671,6 +1671,9 @@ static int tls_send_client_key_exchange_ecdhe ( struct tls_connection *tls ) { uint8_t public[0]; } __attribute__ (( packed )) *ecdh; size_t param_len; + size_t pointsize; + size_t keysize; + size_t offset; int rc; /* Parse ServerKeyExchange record */ @@ -1706,9 +1709,13 @@ static int tls_send_client_key_exchange_ecdhe ( struct tls_connection *tls ) { tls->server.exchange_len ); return -ENOTSUP_CURVE; } + DBGC ( tls, "TLS %p using named curve %s\n", tls, curve->curve->name ); + pointsize = curve->curve->pointsize; + keysize = curve->curve->keysize; + offset = ( curve->format ? 1 : 0 ); /* Check key length */ - if ( ecdh->public_len != curve->curve->keysize ) { + if ( ecdh->public_len != ( offset + pointsize ) ) { DBGC ( tls, "TLS %p invalid %s key\n", tls, curve->curve->name ); DBGC_HDA ( tls, 0, tls->server.exchange, @@ -1716,15 +1723,23 @@ static int tls_send_client_key_exchange_ecdhe ( struct tls_connection *tls ) { return -EINVAL_KEY_EXCHANGE; } + /* Check curve point format byte (if present) */ + if ( curve->format && ( ecdh->public[0] != curve->format ) ) { + DBGC ( tls, "TLS %p invalid %s curve point format\n", + tls, curve->curve->name ); + DBGC_HDA ( tls, 0, tls->server.exchange, + tls->server.exchange_len ); + return -EINVAL_KEY_EXCHANGE; + } + /* Construct pre-master secret and ClientKeyExchange record */ { - size_t len = curve->curve->keysize; - uint8_t private[len]; - uint8_t pre_master_secret[len]; + uint8_t private[keysize]; + uint8_t pre_master_secret[pointsize]; struct { uint32_t type_length; uint8_t public_len; - uint8_t public[len]; + uint8_t public[ecdh->public_len]; } __attribute__ (( packed )) key_xchg; /* Generate ephemeral private key */ @@ -1733,9 +1748,9 @@ static int tls_send_client_key_exchange_ecdhe ( struct tls_connection *tls ) { return rc; } - /* Calculate pre-master secret */ - if ( ( rc = ecdhe_key ( curve->curve, ecdh->public, - private, key_xchg.public, + /* Exchange keys */ + if ( ( rc = ecdhe_key ( curve->curve, ( ecdh->public + offset ), + private, ( key_xchg.public + offset ), pre_master_secret ) ) != 0 ) { DBGC ( tls, "TLS %p could not exchange ECDHE key: %s\n", tls, strerror ( rc ) ); @@ -1743,14 +1758,17 @@ static int tls_send_client_key_exchange_ecdhe ( struct tls_connection *tls ) { } /* Generate master secret */ - tls_generate_master_secret ( tls, pre_master_secret, len ); + tls_generate_master_secret ( tls, pre_master_secret, + curve->pre_master_secret_len ); /* Generate Client Key Exchange record */ key_xchg.type_length = ( cpu_to_le32 ( TLS_CLIENT_KEY_EXCHANGE ) | htonl ( sizeof ( key_xchg ) - sizeof ( key_xchg.type_length ) ) ); - key_xchg.public_len = len; + key_xchg.public_len = sizeof ( key_xchg.public ); + if ( curve->format ) + key_xchg.public[0] = curve->format; /* Transmit Client Key Exchange record */ if ( ( rc = tls_send_handshake ( tls, &key_xchg, From c2f21a21852741e869b5a64d02d6a49ef16a94cd Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 22 Jan 2025 12:58:54 +0000 Subject: [PATCH 10/50] [test] Add generic tests for elliptic curve point multiplication Signed-off-by: Michael Brown --- src/tests/elliptic_test.c | 76 ++++++++++++++++++++++++++++++++++++++ src/tests/elliptic_test.h | 77 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 153 insertions(+) create mode 100644 src/tests/elliptic_test.c create mode 100644 src/tests/elliptic_test.h diff --git a/src/tests/elliptic_test.c b/src/tests/elliptic_test.c new file mode 100644 index 000000000..4c42e5848 --- /dev/null +++ b/src/tests/elliptic_test.c @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2025 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * Elliptic curve self-tests + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include +#include +#include +#include +#include +#include "elliptic_test.h" + +/** + * Report elliptic curve point multiplication test result + * + * @v test Elliptic curve point multiplication test + * @v file Test code file + * @v line Test code line + */ +void elliptic_okx ( struct elliptic_test *test, const char *file, + unsigned int line ) { + struct elliptic_curve *curve = test->curve; + size_t pointsize = curve->pointsize; + size_t keysize = curve->keysize; + uint8_t actual[pointsize]; + int rc; + + /* Sanity checks */ + okx ( ( test->base_len == pointsize ) || ( ! test->base_len ), + file, line ); + okx ( test->scalar_len == keysize, file, line ); + okx ( ( test->expected_len == pointsize ) || ( ! test->expected_len ), + file, line ); + + /* Perform point multiplication */ + rc = elliptic_multiply ( curve, ( test->base_len ? test->base : NULL ), + test->scalar, actual ); + if ( test->expected_len ) { + okx ( rc == 0, file, line ); + } else { + okx ( rc != 0, file, line ); + } + + /* Check expected result */ + okx ( memcmp ( actual, test->expected, test->expected_len ) == 0, + file, line ); +} diff --git a/src/tests/elliptic_test.h b/src/tests/elliptic_test.h new file mode 100644 index 000000000..94fca60aa --- /dev/null +++ b/src/tests/elliptic_test.h @@ -0,0 +1,77 @@ +#ifndef _ELLIPTIC_TEST_H +#define _ELLIPTIC_TEST_H + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include + +/** An elliptic curve point multiplication test */ +struct elliptic_test { + /** Elliptic curve */ + struct elliptic_curve *curve; + /** Base point */ + const void *base; + /** Length of base point (or 0 to use generator) */ + size_t base_len; + /** Scalar multiple */ + const void *scalar; + /** Length of scalar multiple */ + size_t scalar_len; + /** Expected result point */ + const void *expected; + /** Length of expected result point (or 0 to expect failure) */ + size_t expected_len; +}; + +/** Define inline base point */ +#define BASE(...) { __VA_ARGS__ } + +/** Define base point to be curve's generator */ +#define BASE_GENERATOR BASE() + +/** Define inline scalar multiple */ +#define SCALAR(...) { __VA_ARGS__ } + +/** Define inline expected result point */ +#define EXPECTED(...) { __VA_ARGS__ } + +/** Define result as an expected failure */ +#define EXPECTED_FAIL EXPECTED() + +/** + * Define an elliptic curve point multiplication test + * + * @v name Test name + * @v CURVE Elliptic curve + * @v BASE Base point + * @v SCALAR Scalar multiple + * @v EXPECTED Expected result point + * @ret test Elliptic curve point multiplication test + */ +#define ELLIPTIC_TEST( name, CURVE, BASE, SCALAR, EXPECTED ) \ + static const uint8_t name ## _base[] = BASE; \ + static const uint8_t name ## _scalar[] = SCALAR; \ + static const uint8_t name ## _expected[] = EXPECTED; \ + static struct elliptic_test name = { \ + .curve = CURVE, \ + .base = name ## _base, \ + .base_len = sizeof ( name ## _base ), \ + .scalar = name ## _scalar, \ + .scalar_len = sizeof ( name ## _scalar ), \ + .expected = name ## _expected, \ + .expected_len = sizeof ( name ## _expected ), \ + }; + +extern void elliptic_okx ( struct elliptic_test *test, const char *file, + unsigned int line ); + +/** + * Report an elliptic curve point multiplication test result + * + * @v test Elliptic curve point multiplication test + */ +#define elliptic_ok( test ) elliptic_okx ( test, __FILE__, __LINE__ ) + +#endif /* _ELLIPTIC_TEST_H */ From 66b5d1ec81433d4cbc218ed18f2e4ee04d51aa38 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 22 Jan 2025 12:54:52 +0000 Subject: [PATCH 11/50] [crypto] Add a generic implementation of a Montgomery ladder The Montgomery ladder may be used to perform any operation that is isomorphic to exponentiation, i.e. to compute the result r = g^e = g * g * g * g * .... * g for an arbitrary commutative operation "*", base or generator "g", and exponent "e". Implement a generic Montgomery ladder for use by both modular exponentiation and elliptic curve point multiplication (both of which are isomorphic to exponentiation). Signed-off-by: Michael Brown --- src/crypto/bigint.c | 188 +++++++++++++++++++++++++++++++------- src/include/ipxe/bigint.h | 40 ++++++++ 2 files changed, 194 insertions(+), 34 deletions(-) diff --git a/src/crypto/bigint.c b/src/crypto/bigint.c index dd75cd9d1..ad22af771 100644 --- a/src/crypto/bigint.c +++ b/src/crypto/bigint.c @@ -634,6 +634,150 @@ void bigint_montgomery_raw ( const bigint_element_t *modulus0, assert ( ! bigint_is_geq ( result, modulus ) ); } +/** + * Perform generalised exponentiation via a Montgomery ladder + * + * @v result0 Element 0 of result (initialised to identity element) + * @v multiple0 Element 0 of multiple (initialised to generator) + * @v size Number of elements in result and multiple + * @v exponent0 Element 0 of exponent + * @v exponent_size Number of elements in exponent + * @v op Montgomery ladder commutative operation + * @v ctx Operation context (if needed) + * @v tmp Temporary working space (if needed) + * + * The Montgomery ladder may be used to perform any operation that is + * isomorphic to exponentiation, i.e. to compute the result + * + * r = g^e = g * g * g * g * .... * g + * + * for an arbitrary commutative operation "*", generator "g" and + * exponent "e". + * + * The result "r" is computed in constant time (assuming that the + * underlying operation is constant time) in k steps, where k is the + * number of bits in the big integer representation of the exponent. + * + * The result "r" must be initialised to the operation's identity + * element, and the multiple must be initialised to the generator "g". + * On exit, the multiple will contain + * + * m = r * g = g^(e+1) + * + * Note that the terminology used here refers to exponentiation + * defined as repeated multiplication, but that the ladder may equally + * well be used to perform any isomorphic operation (such as + * multiplication defined as repeated addition). + */ +void bigint_ladder_raw ( bigint_element_t *result0, + bigint_element_t *multiple0, unsigned int size, + const bigint_element_t *exponent0, + unsigned int exponent_size, bigint_ladder_op_t *op, + const void *ctx, void *tmp ) { + bigint_t ( size ) __attribute__ (( may_alias )) + *result = ( ( void * ) result0 ); + bigint_t ( size ) __attribute__ (( may_alias )) + *multiple = ( ( void * ) multiple0 ); + const bigint_t ( exponent_size ) __attribute__ (( may_alias )) + *exponent = ( ( const void * ) exponent0 ); + const unsigned int width = ( 8 * sizeof ( bigint_element_t ) ); + unsigned int bit = ( exponent_size * width ); + int toggle = 0; + int previous; + + /* We have two physical storage locations: Rres (the "result" + * register) and Rmul (the "multiple" register). + * + * On entry we have: + * + * Rres = g^0 = 1 (the identity element) + * Rmul = g (the generator) + * + * For calculation purposes, define two virtual registers R[0] + * and R[1], mapped to the underlying physical storage + * locations via a boolean toggle state "t": + * + * R[t] -> Rres + * R[~t] -> Rmul + * + * Define the initial toggle state to be t=0, so that we have: + * + * R[0] = g^0 = 1 (the identity element) + * R[1] = g (the generator) + * + * The Montgomery ladder then iterates over each bit "b" of + * the exponent "e" (from MSB down to LSB), computing: + * + * R[1] := R[0] * R[1], R[0] := R[0] * R[0] (if b=0) + * R[0] := R[1] * R[0], R[1] := R[1] * R[1] (if b=1) + * + * i.e. + * + * R[~b] := R[b] * R[~b], R[b] := R[b] * R[b] + * + * To avoid data-dependent memory access patterns, we + * implement this as: + * + * Rmul := Rres * Rmul + * Rres := Rres * Rres + * + * and update the toggle state "t" (via constant-time + * conditional swaps) as needed to ensure that R[b] always + * maps to Rres and R[~b] always maps to Rmul. + */ + while ( bit-- ) { + + /* Update toggle state t := b + * + * If the new toggle state is not equal to the old + * toggle state, then we must swap R[0] and R[1] (in + * constant time). + */ + previous = toggle; + toggle = bigint_bit_is_set ( exponent, bit ); + bigint_swap ( result, multiple, ( toggle ^ previous ) ); + + /* Calculate Rmul = R[~b] := R[b] * R[~b] = Rres * Rmul */ + op ( result->element, multiple->element, size, ctx, tmp ); + + /* Calculate Rres = R[b] := R[b] * R[b] = Rres * Rres */ + op ( result->element, result->element, size, ctx, tmp ); + } + + /* Reset toggle state to zero */ + bigint_swap ( result, multiple, toggle ); +} + +/** + * Perform modular multiplication as part of a Montgomery ladder + * + * @v operand Element 0 of first input operand (may overlap result) + * @v result Element 0 of second input operand and result + * @v size Number of elements in operands and result + * @v ctx Operation context (odd modulus, or NULL) + * @v tmp Temporary working space + */ +void bigint_mod_exp_ladder ( const bigint_element_t *multiplier0, + bigint_element_t *result0, unsigned int size, + const void *ctx, void *tmp ) { + const bigint_t ( size ) __attribute__ (( may_alias )) + *multiplier = ( ( const void * ) multiplier0 ); + bigint_t ( size ) __attribute__ (( may_alias )) + *result = ( ( void * ) result0 ); + const bigint_t ( size ) __attribute__ (( may_alias )) + *modulus = ( ( const void * ) ctx ); + bigint_t ( size * 2 ) __attribute__ (( may_alias )) + *product = ( ( void * ) tmp ); + + /* Multiply and reduce */ + bigint_multiply ( result, multiplier, product ); + if ( modulus ) { + bigint_montgomery ( modulus, product, result ); + } else { + bigint_shrink ( product, result ); + } +} + /** * Perform modular exponentiation of big integers * @@ -677,8 +821,7 @@ void bigint_mod_exp_raw ( const bigint_element_t *base0, bigint_element_t submask; unsigned int subsize; unsigned int scale; - unsigned int max; - unsigned int bit; + unsigned int i; /* Sanity check */ assert ( sizeof ( *temp ) == bigint_mod_exp_tmp_len ( modulus ) ); @@ -713,23 +856,8 @@ void bigint_mod_exp_raw ( const bigint_element_t *base0, &temp->stash ); /* Calculate x1 = base^exponent modulo N */ - max = bigint_max_set_bit ( exponent ); - for ( bit = 1 ; bit <= max ; bit++ ) { - - /* Square (and reduce) */ - bigint_multiply ( result, result, &temp->product.full ); - bigint_montgomery ( &temp->modulus, &temp->product.full, - result ); - - /* Multiply (and reduce) */ - bigint_multiply ( &temp->stash, result, &temp->product.full ); - bigint_montgomery ( &temp->modulus, &temp->product.full, - &temp->product.low ); - - /* Conditionally swap the multiplied result */ - bigint_swap ( result, &temp->product.low, - bigint_bit_is_set ( exponent, ( max - bit ) ) ); - } + bigint_ladder ( result, &temp->stash, exponent, bigint_mod_exp_ladder, + &temp->modulus, &temp->product ); /* Convert back out of Montgomery form */ bigint_grow ( result, &temp->product.full ); @@ -754,22 +882,14 @@ void bigint_mod_exp_raw ( const bigint_element_t *base0, /* Calculate x2 = base^exponent modulo 2^k */ bigint_init ( substash, one, sizeof ( one ) ); - for ( bit = 1 ; bit <= max ; bit++ ) { + bigint_copy ( subbase, submodulus ); + bigint_ladder ( substash, submodulus, exponent, + bigint_mod_exp_ladder, NULL, subproduct ); - /* Square (and reduce) */ - bigint_multiply ( substash, substash, - &subproduct->full ); - bigint_copy ( &subproduct->low, substash ); - - /* Multiply (and reduce) */ - bigint_multiply ( subbase, substash, - &subproduct->full ); - - /* Conditionally swap the multiplied result */ - bigint_swap ( substash, &subproduct->low, - bigint_bit_is_set ( exponent, - ( max - bit ) ) ); - } + /* Reconstruct N */ + bigint_copy ( modulus, &temp->modulus ); + for ( i = 0 ; i < scale ; i++ ) + bigint_shr ( &temp->modulus ); /* Calculate N^-1 modulo 2^k */ bigint_mod_invert ( submodulus, &subproduct->low ); diff --git a/src/include/ipxe/bigint.h b/src/include/ipxe/bigint.h index 1cc606b89..410d0fd90 100644 --- a/src/include/ipxe/bigint.h +++ b/src/include/ipxe/bigint.h @@ -304,6 +304,24 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); (result)->element, size ); \ } while ( 0 ) +/** + * Perform generalised exponentiation via a Montgomery ladder + * + * @v result Big integer result (initialised to identity element) + * @v multiple Big integer multiple (initialised to generator) + * @v exponent Big integer exponent + * @v op Montgomery ladder commutative operation + * @v ctx Operation context (if needed) + * @v tmp Temporary working space (if needed) + */ +#define bigint_ladder( result, multiple, exponent, op, ctx, tmp ) do { \ + unsigned int size = bigint_size (result); \ + unsigned int exponent_size = bigint_size (exponent); \ + bigint_ladder_raw ( (result)->element, (multiple)->element, \ + size, (exponent)->element, exponent_size, \ + (op), (ctx), (tmp) ); \ + } while ( 0 ) + /** * Perform modular exponentiation of big integers * @@ -335,6 +353,20 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include +/** + * A big integer Montgomery ladder commutative operation + * + * @v operand Element 0 of first input operand (may overlap result) + * @v result Element 0 of second input operand and result + * @v size Number of elements in operands and result + * @v ctx Operation context (if needed) + * @v tmp Temporary working space (if needed) + */ +typedef void ( bigint_ladder_op_t ) ( const bigint_element_t *operand0, + bigint_element_t *result0, + unsigned int size, const void *ctx, + void *tmp ); + /** * Test if bit is set in big integer * @@ -422,6 +454,14 @@ int bigint_montgomery_relaxed_raw ( const bigint_element_t *modulus0, void bigint_montgomery_raw ( const bigint_element_t *modulus0, bigint_element_t *value0, bigint_element_t *result0, unsigned int size ); +void bigint_ladder_raw ( bigint_element_t *result0, + bigint_element_t *multiple0, unsigned int size, + const bigint_element_t *exponent0, + unsigned int exponent_size, bigint_ladder_op_t *op, + const void *ctx, void *tmp ); +void bigint_mod_exp_ladder ( const bigint_element_t *multiplier0, + bigint_element_t *result0, unsigned int size, + const void *ctx, void *tmp ); void bigint_mod_exp_raw ( const bigint_element_t *base0, const bigint_element_t *modulus0, const bigint_element_t *exponent0, From be9ce490768dd4060fa4737ca5f50777ba6a15a8 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 22 Jan 2025 13:00:03 +0000 Subject: [PATCH 12/50] [crypto] Add support for Weierstrass elliptic curve point multiplication The NIST elliptic curves are Weierstrass curves and have the form y^2 = x^3 + ax + b with each curve defined by its field prime, the constants "a" and "b", and a generator base point. Implement a constant-time algorithm for point addition, based upon Algorithm 1 from "Complete addition formulas for prime order elliptic curves" (Joost Renes, Craig Costello, and Lejla Batina), and use this as a Montgomery ladder commutative operation to perform constant-time point multiplication. The code for point addition is implemented using a custom bytecode interpreter with 16-bit instructions, since this results in substantially smaller code than compiling the somewhat lengthy sequence of arithmetic operations directly. Values are calculated modulo small multiples of the field prime in order to allow for the use of relaxed Montgomery reduction. Signed-off-by: Michael Brown --- src/crypto/weierstrass.c | 877 +++++++++++++++++++++++++++++++++ src/include/ipxe/errfile.h | 1 + src/include/ipxe/weierstrass.h | 166 +++++++ 3 files changed, 1044 insertions(+) create mode 100644 src/crypto/weierstrass.c create mode 100644 src/include/ipxe/weierstrass.h diff --git a/src/crypto/weierstrass.c b/src/crypto/weierstrass.c new file mode 100644 index 000000000..be3909542 --- /dev/null +++ b/src/crypto/weierstrass.c @@ -0,0 +1,877 @@ +/* + * Copyright (C) 2024 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * Weierstrass elliptic curves + * + * The implementation is based upon Algorithm 1 from "Complete + * addition formulas for prime order elliptic curves" (Joost Renes, + * Craig Costello, and Lejla Batina), available from + * + * https://www.microsoft.com/en-us/research/wp-content/uploads/2016/06/complete-2.pdf + * + * The steps within the algorithm have been reordered and temporary + * variables shuffled to reduce stack usage, and calculations are + * carried out modulo small multiples of the field prime in order to + * elide reductions after intermediate addition and subtraction + * operations. + * + * The algorithm is encoded using a bytecode representation, since + * this substantially reduces the code size compared to direct + * implementation of the big integer operations. + */ + +#include +#include + +/** Big integer register names */ +enum weierstrass_register { + + /* + * Read-only registers + */ + + /* Curve constant "a" (for multiply), zero (for add/subtract) */ + WEIERSTRASS_a = 0, + /* Curve constant "3b" */ + WEIERSTRASS_3b, + /* Augend (x,y,z) co-ordinates */ + WEIERSTRASS_x1, + WEIERSTRASS_y1, + WEIERSTRASS_z1, + /* Addend (x,y,z) co-ordinates */ + WEIERSTRASS_x2, + WEIERSTRASS_y2, + WEIERSTRASS_z2, + + /* + * Read-write registers + */ + + /* Temporary working registers */ + WEIERSTRASS_Wt, + WEIERSTRASS_Wxy, + WEIERSTRASS_Wyz, + WEIERSTRASS_Wzx, + /* Low half of multiplication product */ + WEIERSTRASS_Wp, + /* Result (x,y,z) co-ordinates */ + WEIERSTRASS_x3, + WEIERSTRASS_y3, + WEIERSTRASS_z3, + + /* Number of registers */ + WEIERSTRASS_NUM_REGISTERS = 16 +}; + +/** Zero register (for add/subtract operations */ +#define WEIERSTRASS_zero WEIERSTRASS_a + +/** Construct big integer register index */ +#define WEIERSTRASS_REGISTER( name ) _C2 ( WEIERSTRASS_, name ) + +/** Bytecode operation codes */ +enum weierstrass_opcode { + /** Subtract big integers (and add nothing)*/ + WEIERSTRASS_OP_SUB_0N = 0, + /** Subtract big integers (and add 2N) */ + WEIERSTRASS_OP_SUB_2N = WEIERSTRASS_2N, + /** Subtract big integers (and add 4N) */ + WEIERSTRASS_OP_SUB_4N = WEIERSTRASS_4N, + /** Add big integers */ + WEIERSTRASS_OP_ADD, + /** Multiply big integers (and perform Montgomery reduction) */ + WEIERSTRASS_OP_MUL, +}; + +/** + * Define a bytecode operation + * + * @v opcode Operation code + * @v dest Destination big integer register name + * @v left Left source big integer register name + * @v right Right source big integer register name + */ +#define WEIERSTRASS_OP( opcode, dest, left, right ) \ + ( ( (opcode) << 12 ) | \ + ( WEIERSTRASS_REGISTER ( dest ) << 8 ) | \ + ( WEIERSTRASS_REGISTER ( left ) << 4 ) | \ + ( WEIERSTRASS_REGISTER ( right ) << 0 ) ) + +/** Extract bytecode operation code */ +#define WEIERSTRASS_OPCODE( op ) ( ( (op) >> 12 ) & 0xf ) + +/** Extract destination big integer register */ +#define WEIERSTRASS_DEST( op ) ( ( (op) >> 8 ) & 0xf ) + +/** Extract left source big integer register */ +#define WEIERSTRASS_LEFT( op ) ( ( (op) >> 4 ) & 0xf ) + +/** Extract right source big integer register */ +#define WEIERSTRASS_RIGHT( op ) ( ( (op) >> 0 ) & 0xf ) + +/** Define a three-argument addition operation */ +#define WEIERSTRASS_ADD3( dest, augend, addend ) \ + WEIERSTRASS_OP ( WEIERSTRASS_OP_ADD, dest, augend, addend ) + +/** Define a two-argument addition operation */ +#define WEIERSTRASS_ADD2( augend, addend ) \ + WEIERSTRASS_ADD3 ( augend, augend, addend ) + +/** Define a move operation */ +#define WEIERSTRASS_MOV( dest, source ) \ + WEIERSTRASS_ADD3( dest, source, zero ) + +/** Define a three-argument subtraction operation */ +#define WEIERSTRASS_SUB3( dest, minuend, subtrahend, multiple ) \ + WEIERSTRASS_OP ( _C2 ( WEIERSTRASS_OP_SUB_, multiple ), \ + dest, minuend, subtrahend ) + +/** Define a two-argument subtraction operation */ +#define WEIERSTRASS_SUB2( minuend, subtrahend, multiple ) \ + WEIERSTRASS_SUB3 ( minuend, minuend, subtrahend, multiple ) + +/** Define a stop operation */ +#define WEIERSTRASS_STOP WEIERSTRASS_SUB2 ( zero, zero, 0N ) + +/** Define a three-argument multiplication operation */ +#define WEIERSTRASS_MUL3( dest, multiplicand, multiplier ) \ + WEIERSTRASS_OP ( WEIERSTRASS_OP_MUL, dest, multiplicand, multiplier ) + +/** Define a two-argument multiplication operation */ +#define WEIERSTRASS_MUL2( multiplicand, multiplier ) \ + WEIERSTRASS_MUL3 ( multiplicand, multiplicand, multiplier ) + +/** + * Initialise curve + * + * @v curve Weierstrass curve + */ +static void weierstrass_init ( struct weierstrass_curve *curve ) { + unsigned int size = curve->size; + bigint_t ( size ) __attribute__ (( may_alias )) *prime = + ( ( void * ) curve->prime[0] ); + bigint_t ( size ) __attribute__ (( may_alias )) *fermat = + ( ( void * ) curve->fermat ); + bigint_t ( size ) __attribute__ (( may_alias )) *square = + ( ( void * ) curve->square ); + bigint_t ( size ) __attribute__ (( may_alias )) *one = + ( ( void * ) curve->one ); + bigint_t ( size ) __attribute__ (( may_alias )) *a = + ( ( void * ) curve->a ); + bigint_t ( size ) __attribute__ (( may_alias )) *b3 = + ( ( void * ) curve->b3 ); + bigint_t ( size ) __attribute__ (( may_alias )) *mont = + ( ( void * ) curve->mont[0] ); + bigint_t ( size ) __attribute__ (( may_alias )) *temp = + ( ( void * ) curve->prime[1] ); + bigint_t ( size * 2 ) __attribute__ (( may_alias )) *prime_double = + ( ( void * ) prime ); + bigint_t ( size * 2 ) __attribute__ (( may_alias )) *square_double = + ( ( void * ) square ); + bigint_t ( size * 2 ) __attribute__ (( may_alias )) *product = + ( ( void * ) temp ); + bigint_t ( size ) __attribute__ (( may_alias )) *two = + ( ( void * ) temp ); + static const uint8_t one_raw[] = { 1 }; + static const uint8_t two_raw[] = { 2 }; + size_t len = curve->len; + unsigned int i; + + /* Initialise field prime */ + bigint_init ( prime, curve->prime_raw, len ); + DBGC ( curve, "WEIERSTRASS %s N = %s\n", + curve->name, bigint_ntoa ( prime ) ); + + /* Calculate Montgomery constant R^2 mod N + * + * We rely on the fact that the subsequent big integers in the + * cache (i.e. the first prime multiple, and the constant "1") + * have not yet been written to, and so can be treated as + * being the (zero) upper halves required to hold the + * double-width value R^2. + */ + bigint_reduce_supremum ( prime_double, square_double ); + DBGC ( curve, "WEIERSTRASS %s R^2 = %s mod N\n", + curve->name, bigint_ntoa ( square ) ); + + /* Calculate constant "3b" */ + bigint_init ( b3, curve->b_raw, len ); + DBGC ( curve, "WEIERSTRASS %s b = %s\n", + curve->name, bigint_ntoa ( b3 ) ); + bigint_copy ( b3, a ); + bigint_add ( b3, b3 ); + bigint_add ( a, b3 ); + + /* Initialise "a" */ + bigint_init ( a, curve->a_raw, len ); + DBGC ( curve, "WEIERSTRASS %s a = %s\n", + curve->name, bigint_ntoa ( a ) ); + + /* Initialise "1" */ + bigint_init ( one, one_raw, sizeof ( one_raw ) ); + + /* Convert relevant constants to Montgomery form + * + * We rely on the fact that the prime multiples have not yet + * been calculated, and so can be used as a temporary buffer. + */ + for ( i = 0 ; i < WEIERSTRASS_NUM_MONT ; i++ ) { + static const char *names[] = { " ", " a", "3b" }; + bigint_multiply ( &mont[i], square, product ); + bigint_montgomery ( prime, product, &mont[i] ); + DBGC ( curve, "WEIERSTRASS %s %sR = %s mod N\n", + curve->name, names[i], bigint_ntoa ( &mont[i] ) ); + } + + /* Calculate constant "N-2" + * + * We rely on the fact that the prime multiples have not yet + * been calculated, and so can be used as a temporary buffer. + */ + bigint_copy ( prime, fermat ); + bigint_init ( two, two_raw, sizeof ( two_raw ) ); + bigint_subtract ( two, fermat ); + DBGC ( curve, "WEIERSTRASS %s N-2 = %s\n", + curve->name, bigint_ntoa ( fermat ) ); + + /* Calculate multiples of field prime */ + for ( i = 1 ; i < WEIERSTRASS_NUM_MULTIPLES ; i++ ) { + bigint_copy ( &prime[ i - 1 ], &prime[i] ); + bigint_add ( &prime[i], &prime[i] ); + DBGC ( curve, "WEIERSTRASS %s %dN = %s\n", + curve->name, ( 1 << i ), bigint_ntoa ( &prime[i] ) ); + } +} + +/** + * Execute bytecode instruction + * + * @v curve Weierstrass curve + * @v regs Registers + * @v size Big integer size + * @v op Operation + */ +static void weierstrass_exec ( const struct weierstrass_curve *curve, + void **regs, unsigned int size, + unsigned int op ) { + const bigint_t ( size ) __attribute__ (( may_alias )) + *prime = ( ( const void * ) curve->prime[0] ); + bigint_t ( size * 2 ) __attribute__ (( may_alias )) + *product = regs[WEIERSTRASS_Wp]; + bigint_t ( size ) __attribute__ (( may_alias )) *dest; + const bigint_t ( size ) __attribute__ (( may_alias )) *left; + const bigint_t ( size ) __attribute__ (( may_alias )) *right; + const bigint_t ( size ) __attribute__ (( may_alias )) *addend; + const bigint_t ( size ) __attribute__ (( may_alias )) *subtrahend; + unsigned int op_code; + unsigned int op_dest; + unsigned int op_left; + unsigned int op_right; + + /* Decode instruction */ + op_code = WEIERSTRASS_OPCODE ( op ); + op_dest = WEIERSTRASS_DEST ( op ); + op_left = WEIERSTRASS_LEFT ( op ); + op_right = WEIERSTRASS_RIGHT ( op ); + dest = regs[op_dest]; + left = regs[op_left]; + right = regs[op_right]; + + /* Check destination is a writable register */ + assert ( op_dest >= WEIERSTRASS_Wt ); + + /* Handle multiplications */ + if ( op_code == WEIERSTRASS_OP_MUL ) { + assert ( op_left != WEIERSTRASS_Wp ); + assert ( op_right != WEIERSTRASS_Wp ); + bigint_multiply ( left, right, product ); + bigint_montgomery_relaxed ( prime, product, dest ); + DBGCP ( curve, "WEIERSTRASS %s R%d := R%d x R%d = %s\n", + curve->name, op_dest, op_left, op_right, + bigint_ntoa ( dest ) ); + return; + } + + /* Copy left source, if required */ + if ( op_dest != op_left ) + bigint_copy ( left, dest ); + + /* Do nothing more if addend/subtrahend is zero */ + if ( ! op_right ) { + DBGCP ( curve, "WEIERSTRASS %s R%d := R%d = %s\n", + curve->name, op_dest, op_left, bigint_ntoa ( dest ) ); + return; + } + + /* Determine addend and subtrahend */ + addend = NULL; + subtrahend = NULL; + if ( op_code == WEIERSTRASS_OP_ADD ) { + DBGCP ( curve, "WEIERSTRASS %s R%d := R%d + R%d = ", + curve->name, op_dest, op_left, op_right ); + addend = ( ( const void * ) right ); + } else { + subtrahend = ( ( const void * ) right ); + if ( op_code > WEIERSTRASS_OP_SUB_0N ) { + DBGCP ( curve, "WEIERSTRASS %s R%d := R%d - R%d + " + "%dN = ", curve->name, op_dest, op_left, + op_right, ( 1 << op_code ) ); + addend = ( ( const void * ) curve->prime[op_code] ); + } else { + DBGCP ( curve, "WEIERSTRASS %s R%d := R%d - R%d = ", + curve->name, op_dest, op_left, op_right ); + } + } + + /* Perform addition and subtraction */ + if ( addend ) + bigint_add ( addend, dest ); + if ( subtrahend ) + bigint_subtract ( subtrahend, dest ); + DBGCP ( curve, "%s\n", bigint_ntoa ( dest ) ); +} + +/** + * Add points on curve + * + * @v curve Weierstrass curve + * @v augend0 Element 0 of point (x1,y1,z1) to be added + * @v addend0 Element 0 of point (x2,y2,z2) to be added + * @v result0 Element 0 of point (x3,y3,z3) to hold result + * + * Points are represented in projective coordinates, with all values + * in Montgomery form and in the range [0,4N) where N is the field + * prime. + * + * The augend may have the same value as the addend (i.e. this routine + * may be used to perform point doubling as well as point addition), + * and either or both may be the point at infinity. + * + * The result may overlap either input, since the inputs are fully + * consumed before the result is written. + */ +static void weierstrass_add_raw ( const struct weierstrass_curve *curve, + const bigint_element_t *augend0, + const bigint_element_t *addend0, + bigint_element_t *result0 ) { + unsigned int size = curve->size; + const bigint_t ( size ) __attribute__ (( may_alias )) + *prime = ( ( const void * ) curve->prime[0] ); + const bigint_t ( size ) __attribute__ (( may_alias )) + *a = ( ( const void * ) curve->a ); + const bigint_t ( size ) __attribute__ (( may_alias )) + *b3 = ( ( const void * ) curve->b3 ); + const weierstrass_t ( size ) __attribute__ (( may_alias )) + *augend = ( ( const void * ) augend0 ); + const weierstrass_t ( size ) __attribute__ (( may_alias )) + *addend = ( ( const void * ) addend0 ); + weierstrass_t ( size ) __attribute__ (( may_alias )) + *result = ( ( void * ) result0 ); + struct { + bigint_t ( size ) Wt; + bigint_t ( size ) Wxy; + bigint_t ( size ) Wyz; + bigint_t ( size ) Wzx; + bigint_t ( size * 2 ) Wp; + } temp; + void *regs[WEIERSTRASS_NUM_REGISTERS]; + unsigned int schedule; + const uint16_t *op; + unsigned int i; + + /* On entry, we assume that x1, x2, y1, y2, z1, z2 are all in + * the range [0,4N). Additions will extend the range. + * Subtractions will extend the range (and require an addition + * of a suitable multiple of the modulus to ensure that the + * result is a positive value). Relaxed Montgomery + * multiplications will reduce the range to [0,2N). The + * outputs x3, y3, z3 will be in the range [0,4N) and + * therefore usable as subsequent inputs. + */ + static const uint16_t ops[] = { + /* [Wxy] Qxy = (x1+y1)*(x2+y2) (mod 2N) */ + WEIERSTRASS_ADD3 ( Wt, x1, y1 ), + WEIERSTRASS_ADD3 ( Wxy, x2, y2 ), + WEIERSTRASS_MUL2 ( Wxy, Wt ), + /* [Wyz] Qyz = (y1+z1)*(y2+z2) (mod 2N) */ + WEIERSTRASS_ADD3 ( Wt, y1, z1 ), + WEIERSTRASS_ADD3 ( Wyz, y2, z2 ), + WEIERSTRASS_MUL2 ( Wyz, Wt ), + /* [Wzx] Qzx = (z1+x1)*(z2+x2) (mod 2N) */ + WEIERSTRASS_ADD3 ( Wt, z1, x1 ), + WEIERSTRASS_ADD3 ( Wzx, z2, x2 ), + WEIERSTRASS_MUL2 ( Wzx, Wt ), + /* [x3] Px = x1*x2 (mod 2N) */ + WEIERSTRASS_MUL3 ( x3, x1, x2 ), + /* [y3] Py = y1*y2 (mod 2N) */ + WEIERSTRASS_MUL3 ( y3, y1, y2 ), + /* [z3] Pz = z1*z2 (mod 2N) */ + WEIERSTRASS_MUL3 ( z3, z1, z2 ), + /* [Wxy] Rxy = Qxy - Px - Py (mod 6N) + * = (x1+y1)*(x2+y2) - x1*x2 - y1*y2 (mod 6N) + * = x1*y2 + x2*y1 (mod 6N) + */ + WEIERSTRASS_SUB2 ( Wxy, x3, 0N ), + WEIERSTRASS_SUB2 ( Wxy, y3, 4N ), + /* [Wyz] Ryz = Qyz - Py - Pz (mod 6N) + * = (y1+z1)*(y2+z2) - y1*y2 - z1*z2 (mod 6N) + * = y1*z2 + y2*z1 (mod 6N) + */ + WEIERSTRASS_SUB2 ( Wyz, y3, 0N ), + WEIERSTRASS_SUB2 ( Wyz, z3, 4N ), + /* [Wzx] Rzx = Qzx - Pz - Px (mod 6N) + * = (z1+x1)*(z2+x2) - z1*z2 - x1*x2 (mod 6N) + * = x1*z2 + x2*z1 (mod 6N) + */ + WEIERSTRASS_SUB2 ( Wzx, z3, 0N ), + WEIERSTRASS_SUB2 ( Wzx, x3, 4N ), + /* [Wt] aRzx = a * Rzx (mod 2N) + * = a * (x1*z2 + x2*z1) (mod 2N) + */ + WEIERSTRASS_MUL3 ( Wt, a, Wzx ), + /* [Wp] 3bPz = 3b * Pz (mod 2N) + * = 3b*z1*z2 (mod 2N) + */ + WEIERSTRASS_MUL3 ( Wp, 3b, z3 ), + /* [Wp] Sy = aRzx + 3bPz (mod 4N) + * = a*(x1*z2 + x2*z1) + 3b*z1*z2 (mod 4N) + */ + WEIERSTRASS_ADD2 ( Wp, Wt ), + /* [Wt] Syz = Py + Sy (mod 6N) + * = y1*y2 + a*(x1*z2 + x2*z1) + 3b*z1*z2 (mod 6N) + */ + WEIERSTRASS_ADD3 ( Wt, y3, Wp ), + /* [y3] Sxy = Py - Sy (mod 6N) + * = y1*y2 - a*(x1*z2 + x2*z1) - 3b*z1*z2 (mod 6N) + */ + WEIERSTRASS_SUB2 ( y3, Wp, 4N ), + /* [z3] aPz = a * Pz (mod 2N) + * = a * z1*z2 (mod 2N) + */ + WEIERSTRASS_MUL2 ( z3, a ), + /* [Wzx] 3bRzx = 3b * Rzx (mod 2N) + * = 3b * (x1*z2 + x2*z1) (mod 2N) + */ + WEIERSTRASS_MUL2 ( Wzx, 3b ), + /* [x3] aPzx' = Px - aPz (mod 4N) + * = x1*x2 - a*z1*z2 (mod 4N) + */ + WEIERSTRASS_SUB2 ( x3, z3, 2N ), + /* [Wp] Szx = a * aPzx' (mod 2N) + * = a * (x1*x2 - a*z1*z2) (mod 2N) + * = a*x1*x2 - (a^2)*z1*z2 (mod 2N) + */ + WEIERSTRASS_MUL3 ( Wp, a, x3 ), + /* [x3] Px = aPzx' + aPz (mod 6N) + * = x1*x2 - a*z1*z2 + a*z1*z2 (mod 6N) + * = x1*x2 (mod 6N) + */ + WEIERSTRASS_ADD2 ( x3, z3 ), + /* [Wzx] Tzx = 3bRzx + Szx (mod 4N) + * = a*x1*x2 + 3b*(x1*z2 + x2*z1) - + * (a^2)*z1*z2 (mod 4N) + */ + WEIERSTRASS_ADD2 ( Wzx, Wp ), + /* [z3] aPzx = Px + aPz (mod 8N) + * = x1*x2 + a*z1*z2 (mod 8N) + */ + WEIERSTRASS_ADD2 ( z3, x3 ), + /* [x3] 2Px = Px + Px (mod 12N) + * = 2*x1*x2 (mod 12N) + */ + WEIERSTRASS_ADD2 ( x3, x3 ), + /* [x3] Tyz = 2Px + aPzx (mod 20N) + * = 2*x1*x2 + x1*x2 + a*z1*z2 (mod 20N) + * = 3*x1*x2 + a*z1*z2 (mod 20N) + */ + WEIERSTRASS_ADD2 ( x3, z3 ), + /* [z3] Syz = Syz (mod 6N) + * = y1*y2 + a*(x1*z2 + x2*z1) + 3b*z1*z2 (mod 6N) + */ + WEIERSTRASS_MOV ( z3, Wt ), + /* [Wt] Tyz = Tyz (mod 20N) + * = 3*x1*x2 + a*z1*z2 (mod 20N) + */ + WEIERSTRASS_MOV ( Wt, x3 ), + /* [x3] Ux = Rxy * Sxy (mod 2N) + * = (x1*y2 + x2*y1) * + * (y1*y2 - a*(x1*z2 + x2*z1) - 3b*z1*z2) (mod 2N) + */ + WEIERSTRASS_MUL3 ( x3, Wxy, y3 ), + /* [y3] Uy = Syz * Sxy (mod 2N) + * = (y1*y2 + a*(x1*z2 + x2*z1) + 3b*z1*z2) * + * (y1*y2 - a*(x1*z2 + x2*z1) - 3b*z1*z2) (mod 2N) + */ + WEIERSTRASS_MUL2 ( y3, z3 ), + /* [z3] Uz = Ryz * Syz (mod 2N) + * = (y1*z2 + y2*z1) * + * (y1*y2 + a*(x1*z2 + x2*z1) + 3b*z1*z2) (mod 2N) + */ + WEIERSTRASS_MUL2 ( z3, Wyz ), + /* [Wp] Vx = Ryz * Tzx (mod 2N) + * = (y1*z2 + y2*z1) * + * (a*x1*x2 + 3b*(x1*z2 + x2*z1) - (a^2)*z1*z2) + * (mod 2N) + */ + WEIERSTRASS_MUL3 ( Wp, Wyz, Wzx ), + /* [x3] x3 = Ux - Vx (mod 4N) + * = ((x1*y2 + x2*y1) * + * (y1*y2 - a*(x1*z2 + x2*z1) - 3b*z1*z2)) - + * ((y1*z2 + y2*z1) * + * (a*x1*x2 + 3b*(x1*z2 + x2*z1) - (a^2)*z1*z2)) + * (mod 4N) + */ + WEIERSTRASS_SUB2 ( x3, Wp, 2N ), + /* [Wp] Vy = Tyz * Tzx (mod 2N) + * = (3*x1*x2 + a*z1*z2) * + * (a*x1*x2 + 3b*(x1*z2 + x2*z1) - (a^2)*z1*z2) + * (mod 2N) + */ + WEIERSTRASS_MUL3 ( Wp, Wt, Wzx ), + /* [y3] y3 = Vy + Uy (mod 4N) + * = ((3*x1*x2 + a*z1*z2) * + * (a*x1*x2 + 3b*(x1*z2 + x2*z1) - (a^2)*z1*z2)) + + * ((y1*y2 + a*(x1*z2 + x2*z1) + 3b*z1*z2) * + * (y1*y2 - a*(x1*z2 + x2*z1) - 3b*z1*z2)) + * (mod 4N) + */ + WEIERSTRASS_ADD2 ( y3, Wp ), + /* [Wp] Vz = Rxy * Tyz (mod 2N) + * = (x1*y2 + x2*y1) * (3*x1*x2 + a*z1*z2) (mod 2N) + */ + WEIERSTRASS_MUL3 ( Wp, Wxy, Wt ), + /* [z3] z3 = Uz + Vz (mod 4N) + * = ((y1*z2 + y2*z1) * + * (y1*y2 + a*(x1*z2 + x2*z1) + 3b*z1*z2)) + + * ((x1*y2 + x2*y1) * (3*x1*x2 + a*z1*z2)) + * (mod 4N) + */ + WEIERSTRASS_ADD2 ( z3, Wp ), + /* Stop */ + WEIERSTRASS_STOP + }; + + /* Initialise register list */ + regs[WEIERSTRASS_a] = ( ( void * ) a ); + regs[WEIERSTRASS_3b] = ( ( void * ) b3 ); + regs[WEIERSTRASS_x1] = ( ( void * ) &augend->x ); + regs[WEIERSTRASS_x2] = ( ( void * ) &addend->x ); + regs[WEIERSTRASS_x3] = ( ( void * ) &result->x ); + regs[WEIERSTRASS_Wt] = &temp; + schedule = ( ( ( 1 << WEIERSTRASS_NUM_REGISTERS ) - 1 ) + - ( 1 << WEIERSTRASS_a ) + - ( 1 << WEIERSTRASS_3b ) + - ( 1 << WEIERSTRASS_x1 ) + - ( 1 << WEIERSTRASS_x2 ) + - ( 1 << WEIERSTRASS_x3 ) + - ( 1 << WEIERSTRASS_Wt ) ); + for ( i = 0 ; schedule ; i++, schedule >>= 1 ) { + if ( schedule & 1 ) + regs[i] = ( regs[ i - 1 ] + sizeof ( *prime ) ); + } + DBGC2 ( curve, "WEIERSTRASS %s augend (%s,", + curve->name, bigint_ntoa ( &augend->x ) ); + DBGC2 ( curve, "%s,", bigint_ntoa ( &augend->y ) ); + DBGC2 ( curve, "%s)\n", bigint_ntoa ( &augend->z ) ); + DBGC2 ( curve, "WEIERSTRASS %s addend (%s,", + curve->name, bigint_ntoa ( &addend->x ) ); + DBGC2 ( curve, "%s,", bigint_ntoa ( &addend->y ) ); + DBGC2 ( curve, "%s)\n", bigint_ntoa ( &addend->z ) ); + + /* Sanity checks */ + assert ( regs[WEIERSTRASS_a] == a ); + assert ( regs[WEIERSTRASS_3b] == b3 ); + assert ( regs[WEIERSTRASS_x1] == &augend->x ); + assert ( regs[WEIERSTRASS_y1] == &augend->y ); + assert ( regs[WEIERSTRASS_z1] == &augend->z ); + assert ( regs[WEIERSTRASS_x2] == &addend->x ); + assert ( regs[WEIERSTRASS_y2] == &addend->y ); + assert ( regs[WEIERSTRASS_z2] == &addend->z ); + assert ( regs[WEIERSTRASS_x3] == &result->x ); + assert ( regs[WEIERSTRASS_y3] == &result->y ); + assert ( regs[WEIERSTRASS_z3] == &result->z ); + assert ( regs[WEIERSTRASS_Wt] == &temp.Wt ); + assert ( regs[WEIERSTRASS_Wxy] == &temp.Wxy ); + assert ( regs[WEIERSTRASS_Wyz] == &temp.Wyz ); + assert ( regs[WEIERSTRASS_Wzx] == &temp.Wzx ); + assert ( regs[WEIERSTRASS_Wp] == &temp.Wp ); + + /* Execute bytecode instruction sequence */ + for ( op = ops ; *op != WEIERSTRASS_STOP ; op++ ) + weierstrass_exec ( curve, regs, size, *op ); + DBGC2 ( curve, "WEIERSTRASS %s result (%s,", + curve->name, bigint_ntoa ( &result->x ) ); + DBGC2 ( curve, "%s,", bigint_ntoa ( &result->y ) ); + DBGC2 ( curve, "%s)\n", bigint_ntoa ( &result->z ) ); +} + +/** + * Add points on curve + * + * @v curve Weierstrass curve + * @v augend Point (x1,y1,z1) to be added + * @v addend Point (x2,y2,z2) to be added + * @v result0 Point (x3,y3,z3) to hold result + */ +#define weierstrass_add( curve, augend, addend, result ) do { \ + weierstrass_add_raw ( (curve), (augend)->all.element, \ + (addend)->all.element, \ + (result)->all.element ); \ + } while ( 0 ) + +/** + * Add points on curve as part of a Montgomery ladder + * + * @v operand Element 0 of first input operand (may overlap result) + * @v result Element 0 of second input operand and result + * @v size Number of elements in operands and result + * @v ctx Operation context + * @v tmp Temporary working space (not used) + */ +static void weierstrass_add_ladder ( const bigint_element_t *operand0, + bigint_element_t *result0, + unsigned int size, const void *ctx, + void *tmp __unused ) { + const struct weierstrass_curve *curve = ctx; + const weierstrass_t ( curve->size ) __attribute__ (( may_alias )) + *operand = ( ( const void * ) operand0 ); + weierstrass_t ( curve->size ) __attribute__ (( may_alias )) + *result = ( ( void * ) result0 ); + + /* Add curve points */ + assert ( size == bigint_size ( &operand->all ) ); + assert ( size == bigint_size ( &result->all ) ); + weierstrass_add ( curve, operand, result, result ); +} + +/** + * Verify point is on curve + * + * @v curve Weierstrass curve + * @v point0 Element 0 of point (x,y,z) to be verified + * @ret rc Return status code + * + * As with point addition, points are represented in projective + * coordinates, with all values in Montgomery form and in the range + * [0,4N) where N is the field prime. + */ +static int weierstrass_verify_raw ( const struct weierstrass_curve *curve, + const bigint_element_t *point0 ) { + unsigned int size = curve->size; + const bigint_t ( size ) __attribute__ (( may_alias )) + *prime = ( ( const void * ) curve->prime[0] ); + const bigint_t ( size ) __attribute__ (( may_alias )) + *a = ( ( const void * ) curve->a ); + const bigint_t ( size ) __attribute__ (( may_alias )) + *b3 = ( ( const void * ) curve->b3 ); + const weierstrass_t ( size ) __attribute__ (( may_alias )) + *point = ( ( const void * ) point0 ); + struct { + bigint_t ( size ) Wt; + bigint_t ( size * 2 ) Wp; + } temp; + void *regs[WEIERSTRASS_NUM_REGISTERS]; + const uint16_t *op; + + /* Calculate 3*(x^3 + a*x + b - y^2) */ + static const uint16_t ops[] = { + /* [Wt] Tx = x^2 (mod 2N) */ + WEIERSTRASS_MUL3 ( Wt, x1, x1 ), + /* [Wt] Txa = Tx + a (mod 3N) + * = x^2 + a (mod 3N) + */ + WEIERSTRASS_MOV ( Wp, a ), + WEIERSTRASS_ADD2 ( Wt, Wp ), + /* [Wt] Txax = Txa * x (mod 2N) + * = (x^2 + a)*x (mod 2N) + * = x^3 + a*x (mod 2N) + */ + WEIERSTRASS_MUL2 ( Wt, x1 ), + /* [Wp] Ty = y^2 (mod 2N) */ + WEIERSTRASS_MUL3 ( Wp, y1, y1 ), + /* [Wt] Txaxy = Txax - Ty (mod 4N) + * = x^3 + a*x - y^2 (mod 4N) + */ + WEIERSTRASS_SUB2 ( Wt, Wp, 2N ), + /* [Wp] 2Txaxy = Txaxy + Txaxy (mod 8N) + * = 2*(x^3 + a*x - y^2) (mod 8N) + */ + WEIERSTRASS_ADD3 ( Wp, Wt, Wt ), + /* [Wt] 3Txaxy = 2Txaxy + Txaxy (mod 12N) + * = 3*(x^3 + a*x - y^2) (mod 12N) + */ + WEIERSTRASS_ADD2 ( Wt, Wp ), + /* [Wt] 3Txaxyb = 3Txaxy + 3b (mod 13N) + * = 3*(x^3 + a*x - y^2) + 3b (mod 13N) + * = 3*(x^3 + a*x + b - y^2) (mod 13N) + */ + WEIERSTRASS_ADD2 ( Wt, 3b ), + /* Stop */ + WEIERSTRASS_STOP + }; + + /* Initialise register list */ + regs[WEIERSTRASS_a] = ( ( void * ) a ); + regs[WEIERSTRASS_3b] = ( ( void * ) b3 ); + regs[WEIERSTRASS_x1] = ( ( void * ) &point->x ); + regs[WEIERSTRASS_y1] = ( ( void * ) &point->y ); + regs[WEIERSTRASS_Wt] = &temp.Wt; + regs[WEIERSTRASS_Wp] = &temp.Wp; + + /* Execute bytecode instruction sequence */ + for ( op = ops ; *op != WEIERSTRASS_STOP ; op++ ) + weierstrass_exec ( curve, regs, size, *op ); + + /* Check that result is zero (modulo the field prime) */ + bigint_grow ( &temp.Wt, &temp.Wp ); + bigint_montgomery ( prime, &temp.Wp, &temp.Wt ); + if ( ! bigint_is_zero ( &temp.Wt ) ) { + DBGC ( curve, "WEIERSTRASS %s base point is not on curve\n", + curve->name ); + return -EINVAL; + } + + return 0; +} + +/** + * Verify point is on curve + * + * @v curve Weierstrass curve + * @v point Point (x,y,z) to be verified + * @ret rc Return status code + */ +#define weierstrass_verify( curve, point ) ( { \ + weierstrass_verify_raw ( (curve), (point)->all.element ); \ + } ) + +/** + * Multiply curve point by scalar + * + * @v curve Weierstrass curve + * @v base Base point (or NULL to use generator) + * @v scalar Scalar multiple + * @v result Result point to fill in + * @ret rc Return status code + */ +int weierstrass_multiply ( struct weierstrass_curve *curve, const void *base, + const void *scalar, void *result ) { + unsigned int size = curve->size; + size_t len = curve->len; + const bigint_t ( size ) __attribute__ (( may_alias )) *prime = + ( ( const void * ) curve->prime[0] ); + const bigint_t ( size ) __attribute__ (( may_alias )) *prime2 = + ( ( const void * ) curve->prime[WEIERSTRASS_2N] ); + const bigint_t ( size ) __attribute__ (( may_alias )) *fermat = + ( ( const void * ) curve->fermat ); + const bigint_t ( size ) __attribute__ (( may_alias )) *square = + ( ( const void * ) curve->square ); + const bigint_t ( size ) __attribute__ (( may_alias )) *one = + ( ( const void * ) curve->one ); + struct { + union { + weierstrass_t ( size ) result; + bigint_t ( size * 2 ) product_in; + }; + union { + weierstrass_t ( size ) multiple; + bigint_t ( size * 2 ) product_out; + }; + bigint_t ( bigint_required_size ( len ) ) scalar; + } temp; + size_t offset; + unsigned int i; + int rc; + + /* Initialise curve, if not already done + * + * The least significant element of the field prime must be + * odd, and so the least significant element of the + * (initialised) first multiple of the field prime must be + * non-zero. + */ + if ( ! prime2->element[0] ) + weierstrass_init ( curve ); + + /* Use generator if applicable */ + if ( ! base ) + base = curve->base; + + /* Convert input to projective coordinates in Montgomery form */ + DBGC ( curve, "WEIERSTRASS %s base (", curve->name ); + for ( i = 0, offset = 0 ; i < WEIERSTRASS_AXES ; i++, offset += len ) { + bigint_init ( &temp.multiple.axis[i], ( base + offset ), len ); + DBGC ( curve, "%s%s", ( i ? "," : "" ), + bigint_ntoa ( &temp.multiple.axis[i] ) ); + bigint_multiply ( &temp.multiple.axis[i], square, + &temp.product_in ); + bigint_montgomery_relaxed ( prime, &temp.product_in, + &temp.multiple.axis[i] ); + } + bigint_copy ( one, &temp.multiple.z ); + DBGC ( curve, ")\n" ); + + /* Verify point is on curve */ + if ( ( rc = weierstrass_verify ( curve, &temp.multiple ) ) != 0 ) + return rc; + + /* Construct identity element (the point at infinity) */ + memset ( &temp.result, 0, sizeof ( temp.result ) ); + bigint_copy ( one, &temp.result.y ); + + /* Initialise scalar */ + bigint_init ( &temp.scalar, scalar, len ); + DBGC ( curve, "WEIERSTRASS %s scalar %s\n", + curve->name, bigint_ntoa ( &temp.scalar ) ); + + /* Perform multiplication via Montgomery ladder */ + bigint_ladder ( &temp.result.all, &temp.multiple.all, &temp.scalar, + weierstrass_add_ladder, curve, NULL ); + + /* Invert result Z co-ordinate (via Fermat's little theorem) */ + bigint_copy ( one, &temp.multiple.z ); + bigint_ladder ( &temp.multiple.z, &temp.result.z, fermat, + bigint_mod_exp_ladder, prime, &temp.product_out ); + + /* Convert result back to affine co-ordinates */ + DBGC ( curve, "WEIERSTRASS %s result (", curve->name ); + for ( i = 0, offset = 0 ; i < WEIERSTRASS_AXES ; i++, offset += len ) { + bigint_multiply ( &temp.result.axis[i], &temp.multiple.z, + &temp.product_out ); + bigint_montgomery_relaxed ( prime, &temp.product_out, + &temp.result.axis[i] ); + bigint_grow ( &temp.result.axis[i], &temp.product_out ); + bigint_montgomery ( prime, &temp.product_out, + &temp.result.axis[i] ); + DBGC ( curve, "%s%s", ( i ? "," : "" ), + bigint_ntoa ( &temp.result.axis[i] ) ); + bigint_done ( &temp.result.axis[i], ( result + offset ), len ); + } + DBGC ( curve, ")\n" ); + + return 0; +} diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 48b35c537..b826a4a6f 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -426,6 +426,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_form_ui ( ERRFILE_OTHER | 0x00630000 ) #define ERRFILE_usb_cmd ( ERRFILE_OTHER | 0x00640000 ) #define ERRFILE_usb_settings ( ERRFILE_OTHER | 0x00650000 ) +#define ERRFILE_weierstrass ( ERRFILE_OTHER | 0x00660000 ) /** @} */ diff --git a/src/include/ipxe/weierstrass.h b/src/include/ipxe/weierstrass.h new file mode 100644 index 000000000..fb09fa6fa --- /dev/null +++ b/src/include/ipxe/weierstrass.h @@ -0,0 +1,166 @@ +#ifndef _IPXE_WEIERSTRASS_H +#define _IPXE_WEIERSTRASS_H + +/** @file + * + * Weierstrass elliptic curves + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include + +/** Number of axes in Weierstrass curve point representation */ +#define WEIERSTRASS_AXES 2 + +/** + * Maximum multiple of field prime encountered during calculations + * + * Calculations are performed using values modulo a small multiple of + * the field prime, rather than modulo the field prime itself. This + * allows explicit reductions after additions, subtractions, and + * relaxed Montgomery multiplications to be omitted entirely, provided + * that we keep careful track of the field prime multiple for each + * intermediate value. + * + * Relaxed Montgomery multiplication will produce a result in the + * range t < (1+m/k)N, where m is this maximum multiple of the field + * prime, and k is the constant in R > kN representing the leading + * zero padding in the big integer representation of the field prime. + * We choose to set k=m so that multiplications will always produce a + * result in the range t < 2N. + * + * This is expressed as the base-two logarithm of the multiple + * (rounded up), to simplify compile-time calculations. + */ +#define WEIERSTRASS_MAX_MULTIPLE_LOG2 5 /* maximum reached is mod 20N */ + +/** + * Determine number of elements in scalar values for a Weierstrass curve + * + * @v len Length of field prime, in bytes + * @ret size Number of elements + */ +#define weierstrass_size( len ) \ + bigint_required_size ( (len) + \ + ( ( WEIERSTRASS_MAX_MULTIPLE_LOG2 + 7 ) \ + / 8 ) ) + +/** + * Define a Weierstrass projective co-ordinate type + * + * @v size Number of elements in scalar values + * @ret weierstrass_t Projective co-ordinate type + */ +#define weierstrass_t( size ) \ + union { \ + bigint_t ( size ) axis[3]; \ + struct { \ + bigint_t ( size ) x; \ + bigint_t ( size ) y; \ + bigint_t ( size ) z; \ + }; \ + bigint_t ( size * 3 ) all; \ + } + +/** Indexes for stored multiples of the field prime */ +enum weierstrass_multiple { + WEIERSTRASS_N = 0, + WEIERSTRASS_2N, + WEIERSTRASS_4N, + WEIERSTRASS_NUM_MULTIPLES +}; + +/** Number of cached in Montgomery form for each Weierstrass curve */ +#define WEIERSTRASS_NUM_MONT 3 + +/** Number of cached big integers for each Weierstrass curve */ +#define WEIERSTRASS_NUM_CACHED \ + ( WEIERSTRASS_NUM_MULTIPLES + \ + 1 /* fermat */ + 1 /* mont */ + \ + WEIERSTRASS_NUM_MONT ) + +/** + * A Weierstrass elliptic curve + * + * This is an elliptic curve y^2 = x^3 + ax + b + */ +struct weierstrass_curve { + /** Number of elements in scalar values */ + const unsigned int size; + /** Curve name */ + const char *name; + /** Length of raw scalar values */ + size_t len; + /** Field prime */ + const uint8_t *prime_raw; + /** Constant "a" */ + const uint8_t *a_raw; + /** Constant "b" */ + const uint8_t *b_raw; + /** Base point */ + const uint8_t *base; + + /** Cached field prime "N" (and multiples thereof) */ + bigint_element_t *prime[WEIERSTRASS_NUM_CACHED]; + /** Cached constant "N-2" (for Fermat's little theorem) */ + bigint_element_t *fermat; + /** Cached Montgomery constant (R^2 mod N) */ + bigint_element_t *square; + /** Cached constants in Montgomery form */ + union { + struct { + /** Cached constant "1", in Montgomery form */ + bigint_element_t *one; + /** Cached constant "a", in Montgomery form */ + bigint_element_t *a; + /** Cached constant "3b", in Montgomery form */ + bigint_element_t *b3; + }; + bigint_element_t *mont[WEIERSTRASS_NUM_MONT]; + }; +}; + +extern int weierstrass_multiply ( struct weierstrass_curve *curve, + const void *base, const void *scalar, + void *result ); + +/** Define a Weierstrass curve */ +#define WEIERSTRASS_CURVE( _name, _curve, _len, _prime, _a, _b, _base ) \ + static bigint_t ( weierstrass_size(_len) ) \ + _name ## _cache[WEIERSTRASS_NUM_CACHED]; \ + static struct weierstrass_curve _name ## _weierstrass = { \ + .size = weierstrass_size(_len), \ + .name = #_name, \ + .len = (_len), \ + .prime_raw = (_prime), \ + .a_raw = (_a), \ + .b_raw = (_b), \ + .base = (_base), \ + .prime = { \ + (_name ## _cache)[0].element, \ + (_name ## _cache)[1].element, \ + (_name ## _cache)[2].element, \ + }, \ + .fermat = (_name ## _cache)[3].element, \ + .square = (_name ## _cache)[4].element, \ + .one = (_name ## _cache)[5].element, \ + .a = (_name ## _cache)[6].element, \ + .b3 = (_name ## _cache)[7].element, \ + }; \ + static int _name ## _multiply ( const void *base, \ + const void *scalar, \ + void *result ) { \ + return weierstrass_multiply ( &_name ## _weierstrass, \ + base, scalar, result ); \ + } \ + struct elliptic_curve _curve = { \ + .name = #_name, \ + .pointsize = ( WEIERSTRASS_AXES * (_len) ), \ + .keysize = (_len), \ + .multiply = _name ## _multiply, \ + } + +#endif /* _IPXE_WEIERSTRASS_H */ From bc5f3dbe3e03bc67a846981c1fb93206f5557283 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 22 Jan 2025 13:07:23 +0000 Subject: [PATCH 13/50] [crypto] Add definitions and tests for the NIST P-256 elliptic curve Signed-off-by: Michael Brown --- src/config/config_crypto.c | 5 + src/config/crypto.h | 3 + src/crypto/mishmash/oid_p256.c | 47 +++++++++ src/crypto/p256.c | 67 +++++++++++++ src/include/ipxe/asn1.h | 6 ++ src/include/ipxe/p256.h | 19 ++++ src/include/ipxe/tls.h | 1 + src/tests/p256_test.c | 176 +++++++++++++++++++++++++++++++++ src/tests/tests.c | 1 + 9 files changed, 325 insertions(+) create mode 100644 src/crypto/mishmash/oid_p256.c create mode 100644 src/crypto/p256.c create mode 100644 src/include/ipxe/p256.h create mode 100644 src/tests/p256_test.c diff --git a/src/config/config_crypto.c b/src/config/config_crypto.c index f118a9709..99acd3076 100644 --- a/src/config/config_crypto.c +++ b/src/config/config_crypto.c @@ -88,6 +88,11 @@ REQUIRE_OBJECT ( oid_sha512_256 ); REQUIRE_OBJECT ( oid_x25519 ); #endif +/* P-256 */ +#if defined ( CRYPTO_CURVE_P256 ) +REQUIRE_OBJECT ( oid_p256 ); +#endif + /* AES-CBC */ #if defined ( CRYPTO_CIPHER_AES_CBC ) REQUIRE_OBJECT ( oid_aes_cbc ); diff --git a/src/config/crypto.h b/src/config/crypto.h index 589c4f0da..5e96be4aa 100644 --- a/src/config/crypto.h +++ b/src/config/crypto.h @@ -60,6 +60,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** X25519 elliptic curve */ #define CRYPTO_CURVE_X25519 +/** P-256 elliptic curve */ +#define CRYPTO_CURVE_P256 + /** Margin of error (in seconds) allowed in signed timestamps * * We default to allowing a reasonable margin of error: 12 hours to diff --git a/src/crypto/mishmash/oid_p256.c b/src/crypto/mishmash/oid_p256.c new file mode 100644 index 000000000..d473df09f --- /dev/null +++ b/src/crypto/mishmash/oid_p256.c @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2025 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include + +/** "prime256v1" object identifier */ +static uint8_t oid_prime256v1[] = { ASN1_OID_PRIME256V1 }; + +/** "prime256v1" OID-identified algorithm */ +struct asn1_algorithm prime256v1_algorithm __asn1_algorithm = { + .name = "prime256v1", + .curve = &p256_curve, + .oid = ASN1_CURSOR ( oid_prime256v1 ), +}; + +/** P-256 named curve */ +struct tls_named_curve tls_secp256r1_named_curve __tls_named_curve ( 01 ) = { + .curve = &p256_curve, + .code = htons ( TLS_NAMED_CURVE_SECP256R1 ), + .format = TLS_POINT_FORMAT_UNCOMPRESSED, + .pre_master_secret_len = P256_LEN, +}; diff --git a/src/crypto/p256.c b/src/crypto/p256.c new file mode 100644 index 000000000..9b0b542a1 --- /dev/null +++ b/src/crypto/p256.c @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2025 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * NIST P-256 elliptic curve + * + */ + +#include + +/** P-256 field prime */ +static const uint8_t p256_prime[P256_LEN] = { + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; + +/** P-256 constant "a" */ +static const uint8_t p256_a[P256_LEN] = { + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc +}; + +/** P-256 constant "b" */ +static const uint8_t p256_b[P256_LEN] = { + 0x5a, 0xc6, 0x35, 0xd8, 0xaa, 0x3a, 0x93, 0xe7, 0xb3, 0xeb, 0xbd, + 0x55, 0x76, 0x98, 0x86, 0xbc, 0x65, 0x1d, 0x06, 0xb0, 0xcc, 0x53, + 0xb0, 0xf6, 0x3b, 0xce, 0x3c, 0x3e, 0x27, 0xd2, 0x60, 0x4b +}; + +/** P-256 base point */ +static const uint8_t p256_base[ P256_LEN * 2 ] = { + 0x6b, 0x17, 0xd1, 0xf2, 0xe1, 0x2c, 0x42, 0x47, 0xf8, 0xbc, 0xe6, + 0xe5, 0x63, 0xa4, 0x40, 0xf2, 0x77, 0x03, 0x7d, 0x81, 0x2d, 0xeb, + 0x33, 0xa0, 0xf4, 0xa1, 0x39, 0x45, 0xd8, 0x98, 0xc2, 0x96, 0x4f, + 0xe3, 0x42, 0xe2, 0xfe, 0x1a, 0x7f, 0x9b, 0x8e, 0xe7, 0xeb, 0x4a, + 0x7c, 0x0f, 0x9e, 0x16, 0x2b, 0xce, 0x33, 0x57, 0x6b, 0x31, 0x5e, + 0xce, 0xcb, 0xb6, 0x40, 0x68, 0x37, 0xbf, 0x51, 0xf5 +}; + +/** P-256 elliptic curve */ +WEIERSTRASS_CURVE ( p256, p256_curve, P256_LEN, + p256_prime, p256_a, p256_b, p256_base ); diff --git a/src/include/ipxe/asn1.h b/src/include/ipxe/asn1.h index 752b423b9..d503ccf9b 100644 --- a/src/include/ipxe/asn1.h +++ b/src/include/ipxe/asn1.h @@ -127,6 +127,12 @@ struct asn1_builder_header { #define ASN1_OID_TRIPLE( value ) \ ( 0x80 | ( ( (value) >> 14 ) & 0x7f ) ), ASN1_OID_DOUBLE ( (value) ) +/** ASN.1 OID for prime256v1 (1.2.840.10045.3.1.7) */ +#define ASN1_OID_PRIME256V1 \ + ASN1_OID_INITIAL ( 1, 1 ), ASN1_OID_DOUBLE ( 840 ), \ + ASN1_OID_DOUBLE ( 10045 ), ASN1_OID_SINGLE ( 3 ), \ + ASN1_OID_SINGLE ( 1 ), ASN1_OID_SINGLE ( 7 ) + /** ASN.1 OID for rsaEncryption (1.2.840.113549.1.1.1) */ #define ASN1_OID_RSAENCRYPTION \ ASN1_OID_INITIAL ( 1, 2 ), ASN1_OID_DOUBLE ( 840 ), \ diff --git a/src/include/ipxe/p256.h b/src/include/ipxe/p256.h new file mode 100644 index 000000000..0c4e81665 --- /dev/null +++ b/src/include/ipxe/p256.h @@ -0,0 +1,19 @@ +#ifndef _IPXE_P256_H +#define _IPXE_P256_H + +/** @file + * + * NIST P-256 elliptic curve + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** P-256 value length */ +#define P256_LEN ( 256 / 8 ) + +extern struct elliptic_curve p256_curve; + +#endif /* _IPXE_P256_H */ diff --git a/src/include/ipxe/tls.h b/src/include/ipxe/tls.h index bf9807230..685c62e6d 100644 --- a/src/include/ipxe/tls.h +++ b/src/include/ipxe/tls.h @@ -127,6 +127,7 @@ struct tls_header { /* TLS named curve extension */ #define TLS_NAMED_CURVE 10 +#define TLS_NAMED_CURVE_SECP256R1 23 #define TLS_NAMED_CURVE_X25519 29 /* TLS signature algorithms extension */ diff --git a/src/tests/p256_test.c b/src/tests/p256_test.c new file mode 100644 index 000000000..62b8d9247 --- /dev/null +++ b/src/tests/p256_test.c @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2025 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * NIST P-256 elliptic curve self-tests + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include +#include +#include "elliptic_test.h" + +/* http://point-at-infinity.org/ecc/nisttv k=1 */ +ELLIPTIC_TEST ( poi_1, &p256_curve, BASE_GENERATOR, + SCALAR ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 ), + EXPECTED ( 0x6b, 0x17, 0xd1, 0xf2, 0xe1, 0x2c, 0x42, 0x47, + 0xf8, 0xbc, 0xe6, 0xe5, 0x63, 0xa4, 0x40, 0xf2, + 0x77, 0x03, 0x7d, 0x81, 0x2d, 0xeb, 0x33, 0xa0, + 0xf4, 0xa1, 0x39, 0x45, 0xd8, 0x98, 0xc2, 0x96, + 0x4f, 0xe3, 0x42, 0xe2, 0xfe, 0x1a, 0x7f, 0x9b, + 0x8e, 0xe7, 0xeb, 0x4a, 0x7c, 0x0f, 0x9e, 0x16, + 0x2b, 0xce, 0x33, 0x57, 0x6b, 0x31, 0x5e, 0xce, + 0xcb, 0xb6, 0x40, 0x68, 0x37, 0xbf, 0x51, 0xf5 ) ); + +/* http://point-at-infinity.org/ecc/nisttv k=2 */ +ELLIPTIC_TEST ( poi_2, &p256_curve, BASE_GENERATOR, + SCALAR ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 ), + EXPECTED ( 0x7c, 0xf2, 0x7b, 0x18, 0x8d, 0x03, 0x4f, 0x7e, + 0x8a, 0x52, 0x38, 0x03, 0x04, 0xb5, 0x1a, 0xc3, + 0xc0, 0x89, 0x69, 0xe2, 0x77, 0xf2, 0x1b, 0x35, + 0xa6, 0x0b, 0x48, 0xfc, 0x47, 0x66, 0x99, 0x78, + 0x07, 0x77, 0x55, 0x10, 0xdb, 0x8e, 0xd0, 0x40, + 0x29, 0x3d, 0x9a, 0xc6, 0x9f, 0x74, 0x30, 0xdb, + 0xba, 0x7d, 0xad, 0xe6, 0x3c, 0xe9, 0x82, 0x29, + 0x9e, 0x04, 0xb7, 0x9d, 0x22, 0x78, 0x73, 0xd1 ) ); + +/* http://point-at-infinity.org/ecc/nisttv k=2 (as base) to k=20 */ +ELLIPTIC_TEST ( poi_2_20, &p256_curve, + BASE ( 0x7c, 0xf2, 0x7b, 0x18, 0x8d, 0x03, 0x4f, 0x7e, + 0x8a, 0x52, 0x38, 0x03, 0x04, 0xb5, 0x1a, 0xc3, + 0xc0, 0x89, 0x69, 0xe2, 0x77, 0xf2, 0x1b, 0x35, + 0xa6, 0x0b, 0x48, 0xfc, 0x47, 0x66, 0x99, 0x78, + 0x07, 0x77, 0x55, 0x10, 0xdb, 0x8e, 0xd0, 0x40, + 0x29, 0x3d, 0x9a, 0xc6, 0x9f, 0x74, 0x30, 0xdb, + 0xba, 0x7d, 0xad, 0xe6, 0x3c, 0xe9, 0x82, 0x29, + 0x9e, 0x04, 0xb7, 0x9d, 0x22, 0x78, 0x73, 0xd1 ), + SCALAR ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a ), + EXPECTED ( 0x83, 0xa0, 0x1a, 0x93, 0x78, 0x39, 0x5b, 0xab, + 0x9b, 0xcd, 0x6a, 0x0a, 0xd0, 0x3c, 0xc5, 0x6d, + 0x56, 0xe6, 0xb1, 0x92, 0x50, 0x46, 0x5a, 0x94, + 0xa2, 0x34, 0xdc, 0x4c, 0x6b, 0x28, 0xda, 0x9a, + 0x76, 0xe4, 0x9b, 0x6d, 0xe2, 0xf7, 0x32, 0x34, + 0xae, 0x6a, 0x5e, 0xb9, 0xd6, 0x12, 0xb7, 0x5c, + 0x9f, 0x22, 0x02, 0xbb, 0x69, 0x23, 0xf5, 0x4f, + 0xf8, 0x24, 0x0a, 0xaa, 0x86, 0xf6, 0x40, 0xb8 ) ); + +/* http://point-at-infinity.org/ecc/nisttv k=112233445566778899 */ +ELLIPTIC_TEST ( poi_mid, &p256_curve, BASE_GENERATOR, + SCALAR ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x8e, 0xbb, 0xb9, 0x5e, 0xed, 0x0e, 0x13 ), + EXPECTED ( 0x33, 0x91, 0x50, 0x84, 0x4e, 0xc1, 0x52, 0x34, + 0x80, 0x7f, 0xe8, 0x62, 0xa8, 0x6b, 0xe7, 0x79, + 0x77, 0xdb, 0xfb, 0x3a, 0xe3, 0xd9, 0x6f, 0x4c, + 0x22, 0x79, 0x55, 0x13, 0xae, 0xaa, 0xb8, 0x2f, + 0xb1, 0xc1, 0x4d, 0xdf, 0xdc, 0x8e, 0xc1, 0xb2, + 0x58, 0x3f, 0x51, 0xe8, 0x5a, 0x5e, 0xb3, 0xa1, + 0x55, 0x84, 0x0f, 0x20, 0x34, 0x73, 0x0e, 0x9b, + 0x5a, 0xda, 0x38, 0xb6, 0x74, 0x33, 0x6a, 0x21 ) ); + +/* http://point-at-infinity.org/ecc/nisttv k= */ +ELLIPTIC_TEST ( poi_large, &p256_curve, BASE_GENERATOR, + SCALAR ( 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xbc, 0xe6, 0xfa, 0xad, 0xa7, 0x17, 0x9e, 0x84, + 0xf3, 0xb9, 0xca, 0xc2, 0xfc, 0x63, 0x25, 0x50 ), + EXPECTED ( 0x6b, 0x17, 0xd1, 0xf2, 0xe1, 0x2c, 0x42, 0x47, + 0xf8, 0xbc, 0xe6, 0xe5, 0x63, 0xa4, 0x40, 0xf2, + 0x77, 0x03, 0x7d, 0x81, 0x2d, 0xeb, 0x33, 0xa0, + 0xf4, 0xa1, 0x39, 0x45, 0xd8, 0x98, 0xc2, 0x96, + 0xb0, 0x1c, 0xbd, 0x1c, 0x01, 0xe5, 0x80, 0x65, + 0x71, 0x18, 0x14, 0xb5, 0x83, 0xf0, 0x61, 0xe9, + 0xd4, 0x31, 0xcc, 0xa9, 0x94, 0xce, 0xa1, 0x31, + 0x34, 0x49, 0xbf, 0x97, 0xc8, 0x40, 0xae, 0x0a ) ); + +/* Invalid curve point zero */ +ELLIPTIC_TEST ( invalid_zero, &p256_curve, + BASE ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ), + SCALAR ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 ), + EXPECTED_FAIL ); + +/* Invalid curve point (base_x, base_y - 1) */ +ELLIPTIC_TEST ( invalid_one, &p256_curve, + BASE ( 0x6b, 0x17, 0xd1, 0xf2, 0xe1, 0x2c, 0x42, 0x47, + 0xf8, 0xbc, 0xe6, 0xe5, 0x63, 0xa4, 0x40, 0xf2, + 0x77, 0x03, 0x7d, 0x81, 0x2d, 0xeb, 0x33, 0xa0, + 0xf4, 0xa1, 0x39, 0x45, 0xd8, 0x98, 0xc2, 0x96, + 0x4f, 0xe3, 0x42, 0xe2, 0xfe, 0x1a, 0x7f, 0x9b, + 0x8e, 0xe7, 0xeb, 0x4a, 0x7c, 0x0f, 0x9e, 0x16, + 0x2b, 0xce, 0x33, 0x57, 0x6b, 0x31, 0x5e, 0xce, + 0xcb, 0xb6, 0x40, 0x68, 0x37, 0xbf, 0x51, 0xf4 ), + SCALAR ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 ), + EXPECTED_FAIL ); + +/** + * Perform P-256 self-test + * + */ +static void p256_test_exec ( void ) { + + /* Tests from http://point-at-infinity.org/ecc/nisttv */ + elliptic_ok ( &poi_1 ); + elliptic_ok ( &poi_2 ); + elliptic_ok ( &poi_2_20 ); + elliptic_ok ( &poi_mid ); + elliptic_ok ( &poi_large ); + + /* Invalid point tests */ + elliptic_ok ( &invalid_zero ); + elliptic_ok ( &invalid_one ); +} + +/** P-256 self-test */ +struct self_test p256_test __self_test = { + .name = "p256", + .exec = p256_test_exec, +}; diff --git a/src/tests/tests.c b/src/tests/tests.c index 0e9b3e806..a1659fb29 100644 --- a/src/tests/tests.c +++ b/src/tests/tests.c @@ -86,3 +86,4 @@ REQUIRE_OBJECT ( des_test ); REQUIRE_OBJECT ( mschapv2_test ); REQUIRE_OBJECT ( uuid_test ); REQUIRE_OBJECT ( editstring_test ); +REQUIRE_OBJECT ( p256_test ); From c85de315a601d95a6348c4caf5d3af6b146274b7 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 30 Jan 2025 15:35:34 +0000 Subject: [PATCH 14/50] [crypto] Add definitions and tests for the NIST P-384 elliptic curve Signed-off-by: Michael Brown --- src/config/config_crypto.c | 5 + src/config/crypto.h | 3 + src/crypto/mishmash/oid_p384.c | 47 +++++++ src/crypto/p384.c | 76 +++++++++++ src/include/ipxe/asn1.h | 5 + src/include/ipxe/p384.h | 19 +++ src/include/ipxe/tls.h | 1 + src/tests/p384_test.c | 222 +++++++++++++++++++++++++++++++++ src/tests/tests.c | 1 + 9 files changed, 379 insertions(+) create mode 100644 src/crypto/mishmash/oid_p384.c create mode 100644 src/crypto/p384.c create mode 100644 src/include/ipxe/p384.h create mode 100644 src/tests/p384_test.c diff --git a/src/config/config_crypto.c b/src/config/config_crypto.c index 99acd3076..19d6d032e 100644 --- a/src/config/config_crypto.c +++ b/src/config/config_crypto.c @@ -93,6 +93,11 @@ REQUIRE_OBJECT ( oid_x25519 ); REQUIRE_OBJECT ( oid_p256 ); #endif +/* P-384 */ +#if defined ( CRYPTO_CURVE_P384 ) +REQUIRE_OBJECT ( oid_p384 ); +#endif + /* AES-CBC */ #if defined ( CRYPTO_CIPHER_AES_CBC ) REQUIRE_OBJECT ( oid_aes_cbc ); diff --git a/src/config/crypto.h b/src/config/crypto.h index 5e96be4aa..f2ee9fd0d 100644 --- a/src/config/crypto.h +++ b/src/config/crypto.h @@ -63,6 +63,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** P-256 elliptic curve */ #define CRYPTO_CURVE_P256 +/** P-384 elliptic curve */ +#define CRYPTO_CURVE_P384 + /** Margin of error (in seconds) allowed in signed timestamps * * We default to allowing a reasonable margin of error: 12 hours to diff --git a/src/crypto/mishmash/oid_p384.c b/src/crypto/mishmash/oid_p384.c new file mode 100644 index 000000000..968fb45c1 --- /dev/null +++ b/src/crypto/mishmash/oid_p384.c @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2025 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include + +/** "secp384r1" object identifier */ +static uint8_t oid_secp384r1[] = { ASN1_OID_SECP384R1 }; + +/** "secp384r1" OID-identified algorithm */ +struct asn1_algorithm secp384r1_algorithm __asn1_algorithm = { + .name = "secp384r1", + .curve = &p384_curve, + .oid = ASN1_CURSOR ( oid_secp384r1 ), +}; + +/** P-384 named curve */ +struct tls_named_curve tls_secp384r1_named_curve __tls_named_curve ( 01 ) = { + .curve = &p384_curve, + .code = htons ( TLS_NAMED_CURVE_SECP384R1 ), + .format = TLS_POINT_FORMAT_UNCOMPRESSED, + .pre_master_secret_len = P384_LEN, +}; diff --git a/src/crypto/p384.c b/src/crypto/p384.c new file mode 100644 index 000000000..887bf161d --- /dev/null +++ b/src/crypto/p384.c @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2025 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * NIST P-384 elliptic curve + * + */ + +#include + +/** P-384 field prime */ +static const uint8_t p384_prime[P384_LEN] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff +}; + +/** P-384 constant "a" */ +static const uint8_t p384_a[P384_LEN] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xfc +}; + +/** P-384 constant "b" */ +static const uint8_t p384_b[P384_LEN] = { + 0xb3, 0x31, 0x2f, 0xa7, 0xe2, 0x3e, 0xe7, 0xe4, 0x98, 0x8e, 0x05, + 0x6b, 0xe3, 0xf8, 0x2d, 0x19, 0x18, 0x1d, 0x9c, 0x6e, 0xfe, 0x81, + 0x41, 0x12, 0x03, 0x14, 0x08, 0x8f, 0x50, 0x13, 0x87, 0x5a, 0xc6, + 0x56, 0x39, 0x8d, 0x8a, 0x2e, 0xd1, 0x9d, 0x2a, 0x85, 0xc8, 0xed, + 0xd3, 0xec, 0x2a, 0xef +}; + +/** P-384 base point */ +static const uint8_t p384_base[ P384_LEN * 2 ] = { + 0xaa, 0x87, 0xca, 0x22, 0xbe, 0x8b, 0x05, 0x37, 0x8e, 0xb1, 0xc7, + 0x1e, 0xf3, 0x20, 0xad, 0x74, 0x6e, 0x1d, 0x3b, 0x62, 0x8b, 0xa7, + 0x9b, 0x98, 0x59, 0xf7, 0x41, 0xe0, 0x82, 0x54, 0x2a, 0x38, 0x55, + 0x02, 0xf2, 0x5d, 0xbf, 0x55, 0x29, 0x6c, 0x3a, 0x54, 0x5e, 0x38, + 0x72, 0x76, 0x0a, 0xb7, 0x36, 0x17, 0xde, 0x4a, 0x96, 0x26, 0x2c, + 0x6f, 0x5d, 0x9e, 0x98, 0xbf, 0x92, 0x92, 0xdc, 0x29, 0xf8, 0xf4, + 0x1d, 0xbd, 0x28, 0x9a, 0x14, 0x7c, 0xe9, 0xda, 0x31, 0x13, 0xb5, + 0xf0, 0xb8, 0xc0, 0x0a, 0x60, 0xb1, 0xce, 0x1d, 0x7e, 0x81, 0x9d, + 0x7a, 0x43, 0x1d, 0x7c, 0x90, 0xea, 0x0e, 0x5f +}; + +/** P-384 elliptic curve */ +WEIERSTRASS_CURVE ( p384, p384_curve, P384_LEN, + p384_prime, p384_a, p384_b, p384_base ); diff --git a/src/include/ipxe/asn1.h b/src/include/ipxe/asn1.h index d503ccf9b..8a7461cd3 100644 --- a/src/include/ipxe/asn1.h +++ b/src/include/ipxe/asn1.h @@ -198,6 +198,11 @@ struct asn1_builder_header { ASN1_OID_INITIAL ( 1, 3 ), ASN1_OID_SINGLE ( 101 ), \ ASN1_OID_SINGLE ( 110 ) +/** ASN.1 OID for secp384r1 (1.3.132.0.34) */ +#define ASN1_OID_SECP384R1 \ + ASN1_OID_INITIAL ( 1, 3 ), ASN1_OID_DOUBLE ( 132 ), \ + ASN1_OID_SINGLE ( 0 ), ASN1_OID_SINGLE ( 34 ) + /** ASN.1 OID for id-aes128-cbc (2.16.840.1.101.3.4.1.2) */ #define ASN1_OID_AES128_CBC \ ASN1_OID_INITIAL ( 2, 16 ), ASN1_OID_DOUBLE ( 840 ), \ diff --git a/src/include/ipxe/p384.h b/src/include/ipxe/p384.h new file mode 100644 index 000000000..f4631b5f2 --- /dev/null +++ b/src/include/ipxe/p384.h @@ -0,0 +1,19 @@ +#ifndef _IPXE_P384_H +#define _IPXE_P384_H + +/** @file + * + * NIST P-384 elliptic curve + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** P-384 value length */ +#define P384_LEN ( 384 / 8 ) + +extern struct elliptic_curve p384_curve; + +#endif /* _IPXE_P384_H */ diff --git a/src/include/ipxe/tls.h b/src/include/ipxe/tls.h index 685c62e6d..7abbe4ff9 100644 --- a/src/include/ipxe/tls.h +++ b/src/include/ipxe/tls.h @@ -128,6 +128,7 @@ struct tls_header { /* TLS named curve extension */ #define TLS_NAMED_CURVE 10 #define TLS_NAMED_CURVE_SECP256R1 23 +#define TLS_NAMED_CURVE_SECP384R1 24 #define TLS_NAMED_CURVE_X25519 29 /* TLS signature algorithms extension */ diff --git a/src/tests/p384_test.c b/src/tests/p384_test.c new file mode 100644 index 000000000..101cfc24c --- /dev/null +++ b/src/tests/p384_test.c @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2025 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * NIST P-384 elliptic curve self-tests + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include +#include +#include "elliptic_test.h" + +/* http://point-at-infinity.org/ecc/nisttv k=1 */ +ELLIPTIC_TEST ( poi_1, &p384_curve, BASE_GENERATOR, + SCALAR ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 ), + EXPECTED ( 0xaa, 0x87, 0xca, 0x22, 0xbe, 0x8b, 0x05, 0x37, + 0x8e, 0xb1, 0xc7, 0x1e, 0xf3, 0x20, 0xad, 0x74, + 0x6e, 0x1d, 0x3b, 0x62, 0x8b, 0xa7, 0x9b, 0x98, + 0x59, 0xf7, 0x41, 0xe0, 0x82, 0x54, 0x2a, 0x38, + 0x55, 0x02, 0xf2, 0x5d, 0xbf, 0x55, 0x29, 0x6c, + 0x3a, 0x54, 0x5e, 0x38, 0x72, 0x76, 0x0a, 0xb7, + 0x36, 0x17, 0xde, 0x4a, 0x96, 0x26, 0x2c, 0x6f, + 0x5d, 0x9e, 0x98, 0xbf, 0x92, 0x92, 0xdc, 0x29, + 0xf8, 0xf4, 0x1d, 0xbd, 0x28, 0x9a, 0x14, 0x7c, + 0xe9, 0xda, 0x31, 0x13, 0xb5, 0xf0, 0xb8, 0xc0, + 0x0a, 0x60, 0xb1, 0xce, 0x1d, 0x7e, 0x81, 0x9d, + 0x7a, 0x43, 0x1d, 0x7c, 0x90, 0xea, 0x0e, 0x5f ) ); + +/* http://point-at-infinity.org/ecc/nisttv k=2 */ +ELLIPTIC_TEST ( poi_2, &p384_curve, BASE_GENERATOR, + SCALAR ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 ), + EXPECTED ( 0x08, 0xd9, 0x99, 0x05, 0x7b, 0xa3, 0xd2, 0xd9, + 0x69, 0x26, 0x00, 0x45, 0xc5, 0x5b, 0x97, 0xf0, + 0x89, 0x02, 0x59, 0x59, 0xa6, 0xf4, 0x34, 0xd6, + 0x51, 0xd2, 0x07, 0xd1, 0x9f, 0xb9, 0x6e, 0x9e, + 0x4f, 0xe0, 0xe8, 0x6e, 0xbe, 0x0e, 0x64, 0xf8, + 0x5b, 0x96, 0xa9, 0xc7, 0x52, 0x95, 0xdf, 0x61, + 0x8e, 0x80, 0xf1, 0xfa, 0x5b, 0x1b, 0x3c, 0xed, + 0xb7, 0xbf, 0xe8, 0xdf, 0xfd, 0x6d, 0xba, 0x74, + 0xb2, 0x75, 0xd8, 0x75, 0xbc, 0x6c, 0xc4, 0x3e, + 0x90, 0x4e, 0x50, 0x5f, 0x25, 0x6a, 0xb4, 0x25, + 0x5f, 0xfd, 0x43, 0xe9, 0x4d, 0x39, 0xe2, 0x2d, + 0x61, 0x50, 0x1e, 0x70, 0x0a, 0x94, 0x0e, 0x80 ) ); + +/* http://point-at-infinity.org/ecc/nisttv k=2 (as base) to k=20 */ +ELLIPTIC_TEST ( poi_2_20, &p384_curve, + BASE ( 0x08, 0xd9, 0x99, 0x05, 0x7b, 0xa3, 0xd2, 0xd9, + 0x69, 0x26, 0x00, 0x45, 0xc5, 0x5b, 0x97, 0xf0, + 0x89, 0x02, 0x59, 0x59, 0xa6, 0xf4, 0x34, 0xd6, + 0x51, 0xd2, 0x07, 0xd1, 0x9f, 0xb9, 0x6e, 0x9e, + 0x4f, 0xe0, 0xe8, 0x6e, 0xbe, 0x0e, 0x64, 0xf8, + 0x5b, 0x96, 0xa9, 0xc7, 0x52, 0x95, 0xdf, 0x61, + 0x8e, 0x80, 0xf1, 0xfa, 0x5b, 0x1b, 0x3c, 0xed, + 0xb7, 0xbf, 0xe8, 0xdf, 0xfd, 0x6d, 0xba, 0x74, + 0xb2, 0x75, 0xd8, 0x75, 0xbc, 0x6c, 0xc4, 0x3e, + 0x90, 0x4e, 0x50, 0x5f, 0x25, 0x6a, 0xb4, 0x25, + 0x5f, 0xfd, 0x43, 0xe9, 0x4d, 0x39, 0xe2, 0x2d, + 0x61, 0x50, 0x1e, 0x70, 0x0a, 0x94, 0x0e, 0x80 ), + SCALAR ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a ), + EXPECTED ( 0x60, 0x55, 0x08, 0xec, 0x02, 0xc5, 0x34, 0xbc, + 0xee, 0xe9, 0x48, 0x4c, 0x86, 0x08, 0x6d, 0x21, + 0x39, 0x84, 0x9e, 0x2b, 0x11, 0xc1, 0xa9, 0xca, + 0x1e, 0x28, 0x08, 0xde, 0xc2, 0xea, 0xf1, 0x61, + 0xac, 0x8a, 0x10, 0x5d, 0x70, 0xd4, 0xf8, 0x5c, + 0x50, 0x59, 0x9b, 0xe5, 0x80, 0x0a, 0x62, 0x3f, + 0x51, 0x58, 0xee, 0x87, 0x96, 0x2a, 0xc6, 0xb8, + 0x1f, 0x00, 0xa1, 0x03, 0xb8, 0x54, 0x3a, 0x07, + 0x38, 0x1b, 0x76, 0x39, 0xa3, 0xa6, 0x5f, 0x13, + 0x53, 0xae, 0xf1, 0x1b, 0x73, 0x31, 0x06, 0xdd, + 0xe9, 0x2e, 0x99, 0xb7, 0x8d, 0xe3, 0x67, 0xb4, + 0x8e, 0x23, 0x8c, 0x38, 0xda, 0xd8, 0xee, 0xdd ) ); + +/* http://point-at-infinity.org/ecc/nisttv k=112233445566778899 */ +ELLIPTIC_TEST ( poi_mid, &p384_curve, BASE_GENERATOR, + SCALAR ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x8e, 0xbb, 0xb9, 0x5e, 0xed, 0x0e, 0x13 ), + EXPECTED ( 0xa4, 0x99, 0xef, 0xe4, 0x88, 0x39, 0xbc, 0x3a, + 0xbc, 0xd1, 0xc5, 0xce, 0xdb, 0xdd, 0x51, 0x90, + 0x4f, 0x95, 0x14, 0xdb, 0x44, 0xf4, 0x68, 0x6d, + 0xb9, 0x18, 0x98, 0x3b, 0x0c, 0x9d, 0xc3, 0xae, + 0xe0, 0x5a, 0x88, 0xb7, 0x24, 0x33, 0xe9, 0x51, + 0x5f, 0x91, 0xa3, 0x29, 0xf5, 0xf4, 0xfa, 0x60, + 0x3b, 0x7c, 0xa2, 0x8e, 0xf3, 0x1f, 0x80, 0x9c, + 0x2f, 0x1b, 0xa2, 0x4a, 0xae, 0xd8, 0x47, 0xd0, + 0xf8, 0xb4, 0x06, 0xa4, 0xb8, 0x96, 0x85, 0x42, + 0xde, 0x13, 0x9d, 0xb5, 0x82, 0x8c, 0xa4, 0x10, + 0xe6, 0x15, 0xd1, 0x18, 0x2e, 0x25, 0xb9, 0x1b, + 0x11, 0x31, 0xe2, 0x30, 0xb7, 0x27, 0xd3, 0x6a ) ); + +/* http://point-at-infinity.org/ecc/nisttv k= */ +ELLIPTIC_TEST ( poi_large, &p384_curve, BASE_GENERATOR, + SCALAR ( 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xc7, 0x63, 0x4d, 0x81, 0xf4, 0x37, 0x2d, 0xdf, + 0x58, 0x1a, 0x0d, 0xb2, 0x48, 0xb0, 0xa7, 0x7a, + 0xec, 0xec, 0x19, 0x6a, 0xcc, 0xc5, 0x29, 0x72 ), + EXPECTED ( 0xaa, 0x87, 0xca, 0x22, 0xbe, 0x8b, 0x05, 0x37, + 0x8e, 0xb1, 0xc7, 0x1e, 0xf3, 0x20, 0xad, 0x74, + 0x6e, 0x1d, 0x3b, 0x62, 0x8b, 0xa7, 0x9b, 0x98, + 0x59, 0xf7, 0x41, 0xe0, 0x82, 0x54, 0x2a, 0x38, + 0x55, 0x02, 0xf2, 0x5d, 0xbf, 0x55, 0x29, 0x6c, + 0x3a, 0x54, 0x5e, 0x38, 0x72, 0x76, 0x0a, 0xb7, + 0xc9, 0xe8, 0x21, 0xb5, 0x69, 0xd9, 0xd3, 0x90, + 0xa2, 0x61, 0x67, 0x40, 0x6d, 0x6d, 0x23, 0xd6, + 0x07, 0x0b, 0xe2, 0x42, 0xd7, 0x65, 0xeb, 0x83, + 0x16, 0x25, 0xce, 0xec, 0x4a, 0x0f, 0x47, 0x3e, + 0xf5, 0x9f, 0x4e, 0x30, 0xe2, 0x81, 0x7e, 0x62, + 0x85, 0xbc, 0xe2, 0x84, 0x6f, 0x15, 0xf1, 0xa0 ) ); + +/* Invalid curve point zero */ +ELLIPTIC_TEST ( invalid_zero, &p384_curve, + BASE ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ), + SCALAR ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 ), + EXPECTED_FAIL ); + +/* Invalid curve point (base_x, base_y - 1) */ +ELLIPTIC_TEST ( invalid_one, &p384_curve, + BASE ( 0xaa, 0x87, 0xca, 0x22, 0xbe, 0x8b, 0x05, 0x37, + 0x8e, 0xb1, 0xc7, 0x1e, 0xf3, 0x20, 0xad, 0x74, + 0x6e, 0x1d, 0x3b, 0x62, 0x8b, 0xa7, 0x9b, 0x98, + 0x59, 0xf7, 0x41, 0xe0, 0x82, 0x54, 0x2a, 0x38, + 0x55, 0x02, 0xf2, 0x5d, 0xbf, 0x55, 0x29, 0x6c, + 0x3a, 0x54, 0x5e, 0x38, 0x72, 0x76, 0x0a, 0xb7, + 0x36, 0x17, 0xde, 0x4a, 0x96, 0x26, 0x2c, 0x6f, + 0x5d, 0x9e, 0x98, 0xbf, 0x92, 0x92, 0xdc, 0x29, + 0xf8, 0xf4, 0x1d, 0xbd, 0x28, 0x9a, 0x14, 0x7c, + 0xe9, 0xda, 0x31, 0x13, 0xb5, 0xf0, 0xb8, 0xc0, + 0x0a, 0x60, 0xb1, 0xce, 0x1d, 0x7e, 0x81, 0x9d, + 0x7a, 0x43, 0x1d, 0x7c, 0x90, 0xea, 0x0e, 0x5e ), + SCALAR ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 ), + EXPECTED_FAIL ); + +/** + * Perform P-384 self-test + * + */ +static void p384_test_exec ( void ) { + + /* Tests from http://point-at-infinity.org/ecc/nisttv */ + elliptic_ok ( &poi_1 ); + elliptic_ok ( &poi_2 ); + elliptic_ok ( &poi_2_20 ); + elliptic_ok ( &poi_mid ); + elliptic_ok ( &poi_large ); + + /* Invalid point tests */ + elliptic_ok ( &invalid_zero ); + elliptic_ok ( &invalid_one ); +} + +/** P-384 self-test */ +struct self_test p384_test __self_test = { + .name = "p384", + .exec = p384_test_exec, +}; diff --git a/src/tests/tests.c b/src/tests/tests.c index a1659fb29..96687423f 100644 --- a/src/tests/tests.c +++ b/src/tests/tests.c @@ -87,3 +87,4 @@ REQUIRE_OBJECT ( mschapv2_test ); REQUIRE_OBJECT ( uuid_test ); REQUIRE_OBJECT ( editstring_test ); REQUIRE_OBJECT ( p256_test ); +REQUIRE_OBJECT ( p384_test ); From 6f076efa650f5d8cc73bc70402b23f7066943474 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 3 Feb 2025 14:41:35 +0000 Subject: [PATCH 15/50] [malloc] Clean up debug messages Signed-off-by: Michael Brown --- src/core/malloc.c | 59 ++++++++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/src/core/malloc.c b/src/core/malloc.c index 8499ab45a..b4bf4f0a6 100644 --- a/src/core/malloc.c +++ b/src/core/malloc.c @@ -310,7 +310,7 @@ void * alloc_memblock ( size_t size, size_t align, size_t offset ) { assert ( actual_size >= size ); align_mask = ( ( align - 1 ) | ( MIN_MEMBLOCK_SIZE - 1 ) ); - DBGC2 ( &heap, "Allocating %#zx (aligned %#zx+%zx)\n", + DBGC2 ( &heap, "HEAP allocating %#zx (aligned %#zx+%zx)\n", size, align, offset ); while ( 1 ) { /* Search through blocks for the first one with enough space */ @@ -329,7 +329,8 @@ void * alloc_memblock ( size_t size, size_t align, size_t offset ) { pre = block; block = ( ( ( void * ) pre ) + pre_size ); post = ( ( ( void * ) block ) + actual_size ); - DBGC2 ( &heap, "[%p,%p) -> [%p,%p) + [%p,%p)\n", pre, + DBGC2 ( &heap, "HEAP splitting [%p,%p) -> [%p,%p) " + "+ [%p,%p)\n", pre, ( ( ( void * ) pre ) + pre->size ), pre, block, post, ( ( ( void * ) pre ) + pre->size ) ); /* If there is a "post" block, add it in to @@ -364,7 +365,7 @@ void * alloc_memblock ( size_t size, size_t align, size_t offset ) { if ( usedmem > maxusedmem ) maxusedmem = usedmem; /* Return allocated block */ - DBGC2 ( &heap, "Allocated [%p,%p)\n", block, + DBGC2 ( &heap, "HEAP allocated [%p,%p)\n", block, ( ( ( void * ) block ) + size ) ); ptr = block; VALGRIND_MAKE_MEM_UNDEFINED ( ptr, size ); @@ -372,15 +373,16 @@ void * alloc_memblock ( size_t size, size_t align, size_t offset ) { } /* Try discarding some cached data to free up memory */ - DBGC ( &heap, "Attempting discard for %#zx (aligned %#zx+%zx), " - "used %zdkB\n", size, align, offset, ( usedmem >> 10 ) ); + DBGC ( &heap, "HEAP attempting discard for %#zx (aligned " + "%#zx+%zx), used %zdkB\n", size, align, offset, + ( usedmem >> 10 ) ); valgrind_make_blocks_noaccess(); discarded = discard_cache(); valgrind_make_blocks_defined(); check_blocks(); if ( ! discarded ) { /* Nothing available to discard */ - DBGC ( &heap, "Failed to allocate %#zx (aligned " + DBGC ( &heap, "HEAP failed to allocate %#zx (aligned " "%#zx)\n", size, align ); ptr = NULL; goto done; @@ -426,7 +428,7 @@ void free_memblock ( void *ptr, size_t size ) { ~( MIN_MEMBLOCK_SIZE - 1 ) ); freeing = ptr; VALGRIND_MAKE_MEM_UNDEFINED ( freeing, sizeof ( *freeing ) ); - DBGC2 ( &heap, "Freeing [%p,%p)\n", + DBGC2 ( &heap, "HEAP freeing [%p,%p)\n", freeing, ( ( ( void * ) freeing ) + size ) ); /* Check that this block does not overlap the free list */ @@ -437,7 +439,7 @@ void free_memblock ( void *ptr, size_t size ) { ( ( void * ) freeing < ( ( void * ) block + block->size ) ) ) { assert ( 0 ); - DBGC ( &heap, "Double free of [%p,%p) " + DBGC ( &heap, "HEAP double free of [%p,%p) " "overlapping [%p,%p) detected from %p\n", freeing, ( ( ( void * ) freeing ) + size ), block, @@ -457,7 +459,8 @@ void free_memblock ( void *ptr, size_t size ) { ( ( ( void * ) freeing ) + freeing->size ) ); /* Merge with immediately preceding block, if possible */ if ( gap_before == 0 ) { - DBGC2 ( &heap, "[%p,%p) + [%p,%p) -> [%p,%p)\n", block, + DBGC2 ( &heap, "HEAP merging [%p,%p) + [%p,%p) -> " + "[%p,%p)\n", block, ( ( ( void * ) block ) + block->size ), freeing, ( ( ( void * ) freeing ) + freeing->size ), block, @@ -477,13 +480,13 @@ void free_memblock ( void *ptr, size_t size ) { * possible, merge the following block into the "freeing" * block. */ - DBGC2 ( &heap, "[%p,%p)\n", + DBGC2 ( &heap, "HEAP freed [%p,%p)\n", freeing, ( ( ( void * ) freeing ) + freeing->size ) ); list_add_tail ( &freeing->list, &block->list ); if ( gap_after == 0 ) { - DBGC2 ( &heap, "[%p,%p) + [%p,%p) -> [%p,%p)\n", freeing, - ( ( ( void * ) freeing ) + freeing->size ), block, - ( ( ( void * ) block ) + block->size ), freeing, + DBGC2 ( &heap, "HEAP merging [%p,%p) + [%p,%p) -> [%p,%p)\n", + freeing, ( ( ( void * ) freeing ) + freeing->size ), + block, ( ( ( void * ) block ) + block->size ), freeing, ( ( ( void * ) block ) + block->size ) ); freeing->size += block->size; list_del ( &block->list ); @@ -565,8 +568,8 @@ void * realloc ( void *old_ptr, size_t new_size ) { } if ( ASSERTED ) { - DBGC ( &heap, "Possible memory corruption detected from %p\n", - __builtin_return_address ( 0 ) ); + DBGC ( &heap, "HEAP detected possible memory corruption " + "from %p\n", __builtin_return_address ( 0 ) ); } return new_ptr; } @@ -585,8 +588,8 @@ void * malloc ( size_t size ) { ptr = realloc ( NULL, size ); if ( ASSERTED ) { - DBGC ( &heap, "Possible memory corruption detected from %p\n", - __builtin_return_address ( 0 ) ); + DBGC ( &heap, "HEAP detected possible memory corruption " + "from %p\n", __builtin_return_address ( 0 ) ); } return ptr; } @@ -605,8 +608,8 @@ void free ( void *ptr ) { realloc ( ptr, 0 ); if ( ASSERTED ) { - DBGC ( &heap, "Possible memory corruption detected from %p\n", - __builtin_return_address ( 0 ) ); + DBGC ( &heap, "HEAP detected possible memory corruption " + "from %p\n", __builtin_return_address ( 0 ) ); } } @@ -628,8 +631,8 @@ void * zalloc ( size_t size ) { if ( data ) memset ( data, 0, size ); if ( ASSERTED ) { - DBGC ( &heap, "Possible memory corruption detected from %p\n", - __builtin_return_address ( 0 ) ); + DBGC ( &heap, "HEAP detected possible memory corruption " + "from %p\n", __builtin_return_address ( 0 ) ); } return data; } @@ -680,7 +683,7 @@ struct init_fn heap_init_fn __init_fn ( INIT_EARLY ) = { */ static void shutdown_cache ( int booting __unused ) { discard_all_cache(); - DBGC ( &heap, "Maximum heap usage %zdkB\n", ( maxusedmem >> 10 ) ); + DBGC ( &heap, "HEAP maximum usage %zdkB\n", ( maxusedmem >> 10 ) ); } /** Memory allocator shutdown function */ @@ -689,19 +692,17 @@ struct startup_fn heap_startup_fn __startup_fn ( STARTUP_EARLY ) = { .shutdown = shutdown_cache, }; -#if 0 -#include /** - * Dump free block list + * Dump free block list (for debugging) * */ void mdumpfree ( void ) { struct memory_block *block; - printf ( "Free block list:\n" ); + dbg_printf ( "HEAP free block list:\n" ); list_for_each_entry ( block, &free_blocks, list ) { - printf ( "[%p,%p] (size %#zx)\n", block, - ( ( ( void * ) block ) + block->size ), block->size ); + dbg_printf ( "...[%p,%p] (size %#zx)\n", block, + ( ( ( void * ) block ) + block->size ), + block->size ); } } -#endif From 77cc3ed10892f65e5b01af482b5739e29614486e Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 3 Feb 2025 14:43:03 +0000 Subject: [PATCH 16/50] [malloc] Ensure free memory blocks remain aligned When allocating memory with a non-zero alignment offset, the free memory block structure following the allocation may end up improperly aligned. Ensure that free memory blocks always remain aligned to the size of the free memory block structure. Ensure that the initial heap is also correctly aligned, thereby allowing the logic for leaking undersized free memory blocks to be omitted. Signed-off-by: Michael Brown --- src/core/malloc.c | 97 +++++++++++++++++++++++---------------- src/include/ipxe/malloc.h | 1 - 2 files changed, 58 insertions(+), 40 deletions(-) diff --git a/src/core/malloc.c b/src/core/malloc.c index b4bf4f0a6..c499ce6fd 100644 --- a/src/core/malloc.c +++ b/src/core/malloc.c @@ -59,7 +59,8 @@ struct memory_block { struct list_head list; }; -#define MIN_MEMBLOCK_SIZE \ +/** Physical address alignment maintained for free blocks of memory */ +#define MEMBLOCK_ALIGN \ ( ( size_t ) ( 1 << ( fls ( sizeof ( struct memory_block ) - 1 ) ) ) ) /** A block of allocated memory complete with size information */ @@ -107,7 +108,7 @@ size_t maxusedmem; #define HEAP_SIZE ( 512 * 1024 ) /** The heap itself */ -static char heap[HEAP_SIZE] __attribute__ (( aligned ( __alignof__(void *) ))); +static char heap[HEAP_SIZE]; /** * Mark all blocks in free list as defined @@ -211,12 +212,16 @@ static inline void check_blocks ( void ) { list_for_each_entry ( block, &free_blocks, list ) { + /* Check alignment */ + assert ( ( virt_to_phys ( block ) & + ( MEMBLOCK_ALIGN - 1 ) ) == 0 ); + /* Check that list structure is intact */ list_check ( &block->list ); /* Check that block size is not too small */ assert ( block->size >= sizeof ( *block ) ); - assert ( block->size >= MIN_MEMBLOCK_SIZE ); + assert ( block->size >= MEMBLOCK_ALIGN ); /* Check that block does not wrap beyond end of address space */ assert ( ( ( void * ) block + block->size ) > @@ -278,6 +283,7 @@ static void discard_all_cache ( void ) { */ void * alloc_memblock ( size_t size, size_t align, size_t offset ) { struct memory_block *block; + size_t actual_offset; size_t align_mask; size_t actual_size; size_t pre_size; @@ -293,11 +299,13 @@ void * alloc_memblock ( size_t size, size_t align, size_t offset ) { valgrind_make_blocks_defined(); check_blocks(); - /* Round up size to multiple of MIN_MEMBLOCK_SIZE and - * calculate alignment mask. - */ - actual_size = ( ( size + MIN_MEMBLOCK_SIZE - 1 ) & - ~( MIN_MEMBLOCK_SIZE - 1 ) ); + /* Calculate offset of memory block */ + actual_offset = ( offset & ~( MEMBLOCK_ALIGN - 1 ) ); + assert ( actual_offset <= offset ); + + /* Calculate size of memory block */ + actual_size = ( ( size + offset - actual_offset + MEMBLOCK_ALIGN - 1 ) + & ~( MEMBLOCK_ALIGN - 1 ) ); if ( ! actual_size ) { /* The requested size is not permitted to be zero. A * zero result at this point indicates that either the @@ -308,14 +316,16 @@ void * alloc_memblock ( size_t size, size_t align, size_t offset ) { goto done; } assert ( actual_size >= size ); - align_mask = ( ( align - 1 ) | ( MIN_MEMBLOCK_SIZE - 1 ) ); + + /* Calculate alignment mask */ + align_mask = ( ( align - 1 ) | ( MEMBLOCK_ALIGN - 1 ) ); DBGC2 ( &heap, "HEAP allocating %#zx (aligned %#zx+%zx)\n", size, align, offset ); while ( 1 ) { /* Search through blocks for the first one with enough space */ list_for_each_entry ( block, &free_blocks, list ) { - pre_size = ( ( offset - virt_to_phys ( block ) ) + pre_size = ( ( actual_offset - virt_to_phys ( block ) ) & align_mask ); if ( ( block->size < pre_size ) || ( ( block->size - pre_size ) < actual_size ) ) @@ -334,11 +344,10 @@ void * alloc_memblock ( size_t size, size_t align, size_t offset ) { ( ( ( void * ) pre ) + pre->size ), pre, block, post, ( ( ( void * ) pre ) + pre->size ) ); /* If there is a "post" block, add it in to - * the free list. Leak it if it is too small - * (which can happen only at the very end of - * the heap). + * the free list. */ - if ( post_size >= MIN_MEMBLOCK_SIZE ) { + if ( post_size ) { + assert ( post_size >= MEMBLOCK_ALIGN ); VALGRIND_MAKE_MEM_UNDEFINED ( post, sizeof ( *post )); post->size = post_size; @@ -350,14 +359,14 @@ void * alloc_memblock ( size_t size, size_t align, size_t offset ) { */ pre->size = pre_size; /* If there is no "pre" block, remove it from - * the list. Also remove it (i.e. leak it) if - * it is too small, which can happen only at - * the very start of the heap. + * the list. */ - if ( pre_size < MIN_MEMBLOCK_SIZE ) { + if ( ! pre_size ) { list_del ( &pre->list ); VALGRIND_MAKE_MEM_NOACCESS ( pre, sizeof ( *pre ) ); + } else { + assert ( pre_size >= MEMBLOCK_ALIGN ); } /* Update memory usage statistics */ freemem -= actual_size; @@ -365,9 +374,10 @@ void * alloc_memblock ( size_t size, size_t align, size_t offset ) { if ( usedmem > maxusedmem ) maxusedmem = usedmem; /* Return allocated block */ - DBGC2 ( &heap, "HEAP allocated [%p,%p)\n", block, - ( ( ( void * ) block ) + size ) ); - ptr = block; + ptr = ( ( ( void * ) block ) + offset - actual_offset ); + DBGC2 ( &heap, "HEAP allocated [%p,%p) within " + "[%p,%p)\n", ptr, ( ptr + size ), block, + ( ( ( void * ) block ) + actual_size ) ); VALGRIND_MAKE_MEM_UNDEFINED ( ptr, size ); goto done; } @@ -407,6 +417,7 @@ void free_memblock ( void *ptr, size_t size ) { struct memory_block *freeing; struct memory_block *block; struct memory_block *tmp; + size_t sub_offset; size_t actual_size; ssize_t gap_before; ssize_t gap_after = -1; @@ -420,16 +431,18 @@ void free_memblock ( void *ptr, size_t size ) { valgrind_make_blocks_defined(); check_blocks(); - /* Round up size to match actual size that alloc_memblock() - * would have used. + /* Round up to match actual block that alloc_memblock() would + * have allocated. */ assert ( size != 0 ); - actual_size = ( ( size + MIN_MEMBLOCK_SIZE - 1 ) & - ~( MIN_MEMBLOCK_SIZE - 1 ) ); - freeing = ptr; + sub_offset = ( virt_to_phys ( ptr ) & ( MEMBLOCK_ALIGN - 1) ); + freeing = ( ptr - sub_offset ); + actual_size = ( ( size + sub_offset + MEMBLOCK_ALIGN - 1 ) & + ~( MEMBLOCK_ALIGN - 1 ) ); + DBGC2 ( &heap, "HEAP freeing [%p,%p) within [%p,%p)\n", + ptr, ( ptr + size ), freeing, + ( ( ( void * ) freeing ) + actual_size ) ); VALGRIND_MAKE_MEM_UNDEFINED ( freeing, sizeof ( *freeing ) ); - DBGC2 ( &heap, "HEAP freeing [%p,%p)\n", - freeing, ( ( ( void * ) freeing ) + size ) ); /* Check that this block does not overlap the free list */ if ( ASSERTING ) { @@ -546,7 +559,7 @@ void * realloc ( void *old_ptr, size_t new_size ) { new_ptr = &new_block->data; VALGRIND_MALLOCLIKE_BLOCK ( new_ptr, new_size, 0, 0 ); } - + /* Copy across relevant part of the old data region (if any), * then free it. Note that at this point either (a) new_ptr * is valid, or (b) new_size is 0; either way, the memcpy() is @@ -641,19 +654,25 @@ void * zalloc ( size_t size ) { * Add memory to allocation pool * * @v start Start address - * @v end End address + * @v len Length of memory * - * Adds a block of memory [start,end) to the allocation pool. This is - * a one-way operation; there is no way to reclaim this memory. - * - * @c start must be aligned to at least a multiple of sizeof(void*). + * Adds a block of memory to the allocation pool. This is a one-way + * operation; there is no way to reclaim this memory. */ -void mpopulate ( void *start, size_t len ) { +static void mpopulate ( void *start, size_t len ) { + size_t skip; - /* Prevent free_memblock() from rounding up len beyond the end - * of what we were actually given... - */ - len &= ~( MIN_MEMBLOCK_SIZE - 1 ); + /* Align start of block */ + skip = ( ( -virt_to_phys ( start ) ) & ( MEMBLOCK_ALIGN - 1 ) ); + if ( skip > len ) + return; + start += skip; + len -= skip; + + /* Align end of block */ + len &= ~( MEMBLOCK_ALIGN - 1 ); + if ( ! len ) + return; /* Add to allocation pool */ free_memblock ( start, len ); diff --git a/src/include/ipxe/malloc.h b/src/include/ipxe/malloc.h index 180ca001d..f0fde0bc3 100644 --- a/src/include/ipxe/malloc.h +++ b/src/include/ipxe/malloc.h @@ -28,7 +28,6 @@ extern size_t maxusedmem; extern void * __malloc alloc_memblock ( size_t size, size_t align, size_t offset ); extern void free_memblock ( void *ptr, size_t size ); -extern void mpopulate ( void *start, size_t len ); extern void mdumpfree ( void ); /** From bd90abf487a6b0500f457193f86ff54fd2be3143 Mon Sep 17 00:00:00 2001 From: Joseph Wong Date: Wed, 22 Jan 2025 16:06:18 -0800 Subject: [PATCH 17/50] [bnxt] Allocate TX rings with firmware input Use queue_id value retrieved from firmware unconditionally when allocating TX rings. Signed-off by: Joseph Wong --- src/drivers/net/bnxt/bnxt.c | 2 +- src/drivers/net/bnxt/bnxt.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/drivers/net/bnxt/bnxt.c b/src/drivers/net/bnxt/bnxt.c index a127f6cef..5de8d094e 100644 --- a/src/drivers/net/bnxt/bnxt.c +++ b/src/drivers/net/bnxt/bnxt.c @@ -1840,7 +1840,7 @@ static int bnxt_hwrm_ring_alloc ( struct bnxt *bp, u8 type ) req->page_size = LM_PAGE_BITS ( 8 ); req->int_mode = RING_ALLOC_REQ_INT_MODE_POLL; req->length = ( u32 )bp->tx.ring_cnt; - req->queue_id = TX_RING_QID; + req->queue_id = ( u16 )bp->queue_id; req->stat_ctx_id = ( u32 )bp->stat_ctx_id; req->cmpl_ring_id = bp->cq_ring_id; req->page_tbl_addr = virt_to_bus ( bp->tx.bd_virt ); diff --git a/src/drivers/net/bnxt/bnxt.h b/src/drivers/net/bnxt/bnxt.h index 8c8a33282..782881964 100644 --- a/src/drivers/net/bnxt/bnxt.h +++ b/src/drivers/net/bnxt/bnxt.h @@ -178,7 +178,6 @@ union dma_addr64_t { RX_MASK_ACCEPT_MULTICAST) #define MAX_NQ_DESC_CNT 64 #define NQ_RING_BUFFER_SIZE (MAX_NQ_DESC_CNT * sizeof(struct cmpl_base)) -#define TX_RING_QID (FLAG_TEST(bp->flags, BNXT_FLAG_IS_CHIP_P5_PLUS) ? (u16)bp->queue_id : ((u16)bp->port_idx * 10)) #define RX_RING_QID (FLAG_TEST(bp->flags, BNXT_FLAG_IS_CHIP_P5_PLUS) ? bp->queue_id : 0) #define STAT_CTX_ID ((bp->vf || FLAG_TEST(bp->flags, BNXT_FLAG_IS_CHIP_P5_PLUS)) ? bp->stat_ctx_id : 0) #define TX_AVAIL(r) (r - 1) From 5056e8ad936742ba410031cff14c0f72d87805fc Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 13 Feb 2025 14:18:15 +0000 Subject: [PATCH 18/50] [crypto] Expose shifted out bit from big integer shifts Expose the bit shifted out as a result of shifting a big integer left or right. Signed-off-by: Michael Brown --- src/arch/arm32/include/bits/bigint.h | 22 +++++--- src/arch/arm64/include/bits/bigint.h | 31 ++++++----- src/arch/loong64/include/bits/bigint.h | 36 +++++++------ src/arch/riscv/include/bits/bigint.h | 36 +++++++------ src/arch/x86/include/bits/bigint.h | 19 ++++--- src/include/ipxe/bigint.h | 14 ++--- src/tests/bigint_test.c | 73 +++++++++++++++++++------- 7 files changed, 146 insertions(+), 85 deletions(-) diff --git a/src/arch/arm32/include/bits/bigint.h b/src/arch/arm32/include/bits/bigint.h index 95de32d83..988bef5ff 100644 --- a/src/arch/arm32/include/bits/bigint.h +++ b/src/arch/arm32/include/bits/bigint.h @@ -123,16 +123,18 @@ bigint_subtract_raw ( const uint32_t *subtrahend0, uint32_t *value0, * * @v value0 Element 0 of big integer * @v size Number of elements + * @ret out Bit shifted out */ -static inline __attribute__ (( always_inline )) void +static inline __attribute__ (( always_inline )) int bigint_shl_raw ( uint32_t *value0, unsigned int size ) { bigint_t ( size ) __attribute__ (( may_alias )) *value = ( ( void * ) value0 ); uint32_t *discard_value; uint32_t *discard_end; uint32_t discard_value_i; + int carry; - __asm__ __volatile__ ( "adds %1, %0, %5, lsl #2\n\t" /* clear CF */ + __asm__ __volatile__ ( "adds %1, %0, %1, lsl #2\n\t" /* clear CF */ "\n1:\n\t" "ldr %2, [%0]\n\t" "adcs %2, %2\n\t" @@ -142,9 +144,10 @@ bigint_shl_raw ( uint32_t *value0, unsigned int size ) { : "=l" ( discard_value ), "=l" ( discard_end ), "=l" ( discard_value_i ), + "=@cccs" ( carry ), "+m" ( *value ) - : "0" ( value0 ), "1" ( size ) - : "cc" ); + : "0" ( value0 ), "1" ( size ) ); + return carry; } /** @@ -152,16 +155,18 @@ bigint_shl_raw ( uint32_t *value0, unsigned int size ) { * * @v value0 Element 0 of big integer * @v size Number of elements + * @ret out Bit shifted out */ -static inline __attribute__ (( always_inline )) void +static inline __attribute__ (( always_inline )) int bigint_shr_raw ( uint32_t *value0, unsigned int size ) { bigint_t ( size ) __attribute__ (( may_alias )) *value = ( ( void * ) value0 ); uint32_t *discard_value; uint32_t *discard_end; uint32_t discard_value_i; + int carry; - __asm__ __volatile__ ( "adds %1, %0, %5, lsl #2\n\t" /* clear CF */ + __asm__ __volatile__ ( "adds %1, %0, %1, lsl #2\n\t" /* clear CF */ "\n1:\n\t" "ldmdb %1!, {%2}\n\t" "rrxs %2, %2\n\t" @@ -171,9 +176,10 @@ bigint_shr_raw ( uint32_t *value0, unsigned int size ) { : "=l" ( discard_value ), "=l" ( discard_end ), "=l" ( discard_value_i ), + "=@cccs" ( carry ), "+m" ( *value ) - : "0" ( value0 ), "1" ( size ) - : "cc" ); + : "0" ( value0 ), "1" ( size ) ); + return carry; } /** diff --git a/src/arch/arm64/include/bits/bigint.h b/src/arch/arm64/include/bits/bigint.h index 5f79dc0c3..f4032e335 100644 --- a/src/arch/arm64/include/bits/bigint.h +++ b/src/arch/arm64/include/bits/bigint.h @@ -122,14 +122,16 @@ bigint_subtract_raw ( const uint64_t *subtrahend0, uint64_t *value0, * * @v value0 Element 0 of big integer * @v size Number of elements + * @ret out Bit shifted out */ -static inline __attribute__ (( always_inline )) void +static inline __attribute__ (( always_inline )) int bigint_shl_raw ( uint64_t *value0, unsigned int size ) { bigint_t ( size ) __attribute__ (( may_alias )) *value = ( ( void * ) value0 ); uint64_t *discard_value; uint64_t discard_value_i; unsigned int discard_size; + int carry; __asm__ __volatile__ ( "cmn xzr, xzr\n\t" /* clear CF */ "\n1:\n\t" @@ -141,9 +143,10 @@ bigint_shl_raw ( uint64_t *value0, unsigned int size ) { : "=r" ( discard_value ), "=r" ( discard_size ), "=r" ( discard_value_i ), + "=@cccs" ( carry ), "+m" ( *value ) - : "0" ( value0 ), "1" ( size ) - : "cc" ); + : "0" ( value0 ), "1" ( size ) ); + return carry; } /** @@ -151,30 +154,32 @@ bigint_shl_raw ( uint64_t *value0, unsigned int size ) { * * @v value0 Element 0 of big integer * @v size Number of elements + * @ret out Bit shifted out */ -static inline __attribute__ (( always_inline )) void +static inline __attribute__ (( always_inline )) int bigint_shr_raw ( uint64_t *value0, unsigned int size ) { bigint_t ( size ) __attribute__ (( may_alias )) *value = ( ( void * ) value0 ); uint64_t *discard_value; - uint64_t discard_value_i; - uint64_t discard_value_j; + uint64_t discard_high; unsigned int discard_size; + uint64_t low; - __asm__ __volatile__ ( "mov %3, #0\n\t" + __asm__ __volatile__ ( "mov %2, #0\n\t" "\n1:\n\t" "sub %w1, %w1, #1\n\t" - "ldr %2, [%0, %1, lsl #3]\n\t" - "extr %3, %3, %2, #1\n\t" - "str %3, [%0, %1, lsl #3]\n\t" - "mov %3, %2\n\t" + "ldr %3, [%0, %1, lsl #3]\n\t" + "extr %2, %2, %3, #1\n\t" + "str %2, [%0, %1, lsl #3]\n\t" + "mov %2, %3\n\t" "cbnz %w1, 1b\n\t" : "=r" ( discard_value ), "=r" ( discard_size ), - "=r" ( discard_value_i ), - "=r" ( discard_value_j ), + "=r" ( discard_high ), + "=r" ( low ), "+m" ( *value ) : "0" ( value0 ), "1" ( size ) ); + return ( low & 1 ); } /** diff --git a/src/arch/loong64/include/bits/bigint.h b/src/arch/loong64/include/bits/bigint.h index 0222354df..6a879503a 100644 --- a/src/arch/loong64/include/bits/bigint.h +++ b/src/arch/loong64/include/bits/bigint.h @@ -144,26 +144,27 @@ bigint_subtract_raw ( const uint64_t *subtrahend0, uint64_t *value0, * * @v value0 Element 0 of big integer * @v size Number of elements + * @ret out Bit shifted out */ -static inline __attribute__ (( always_inline )) void +static inline __attribute__ (( always_inline )) int bigint_shl_raw ( uint64_t *value0, unsigned int size ) { bigint_t ( size ) __attribute__ (( may_alias )) *value = ( ( void * ) value0 ); uint64_t *discard_value; uint64_t discard_value_i; - uint64_t discard_carry; uint64_t discard_temp; unsigned int discard_size; + uint64_t carry; __asm__ __volatile__ ( "\n1:\n\t" /* Load value[i] */ "ld.d %2, %0, 0\n\t" /* Shift left */ "rotri.d %2, %2, 63\n\t" - "andi %4, %2, 1\n\t" - "xor %2, %2, %4\n\t" - "or %2, %2, %3\n\t" - "move %3, %4\n\t" + "andi %3, %2, 1\n\t" + "xor %2, %2, %3\n\t" + "or %2, %2, %4\n\t" + "move %4, %3\n\t" /* Store value[i] */ "st.d %2, %0, 0\n\t" /* Loop */ @@ -173,11 +174,12 @@ bigint_shl_raw ( uint64_t *value0, unsigned int size ) { : "=r" ( discard_value ), "=r" ( discard_size ), "=r" ( discard_value_i ), - "=r" ( discard_carry ), "=r" ( discard_temp ), + "=r" ( carry ), "+m" ( *value ) - : "0" ( value0 ), "1" ( size ), "3" ( 0 ) + : "0" ( value0 ), "1" ( size ), "4" ( 0 ) : "cc" ); + return ( carry & 1 ); } /** @@ -185,25 +187,26 @@ bigint_shl_raw ( uint64_t *value0, unsigned int size ) { * * @v value0 Element 0 of big integer * @v size Number of elements + * @ret out Bit shifted out */ -static inline __attribute__ (( always_inline )) void +static inline __attribute__ (( always_inline )) int bigint_shr_raw ( uint64_t *value0, unsigned int size ) { bigint_t ( size ) __attribute__ (( may_alias )) *value = ( ( void * ) value0 ); uint64_t *discard_value; uint64_t discard_value_i; - uint64_t discard_carry; uint64_t discard_temp; unsigned int discard_size; + uint64_t carry; __asm__ __volatile__ ( "\n1:\n\t" /* Load value[i] */ "ld.d %2, %0, -8\n\t" /* Shift right */ - "andi %4, %2, 1\n\t" - "xor %2, %2, %4\n\t" - "or %2, %2, %3\n\t" - "move %3, %4\n\t" + "andi %3, %2, 1\n\t" + "xor %2, %2, %3\n\t" + "or %2, %2, %4\n\t" + "move %4, %3\n\t" "rotri.d %2, %2, 1\n\t" /* Store value[i] */ "st.d %2, %0, -8\n\t" @@ -214,11 +217,12 @@ bigint_shr_raw ( uint64_t *value0, unsigned int size ) { : "=r" ( discard_value ), "=r" ( discard_size ), "=r" ( discard_value_i ), - "=r" ( discard_carry ), "=r" ( discard_temp ), + "=r" ( carry ), "+m" ( *value ) - : "0" ( value0 + size ), "1" ( size ), "3" ( 0 ) + : "0" ( value0 + size ), "1" ( size ), "4" ( 0 ) : "cc" ); + return ( carry & 1 ); } /** diff --git a/src/arch/riscv/include/bits/bigint.h b/src/arch/riscv/include/bits/bigint.h index ab1070d88..7f87d9748 100644 --- a/src/arch/riscv/include/bits/bigint.h +++ b/src/arch/riscv/include/bits/bigint.h @@ -143,38 +143,40 @@ bigint_subtract_raw ( const unsigned long *subtrahend0, unsigned long *value0, * * @v value0 Element 0 of big integer * @v size Number of elements + * @ret out Bit shifted out */ -static inline __attribute__ (( always_inline )) void +static inline __attribute__ (( always_inline )) int bigint_shl_raw ( unsigned long *value0, unsigned int size ) { bigint_t ( size ) __attribute__ (( may_alias )) *value = ( ( void * ) value0 ); unsigned long *valueN = ( value0 + size ); unsigned long *discard_value; unsigned long discard_value_i; - unsigned long discard_carry; unsigned long discard_temp; + unsigned long carry; __asm__ __volatile__ ( "\n1:\n\t" /* Load value[i] */ LOADN " %1, (%0)\n\t" /* Shift left */ - "slli %3, %1, 1\n\t" - "or %3, %3, %2\n\t" - "srli %2, %1, %7\n\t" + "slli %2, %1, 1\n\t" + "or %2, %2, %3\n\t" + "srli %3, %1, %7\n\t" /* Store value[i] */ - STOREN " %3, (%0)\n\t" + STOREN " %2, (%0)\n\t" /* Loop */ "addi %0, %0, %6\n\t" "bne %0, %5, 1b\n\t" : "=&r" ( discard_value ), "=&r" ( discard_value_i ), - "=&r" ( discard_carry ), "=&r" ( discard_temp ), + "=&r" ( carry ), "+m" ( *value ) : "r" ( valueN ), "i" ( sizeof ( unsigned long ) ), "i" ( ( 8 * sizeof ( unsigned long ) - 1 ) ), - "0" ( value0 ), "2" ( 0 ) ); + "0" ( value0 ), "3" ( 0 ) ); + return carry; } /** @@ -182,38 +184,40 @@ bigint_shl_raw ( unsigned long *value0, unsigned int size ) { * * @v value0 Element 0 of big integer * @v size Number of elements + * @ret out Bit shifted out */ -static inline __attribute__ (( always_inline )) void +static inline __attribute__ (( always_inline )) int bigint_shr_raw ( unsigned long *value0, unsigned int size ) { bigint_t ( size ) __attribute__ (( may_alias )) *value = ( ( void * ) value0 ); unsigned long *valueN = ( value0 + size ); unsigned long *discard_value; unsigned long discard_value_i; - unsigned long discard_carry; unsigned long discard_temp; + unsigned long carry; __asm__ __volatile__ ( "\n1:\n\t" /* Load value[i] */ LOADN " %1, %6(%0)\n\t" /* Shift right */ - "srli %3, %1, 1\n\t" - "or %3, %3, %2\n\t" - "slli %2, %1, %7\n\t" + "srli %2, %1, 1\n\t" + "or %2, %2, %3\n\t" + "slli %3, %1, %7\n\t" /* Store value[i] */ - STOREN " %3, %6(%0)\n\t" + STOREN " %2, %6(%0)\n\t" /* Loop */ "addi %0, %0, %6\n\t" "bne %0, %5, 1b\n\t" : "=&r" ( discard_value ), "=&r" ( discard_value_i ), - "=&r" ( discard_carry ), "=&r" ( discard_temp ), + "=&r" ( carry ), "+m" ( *value ) : "r" ( value0 ), "i" ( -( sizeof ( unsigned long ) ) ), "i" ( ( 8 * sizeof ( unsigned long ) - 1 ) ), - "0" ( valueN ), "2" ( 0 ) ); + "0" ( valueN ), "3" ( 0 ) ); + return ( !! carry ); } /** diff --git a/src/arch/x86/include/bits/bigint.h b/src/arch/x86/include/bits/bigint.h index 4026ca481..4bab3ebf7 100644 --- a/src/arch/x86/include/bits/bigint.h +++ b/src/arch/x86/include/bits/bigint.h @@ -116,22 +116,25 @@ bigint_subtract_raw ( const uint32_t *subtrahend0, uint32_t *value0, * * @v value0 Element 0 of big integer * @v size Number of elements + * @ret out Bit shifted out */ -static inline __attribute__ (( always_inline )) void +static inline __attribute__ (( always_inline )) int bigint_shl_raw ( uint32_t *value0, unsigned int size ) { bigint_t ( size ) __attribute__ (( may_alias )) *value = ( ( void * ) value0 ); long index; long discard_c; + int out; __asm__ __volatile__ ( "xor %0, %0\n\t" /* Zero %0 and clear CF */ "\n1:\n\t" - "rcll $1, (%3,%0,4)\n\t" + "rcll $1, (%4,%0,4)\n\t" "inc %0\n\t" /* Does not affect CF */ "loop 1b\n\t" : "=&r" ( index ), "=&c" ( discard_c ), - "+m" ( *value ) + "=@ccc" ( out ), "+m" ( *value ) : "r" ( value0 ), "1" ( size ) ); + return out; } /** @@ -139,19 +142,23 @@ bigint_shl_raw ( uint32_t *value0, unsigned int size ) { * * @v value0 Element 0 of big integer * @v size Number of elements + * @ret out Bit shifted out */ -static inline __attribute__ (( always_inline )) void +static inline __attribute__ (( always_inline )) int bigint_shr_raw ( uint32_t *value0, unsigned int size ) { bigint_t ( size ) __attribute__ (( may_alias )) *value = ( ( void * ) value0 ); long discard_c; + int out; __asm__ __volatile__ ( "clc\n\t" "\n1:\n\t" - "rcrl $1, -4(%2,%0,4)\n\t" + "rcrl $1, -4(%3,%0,4)\n\t" "loop 1b\n\t" - : "=&c" ( discard_c ), "+m" ( *value ) + : "=&c" ( discard_c ), "=@ccc" ( out ), + "+m" ( *value ) : "r" ( value0 ), "0" ( size ) ); + return out; } /** diff --git a/src/include/ipxe/bigint.h b/src/include/ipxe/bigint.h index 410d0fd90..d8f4571e3 100644 --- a/src/include/ipxe/bigint.h +++ b/src/include/ipxe/bigint.h @@ -105,21 +105,23 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * Shift big integer left * * @v value Big integer + * @ret out Bit shifted out */ -#define bigint_shl( value ) do { \ +#define bigint_shl( value ) ( { \ unsigned int size = bigint_size (value); \ bigint_shl_raw ( (value)->element, size ); \ - } while ( 0 ) + } ) /** * Shift big integer right * * @v value Big integer + * @ret out Bit shifted out */ -#define bigint_shr( value ) do { \ +#define bigint_shr( value ) ( { \ unsigned int size = bigint_size (value); \ bigint_shr_raw ( (value)->element, size ); \ - } while ( 0 ) + } ) /** * Test if big integer is equal to zero @@ -413,8 +415,8 @@ int bigint_add_raw ( const bigint_element_t *addend0, bigint_element_t *value0, unsigned int size ); int bigint_subtract_raw ( const bigint_element_t *subtrahend0, bigint_element_t *value0, unsigned int size ); -void bigint_shl_raw ( bigint_element_t *value0, unsigned int size ); -void bigint_shr_raw ( bigint_element_t *value0, unsigned int size ); +int bigint_shl_raw ( bigint_element_t *value0, unsigned int size ); +int bigint_shr_raw ( bigint_element_t *value0, unsigned int size ); int bigint_is_zero_raw ( const bigint_element_t *value0, unsigned int size ); int bigint_is_geq_raw ( const bigint_element_t *value0, const bigint_element_t *reference0, diff --git a/src/tests/bigint_test.c b/src/tests/bigint_test.c index fce5f5ca3..a1207fedd 100644 --- a/src/tests/bigint_test.c +++ b/src/tests/bigint_test.c @@ -78,18 +78,18 @@ int bigint_subtract_sample ( const bigint_element_t *subtrahend0, return bigint_subtract ( subtrahend, value ); } -void bigint_shl_sample ( bigint_element_t *value0, unsigned int size ) { +int bigint_shl_sample ( bigint_element_t *value0, unsigned int size ) { bigint_t ( size ) *value __attribute__ (( may_alias )) = ( ( void * ) value0 ); - bigint_shl ( value ); + return bigint_shl ( value ); } -void bigint_shr_sample ( bigint_element_t *value0, unsigned int size ) { +int bigint_shr_sample ( bigint_element_t *value0, unsigned int size ) { bigint_t ( size ) *value __attribute__ (( may_alias )) = ( ( void * ) value0 ); - bigint_shr ( value ); + return bigint_shr ( value ); } int bigint_is_zero_sample ( const bigint_element_t *value0, @@ -321,25 +321,31 @@ void bigint_mod_exp_sample ( const bigint_element_t *base0, * * @v value Big integer * @v expected Big integer expected result + * @v bit Expected bit shifted out */ -#define bigint_shl_ok( value, expected ) do { \ +#define bigint_shl_ok( value, expected, bit ) do { \ static const uint8_t value_raw[] = value; \ static const uint8_t expected_raw[] = expected; \ uint8_t result_raw[ sizeof ( expected_raw ) ]; \ unsigned int size = \ bigint_required_size ( sizeof ( value_raw ) ); \ + unsigned int msb = ( 8 * sizeof ( value_raw ) ); \ bigint_t ( size ) value_temp; \ + int out; \ {} /* Fix emacs alignment */ \ \ bigint_init ( &value_temp, value_raw, sizeof ( value_raw ) ); \ DBG ( "Shift left:\n" ); \ DBG_HDA ( 0, &value_temp, sizeof ( value_temp ) ); \ - bigint_shl ( &value_temp ); \ + out = bigint_shl ( &value_temp ); \ DBG_HDA ( 0, &value_temp, sizeof ( value_temp ) ); \ bigint_done ( &value_temp, result_raw, sizeof ( result_raw ) ); \ \ ok ( memcmp ( result_raw, expected_raw, \ sizeof ( result_raw ) ) == 0 ); \ + if ( sizeof ( result_raw ) < sizeof ( value_temp ) ) \ + out += bigint_bit_is_set ( &value_temp, msb ); \ + ok ( out == bit ); \ } while ( 0 ) /** @@ -347,25 +353,28 @@ void bigint_mod_exp_sample ( const bigint_element_t *base0, * * @v value Big integer * @v expected Big integer expected result + * @v bit Expected bit shifted out */ -#define bigint_shr_ok( value, expected ) do { \ +#define bigint_shr_ok( value, expected, bit ) do { \ static const uint8_t value_raw[] = value; \ static const uint8_t expected_raw[] = expected; \ uint8_t result_raw[ sizeof ( expected_raw ) ]; \ unsigned int size = \ bigint_required_size ( sizeof ( value_raw ) ); \ bigint_t ( size ) value_temp; \ + int out; \ {} /* Fix emacs alignment */ \ \ bigint_init ( &value_temp, value_raw, sizeof ( value_raw ) ); \ DBG ( "Shift right:\n" ); \ DBG_HDA ( 0, &value_temp, sizeof ( value_temp ) ); \ - bigint_shr ( &value_temp ); \ + out = bigint_shr ( &value_temp ); \ DBG_HDA ( 0, &value_temp, sizeof ( value_temp ) ); \ bigint_done ( &value_temp, result_raw, sizeof ( result_raw ) ); \ \ ok ( memcmp ( result_raw, expected_raw, \ sizeof ( result_raw ) ) == 0 ); \ + ok ( out == bit ); \ } while ( 0 ) /** @@ -896,13 +905,13 @@ static void bigint_test_exec ( void ) { 0x7f, 0xcb, 0x94, 0x31, 0x1d, 0xbc, 0x44, 0x1a ), 0 ); bigint_shl_ok ( BIGINT ( 0xe0 ), - BIGINT ( 0xc0 ) ); + BIGINT ( 0xc0 ), 1 ); bigint_shl_ok ( BIGINT ( 0x43, 0x1d ), - BIGINT ( 0x86, 0x3a ) ); + BIGINT ( 0x86, 0x3a ), 0 ); bigint_shl_ok ( BIGINT ( 0xac, 0xed, 0x9b ), - BIGINT ( 0x59, 0xdb, 0x36 ) ); + BIGINT ( 0x59, 0xdb, 0x36 ), 1 ); bigint_shl_ok ( BIGINT ( 0x2c, 0xe8, 0x3a, 0x22 ), - BIGINT ( 0x59, 0xd0, 0x74, 0x44 ) ); + BIGINT ( 0x59, 0xd0, 0x74, 0x44 ), 0 ); bigint_shl_ok ( BIGINT ( 0x4e, 0x88, 0x4a, 0x05, 0x5e, 0x10, 0xee, 0x5b, 0xc6, 0x40, 0x0e, 0x03, 0xd7, 0x0d, 0x28, 0xa5, 0x55, 0xb2, 0x50, 0xef, 0x69, @@ -910,7 +919,19 @@ static void bigint_test_exec ( void ) { BIGINT ( 0x9d, 0x10, 0x94, 0x0a, 0xbc, 0x21, 0xdc, 0xb7, 0x8c, 0x80, 0x1c, 0x07, 0xae, 0x1a, 0x51, 0x4a, 0xab, 0x64, 0xa1, 0xde, 0xd3, - 0xa2, 0x3a ) ); + 0xa2, 0x3a ), 0 ); + bigint_shl_ok ( BIGINT ( 0x84, 0x56, 0xaa, 0xb4, 0x23, 0xd4, 0x4e, + 0xea, 0x92, 0x34, 0x61, 0x11, 0x3e, 0x38, + 0x31, 0x8b ), + BIGINT ( 0x08, 0xad, 0x55, 0x68, 0x47, 0xa8, 0x9d, + 0xd5, 0x24, 0x68, 0xc2, 0x22, 0x7c, 0x70, + 0x63, 0x16 ), 1 ); + bigint_shl_ok ( BIGINT ( 0x4a, 0x2b, 0x6b, 0x7c, 0xbf, 0x8a, 0x43, + 0x71, 0x96, 0xd6, 0x2f, 0x14, 0xa0, 0x2a, + 0xf8, 0x15 ), + BIGINT ( 0x94, 0x56, 0xd6, 0xf9, 0x7f, 0x14, 0x86, + 0xe3, 0x2d, 0xac, 0x5e, 0x29, 0x40, 0x55, + 0xf0, 0x2a ), 0 ); bigint_shl_ok ( BIGINT ( 0x07, 0x62, 0x78, 0x70, 0x2e, 0xd4, 0x41, 0xaa, 0x9b, 0x50, 0xb1, 0x9a, 0x71, 0xf5, 0x1c, 0x2f, 0xe7, 0x0d, 0xf1, 0x46, 0x57, @@ -948,15 +969,15 @@ static void bigint_test_exec ( void ) { 0x10, 0xee, 0x09, 0xea, 0x09, 0x9a, 0xd6, 0x49, 0x7c, 0x1e, 0xdb, 0xc7, 0x65, 0xa6, 0x0e, 0xd1, 0xd2, 0x00, 0xb3, 0x41, 0xc9, - 0x3c, 0xbc ) ); + 0x3c, 0xbc ), 0 ); bigint_shr_ok ( BIGINT ( 0x8f ), - BIGINT ( 0x47 ) ); + BIGINT ( 0x47 ), 1 ); bigint_shr_ok ( BIGINT ( 0xaa, 0x1d ), - BIGINT ( 0x55, 0x0e ) ); + BIGINT ( 0x55, 0x0e ), 1 ); bigint_shr_ok ( BIGINT ( 0xf0, 0xbd, 0x68 ), - BIGINT ( 0x78, 0x5e, 0xb4 ) ); + BIGINT ( 0x78, 0x5e, 0xb4 ), 0 ); bigint_shr_ok ( BIGINT ( 0x33, 0xa0, 0x3d, 0x95 ), - BIGINT ( 0x19, 0xd0, 0x1e, 0xca ) ); + BIGINT ( 0x19, 0xd0, 0x1e, 0xca ), 1 ); bigint_shr_ok ( BIGINT ( 0xa1, 0xf4, 0xb9, 0x64, 0x91, 0x99, 0xa1, 0xf4, 0xae, 0xeb, 0x71, 0x97, 0x1b, 0x71, 0x09, 0x38, 0x3f, 0x8f, 0xc5, 0x3a, 0xb9, @@ -964,7 +985,19 @@ static void bigint_test_exec ( void ) { BIGINT ( 0x50, 0xfa, 0x5c, 0xb2, 0x48, 0xcc, 0xd0, 0xfa, 0x57, 0x75, 0xb8, 0xcb, 0x8d, 0xb8, 0x84, 0x9c, 0x1f, 0xc7, 0xe2, 0x9d, 0x5c, - 0xba, 0xca ) ); + 0xba, 0xca ), 0 ); + bigint_shr_ok ( BIGINT ( 0xa4, 0x19, 0xbb, 0x93, 0x0b, 0x3f, 0x47, + 0x5b, 0xb4, 0xb5, 0x7d, 0x75, 0x42, 0x22, + 0xcc, 0xdf ), + BIGINT ( 0x52, 0x0c, 0xdd, 0xc9, 0x85, 0x9f, 0xa3, + 0xad, 0xda, 0x5a, 0xbe, 0xba, 0xa1, 0x11, + 0x66, 0x6f ), 1 ); + bigint_shr_ok ( BIGINT ( 0x27, 0x7e, 0x8f, 0x60, 0x40, 0x93, 0x43, + 0xd6, 0x89, 0x3e, 0x40, 0x61, 0x9a, 0x04, + 0x4c, 0x02 ), + BIGINT ( 0x13, 0xbf, 0x47, 0xb0, 0x20, 0x49, 0xa1, + 0xeb, 0x44, 0x9f, 0x20, 0x30, 0xcd, 0x02, + 0x26, 0x01 ), 0 ); bigint_shr_ok ( BIGINT ( 0xc0, 0xb3, 0x78, 0x46, 0x69, 0x6e, 0x35, 0x94, 0xed, 0x28, 0xdc, 0xfd, 0xf6, 0xdb, 0x2d, 0x24, 0xcb, 0xa4, 0x6f, 0x0e, 0x58, @@ -1002,7 +1035,7 @@ static void bigint_test_exec ( void ) { 0x10, 0x64, 0x55, 0x07, 0xec, 0xa8, 0xc7, 0x46, 0xa8, 0x94, 0xb0, 0xf7, 0xa4, 0x57, 0x1f, 0x72, 0x88, 0x5f, 0xed, 0x4d, 0xc9, - 0x59, 0xbb ) ); + 0x59, 0xbb ), 1 ); bigint_is_zero_ok ( BIGINT ( 0x9b ), 0 ); bigint_is_zero_ok ( BIGINT ( 0x5a, 0x9d ), From 8e6b914c53732b6764c344856787cf67dd44026c Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 13 Feb 2025 13:35:45 +0000 Subject: [PATCH 19/50] [crypto] Support direct reduction only for Montgomery constant R^2 mod N The only remaining use case for direct reduction (outside of the unit tests) is in calculating the constant R^2 mod N used during Montgomery multiplication. The current implementation of direct reduction requires a writable copy of the modulus (to allow for shifting), and both the modulus and the result buffer must be padded to be large enough to hold (R^2 - N), which is twice the size of the actual values involved. For the special case of reducing R^2 mod N (or any power of two mod N), we can run the same algorithm without needing either a writable copy of the modulus or a padded result buffer. The working state required is only two bits larger than the result buffer, and these additional bits may be held in local variables instead. Rewrite bigint_reduce() to handle only this use case, and remove the no longer necessary uses of double-sized big integers. Signed-off-by: Michael Brown --- src/crypto/bigint.c | 298 ++++++++++++++++---------------------- src/crypto/weierstrass.c | 15 +- src/include/ipxe/bigint.h | 85 ++++++++--- src/tests/bigint_test.c | 108 +++++++------- 4 files changed, 247 insertions(+), 259 deletions(-) diff --git a/src/crypto/bigint.c b/src/crypto/bigint.c index ad22af771..9ccd9ff88 100644 --- a/src/crypto/bigint.c +++ b/src/crypto/bigint.c @@ -27,7 +27,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include -#include #include /** @file @@ -35,10 +34,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * Big integer support */ -/** Modular direct reduction profiler */ -static struct profiler bigint_mod_profiler __profiler = - { .name = "bigint_mod" }; - /** Minimum number of least significant bytes included in transcription */ #define BIGINT_NTOA_LSB_MIN 16 @@ -180,172 +175,136 @@ void bigint_multiply_raw ( const bigint_element_t *multiplicand0, } /** - * Reduce big integer - * - * @v modulus0 Element 0 of big integer modulus - * @v value0 Element 0 of big integer to be reduced - * @v size Number of elements in modulus and value - */ -void bigint_reduce_raw ( bigint_element_t *modulus0, bigint_element_t *value0, - unsigned int size ) { - bigint_t ( size ) __attribute__ (( may_alias )) - *modulus = ( ( void * ) modulus0 ); - bigint_t ( size ) __attribute__ (( may_alias )) - *value = ( ( void * ) value0 ); - const unsigned int width = ( 8 * sizeof ( bigint_element_t ) ); - bigint_element_t *element; - unsigned int modulus_max; - unsigned int value_max; - unsigned int subshift; - int offset; - int shift; - int msb; - int i; - - /* Start profiling */ - profile_start ( &bigint_mod_profiler ); - - /* Normalise the modulus - * - * Scale the modulus by shifting left such that both modulus - * "m" and value "x" have the same most significant set bit. - * (If this is not possible, then the value is already less - * than the modulus, and we may therefore skip reduction - * completely.) - */ - value_max = bigint_max_set_bit ( value ); - modulus_max = bigint_max_set_bit ( modulus ); - shift = ( value_max - modulus_max ); - if ( shift < 0 ) - goto skip; - subshift = ( shift & ( width - 1 ) ); - offset = ( shift / width ); - element = modulus->element; - for ( i = ( ( value_max - 1 ) / width ) ; ; i-- ) { - element[i] = ( element[ i - offset ] << subshift ); - if ( i <= offset ) - break; - if ( subshift ) { - element[i] |= ( element[ i - offset - 1 ] - >> ( width - subshift ) ); - } - } - for ( i-- ; i >= 0 ; i-- ) - element[i] = 0; - - /* Reduce the value "x" by iteratively adding or subtracting - * the scaled modulus "m". - * - * On each loop iteration, we maintain the invariant: - * - * -2m <= x < 2m - * - * If x is positive, we obtain the new value x' by - * subtracting m, otherwise we add m: - * - * 0 <= x < 2m => x' := x - m => -m <= x' < m - * -2m <= x < 0 => x' := x + m => -m <= x' < m - * - * and then halve the modulus (by shifting right): - * - * m' = m/2 - * - * We therefore end up with: - * - * -m <= x' < m => -2m' <= x' < 2m' - * - * i.e. we have preseved the invariant while reducing the - * bounds on x' by one power of two. - * - * The issue remains of how to determine on each iteration - * whether or not x is currently positive, given that both - * input values are unsigned big integers that may use all - * available bits (including the MSB). - * - * On the first loop iteration, we may simply assume that x is - * positive, since it is unmodified from the input value and - * so is positive by definition (even if the MSB is set). We - * therefore unconditionally perform a subtraction on the - * first loop iteration. - * - * Let k be the MSB after normalisation. We then have: - * - * 2^k <= m < 2^(k+1) - * 2^k <= x < 2^(k+1) - * - * On the first loop iteration, we therefore have: - * - * x' = (x - m) - * < 2^(k+1) - 2^k - * < 2^k - * - * Any positive value of x' therefore has its MSB set to zero, - * and so we may validly treat the MSB of x' as a sign bit at - * the end of the first loop iteration. - * - * On all subsequent loop iterations, the starting value m is - * guaranteed to have its MSB set to zero (since it has - * already been shifted right at least once). Since we know - * from above that we preserve the loop invariant: - * - * -m <= x' < m - * - * we immediately know that any positive value of x' also has - * its MSB set to zero, and so we may validly treat the MSB of - * x' as a sign bit at the end of all subsequent loop - * iterations. - * - * After the last loop iteration (when m' has been shifted - * back down to the original value of the modulus), we may - * need to add a single multiple of m' to ensure that x' is - * positive, i.e. lies within the range 0 <= x' < m'. To - * allow for reusing the (inlined) expansion of - * bigint_subtract(), we achieve this via a potential - * additional loop iteration that performs the addition and is - * then guaranteed to terminate (since the result will be - * positive). - */ - for ( msb = 0 ; ( msb || ( shift >= 0 ) ) ; shift-- ) { - if ( msb ) { - bigint_add ( modulus, value ); - } else { - bigint_subtract ( modulus, value ); - } - msb = bigint_msb_is_set ( value ); - if ( shift > 0 ) - bigint_shr ( modulus ); - } - - skip: - /* Sanity check */ - assert ( ! bigint_is_geq ( value, modulus ) ); - - /* Stop profiling */ - profile_stop ( &bigint_mod_profiler ); -} - -/** - * Reduce supremum of big integer representation + * Reduce big integer R^2 modulo N * * @v modulus0 Element 0 of big integer modulus * @v result0 Element 0 of big integer to hold result - * @v size Number of elements in modulus and value + * @v size Number of elements in modulus and result * - * Reduce the value 2^k (where k is the bit width of the big integer - * representation) modulo the specified modulus. + * Reduce the value R^2 modulo N, where R=2^n and n is the number of + * bits in the representation of the modulus N, including any leading + * zero bits. */ -void bigint_reduce_supremum_raw ( bigint_element_t *modulus0, - bigint_element_t *result0, - unsigned int size ) { - bigint_t ( size ) __attribute__ (( may_alias )) - *modulus = ( ( void * ) modulus0 ); +void bigint_reduce_raw ( const bigint_element_t *modulus0, + bigint_element_t *result0, unsigned int size ) { + const bigint_t ( size ) __attribute__ (( may_alias )) + *modulus = ( ( const void * ) modulus0 ); bigint_t ( size ) __attribute__ (( may_alias )) *result = ( ( void * ) result0 ); + const unsigned int width = ( 8 * sizeof ( bigint_element_t ) ); + unsigned int shift; + int max; + int sign; + int msb; + int carry; - /* Calculate (2^k) mod N via direct reduction of (2^k - N) mod N */ + /* We have the constants: + * + * N = modulus + * + * n = number of bits in the modulus (including any leading zeros) + * + * R = 2^n + * + * Let r be the extension of the n-bit result register by a + * separate two's complement sign bit, such that -R <= r < R, + * and define: + * + * x = r * 2^k + * + * as the value being reduced modulo N, where k is a + * non-negative integer bit shift. + * + * We want to reduce the initial value R^2=2^(2n), which we + * may trivially represent using r=1 and k=2n. + * + * We then iterate over decrementing k, maintaining the loop + * invariant: + * + * -N <= r < N + * + * On each iteration we must first double r, to compensate for + * having decremented k: + * + * k' = k - 1 + * + * r' = 2r + * + * x = r * 2^k = 2r * 2^(k-1) = r' * 2^k' + * + * Note that doubling the n-bit result register will create a + * value of n+1 bits: this extra bit needs to be handled + * separately during the calculation. + * + * We then subtract N (if r is currently non-negative) or add + * N (if r is currently negative) to restore the loop + * invariant: + * + * 0 <= r < N => r" = 2r - N => -N <= r" < N + * -N <= r < 0 => r" = 2r + N => -N <= r" < N + * + * Note that since N may use all n bits, the most significant + * bit of the n-bit result register is not a valid two's + * complement sign bit for r: the extra sign bit therefore + * also needs to be handled separately. + * + * Once we reach k=0, we have x=r and therefore: + * + * -N <= x < N + * + * After this last loop iteration (with k=0), we may need to + * add a single multiple of N to ensure that x is positive, + * i.e. lies within the range 0 <= x < N. + * + * Since neither the modulus nor the value R^2 are secret, we + * may elide approximately half of the total number of + * iterations by constructing the initial representation of + * R^2 as r=2^m and k=2n-m (for some m such that 2^m < N). + */ + + /* Initialise x=R^2 */ memset ( result, 0, sizeof ( *result ) ); - bigint_subtract ( modulus, result ); - bigint_reduce ( modulus, result ); + max = ( bigint_max_set_bit ( modulus ) - 2 ); + if ( max < 0 ) { + /* Degenerate case of N=0 or N=1: return a zero result */ + return; + } + bigint_set_bit ( result, max ); + shift = ( ( 2 * size * width ) - max ); + sign = 0; + + /* Iterate as described above */ + while ( shift-- ) { + + /* Calculate 2r, storing extra bit separately */ + msb = bigint_shl ( result ); + + /* Add or subtract N according to current sign */ + if ( sign ) { + carry = bigint_add ( modulus, result ); + } else { + carry = bigint_subtract ( modulus, result ); + } + + /* Calculate new sign of result + * + * We know the result lies in the range -N <= r < N + * and so the tuple (old sign, msb, carry) cannot ever + * take the values (0, 1, 0) or (1, 0, 0). We can + * therefore treat these as don't-care inputs, which + * allows us to simplify the boolean expression by + * ignoring the old sign completely. + */ + assert ( ( sign == msb ) || carry ); + sign = ( msb ^ carry ); + } + + /* Add N to make result positive if necessary */ + if ( sign ) + bigint_add ( modulus, result ); + + /* Sanity check */ + assert ( ! bigint_is_geq ( result, modulus ) ); } /** @@ -805,12 +764,9 @@ void bigint_mod_exp_raw ( const bigint_element_t *base0, ( ( void * ) result0 ); const unsigned int width = ( 8 * sizeof ( bigint_element_t ) ); struct { - union { - bigint_t ( 2 * size ) padded_modulus; - struct { - bigint_t ( size ) modulus; - bigint_t ( size ) stash; - }; + struct { + bigint_t ( size ) modulus; + bigint_t ( size ) stash; }; union { bigint_t ( 2 * size ) full; @@ -833,7 +789,7 @@ void bigint_mod_exp_raw ( const bigint_element_t *base0, } /* Factor modulus as (N * 2^scale) where N is odd */ - bigint_grow ( modulus, &temp->padded_modulus ); + bigint_copy ( modulus, &temp->modulus ); for ( scale = 0 ; ( ! bigint_bit_is_set ( &temp->modulus, 0 ) ) ; scale++ ) { bigint_shr ( &temp->modulus ); @@ -844,10 +800,10 @@ void bigint_mod_exp_raw ( const bigint_element_t *base0, submask = ~submask; /* Calculate (R^2 mod N) */ - bigint_reduce_supremum ( &temp->padded_modulus, &temp->product.full ); - bigint_copy ( &temp->product.low, &temp->stash ); + bigint_reduce ( &temp->modulus, &temp->stash ); /* Initialise result = Montgomery(1, R^2 mod N) */ + bigint_grow ( &temp->stash, &temp->product.full ); bigint_montgomery ( &temp->modulus, &temp->product.full, result ); /* Convert base into Montgomery form */ diff --git a/src/crypto/weierstrass.c b/src/crypto/weierstrass.c index be3909542..c149c7b21 100644 --- a/src/crypto/weierstrass.c +++ b/src/crypto/weierstrass.c @@ -188,10 +188,6 @@ static void weierstrass_init ( struct weierstrass_curve *curve ) { ( ( void * ) curve->mont[0] ); bigint_t ( size ) __attribute__ (( may_alias )) *temp = ( ( void * ) curve->prime[1] ); - bigint_t ( size * 2 ) __attribute__ (( may_alias )) *prime_double = - ( ( void * ) prime ); - bigint_t ( size * 2 ) __attribute__ (( may_alias )) *square_double = - ( ( void * ) square ); bigint_t ( size * 2 ) __attribute__ (( may_alias )) *product = ( ( void * ) temp ); bigint_t ( size ) __attribute__ (( may_alias )) *two = @@ -206,15 +202,8 @@ static void weierstrass_init ( struct weierstrass_curve *curve ) { DBGC ( curve, "WEIERSTRASS %s N = %s\n", curve->name, bigint_ntoa ( prime ) ); - /* Calculate Montgomery constant R^2 mod N - * - * We rely on the fact that the subsequent big integers in the - * cache (i.e. the first prime multiple, and the constant "1") - * have not yet been written to, and so can be treated as - * being the (zero) upper halves required to hold the - * double-width value R^2. - */ - bigint_reduce_supremum ( prime_double, square_double ); + /* Calculate Montgomery constant R^2 mod N */ + bigint_reduce ( prime, square ); DBGC ( curve, "WEIERSTRASS %s R^2 = %s mod N\n", curve->name, bigint_ntoa ( square ) ); diff --git a/src/include/ipxe/bigint.h b/src/include/ipxe/bigint.h index d8f4571e3..396462f33 100644 --- a/src/include/ipxe/bigint.h +++ b/src/include/ipxe/bigint.h @@ -146,6 +146,28 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); bigint_is_geq_raw ( (value)->element, (reference)->element, \ size ); } ) +/** + * Set bit in big integer + * + * @v value Big integer + * @v bit Bit to set + */ +#define bigint_set_bit( value, bit ) do { \ + unsigned int size = bigint_size (value); \ + bigint_set_bit_raw ( (value)->element, size, bit ); \ + } while ( 0 ) + +/** + * Clear bit in big integer + * + * @v value Big integer + * @v bit Bit to set + */ +#define bigint_clear_bit( value, bit ) do { \ + unsigned int size = bigint_size (value); \ + bigint_clear_bit_raw ( (value)->element, size, bit ); \ + } while ( 0 ) + /** * Test if bit is set in big integer * @@ -243,29 +265,17 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); } while ( 0 ) /** - * Reduce big integer + * Reduce big integer R^2 modulo N * * @v modulus Big integer modulus - * @v value Big integer to be reduced + * @v result Big integer to hold result */ -#define bigint_reduce( modulus, value ) do { \ +#define bigint_reduce( modulus, result ) do { \ unsigned int size = bigint_size (modulus); \ - bigint_reduce_raw ( (modulus)->element, (value)->element, \ + bigint_reduce_raw ( (modulus)->element, (result)->element, \ size ); \ } while ( 0 ) -/** - * Reduce supremum of big integer representation - * - * @v modulus0 Big integer modulus - * @v result0 Big integer to hold result - */ -#define bigint_reduce_supremum( modulus, result ) do { \ - unsigned int size = bigint_size (modulus); \ - bigint_reduce_supremum_raw ( (modulus)->element, \ - (result)->element, size ); \ - } while ( 0 ) - /** * Compute inverse of odd big integer modulo any power of two * @@ -369,6 +379,42 @@ typedef void ( bigint_ladder_op_t ) ( const bigint_element_t *operand0, unsigned int size, const void *ctx, void *tmp ); +/** + * Set bit in big integer + * + * @v value0 Element 0 of big integer + * @v size Number of elements + * @v bit Bit to set + */ +static inline __attribute__ (( always_inline )) void +bigint_set_bit_raw ( bigint_element_t *value0, unsigned int size, + unsigned int bit ) { + bigint_t ( size ) __attribute__ (( may_alias )) *value = + ( ( void * ) value0 ); + unsigned int index = ( bit / ( 8 * sizeof ( value->element[0] ) ) ); + unsigned int subindex = ( bit % ( 8 * sizeof ( value->element[0] ) ) ); + + value->element[index] |= ( 1UL << subindex ); +} + +/** + * Clear bit in big integer + * + * @v value0 Element 0 of big integer + * @v size Number of elements + * @v bit Bit to clear + */ +static inline __attribute__ (( always_inline )) void +bigint_clear_bit_raw ( bigint_element_t *value0, unsigned int size, + unsigned int bit ) { + bigint_t ( size ) __attribute__ (( may_alias )) *value = + ( ( void * ) value0 ); + unsigned int index = ( bit / ( 8 * sizeof ( value->element[0] ) ) ); + unsigned int subindex = ( bit % ( 8 * sizeof ( value->element[0] ) ) ); + + value->element[index] &= ~( 1UL << subindex ); +} + /** * Test if bit is set in big integer * @@ -442,11 +488,8 @@ void bigint_multiply_raw ( const bigint_element_t *multiplicand0, const bigint_element_t *multiplier0, unsigned int multiplier_size, bigint_element_t *result0 ); -void bigint_reduce_raw ( bigint_element_t *modulus0, bigint_element_t *value0, - unsigned int size ); -void bigint_reduce_supremum_raw ( bigint_element_t *modulus0, - bigint_element_t *value0, - unsigned int size ); +void bigint_reduce_raw ( const bigint_element_t *modulus0, + bigint_element_t *result0, unsigned int size ); void bigint_mod_invert_raw ( const bigint_element_t *invertend0, bigint_element_t *inverse0, unsigned int size ); int bigint_montgomery_relaxed_raw ( const bigint_element_t *modulus0, diff --git a/src/tests/bigint_test.c b/src/tests/bigint_test.c index a1207fedd..496efdfe2 100644 --- a/src/tests/bigint_test.c +++ b/src/tests/bigint_test.c @@ -185,14 +185,14 @@ void bigint_multiply_sample ( const bigint_element_t *multiplicand0, bigint_multiply ( multiplicand, multiplier, result ); } -void bigint_reduce_sample ( bigint_element_t *modulus0, - bigint_element_t *value0, unsigned int size ) { +void bigint_reduce_sample ( const bigint_element_t *modulus0, + bigint_element_t *result0, unsigned int size ) { + const bigint_t ( size ) __attribute__ (( may_alias )) + *modulus = ( ( const void * ) modulus0 ); bigint_t ( size ) __attribute__ (( may_alias )) - *modulus = ( ( void * ) modulus0 ); - bigint_t ( size ) __attribute__ (( may_alias )) - *value = ( ( void * ) value0 ); + *result = ( ( void * ) result0 ); - bigint_reduce ( modulus, value ); + bigint_reduce ( modulus, result ); } void bigint_mod_invert_sample ( const bigint_element_t *invertend0, @@ -553,42 +553,35 @@ void bigint_mod_exp_sample ( const bigint_element_t *base0, } while ( 0 ) /** - * Report result of big integer modular direct reduction test + * Report result of big integer modular direct reduction of R^2 test * * @v modulus Big integer modulus - * @v value Big integer to be reduced * @v expected Big integer expected result */ -#define bigint_reduce_ok( modulus, value, expected ) do { \ +#define bigint_reduce_ok( modulus, expected ) do { \ static const uint8_t modulus_raw[] = modulus; \ - static const uint8_t value_raw[] = value; \ static const uint8_t expected_raw[] = expected; \ uint8_t result_raw[ sizeof ( expected_raw ) ]; \ unsigned int size = \ bigint_required_size ( sizeof ( modulus_raw ) ); \ bigint_t ( size ) modulus_temp; \ - bigint_t ( size ) value_temp; \ + bigint_t ( size ) result_temp; \ {} /* Fix emacs alignment */ \ \ assert ( bigint_size ( &modulus_temp ) == \ - bigint_size ( &value_temp ) ); \ + bigint_size ( &result_temp ) ); \ + assert ( sizeof ( result_temp ) == sizeof ( result_raw ) ); \ bigint_init ( &modulus_temp, modulus_raw, \ sizeof ( modulus_raw ) ); \ - bigint_init ( &value_temp, value_raw, sizeof ( value_raw ) ); \ - DBG ( "Modular reduce:\n" ); \ + DBG ( "Modular reduce R^2:\n" ); \ DBG_HDA ( 0, &modulus_temp, sizeof ( modulus_temp ) ); \ - DBG_HDA ( 0, &value_temp, sizeof ( value_temp ) ); \ - bigint_reduce ( &modulus_temp, &value_temp ); \ - DBG_HDA ( 0, &value_temp, sizeof ( value_temp ) ); \ - bigint_done ( &value_temp, result_raw, sizeof ( result_raw ) ); \ + bigint_reduce ( &modulus_temp, &result_temp ); \ + DBG_HDA ( 0, &result_temp, sizeof ( result_temp ) ); \ + bigint_done ( &result_temp, result_raw, \ + sizeof ( result_raw ) ); \ \ ok ( memcmp ( result_raw, expected_raw, \ sizeof ( result_raw ) ) == 0 ); \ - \ - bigint_init ( &value_temp, modulus_raw, \ - sizeof ( modulus_raw ) ); \ - ok ( memcmp ( &modulus_temp, &value_temp, \ - sizeof ( modulus_temp ) ) == 0 ); \ } while ( 0 ) /** @@ -1801,39 +1794,46 @@ static void bigint_test_exec ( void ) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 ) ); - bigint_reduce_ok ( BIGINT ( 0xaf ), - BIGINT ( 0x00 ), - BIGINT ( 0x00 ) ); - bigint_reduce_ok ( BIGINT ( 0xab ), - BIGINT ( 0xab ), - BIGINT ( 0x00 ) ); - bigint_reduce_ok ( BIGINT ( 0xcc, 0x9d, 0xa0, 0x79, 0x96, 0x6a, 0x46, - 0xd5, 0xb4, 0x30, 0xd2, 0x2b, 0xbf ), - BIGINT ( 0x1d, 0x97, 0x63, 0xc9, 0x97, 0xcd, 0x43, - 0xcb, 0x8e, 0x71, 0xac, 0x41, 0xdd ), - BIGINT ( 0x1d, 0x97, 0x63, 0xc9, 0x97, 0xcd, 0x43, - 0xcb, 0x8e, 0x71, 0xac, 0x41, 0xdd ) ); - bigint_reduce_ok ( BIGINT ( 0x21, 0xfa, 0x4f, 0xce, 0x0f, 0x0f, 0x4d, - 0x43, 0xaa, 0xad, 0x21, 0x30, 0xe5 ), - BIGINT ( 0x21, 0xfa, 0x4f, 0xce, 0x0f, 0x0f, 0x4d, - 0x43, 0xaa, 0xad, 0x21, 0x30, 0xe5 ), - BIGINT ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ) ); bigint_reduce_ok ( BIGINT ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xf3, 0x65, 0x35, 0x41, - 0x66, 0x65 ), - BIGINT ( 0xf9, 0x78, 0x96, 0x39, 0xee, 0x98, 0x42, - 0x6a, 0xb8, 0x74, 0x0b, 0xe8, 0x5c, 0x76, - 0x34, 0xaf ), + 0x00 ), BIGINT ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xb3, 0x07, 0xe8, 0xb7, - 0x01, 0xf6 ) ); - bigint_reduce_ok ( BIGINT ( 0x47, 0xaa, 0x88, 0x00, 0xd0, 0x30, 0x62, - 0xfb, 0x5d, 0x55 ), - BIGINT ( 0xfe, 0x30, 0xe1, 0xc6, 0x65, 0x97, 0x48, - 0x2e, 0x94, 0xd4 ), - BIGINT ( 0x27, 0x31, 0x49, 0xc3, 0xf5, 0x06, 0x1f, - 0x3c, 0x7c, 0xd5 ) ); + 0x00 ) ); + bigint_reduce_ok ( BIGINT ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01 ), + BIGINT ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00 ) ); + bigint_reduce_ok ( BIGINT ( 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x00 ), + BIGINT ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00 ) ); + bigint_reduce_ok ( BIGINT ( 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00 ), + BIGINT ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00 ) ); + bigint_reduce_ok ( BIGINT ( 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff ), + BIGINT ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01 ) ); + bigint_reduce_ok ( BIGINT ( 0x39, 0x18, 0x47, 0xc9, 0xa2, 0x1d, 0x4b, + 0xa6 ), + BIGINT ( 0x30, 0x9d, 0xcc, 0xac, 0xd6, 0xf9, 0x2f, + 0xa0 ) ); + bigint_reduce_ok ( BIGINT ( 0x81, 0x96, 0xdb, 0x36, 0xa6, 0xb7, 0x41, + 0x45, 0x92, 0x37, 0x7d, 0x48, 0x1b, 0x2f, + 0x3c, 0xa6 ), + BIGINT ( 0x4a, 0x68, 0x25, 0xf7, 0x2b, 0x72, 0x91, + 0x6e, 0x09, 0x83, 0xca, 0xf1, 0x45, 0x79, + 0x84, 0x18 ) ); + bigint_reduce_ok ( BIGINT ( 0x84, 0x2d, 0xe4, 0x1c, 0xc3, 0x11, 0x4f, + 0xa0, 0x90, 0x4b, 0xa9, 0xa1, 0xdf, 0xed, + 0x4b, 0xe0, 0xb7, 0xfc, 0x5e, 0xd1, 0x91, + 0x59, 0x4d, 0xc2, 0xae, 0x2f, 0x46, 0x9e, + 0x32, 0x6e, 0xf4, 0x67 ), + BIGINT ( 0x46, 0xdd, 0x36, 0x6c, 0x0b, 0xac, 0x3a, + 0x8f, 0x9a, 0x25, 0x90, 0xb2, 0x39, 0xe9, + 0xa4, 0x65, 0xc1, 0xd4, 0xc1, 0x99, 0x61, + 0x95, 0x47, 0xab, 0x4f, 0xd7, 0xad, 0xd4, + 0x3e, 0xe9, 0x9c, 0xfc ) ); bigint_mod_invert_ok ( BIGINT ( 0x01 ), BIGINT ( 0x01 ) ); bigint_mod_invert_ok ( BIGINT ( 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff ), From b35300fc67cc050ce3f709a88a4437153d85e0ee Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 17 Feb 2025 13:11:28 +0000 Subject: [PATCH 20/50] [efi] Increase download timeout for autoexec.ipxe In almost all cases, the download timeout for autoexec.ipxe is irrelevant: the operation will either succeed or fail relatively quickly (e.g. due to a nonexistent file). The overall download timeout exists only to ensure that an unattended or headless system will not wait indefinitely in the case of a degenerate network response (e.g. an HTTP server that returns an endless trickle of data using chunked transfer encoding without ever reaching the end of the file). The current download timeout is too short if PeerDist content encoding is enabled, since the overall download will abort before the first peer discovery attempt has completed, and without allowing sufficient time for an origin server range request. The single timeout value is currently used for both the download timeout and the sync timeout. The latter timeout exists only to allow network communication to be gracefully quiesced before removing the temporary MNP network device, and may safely be shortened without affecting functionality. Fix by increasing the download timeout from two seconds to 30 seconds, and defining a separate one-second timeout for the sync operation. Reported-by: Michael Niehaus Signed-off-by: Michael Brown --- src/interface/efi/efi_autoexec.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/interface/efi/efi_autoexec.c b/src/interface/efi/efi_autoexec.c index 44b8b645b..73ba5df33 100644 --- a/src/interface/efi/efi_autoexec.c +++ b/src/interface/efi/efi_autoexec.c @@ -43,7 +43,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ /** Timeout for autoexec script downloads */ -#define EFI_AUTOEXEC_TIMEOUT ( 2 * TICKS_PER_SEC ) +#define EFI_AUTOEXEC_TIMEOUT ( 30 * TICKS_PER_SEC ) + +/** Timeout for autoexec pending operation completion */ +#define EFI_AUTOEXEC_SYNC_TIMEOUT ( 1 * TICKS_PER_SEC ) /** Autoexec script image name */ #define EFI_AUTOEXEC_NAME "autoexec.ipxe" @@ -136,7 +139,7 @@ static int efi_autoexec_network ( EFI_HANDLE handle, struct image **image ) { } /* Ensure network exchanges have completed */ - sync ( EFI_AUTOEXEC_TIMEOUT ); + sync ( EFI_AUTOEXEC_SYNC_TIMEOUT ); err_open: err_cwuri: From ccd62005490de907105c92444631a5914500cb32 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 18 Feb 2025 15:28:55 +0000 Subject: [PATCH 21/50] [crypto] Start up RBG on demand if needed The ANS X9.82 specification implicitly assumes that the RBG_Startup function will be called before it is needed, and includes checks to make sure that Generate_function fails if this has not happened. However, there is no well-defined point at which the RBG_Startup function is to be called: it's just assumed that this happens as part of system startup. We currently call RBG_Startup to instantiate the DRBG as an iPXE startup function, with the corresponding shutdown function uninstantiating the DRBG. This works for most use cases, and avoids an otherwise unexpected user-visible delay when a caller first attempts to use the DRBG (e.g. by attempting an HTTPS download). The download of autoexec.ipxe for UEFI is triggered by the EFI root bus probe in efi_probe(). Both the root bus probe and the RBG startup function run at STARTUP_NORMAL, so there is no defined ordering between them. If the base URI for autoexec.ipxe uses HTTPS, then this may cause random bits to be requested before the RBG has been started. Extend the logic in rbg_generate() to automatically start up the RBG if startup has not already been attempted. If startup fails (e.g. because the entropy source is broken), then do not automatically retry since this could result in extremely long delays waiting for entropy that will never arrive. Reported-by: Michael Niehaus Signed-off-by: Michael Brown --- src/crypto/rbg.c | 43 ++++++++++++++++++++++++++++++++++++++---- src/include/ipxe/rbg.h | 23 ++++------------------ 2 files changed, 43 insertions(+), 23 deletions(-) diff --git a/src/crypto/rbg.c b/src/crypto/rbg.c index 4b45b3474..5e1c25f53 100644 --- a/src/crypto/rbg.c +++ b/src/crypto/rbg.c @@ -75,6 +75,9 @@ static int rbg_startup ( void ) { int len; int rc; + /* Record that startup has been attempted (even if unsuccessful) */ + rbg.started = 1; + /* Try to obtain system UUID for use as personalisation * string, in accordance with ANS X9.82 Part 3-2007 Section * 8.5.2. If no UUID is available, proceed without a @@ -97,6 +100,33 @@ static int rbg_startup ( void ) { return 0; } +/** + * Generate bits using RBG + * + * @v additional Additional input + * @v additional_len Length of additional input + * @v prediction_resist Prediction resistance is required + * @v data Output buffer + * @v len Length of output buffer + * @ret rc Return status code + * + * This is the RBG_Generate function defined in ANS X9.82 Part 4 + * (April 2011 Draft) Section 9.1.2.2. + */ +int rbg_generate ( const void *additional, size_t additional_len, + int prediction_resist, void *data, size_t len ) { + + /* Attempt startup, if not already attempted */ + if ( ! rbg.started ) + rbg_startup(); + + /* Generate bits. The DRBG will itself return an error if it + * is not valid (e.g. due to an instantiation failure). + */ + return drbg_generate ( &rbg.state, additional, additional_len, + prediction_resist, data, len ); +} + /** * Shut down RBG * @@ -105,16 +135,21 @@ static void rbg_shutdown ( void ) { /* Uninstantiate DRBG */ drbg_uninstantiate ( &rbg.state ); + + /* Clear startup attempted flag */ + rbg.started = 0; } /** RBG startup function */ static void rbg_startup_fn ( void ) { - /* Start up RBG. There is no way to report an error at this - * stage, but a failed startup will result in an invalid DRBG - * that refuses to generate bits. + /* Start up RBG (if not already started on demand). There is + * no way to report an error at this stage, but a failed + * startup will result in an invalid DRBG that refuses to + * generate bits. */ - rbg_startup(); + if ( ! rbg.started ) + rbg_startup(); } /** RBG shutdown function */ diff --git a/src/include/ipxe/rbg.h b/src/include/ipxe/rbg.h index 758238a65..4bf3055d1 100644 --- a/src/include/ipxe/rbg.h +++ b/src/include/ipxe/rbg.h @@ -16,28 +16,13 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct random_bit_generator { /** DRBG state */ struct drbg_state state; + /** Startup has been attempted */ + int started; }; extern struct random_bit_generator rbg; -/** - * Generate bits using RBG - * - * @v additional Additional input - * @v additional_len Length of additional input - * @v prediction_resist Prediction resistance is required - * @v data Output buffer - * @v len Length of output buffer - * @ret rc Return status code - * - * This is the RBG_Generate function defined in ANS X9.82 Part 4 - * (April 2011 Draft) Section 9.1.2.2. - */ -static inline int rbg_generate ( const void *additional, size_t additional_len, - int prediction_resist, void *data, - size_t len ) { - return drbg_generate ( &rbg.state, additional, additional_len, - prediction_resist, data, len ); -} +extern int rbg_generate ( const void *additional, size_t additional_len, + int prediction_resist, void *data, size_t len ); #endif /* _IPXE_RBG_H */ From e7595fe88d8c32354cd61055cbc9f2e7b3f91ff8 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 19 Feb 2025 13:12:29 +0000 Subject: [PATCH 22/50] [menu] Allow a post-activity timeout to be defined Allow the "--retimeout" option to be used to specify a timeout value that will be (re)applied after each keypress activity. This allows script authors to ensure that a single (potentially accidental) keypress will not pause the boot process indefinitely. Signed-off-by: Michael Brown --- src/hci/commands/dynui_cmd.c | 10 +++++++--- src/hci/tui/menu_ui.c | 16 +++++++++++----- src/include/ipxe/dynui.h | 3 ++- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/hci/commands/dynui_cmd.c b/src/hci/commands/dynui_cmd.c index d4446dc7c..6cad63868 100644 --- a/src/hci/commands/dynui_cmd.c +++ b/src/hci/commands/dynui_cmd.c @@ -207,8 +207,10 @@ static int item_exec ( int argc, char **argv ) { struct choose_options { /** Dynamic user interface name */ char *dynui; - /** Timeout */ + /** Initial timeout */ unsigned long timeout; + /** Post-activity timeout */ + unsigned long retimeout; /** Default selection */ char *select; /** Keep dynamic user interface */ @@ -223,6 +225,8 @@ static struct option_descriptor choose_opts[] = { struct choose_options, select, parse_string ), OPTION_DESC ( "timeout", 't', required_argument, struct choose_options, timeout, parse_timeout ), + OPTION_DESC ( "retimeout", 'r', required_argument, + struct choose_options, retimeout, parse_timeout ), OPTION_DESC ( "keep", 'k', no_argument, struct choose_options, keep, parse_flag ), }; @@ -259,8 +263,8 @@ static int choose_exec ( int argc, char **argv ) { goto err_parse_dynui; /* Show as menu */ - if ( ( rc = show_menu ( dynui, opts.timeout, opts.select, - &item ) ) != 0 ) + if ( ( rc = show_menu ( dynui, opts.timeout, opts.retimeout, + opts.select, &item ) ) != 0 ) goto err_show_menu; /* Apply default type if necessary */ diff --git a/src/hci/tui/menu_ui.c b/src/hci/tui/menu_ui.c index b7b52ee62..c7fad4a6b 100644 --- a/src/hci/tui/menu_ui.c +++ b/src/hci/tui/menu_ui.c @@ -53,8 +53,10 @@ struct menu_ui { struct dynamic_ui *dynui; /** Jump scroller */ struct jump_scroller scroll; - /** Timeout (0=indefinite) */ + /** Remaining timeout (0=indefinite) */ unsigned long timeout; + /** Post-activity timeout (0=indefinite) */ + unsigned long retimeout; }; /** @@ -180,8 +182,8 @@ static int menu_loop ( struct menu_ui *ui, struct dynamic_item **selected ) { if ( ui->timeout == 0 ) chosen = 1; } else { - /* Cancel any timeout */ - ui->timeout = 0; + /* Reset timeout after activity */ + ui->timeout = ui->retimeout; /* Handle scroll keys */ move = jump_scroll_key ( &ui->scroll, key ); @@ -241,12 +243,14 @@ static int menu_loop ( struct menu_ui *ui, struct dynamic_item **selected ) { * Show menu * * @v dynui Dynamic user interface - * @v timeout Timeout period, in ticks (0=indefinite) + * @v timeout Initial timeout period, in ticks (0=indefinite) + * @v retimeout Post-activity timeout period, in ticks (0=indefinite) * @ret selected Selected item * @ret rc Return status code */ int show_menu ( struct dynamic_ui *dynui, unsigned long timeout, - const char *select, struct dynamic_item **selected ) { + unsigned long retimeout, const char *select, + struct dynamic_item **selected ) { struct dynamic_item *item; struct menu_ui ui; char buf[ MENU_COLS + 1 /* NUL */ ]; @@ -258,6 +262,8 @@ int show_menu ( struct dynamic_ui *dynui, unsigned long timeout, ui.dynui = dynui; ui.scroll.rows = MENU_ROWS; ui.timeout = timeout; + ui.retimeout = retimeout; + list_for_each_entry ( item, &dynui->items, list ) { if ( item->name ) { if ( ! named_count ) diff --git a/src/include/ipxe/dynui.h b/src/include/ipxe/dynui.h index 67eb8b8f8..f47f5cb36 100644 --- a/src/include/ipxe/dynui.h +++ b/src/include/ipxe/dynui.h @@ -60,7 +60,8 @@ extern struct dynamic_item * dynui_item ( struct dynamic_ui *dynui, extern struct dynamic_item * dynui_shortcut ( struct dynamic_ui *dynui, int key ); extern int show_menu ( struct dynamic_ui *dynui, unsigned long timeout, - const char *select, struct dynamic_item **selected ); + unsigned long retimeout, const char *select, + struct dynamic_item **selected ); extern int show_form ( struct dynamic_ui *dynui ); #endif /* _IPXE_DYNUI_H */ From 12ea8c40741786ea33c866d131d510ae70897728 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 24 Feb 2025 14:04:06 +0000 Subject: [PATCH 23/50] [cpio] Allow for construction of parent directories as needed iPXE allows individual raw files to be automatically wrapped with suitable CPIO headers and injected into the magic initrd image as exposed to a booted Linux kernel. This feature is currently limited to placing files within directories that already exist in the initrd filesystem. Remove this limitation by adding the ability for iPXE to construct CPIO headers for parent directories as needed, under control of the "mkdir=" command-line argument. For example: initrd config.ign /usr/share/oem/config.ign mkdir=1 will create CPIO headers for the "/usr/share/oem" directory as well as for the "/usr/share/oem/config.ign" file itself. This simplifies the process of booting operating systems such as Flatcar Linux, which otherwise require the single "config.ign" file to be manually wrapped up as a CPIO archive solely in order to create the relevant parent directory entries. The value may be used to control the number of parent directory entries that are created. For example, "mkdir=2" would cause up to two parent directories to be created (i.e. "/usr/share" and "/usr/share/oem" in the above example). A negative value such as "mkdir=-1" may be used to create all parent directories up to the root of the tree. Do not create any parent directory entries by default, since doing so would potentially cause the modes and ownership information for existing directories to be overwritten. Signed-off-by: Michael Brown --- src/arch/x86/image/bzimage.c | 37 ++++++---- src/core/cpio.c | 135 +++++++++++++++++++++++++++-------- src/include/ipxe/cpio.h | 25 ++++++- src/interface/efi/efi_file.c | 16 +++-- 4 files changed, 161 insertions(+), 52 deletions(-) diff --git a/src/arch/x86/image/bzimage.c b/src/arch/x86/image/bzimage.c index 2c776147d..d2534bdad 100644 --- a/src/arch/x86/image/bzimage.c +++ b/src/arch/x86/image/bzimage.c @@ -353,24 +353,35 @@ static size_t bzimage_load_initrd ( struct image *image, const char *filename = cpio_name ( initrd ); struct cpio_header cpio; size_t offset; + size_t cpio_len; size_t pad_len; + size_t len; + unsigned int i; /* Skip hidden images */ if ( initrd->flags & IMAGE_HIDDEN ) return 0; - /* Create cpio header for non-prebuilt images */ - offset = cpio_header ( initrd, &cpio ); + /* Determine length of cpio headers for non-prebuilt images */ + len = 0; + for ( i = 0 ; ( cpio_len = cpio_header ( initrd, i, &cpio ) ) ; i++ ) + len += ( cpio_len + cpio_pad_len ( cpio_len ) ); - /* Copy in initrd image body (and cpio header if applicable) */ + /* Copy in initrd image body and construct any cpio headers */ if ( address ) { - memmove_user ( address, offset, initrd->data, 0, initrd->len ); - if ( offset ) { - memset_user ( address, 0, 0, offset ); - copy_to_user ( address, 0, &cpio, sizeof ( cpio ) ); - copy_to_user ( address, sizeof ( cpio ), filename, - cpio_name_len ( initrd ) ); + memmove_user ( address, len, initrd->data, 0, initrd->len ); + memset_user ( address, 0, 0, len ); + offset = 0; + for ( i = 0 ; ( cpio_len = cpio_header ( initrd, i, &cpio ) ) ; + i++ ) { + copy_to_user ( address, offset, &cpio, + sizeof ( cpio ) ); + copy_to_user ( address, ( offset + sizeof ( cpio ) ), + filename, + ( cpio_len - sizeof ( cpio ) ) ); + offset += ( cpio_len + cpio_pad_len ( cpio_len ) ); } + assert ( offset == len ); DBGC ( image, "bzImage %p initrd %p [%#08lx,%#08lx,%#08lx)" "%s%s\n", image, initrd, user_to_phys ( address, 0 ), user_to_phys ( address, offset ), @@ -379,14 +390,14 @@ static size_t bzimage_load_initrd ( struct image *image, DBGC2_MD5A ( image, user_to_phys ( address, offset ), user_to_virt ( address, offset ), initrd->len ); } - offset += initrd->len; + len += initrd->len; /* Zero-pad to next INITRD_ALIGN boundary */ - pad_len = ( ( -offset ) & ( INITRD_ALIGN - 1 ) ); + pad_len = ( ( -len ) & ( INITRD_ALIGN - 1 ) ); if ( address ) - memset_user ( address, offset, 0, pad_len ); + memset_user ( address, len, 0, pad_len ); - return offset; + return len; } /** diff --git a/src/core/cpio.c b/src/core/cpio.c index 4b607e260..4a7061960 100644 --- a/src/core/cpio.c +++ b/src/core/cpio.c @@ -34,13 +34,19 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include +/** CPIO default file mode */ +#define CPIO_DEFAULT_MODE 0644 + +/** CPIO directory mode */ +#define CPIO_DEFAULT_DIR_MODE 0755 + /** * Set field within a CPIO header * * @v field Field within CPIO header * @v value Value to set */ -void cpio_set_field ( char *field, unsigned long value ) { +static void cpio_set_field ( char *field, unsigned long value ) { char buf[9]; snprintf ( buf, sizeof ( buf ), "%08lx", value ); @@ -48,23 +54,49 @@ void cpio_set_field ( char *field, unsigned long value ) { } /** - * Get CPIO image filename + * Get maximum number of CPIO headers (i.e. number of path components) * * @v image Image - * @ret len CPIO filename length (0 for no filename) + * @ret max Maximum number of CPIO headers */ -size_t cpio_name_len ( struct image *image ) { +static unsigned int cpio_max ( struct image *image ) { const char *name = cpio_name ( image ); - char *sep; - size_t len; + unsigned int max = 0; + char c; /* Check for existence of CPIO filename */ if ( ! name ) return 0; - /* Locate separator (if any) */ - sep = strchr ( name, ' ' ); - len = ( sep ? ( ( size_t ) ( sep - name ) ) : strlen ( name ) ); + /* Count number of path separators */ + while ( ( ( c = *(name++) ) ) && ( c != ' ' ) ) { + if ( c == '/' ) + max++; + } + + return max; +} + +/** + * Get CPIO image filename + * + * @v image Image + * @v depth Path depth + * @ret len Filename length + */ +static size_t cpio_name_len ( struct image *image, unsigned int depth ) { + const char *name = cpio_name ( image ); + size_t len; + char c; + + /* Sanity check */ + assert ( name != NULL ); + + /* Calculate length up to specified path depth */ + for ( len = 0 ; ( ( ( c = name[len] ) ) && ( c != ' ' ) ) ; len++ ) { + if ( ( c == '/' ) && ( depth-- == 0 ) ) + break; + } return len; } @@ -73,55 +105,100 @@ size_t cpio_name_len ( struct image *image ) { * Parse CPIO image parameters * * @v image Image - * @v cpio CPIO header to fill in + * @v mode Mode to fill in + * @v count Number of CPIO headers to fill in */ -static void cpio_parse_cmdline ( struct image *image, - struct cpio_header *cpio ) { +static void cpio_parse_cmdline ( struct image *image, unsigned int *mode, + unsigned int *count ) { const char *arg; char *end; - unsigned int mode; - /* Look for "mode=" */ + /* Set default values */ + *mode = CPIO_DEFAULT_MODE; + *count = 1; + + /* Parse "mode=...", if present */ if ( ( arg = image_argument ( image, "mode=" ) ) ) { - mode = strtoul ( arg, &end, 8 /* Octal for file mode */ ); + *mode = strtoul ( arg, &end, 8 /* Octal for file mode */ ); if ( *end && ( *end != ' ' ) ) { DBGC ( image, "CPIO %p strange \"mode=\" " "terminator '%c'\n", image, *end ); } - cpio_set_field ( cpio->c_mode, ( 0100000 | mode ) ); } + + /* Parse "mkdir=...", if present */ + if ( ( arg = image_argument ( image, "mkdir=" ) ) ) { + *count += strtoul ( arg, &end, 10 ); + if ( *end && ( *end != ' ' ) ) { + DBGC ( image, "CPIO %p strange \"mkdir=\" " + "terminator '%c'\n", image, *end ); + } + } + + /* Allow "mkdir=-1" to request creation of full directory tree */ + if ( ! *count ) + *count = -1U; } /** * Construct CPIO header for image, if applicable * * @v image Image + * @v index CPIO header index * @v cpio CPIO header to fill in - * @ret len Length of magic CPIO header (including filename) + * @ret len Length of CPIO header (including name, excluding NUL) */ -size_t cpio_header ( struct image *image, struct cpio_header *cpio ) { +size_t cpio_header ( struct image *image, unsigned int index, + struct cpio_header *cpio ) { + const char *name = cpio_name ( image ); + unsigned int mode; + unsigned int count; + unsigned int max; + unsigned int depth; + unsigned int i; size_t name_len; size_t len; - /* Get filename length */ - name_len = cpio_name_len ( image ); + /* Parse command line arguments */ + cpio_parse_cmdline ( image, &mode, &count ); - /* Images with no filename are assumed to already be CPIO archives */ - if ( ! name_len ) + /* Determine number of CPIO headers to be constructed */ + max = cpio_max ( image ); + if ( count > max ) + count = max; + + /* Determine path depth of this CPIO header */ + if ( index >= count ) return 0; + depth = ( max - count + index + 1 ); + + /* Get filename length */ + name_len = cpio_name_len ( image, depth ); + + /* Calculate mode and length */ + if ( depth < max ) { + /* Directory */ + mode = ( CPIO_MODE_DIR | CPIO_DEFAULT_DIR_MODE ); + len = 0; + } else { + /* File */ + mode |= CPIO_MODE_FILE; + len = image->len; + } /* Construct CPIO header */ memset ( cpio, '0', sizeof ( *cpio ) ); memcpy ( cpio->c_magic, CPIO_MAGIC, sizeof ( cpio->c_magic ) ); - cpio_set_field ( cpio->c_mode, 0100644 ); + cpio_set_field ( cpio->c_mode, mode ); cpio_set_field ( cpio->c_nlink, 1 ); - cpio_set_field ( cpio->c_filesize, image->len ); + cpio_set_field ( cpio->c_filesize, len ); cpio_set_field ( cpio->c_namesize, ( name_len + 1 /* NUL */ ) ); - cpio_parse_cmdline ( image, cpio ); + DBGC ( image, "CPIO %s %d/%d \"", image->name, depth, max ); + for ( i = 0 ; i < name_len ; i++ ) + DBGC ( image, "%c", name[i] ); + DBGC ( image, "\"\n" ); + DBGC2_HDA ( image, 0, cpio, sizeof ( *cpio ) ); /* Calculate total length */ - len = ( ( sizeof ( *cpio ) + name_len + 1 /* NUL */ + CPIO_ALIGN - 1 ) - & ~( CPIO_ALIGN - 1 ) ); - - return len; + return ( sizeof ( *cpio ) + name_len ); } diff --git a/src/include/ipxe/cpio.h b/src/include/ipxe/cpio.h index 9c5e22d5a..c45c12b11 100644 --- a/src/include/ipxe/cpio.h +++ b/src/include/ipxe/cpio.h @@ -9,6 +9,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +#include #include /** A CPIO archive header @@ -50,6 +51,12 @@ struct cpio_header { /** CPIO magic */ #define CPIO_MAGIC "070701" +/** CPIO type for regular files */ +#define CPIO_MODE_FILE 0100000 + +/** CPIO type for directories */ +#define CPIO_MODE_DIR 0040000 + /** CPIO header length alignment */ #define CPIO_ALIGN 4 @@ -67,8 +74,20 @@ cpio_name ( struct image *image ) { return image->cmdline; } -extern void cpio_set_field ( char *field, unsigned long value ); -extern size_t cpio_name_len ( struct image *image ); -extern size_t cpio_header ( struct image *image, struct cpio_header *cpio ); +/** + * Get CPIO header zero-padding length + * + * @v len Length of CPIO header (including name, excluding NUL) + * @ret pad_len Padding length + */ +static inline __attribute__ (( always_inline )) size_t +cpio_pad_len ( size_t len ) { + + /* Pad by at least one byte (for name's terminating NUL) */ + return ( CPIO_ALIGN - ( len % CPIO_ALIGN ) ); +} + +extern size_t cpio_header ( struct image *image, unsigned int index, + struct cpio_header *cpio ); #endif /* _IPXE_CPIO_H */ diff --git a/src/interface/efi/efi_file.c b/src/interface/efi/efi_file.c index 2ae3a0cb4..e10c2ec4e 100644 --- a/src/interface/efi/efi_file.c +++ b/src/interface/efi/efi_file.c @@ -245,6 +245,7 @@ static size_t efi_file_read_initrd ( struct efi_file_reader *reader ) { size_t cpio_len; size_t name_len; size_t len; + unsigned int i; /* Read from file */ len = 0; @@ -263,15 +264,16 @@ static size_t efi_file_read_initrd ( struct efi_file_reader *reader ) { } len += efi_file_read_chunk ( reader, UNULL, pad_len ); - /* Read CPIO header, if applicable */ - cpio_len = cpio_header ( image, &cpio ); - if ( cpio_len ) { - name = cpio_name ( image ); - name_len = cpio_name_len ( image ); - pad_len = ( cpio_len - sizeof ( cpio ) - name_len ); + /* Read CPIO header(s), if applicable */ + name = cpio_name ( image ); + for ( i = 0 ; ( cpio_len = cpio_header ( image, i, &cpio ) ); + i++ ) { + name_len = ( cpio_len - sizeof ( cpio ) ); + pad_len = cpio_pad_len ( cpio_len ); DBGC ( file, "EFIFILE %s [%#08zx,%#08zx) %s header\n", efi_file_name ( file ), reader->pos, - ( reader->pos + cpio_len ), image->name ); + ( reader->pos + cpio_len + pad_len ), + image->name ); len += efi_file_read_chunk ( reader, virt_to_user ( &cpio ), sizeof ( cpio ) ); From be3a78eaf804a2c437aa055d5c1e2f4a1310a0c1 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 28 Feb 2025 11:27:21 +0000 Subject: [PATCH 24/50] [lkrnprefix] Support a longer version string The bzImage specification allows two bytes for the setup code jump instruction at offset 0x200, which limits its relative offset to +0x7f bytes. This currently imposes an upper limit on the length of the version string, which currently precedes the setup code. Fix by moving the version string to the .prefix.data section, so that it no longer affects the placement of the setup code. Originally-fixed-by: Miao Wang Signed-off-by: Michael Brown --- src/arch/x86/prefix/lkrnprefix.S | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/arch/x86/prefix/lkrnprefix.S b/src/arch/x86/prefix/lkrnprefix.S index c8a04c9d7..19d30141b 100644 --- a/src/arch/x86/prefix/lkrnprefix.S +++ b/src/arch/x86/prefix/lkrnprefix.S @@ -104,6 +104,7 @@ hardware_subarch: hardware_subarch_data: .byte 0, 0, 0, 0, 0, 0, 0, 0 + .section ".prefix.data", "aw", @progbits version_string: .asciz VERSION @@ -113,6 +114,7 @@ version_string: * */ + .section ".prefix", "ax", @progbits setup: /* Fix up code segment */ pushw %ds From 82fac51626ea524d60d77cebfbc98318b0a0f236 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 10 Mar 2025 11:15:04 +0000 Subject: [PATCH 25/50] [efi] Mark UsbHostController.h as a non-imported header The UsbHostController.h header has been removed from the EDK2 codebase since it was never defined in a released UEFI specification. However, we may still encounter it in the wild and so it is useful to retain the GUID and the corresponding protocol name for debug messages. Add an iPXE include guard to this file so that the EDK2 header import script will no longer attempt to import it from the EDK2 tree. Signed-off-by: Michael Brown --- src/include/ipxe/efi/Protocol/UsbHostController.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/include/ipxe/efi/Protocol/UsbHostController.h b/src/include/ipxe/efi/Protocol/UsbHostController.h index 5336f00b3..3ba091122 100644 --- a/src/include/ipxe/efi/Protocol/UsbHostController.h +++ b/src/include/ipxe/efi/Protocol/UsbHostController.h @@ -1,3 +1,6 @@ +#ifndef _IPXE_EFI_USBHOSTCONTROLLER_H +#define _IPXE_EFI_USBHOSTCONTROLLER_H + /** @file EFI_USB_HC_PROTOCOL as defined in EFI 1.10. @@ -501,3 +504,5 @@ struct _EFI_USB_HC_PROTOCOL { extern EFI_GUID gEfiUsbHcProtocolGuid; #endif + +#endif /* _IPXE_EFI_USBHOSTCONTROLLER_H */ From 32d706a9ff8d053d31cad7f996defccd92a634b4 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 10 Mar 2025 12:23:02 +0000 Subject: [PATCH 26/50] [build] Use -fshort-wchar when building EFI host utilities The EFI host utilities (such as elf2efi64, efirom, etc) include the EDK2 headers, which include static assertions to ensure that they are built with -fshort-wchar enabled. When building the host utilities, we currently bypass these assertions by defining MDE_CPU_EBC. The EBC compiler apparently does not support static assertions, and defining MDE_CPU_EBC therefore causes EDK2's Base.h to define STATIC_ASSERT() as a no-op. Newer versions of the EDK2 headers omit the check for MDE_CPU_EBC (and will presumably therefore fail to build with the EBC compiler). This causes our host utility builds to fail since the static assertion now detects that we are building with the host's default ABI (i.e. without enabling -fshort-wchar). Fix by enabling -fshort-wchar when building EFI host utilities. This produces binaries that are technically incompatible with the host ABI. However, since our host utilities never handle any wide-character strings, this nominal ABI incompatiblity has no effect. Signed-off-by: Michael Brown --- src/Makefile.housekeeping | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Makefile.housekeeping b/src/Makefile.housekeeping index 1926920fc..d99a6fe6a 100644 --- a/src/Makefile.housekeeping +++ b/src/Makefile.housekeeping @@ -484,6 +484,7 @@ CFLAGS += $(WORKAROUND_CFLAGS) $(EXTRA_CFLAGS) ASFLAGS += $(WORKAROUND_ASFLAGS) $(EXTRA_ASFLAGS) LDFLAGS += $(WORKAROUND_LDFLAGS) $(EXTRA_LDFLAGS) HOST_CFLAGS += -O2 -g +HOST_EFI_CFLAGS += -fshort-wchar # Inhibit -Werror if NO_WERROR is specified on make command line # @@ -1449,22 +1450,22 @@ CLEANUP += $(ZBIN) $(ELF2EFI32) : util/elf2efi.c $(MAKEDEPS) $(QM)$(ECHO) " [HOSTCC] $@" - $(Q)$(HOST_CC) $(HOST_CFLAGS) -idirafter include -DEFI_TARGET32 $< -o $@ + $(Q)$(HOST_CC) $(HOST_CFLAGS) $(HOST_EFI_CFLAGS) -idirafter include -DEFI_TARGET32 $< -o $@ CLEANUP += $(ELF2EFI32) $(ELF2EFI64) : util/elf2efi.c $(MAKEDEPS) $(QM)$(ECHO) " [HOSTCC] $@" - $(Q)$(HOST_CC) $(HOST_CFLAGS) -idirafter include -DEFI_TARGET64 $< -o $@ + $(Q)$(HOST_CC) $(HOST_CFLAGS) $(HOST_EFI_CFLAGS) -idirafter include -DEFI_TARGET64 $< -o $@ CLEANUP += $(ELF2EFI64) $(EFIROM) : util/efirom.c util/eficompress.c $(MAKEDEPS) $(QM)$(ECHO) " [HOSTCC] $@" - $(Q)$(HOST_CC) $(HOST_CFLAGS) -idirafter include -o $@ $< + $(Q)$(HOST_CC) $(HOST_CFLAGS) $(HOST_EFI_CFLAGS) -idirafter include -o $@ $< CLEANUP += $(EFIROM) $(EFIFATBIN) : util/efifatbin.c $(MAKEDEPS) $(QM)$(ECHO) " [HOSTCC] $@" - $(Q)$(HOST_CC) $(HOST_CFLAGS) -idirafter include -o $@ $< + $(Q)$(HOST_CC) $(HOST_CFLAGS) $(HOST_EFI_CFLAGS) -idirafter include -o $@ $< CLEANUP += $(EFIFATBIN) ############################################################################### From a3ede1078826137d3c13ca2891692b84669b7f73 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 10 Mar 2025 11:19:26 +0000 Subject: [PATCH 27/50] [efi] Update to current EDK2 headers Signed-off-by: Michael Brown --- src/include/ipxe/efi/Base.h | 10 +- src/include/ipxe/efi/Guid/Rng.h | 157 ++++++++++++++++++ .../ipxe/efi/IndustryStandard/Acpi30.h | 11 ++ .../ipxe/efi/IndustryStandard/Acpi40.h | 11 ++ .../ipxe/efi/IndustryStandard/Acpi50.h | 11 ++ .../ipxe/efi/IndustryStandard/Acpi51.h | 11 ++ .../ipxe/efi/IndustryStandard/Acpi60.h | 11 ++ src/include/ipxe/efi/Pi/PiHob.h | 3 +- src/include/ipxe/efi/Pi/PiStatusCode.h | 12 ++ src/include/ipxe/efi/Protocol/PxeBaseCode.h | 2 +- src/include/ipxe/efi/Protocol/Rng.h | 142 +--------------- src/include/ipxe/efi/Uefi/UefiSpec.h | 43 +++-- 12 files changed, 265 insertions(+), 159 deletions(-) create mode 100644 src/include/ipxe/efi/Guid/Rng.h diff --git a/src/include/ipxe/efi/Base.h b/src/include/ipxe/efi/Base.h index abc4e4627..ff90beef5 100644 --- a/src/include/ipxe/efi/Base.h +++ b/src/include/ipxe/efi/Base.h @@ -802,12 +802,12 @@ typedef UINTN *BASE_LIST; @param Message Raised compiler diagnostic message when expression is false. **/ -#ifdef MDE_CPU_EBC -#define STATIC_ASSERT(Expression, Message) -#elif defined (_MSC_EXTENSIONS) || defined (__cplusplus) +#if defined (__cplusplus) #define STATIC_ASSERT static_assert -#else +#elif defined (__GNUC__) || defined (__clang__) #define STATIC_ASSERT _Static_assert +#elif defined (_MSC_EXTENSIONS) +#define STATIC_ASSERT static_assert #endif // @@ -890,7 +890,7 @@ STATIC_ASSERT (ALIGNOF (__VERIFY_INT32_ENUM_SIZE) == sizeof (__VERIFY_INT32_ENUM @return A pointer to the structure from one of it's elements. **/ -#define BASE_CR(Record, TYPE, Field) ((TYPE *) ((CHAR8 *) (Record) - OFFSET_OF (TYPE, Field))) +#define BASE_CR(Record, TYPE, Field) ((TYPE *) (VOID *) ((CHAR8 *) (Record) - OFFSET_OF (TYPE, Field))) /** Checks whether a value is a power of two. diff --git a/src/include/ipxe/efi/Guid/Rng.h b/src/include/ipxe/efi/Guid/Rng.h new file mode 100644 index 000000000..26f6375cb --- /dev/null +++ b/src/include/ipxe/efi/Guid/Rng.h @@ -0,0 +1,157 @@ +/** @file + Random Number Generator (RNG) GUIDs and structures shared across RNG interfaces. + + Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.
+ Copyright (c) Microsoft Corporation. + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef RNG_GUID_H_ +#define RNG_GUID_H_ + +FILE_LICENCE ( BSD2_PATENT ); + +typedef struct _EFI_RNG_INTERFACE EFI_RNG_INTERFACE; + +/// +/// A selection of EFI_RNG_PROTOCOL algorithms. +/// The algorithms listed are optional, not meant to be exhaustive and be argmented by +/// vendors or other industry standards. +/// +typedef EFI_GUID EFI_RNG_ALGORITHM; + +/// +/// The algorithms corresponds to SP800-90 as defined in +/// NIST SP 800-90, "Recommendation for Random Number Generation Using Deterministic Random +/// Bit Generators", March 2007. +/// +#define EFI_RNG_ALGORITHM_SP800_90_HASH_256_GUID \ + { \ + 0xa7af67cb, 0x603b, 0x4d42, {0xba, 0x21, 0x70, 0xbf, 0xb6, 0x29, 0x3f, 0x96 } \ + } +#define EFI_RNG_ALGORITHM_SP800_90_HMAC_256_GUID \ + { \ + 0xc5149b43, 0xae85, 0x4f53, {0x99, 0x82, 0xb9, 0x43, 0x35, 0xd3, 0xa9, 0xe7 } \ + } +#define EFI_RNG_ALGORITHM_SP800_90_CTR_256_GUID \ + { \ + 0x44f0de6e, 0x4d8c, 0x4045, {0xa8, 0xc7, 0x4d, 0xd1, 0x68, 0x85, 0x6b, 0x9e } \ + } + +/// +/// The algorithms correspond to X9.31 as defined in +/// NIST, "Recommended Random Number Generator Based on ANSI X9.31 Appendix A.2.4 Using +/// the 3-Key Triple DES and AES Algorithm", January 2005. +/// +#define EFI_RNG_ALGORITHM_X9_31_3DES_GUID \ + { \ + 0x63c4785a, 0xca34, 0x4012, {0xa3, 0xc8, 0x0b, 0x6a, 0x32, 0x4f, 0x55, 0x46 } \ + } +#define EFI_RNG_ALGORITHM_X9_31_AES_GUID \ + { \ + 0xacd03321, 0x777e, 0x4d3d, {0xb1, 0xc8, 0x20, 0xcf, 0xd8, 0x88, 0x20, 0xc9 } \ + } + +/// +/// The "raw" algorithm, when supported, is intended to provide entropy directly from +/// the source, without it going through some deterministic random bit generator. +/// +#define EFI_RNG_ALGORITHM_RAW \ + { \ + 0xe43176d7, 0xb6e8, 0x4827, {0xb7, 0x84, 0x7f, 0xfd, 0xc4, 0xb6, 0x85, 0x61 } \ + } + +/// +/// The Arm Architecture states the RNDR that the DRBG algorithm should be compliant +/// with NIST SP800-90A, while not mandating a particular algorithm, so as to be +/// inclusive of different geographies. +/// +#define EFI_RNG_ALGORITHM_ARM_RNDR \ + { \ + 0x43d2fde3, 0x9d4e, 0x4d79, {0x02, 0x96, 0xa8, 0x9b, 0xca, 0x78, 0x08, 0x41} \ + } + +/** + Returns information about the random number generation implementation. + + @param[in] This A pointer to this interface instance. + @param[in,out] RNGAlgorithmListSize On input, the size in bytes of RNGAlgorithmList. + On output with a return code of EFI_SUCCESS, the size + in bytes of the data returned in RNGAlgorithmList. On output + with a return code of EFI_BUFFER_TOO_SMALL, + the size of RNGAlgorithmList required to obtain the list. + @param[out] RNGAlgorithmList A caller-allocated memory buffer filled by the driver + with one EFI_RNG_ALGORITHM element for each supported + RNG algorithm. The list must not change across multiple + calls to the same driver. The first algorithm in the list + is the default algorithm for the driver. + + @retval EFI_SUCCESS The RNG algorithm list was returned successfully. + @retval EFI_UNSUPPORTED The services is not supported by this driver. + @retval EFI_DEVICE_ERROR The list of algorithms could not be retrieved due to a + hardware or firmware error. + @retval EFI_INVALID_PARAMETER One or more of the parameters are incorrect. + @retval EFI_BUFFER_TOO_SMALL The buffer RNGAlgorithmList is too small to hold the result. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_RNG_GET_INFO)( + IN EFI_RNG_INTERFACE *This, + IN OUT UINTN *RNGAlgorithmListSize, + OUT EFI_RNG_ALGORITHM *RNGAlgorithmList + ); + +/** + Produces and returns an RNG value using either the default or specified RNG algorithm. + + @param[in] This A pointer to this interface instance. + @param[in] RNGAlgorithm A pointer to the EFI_RNG_ALGORITHM that identifies the RNG + algorithm to use. May be NULL in which case the function will + use its default RNG algorithm. + @param[in] RNGValueLength The length in bytes of the memory buffer pointed to by + RNGValue. The driver shall return exactly this numbers of bytes. + @param[out] RNGValue A caller-allocated memory buffer filled by the driver with the + resulting RNG value. + + @retval EFI_SUCCESS The RNG value was returned successfully. + @retval EFI_UNSUPPORTED The algorithm specified by RNGAlgorithm is not supported by + this driver. + @retval EFI_DEVICE_ERROR An RNG value could not be retrieved due to a hardware or + firmware error. + @retval EFI_NOT_READY There is not enough random data available to satisfy the length + requested by RNGValueLength. + @retval EFI_INVALID_PARAMETER RNGValue is NULL or RNGValueLength is zero. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_RNG_GET_RNG)( + IN EFI_RNG_INTERFACE *This, + IN EFI_RNG_ALGORITHM *RNGAlgorithm OPTIONAL, + IN UINTN RNGValueLength, + OUT UINT8 *RNGValue + ); + +/// +/// The Random Number Generator (RNG) interface provides random bits for use in +/// applications, or entropy for seeding other random number generators. +/// +/// This interface is shared between the RNG Protocol defined in the UEFI 2.4 Specification +/// and the RNG PPI defined in the PI 1.9 Specification. +/// +struct _EFI_RNG_INTERFACE { + EFI_RNG_GET_INFO GetInfo; + EFI_RNG_GET_RNG GetRNG; +}; + +extern EFI_GUID gEfiRngAlgorithmSp80090Hash256Guid; +extern EFI_GUID gEfiRngAlgorithmSp80090Hmac256Guid; +extern EFI_GUID gEfiRngAlgorithmSp80090Ctr256Guid; +extern EFI_GUID gEfiRngAlgorithmX9313DesGuid; +extern EFI_GUID gEfiRngAlgorithmX931AesGuid; +extern EFI_GUID gEfiRngAlgorithmRaw; +extern EFI_GUID gEfiRngAlgorithmArmRndr; + +#endif // #ifndef RNG_GUID_H_ diff --git a/src/include/ipxe/efi/IndustryStandard/Acpi30.h b/src/include/ipxe/efi/IndustryStandard/Acpi30.h index ff82bf203..867f26d36 100644 --- a/src/include/ipxe/efi/IndustryStandard/Acpi30.h +++ b/src/include/ipxe/efi/IndustryStandard/Acpi30.h @@ -2,6 +2,7 @@ ACPI 3.0 definitions from the ACPI Specification Revision 3.0b October 10, 2006 Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+ Copyright (C) 2025, Advanced Micro Devices, Inc. All rights reserved. SPDX-License-Identifier: BSD-2-Clause-Patent **/ @@ -12,6 +13,16 @@ FILE_LICENCE ( BSD2_PATENT ); #include +/// +/// _CSD Revision for ACPI 3.0 +/// +#define EFI_ACPI_3_0_AML_CSD_REVISION 0 + +/// +/// _CSD NumEntries for ACPI 3.0 +/// +#define EFI_ACPI_3_0_AML_CSD_NUM_ENTRIES 6 + // // Define for Descriptor // diff --git a/src/include/ipxe/efi/IndustryStandard/Acpi40.h b/src/include/ipxe/efi/IndustryStandard/Acpi40.h index 97b817039..6b8a0c981 100644 --- a/src/include/ipxe/efi/IndustryStandard/Acpi40.h +++ b/src/include/ipxe/efi/IndustryStandard/Acpi40.h @@ -2,6 +2,7 @@ ACPI 4.0 definitions from the ACPI Specification Revision 4.0a April 5, 2010 Copyright (c) 2010 - 2022, Intel Corporation. All rights reserved.
+ Copyright (C) 2025, Advanced Micro Devices, Inc. All rights reserved. SPDX-License-Identifier: BSD-2-Clause-Patent **/ @@ -12,6 +13,16 @@ FILE_LICENCE ( BSD2_PATENT ); #include +/// +/// _CSD Revision for ACPI 4.0 +/// +#define EFI_ACPI_4_0_AML_CSD_REVISION 0 + +/// +/// _CSD NumEntries for ACPI 4.0 +/// +#define EFI_ACPI_4_0_AML_CSD_NUM_ENTRIES 6 + /// /// _PSD Revision for ACPI 4.0 /// diff --git a/src/include/ipxe/efi/IndustryStandard/Acpi50.h b/src/include/ipxe/efi/IndustryStandard/Acpi50.h index 2addcb008..600858267 100644 --- a/src/include/ipxe/efi/IndustryStandard/Acpi50.h +++ b/src/include/ipxe/efi/IndustryStandard/Acpi50.h @@ -4,6 +4,7 @@ Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
Copyright (c) 2011 - 2022, Intel Corporation. All rights reserved.
Copyright (c) 2020, ARM Ltd. All rights reserved.
+ Copyright (C) 2025, Advanced Micro Devices, Inc. All rights reserved. SPDX-License-Identifier: BSD-2-Clause-Patent **/ @@ -14,6 +15,16 @@ FILE_LICENCE ( BSD2_PATENT ); #include +/// +/// _CSD Revision for ACPI 5.0 +/// +#define EFI_ACPI_5_0_AML_CSD_REVISION 0 + +/// +/// _CSD NumEntries for ACPI 5.0 +/// +#define EFI_ACPI_5_0_AML_CSD_NUM_ENTRIES 6 + // // Define for Descriptor // diff --git a/src/include/ipxe/efi/IndustryStandard/Acpi51.h b/src/include/ipxe/efi/IndustryStandard/Acpi51.h index 101d7b5d1..de2ed987c 100644 --- a/src/include/ipxe/efi/IndustryStandard/Acpi51.h +++ b/src/include/ipxe/efi/IndustryStandard/Acpi51.h @@ -5,6 +5,7 @@ Copyright (c) 2014 - 2022, Intel Corporation. All rights reserved.
(C) Copyright 2015 Hewlett Packard Enterprise Development LP
Copyright (c) 2020, ARM Ltd. All rights reserved.
+ Copyright (C) 2024, Advanced Micro Devices, Inc. All rights reserved. SPDX-License-Identifier: BSD-2-Clause-Patent **/ @@ -15,6 +16,16 @@ FILE_LICENCE ( BSD2_PATENT ); #include +/// +/// _CSD Revision for ACPI 5.1 +/// +#define EFI_ACPI_5_1_AML_CSD_REVISION 0 + +/// +/// _CSD NumEntries for ACPI 5.1 +/// +#define EFI_ACPI_5_1_AML_CSD_NUM_ENTRIES 6 + /// /// _PSD Revision for ACPI 5.1 /// diff --git a/src/include/ipxe/efi/IndustryStandard/Acpi60.h b/src/include/ipxe/efi/IndustryStandard/Acpi60.h index 19c51df5c..2b7d52ac4 100644 --- a/src/include/ipxe/efi/IndustryStandard/Acpi60.h +++ b/src/include/ipxe/efi/IndustryStandard/Acpi60.h @@ -4,6 +4,7 @@ Copyright (c) 2015 - 2022, Intel Corporation. All rights reserved.
(C) Copyright 2015-2016 Hewlett Packard Enterprise Development LP
Copyright (c) 2020, ARM Ltd. All rights reserved.
+ Copyright (C) 2025, Advanced Micro Devices, Inc. All rights reserved. SPDX-License-Identifier: BSD-2-Clause-Patent **/ @@ -14,6 +15,16 @@ FILE_LICENCE ( BSD2_PATENT ); #include +/// +/// _CSD Revision for ACPI 6.0 +/// +#define EFI_ACPI_6_0_AML_CSD_REVISION 0 + +/// +/// _CSD NumEntries for ACPI 6.0 +/// +#define EFI_ACPI_6_0_AML_CSD_NUM_ENTRIES 6 + /// /// _PSD Revision for ACPI 6.0 /// diff --git a/src/include/ipxe/efi/Pi/PiHob.h b/src/include/ipxe/efi/Pi/PiHob.h index 1a19c34a0..7cc19c61d 100644 --- a/src/include/ipxe/efi/Pi/PiHob.h +++ b/src/include/ipxe/efi/Pi/PiHob.h @@ -5,7 +5,7 @@ Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent @par Revision Reference: - PI Version 1.6 + PI Version 1.9 **/ @@ -293,6 +293,7 @@ typedef UINT32 EFI_RESOURCE_ATTRIBUTE_TYPE; #define EFI_RESOURCE_ATTRIBUTE_ENCRYPTED 0x04000000 #define EFI_RESOURCE_ATTRIBUTE_SPECIAL_PURPOSE 0x08000000 +#define EFI_RESOURCE_ATTRIBUTE_HOT_PLUGGABLE 0x10000000 // // Physical memory relative reliability attribute. This // memory provides higher reliability relative to other diff --git a/src/include/ipxe/efi/Pi/PiStatusCode.h b/src/include/ipxe/efi/Pi/PiStatusCode.h index 427e5061d..3bdc96edf 100644 --- a/src/include/ipxe/efi/Pi/PiStatusCode.h +++ b/src/include/ipxe/efi/Pi/PiStatusCode.h @@ -2,6 +2,7 @@ StatusCode related definitions in PI. Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.
+Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: BSD-2-Clause-Patent @par Revision Reference: @@ -142,6 +143,7 @@ typedef struct { #define EFI_COMPUTING_UNIT_CACHE (EFI_COMPUTING_UNIT | 0x00040000) #define EFI_COMPUTING_UNIT_MEMORY (EFI_COMPUTING_UNIT | 0x00050000) #define EFI_COMPUTING_UNIT_CHIPSET (EFI_COMPUTING_UNIT | 0x00060000) +#define EFI_COMPUTING_UNIT_MANAGEABILITY (EFI_COMPUTING_UNIT | 0x00070000) ///@} /// @@ -345,6 +347,16 @@ typedef struct { #define EFI_CHIPSET_EC_INTRUDER_DETECT (EFI_SUBCLASS_SPECIFIC | 0x00000003) ///@} +/// +/// Computing Unit Manageability Subclass Error Code definitions. +/// The detail information is reported by REPORT_STATUS_CODE_WITH_EXTENDED_DATA +// with ASCII string in EFI_STATUS_CODE_STRING_DATA. +///@{ +#define EFI_MANAGEABILITY_EC_REDFISH_COMMUNICATION_ERROR (EFI_SUBCLASS_SPECIFIC | 0x00000000) +#define EFI_MANAGEABILITY_EC_REDFISH_HOST_INTERFACE_ERROR (EFI_SUBCLASS_SPECIFIC | 0x00000001) +#define EFI_MANAGEABILITY_EC_REDFISH_BOOTSTRAP_CREDENTIAL_ERROR (EFI_SUBCLASS_SPECIFIC | 0x00000002) +///@} + /// /// Peripheral Subclass definitions. /// Values of 12-127 are reserved for future use by this specification. diff --git a/src/include/ipxe/efi/Protocol/PxeBaseCode.h b/src/include/ipxe/efi/Protocol/PxeBaseCode.h index 20efd7280..212dd677b 100644 --- a/src/include/ipxe/efi/Protocol/PxeBaseCode.h +++ b/src/include/ipxe/efi/Protocol/PxeBaseCode.h @@ -36,7 +36,7 @@ typedef EFI_PXE_BASE_CODE_PROTOCOL EFI_PXE_BASE_CODE; /// /// Default IP TTL and ToS. /// -#define DEFAULT_TTL 16 +#define DEFAULT_TTL 64 #define DEFAULT_ToS 0 /// diff --git a/src/include/ipxe/efi/Protocol/Rng.h b/src/include/ipxe/efi/Protocol/Rng.h index 92d648bee..a252a222c 100644 --- a/src/include/ipxe/efi/Protocol/Rng.h +++ b/src/include/ipxe/efi/Protocol/Rng.h @@ -8,11 +8,13 @@ SPDX-License-Identifier: BSD-2-Clause-Patent **/ -#ifndef __EFI_RNG_PROTOCOL_H__ -#define __EFI_RNG_PROTOCOL_H__ +#ifndef EFI_RNG_PROTOCOL_H_ +#define EFI_RNG_PROTOCOL_H_ FILE_LICENCE ( BSD2_PATENT ); +#include + /// /// Global ID for the Random Number Generator Protocol /// @@ -21,142 +23,8 @@ FILE_LICENCE ( BSD2_PATENT ); 0x3152bca5, 0xeade, 0x433d, {0x86, 0x2e, 0xc0, 0x1c, 0xdc, 0x29, 0x1f, 0x44 } \ } -typedef struct _EFI_RNG_PROTOCOL EFI_RNG_PROTOCOL; - -/// -/// A selection of EFI_RNG_PROTOCOL algorithms. -/// The algorithms listed are optional, not meant to be exhaustive and be argmented by -/// vendors or other industry standards. -/// - -typedef EFI_GUID EFI_RNG_ALGORITHM; - -/// -/// The algorithms corresponds to SP800-90 as defined in -/// NIST SP 800-90, "Recommendation for Random Number Generation Using Deterministic Random -/// Bit Generators", March 2007. -/// -#define EFI_RNG_ALGORITHM_SP800_90_HASH_256_GUID \ - { \ - 0xa7af67cb, 0x603b, 0x4d42, {0xba, 0x21, 0x70, 0xbf, 0xb6, 0x29, 0x3f, 0x96 } \ - } -#define EFI_RNG_ALGORITHM_SP800_90_HMAC_256_GUID \ - { \ - 0xc5149b43, 0xae85, 0x4f53, {0x99, 0x82, 0xb9, 0x43, 0x35, 0xd3, 0xa9, 0xe7 } \ - } -#define EFI_RNG_ALGORITHM_SP800_90_CTR_256_GUID \ - { \ - 0x44f0de6e, 0x4d8c, 0x4045, {0xa8, 0xc7, 0x4d, 0xd1, 0x68, 0x85, 0x6b, 0x9e } \ - } -/// -/// The algorithms correspond to X9.31 as defined in -/// NIST, "Recommended Random Number Generator Based on ANSI X9.31 Appendix A.2.4 Using -/// the 3-Key Triple DES and AES Algorithm", January 2005. -/// -#define EFI_RNG_ALGORITHM_X9_31_3DES_GUID \ - { \ - 0x63c4785a, 0xca34, 0x4012, {0xa3, 0xc8, 0x0b, 0x6a, 0x32, 0x4f, 0x55, 0x46 } \ - } -#define EFI_RNG_ALGORITHM_X9_31_AES_GUID \ - { \ - 0xacd03321, 0x777e, 0x4d3d, {0xb1, 0xc8, 0x20, 0xcf, 0xd8, 0x88, 0x20, 0xc9 } \ - } -/// -/// The "raw" algorithm, when supported, is intended to provide entropy directly from -/// the source, without it going through some deterministic random bit generator. -/// -#define EFI_RNG_ALGORITHM_RAW \ - { \ - 0xe43176d7, 0xb6e8, 0x4827, {0xb7, 0x84, 0x7f, 0xfd, 0xc4, 0xb6, 0x85, 0x61 } \ - } -/// -/// The Arm Architecture states the RNDR that the DRBG algorithm should be compliant -/// with NIST SP800-90A, while not mandating a particular algorithm, so as to be -/// inclusive of different geographies. -/// -#define EFI_RNG_ALGORITHM_ARM_RNDR \ - { \ - 0x43d2fde3, 0x9d4e, 0x4d79, {0x02, 0x96, 0xa8, 0x9b, 0xca, 0x78, 0x08, 0x41} \ - } - -/** - Returns information about the random number generation implementation. - - @param[in] This A pointer to the EFI_RNG_PROTOCOL instance. - @param[in,out] RNGAlgorithmListSize On input, the size in bytes of RNGAlgorithmList. - On output with a return code of EFI_SUCCESS, the size - in bytes of the data returned in RNGAlgorithmList. On output - with a return code of EFI_BUFFER_TOO_SMALL, - the size of RNGAlgorithmList required to obtain the list. - @param[out] RNGAlgorithmList A caller-allocated memory buffer filled by the driver - with one EFI_RNG_ALGORITHM element for each supported - RNG algorithm. The list must not change across multiple - calls to the same driver. The first algorithm in the list - is the default algorithm for the driver. - - @retval EFI_SUCCESS The RNG algorithm list was returned successfully. - @retval EFI_UNSUPPORTED The services is not supported by this driver. - @retval EFI_DEVICE_ERROR The list of algorithms could not be retrieved due to a - hardware or firmware error. - @retval EFI_INVALID_PARAMETER One or more of the parameters are incorrect. - @retval EFI_BUFFER_TOO_SMALL The buffer RNGAlgorithmList is too small to hold the result. - -**/ -typedef -EFI_STATUS -(EFIAPI *EFI_RNG_GET_INFO)( - IN EFI_RNG_PROTOCOL *This, - IN OUT UINTN *RNGAlgorithmListSize, - OUT EFI_RNG_ALGORITHM *RNGAlgorithmList - ); - -/** - Produces and returns an RNG value using either the default or specified RNG algorithm. - - @param[in] This A pointer to the EFI_RNG_PROTOCOL instance. - @param[in] RNGAlgorithm A pointer to the EFI_RNG_ALGORITHM that identifies the RNG - algorithm to use. May be NULL in which case the function will - use its default RNG algorithm. - @param[in] RNGValueLength The length in bytes of the memory buffer pointed to by - RNGValue. The driver shall return exactly this numbers of bytes. - @param[out] RNGValue A caller-allocated memory buffer filled by the driver with the - resulting RNG value. - - @retval EFI_SUCCESS The RNG value was returned successfully. - @retval EFI_UNSUPPORTED The algorithm specified by RNGAlgorithm is not supported by - this driver. - @retval EFI_DEVICE_ERROR An RNG value could not be retrieved due to a hardware or - firmware error. - @retval EFI_NOT_READY There is not enough random data available to satisfy the length - requested by RNGValueLength. - @retval EFI_INVALID_PARAMETER RNGValue is NULL or RNGValueLength is zero. - -**/ -typedef -EFI_STATUS -(EFIAPI *EFI_RNG_GET_RNG)( - IN EFI_RNG_PROTOCOL *This, - IN EFI_RNG_ALGORITHM *RNGAlgorithm OPTIONAL, - IN UINTN RNGValueLength, - OUT UINT8 *RNGValue - ); - -/// -/// The Random Number Generator (RNG) protocol provides random bits for use in -/// applications, or entropy for seeding other random number generators. -/// -struct _EFI_RNG_PROTOCOL { - EFI_RNG_GET_INFO GetInfo; - EFI_RNG_GET_RNG GetRNG; -}; +typedef EFI_RNG_INTERFACE EFI_RNG_PROTOCOL; extern EFI_GUID gEfiRngProtocolGuid; -extern EFI_GUID gEfiRngAlgorithmSp80090Hash256Guid; -extern EFI_GUID gEfiRngAlgorithmSp80090Hmac256Guid; -extern EFI_GUID gEfiRngAlgorithmSp80090Ctr256Guid; -extern EFI_GUID gEfiRngAlgorithmX9313DesGuid; -extern EFI_GUID gEfiRngAlgorithmX931AesGuid; -extern EFI_GUID gEfiRngAlgorithmRaw; -extern EFI_GUID gEfiRngAlgorithmArmRndr; #endif diff --git a/src/include/ipxe/efi/Uefi/UefiSpec.h b/src/include/ipxe/efi/Uefi/UefiSpec.h index 4dfc346df..b007afee0 100644 --- a/src/include/ipxe/efi/Uefi/UefiSpec.h +++ b/src/include/ipxe/efi/Uefi/UefiSpec.h @@ -107,6 +107,16 @@ typedef enum { // #define EFI_MEMORY_CPU_CRYPTO 0x0000000000080000ULL +// +// If this flag is set, the memory region is present and capable of having +// memory dynamically removed from the platform. This attribute serves as +// a hint to the OS prior to its ACPI subsystem initialization to avoid +// allocating this memory for core OS data or code that cannot be dynamically +// relocated at runtime. If this flag is clear, the memory region is not +// capable of being dynamically removed from the platform at runtime. +// +#define EFI_MEMORY_HOT_PLUGGABLE 0x0000000000100000 + // // Runtime memory attribute // @@ -1841,21 +1851,24 @@ EFI_STATUS // // EFI Runtime Services Table // -#define EFI_SYSTEM_TABLE_SIGNATURE SIGNATURE_64 ('I','B','I',' ','S','Y','S','T') -#define EFI_2_80_SYSTEM_TABLE_REVISION ((2 << 16) | (80)) -#define EFI_2_70_SYSTEM_TABLE_REVISION ((2 << 16) | (70)) -#define EFI_2_60_SYSTEM_TABLE_REVISION ((2 << 16) | (60)) -#define EFI_2_50_SYSTEM_TABLE_REVISION ((2 << 16) | (50)) -#define EFI_2_40_SYSTEM_TABLE_REVISION ((2 << 16) | (40)) -#define EFI_2_31_SYSTEM_TABLE_REVISION ((2 << 16) | (31)) -#define EFI_2_30_SYSTEM_TABLE_REVISION ((2 << 16) | (30)) -#define EFI_2_20_SYSTEM_TABLE_REVISION ((2 << 16) | (20)) -#define EFI_2_10_SYSTEM_TABLE_REVISION ((2 << 16) | (10)) -#define EFI_2_00_SYSTEM_TABLE_REVISION ((2 << 16) | (00)) -#define EFI_1_10_SYSTEM_TABLE_REVISION ((1 << 16) | (10)) -#define EFI_1_02_SYSTEM_TABLE_REVISION ((1 << 16) | (02)) -#define EFI_SYSTEM_TABLE_REVISION EFI_2_70_SYSTEM_TABLE_REVISION -#define EFI_SPECIFICATION_VERSION EFI_SYSTEM_TABLE_REVISION +#define EFI_SYSTEM_TABLE_SIGNATURE SIGNATURE_64 ('I','B','I',' ','S','Y','S','T') +#define EFI_2_110_SYSTEM_TABLE_REVISION ((2 << 16) | (110)) +#define EFI_2_100_SYSTEM_TABLE_REVISION ((2 << 16) | (100)) +#define EFI_2_90_SYSTEM_TABLE_REVISION ((2 << 16) | (90)) +#define EFI_2_80_SYSTEM_TABLE_REVISION ((2 << 16) | (80)) +#define EFI_2_70_SYSTEM_TABLE_REVISION ((2 << 16) | (70)) +#define EFI_2_60_SYSTEM_TABLE_REVISION ((2 << 16) | (60)) +#define EFI_2_50_SYSTEM_TABLE_REVISION ((2 << 16) | (50)) +#define EFI_2_40_SYSTEM_TABLE_REVISION ((2 << 16) | (40)) +#define EFI_2_31_SYSTEM_TABLE_REVISION ((2 << 16) | (31)) +#define EFI_2_30_SYSTEM_TABLE_REVISION ((2 << 16) | (30)) +#define EFI_2_20_SYSTEM_TABLE_REVISION ((2 << 16) | (20)) +#define EFI_2_10_SYSTEM_TABLE_REVISION ((2 << 16) | (10)) +#define EFI_2_00_SYSTEM_TABLE_REVISION ((2 << 16) | (00)) +#define EFI_1_10_SYSTEM_TABLE_REVISION ((1 << 16) | (10)) +#define EFI_1_02_SYSTEM_TABLE_REVISION ((1 << 16) | (02)) +#define EFI_SYSTEM_TABLE_REVISION EFI_2_70_SYSTEM_TABLE_REVISION +#define EFI_SPECIFICATION_VERSION EFI_SYSTEM_TABLE_REVISION #define EFI_RUNTIME_SERVICES_SIGNATURE SIGNATURE_64 ('R','U','N','T','S','E','R','V') #define EFI_RUNTIME_SERVICES_REVISION EFI_SPECIFICATION_VERSION From 8706ae36d3bbfaee3ac0e79555f68ac43e1da1af Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 10 Mar 2025 11:20:15 +0000 Subject: [PATCH 28/50] [efi] Add EFI_SIGNATURE_LIST header and GUID definitions Signed-off-by: Michael Brown --- src/include/ipxe/efi/Guid/GlobalVariable.h | 194 +++++++++ .../ipxe/efi/Guid/ImageAuthentication.h | 387 ++++++++++++++++++ src/include/ipxe/efi/Protocol/Hash.h | 171 ++++++++ src/include/ipxe/efi/efi.h | 1 + src/interface/efi/efi_debug.c | 2 + src/interface/efi/efi_guid.c | 4 + 6 files changed, 759 insertions(+) create mode 100644 src/include/ipxe/efi/Guid/GlobalVariable.h create mode 100644 src/include/ipxe/efi/Guid/ImageAuthentication.h create mode 100644 src/include/ipxe/efi/Protocol/Hash.h diff --git a/src/include/ipxe/efi/Guid/GlobalVariable.h b/src/include/ipxe/efi/Guid/GlobalVariable.h new file mode 100644 index 000000000..e8f4e0d2b --- /dev/null +++ b/src/include/ipxe/efi/Guid/GlobalVariable.h @@ -0,0 +1,194 @@ +/** @file + GUID for EFI (NVRAM) Variables. + + Copyright (c) 2006 - 2024, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + + @par Revision Reference: + GUID defined in UEFI 2.1 +**/ + +#ifndef __GLOBAL_VARIABLE_GUID_H__ +#define __GLOBAL_VARIABLE_GUID_H__ + +FILE_LICENCE ( BSD2_PATENT ); + +#define EFI_GLOBAL_VARIABLE \ + { \ + 0x8BE4DF61, 0x93CA, 0x11d2, {0xAA, 0x0D, 0x00, 0xE0, 0x98, 0x03, 0x2B, 0x8C } \ + } + +extern EFI_GUID gEfiGlobalVariableGuid; + +// +// Follow UEFI 2.4 spec: +// To prevent name collisions with possible future globally defined variables, +// other internal firmware data variables that are not defined here must be +// saved with a unique VendorGuid other than EFI_GLOBAL_VARIABLE or +// any other GUID defined by the UEFI Specification. Implementations must +// only permit the creation of variables with a UEFI Specification-defined +// VendorGuid when these variables are documented in the UEFI Specification. +// +// Note: except the globally defined variables defined below, the spec also defines +// L"Boot####" - A boot load option. +// L"Driver####" - A driver load option. +// L"SysPrep####" - A System Prep application load option. +// L"Key####" - Describes hot key relationship with a Boot#### load option. +// The attribute for them is NV+BS+RT, #### is a printed hex value, and no 0x or h +// is included in the hex value. They can not be expressed as a #define like other globally +// defined variables, it is because we can not list the Boot0000, Boot0001, etc one by one. +// + +/// +/// The language codes that the firmware supports. This value is deprecated. +/// Its attribute is BS+RT. +/// +#define EFI_LANG_CODES_VARIABLE_NAME L"LangCodes" +/// +/// The language code that the system is configured for. This value is deprecated. +/// Its attribute is NV+BS+RT. +/// +#define EFI_LANG_VARIABLE_NAME L"Lang" +/// +/// The firmware's boot managers timeout, in seconds, before initiating the default boot selection. +/// Its attribute is NV+BS+RT. +/// +#define EFI_TIME_OUT_VARIABLE_NAME L"Timeout" +/// +/// The language codes that the firmware supports. +/// Its attribute is BS+RT. +/// +#define EFI_PLATFORM_LANG_CODES_VARIABLE_NAME L"PlatformLangCodes" +/// +/// The language code that the system is configured for. +/// Its attribute is NV+BS+RT. +/// +#define EFI_PLATFORM_LANG_VARIABLE_NAME L"PlatformLang" +/// +/// The device path of the default input/output/error output console. +/// Its attribute is NV+BS+RT. +/// +#define EFI_CON_IN_VARIABLE_NAME L"ConIn" +#define EFI_CON_OUT_VARIABLE_NAME L"ConOut" +#define EFI_ERR_OUT_VARIABLE_NAME L"ErrOut" +/// +/// The device path of all possible input/output/error output devices. +/// Its attribute is BS+RT. +/// +#define EFI_CON_IN_DEV_VARIABLE_NAME L"ConInDev" +#define EFI_CON_OUT_DEV_VARIABLE_NAME L"ConOutDev" +#define EFI_ERR_OUT_DEV_VARIABLE_NAME L"ErrOutDev" +/// +/// The ordered boot option load list. +/// Its attribute is NV+BS+RT. +/// +#define EFI_BOOT_ORDER_VARIABLE_NAME L"BootOrder" +/// +/// The boot option for the next boot only. +/// Its attribute is NV+BS+RT. +/// +#define EFI_BOOT_NEXT_VARIABLE_NAME L"BootNext" +/// +/// The boot option that was selected for the current boot. +/// Its attribute is BS+RT. +/// +#define EFI_BOOT_CURRENT_VARIABLE_NAME L"BootCurrent" +/// +/// The types of boot options supported by the boot manager. Should be treated as read-only. +/// Its attribute is BS+RT. +/// +#define EFI_BOOT_OPTION_SUPPORT_VARIABLE_NAME L"BootOptionSupport" +/// +/// The ordered driver load option list. +/// Its attribute is NV+BS+RT. +/// +#define EFI_DRIVER_ORDER_VARIABLE_NAME L"DriverOrder" +/// +/// The ordered System Prep Application load option list. +/// Its attribute is NV+BS+RT. +/// +#define EFI_SYS_PREP_ORDER_VARIABLE_NAME L"SysPrepOrder" +/// +/// Identifies the level of hardware error record persistence +/// support implemented by the platform. This variable is +/// only modified by firmware and is read-only to the OS. +/// Its attribute is NV+BS+RT. +/// +#define EFI_HW_ERR_REC_SUPPORT_VARIABLE_NAME L"HwErrRecSupport" +/// +/// Whether the system is operating in setup mode (1) or not (0). +/// All other values are reserved. Should be treated as read-only. +/// Its attribute is BS+RT. +/// +#define EFI_SETUP_MODE_NAME L"SetupMode" +/// +/// The Key Exchange Key Signature Database. +/// Its attribute is NV+BS+RT+AT. +/// +#define EFI_KEY_EXCHANGE_KEY_NAME L"KEK" +/// +/// The public Platform Key. +/// Its attribute is NV+BS+RT+AT. +/// +#define EFI_PLATFORM_KEY_NAME L"PK" +/// +/// Array of GUIDs representing the type of signatures supported +/// by the platform firmware. Should be treated as read-only. +/// Its attribute is BS+RT. +/// +#define EFI_SIGNATURE_SUPPORT_NAME L"SignatureSupport" +/// +/// Whether the platform firmware is operating in Secure boot mode (1) or not (0). +/// All other values are reserved. Should be treated as read-only. +/// Its attribute is BS+RT. +/// +#define EFI_SECURE_BOOT_MODE_NAME L"SecureBoot" +/// +/// The OEM's default Key Exchange Key Signature Database. Should be treated as read-only. +/// Its attribute is BS+RT. +/// +#define EFI_KEK_DEFAULT_VARIABLE_NAME L"KEKDefault" +/// +/// The OEM's default public Platform Key. Should be treated as read-only. +/// Its attribute is BS+RT. +/// +#define EFI_PK_DEFAULT_VARIABLE_NAME L"PKDefault" +/// +/// The OEM's default secure boot signature store. Should be treated as read-only. +/// Its attribute is BS+RT. +/// +#define EFI_DB_DEFAULT_VARIABLE_NAME L"dbDefault" +/// +/// The OEM's default secure boot blacklist signature store. Should be treated as read-only. +/// Its attribute is BS+RT. +/// +#define EFI_DBX_DEFAULT_VARIABLE_NAME L"dbxDefault" +/// +/// The OEM's default secure boot timestamp signature store. Should be treated as read-only. +/// Its attribute is BS+RT. +/// +#define EFI_DBT_DEFAULT_VARIABLE_NAME L"dbtDefault" +/// +/// Allows the firmware to indicate supported features and actions to the OS. +/// Its attribute is BS+RT. +/// +#define EFI_OS_INDICATIONS_SUPPORT_VARIABLE_NAME L"OsIndicationsSupported" +/// +/// Allows the OS to request the firmware to enable certain features and to take certain actions. +/// Its attribute is NV+BS+RT. +/// +#define EFI_OS_INDICATIONS_VARIABLE_NAME L"OsIndications" +/// +/// Whether the system is configured to use only vendor provided +/// keys or not. Should be treated as read-only. +/// Its attribute is BS+RT. +/// +#define EFI_VENDOR_KEYS_VARIABLE_NAME L"VendorKeys" + +/// +/// Whether the platform firmware is operating in device authentication boot mode (1) or not (0). +/// The content is UINT8. +/// +#define EFI_DEVICE_AUTH_BOOT_MODE_NAME L"devAuthBoot" + +#endif diff --git a/src/include/ipxe/efi/Guid/ImageAuthentication.h b/src/include/ipxe/efi/Guid/ImageAuthentication.h new file mode 100644 index 000000000..9af0b41af --- /dev/null +++ b/src/include/ipxe/efi/Guid/ImageAuthentication.h @@ -0,0 +1,387 @@ +/** @file + Image signature database are defined for the signed image validation. + + Copyright (c) 2009 - 2024, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + + @par Revision Reference: + GUIDs defined in UEFI 2.5 spec. +**/ + +#ifndef __IMAGE_AUTHTICATION_H__ +#define __IMAGE_AUTHTICATION_H__ + +FILE_LICENCE ( BSD2_PATENT ); + +#include +#include + +#define EFI_IMAGE_SECURITY_DATABASE_GUID \ + { \ + 0xd719b2cb, 0x3d3a, 0x4596, { 0xa3, 0xbc, 0xda, 0xd0, 0xe, 0x67, 0x65, 0x6f } \ + } + +/// +/// Varialbe name with guid EFI_IMAGE_SECURITY_DATABASE_GUID +/// for the authorized signature database. +/// +#define EFI_IMAGE_SECURITY_DATABASE L"db" +/// +/// Varialbe name with guid EFI_IMAGE_SECURITY_DATABASE_GUID +/// for the forbidden signature database. +/// +#define EFI_IMAGE_SECURITY_DATABASE1 L"dbx" +/// +/// Variable name with guid EFI_IMAGE_SECURITY_DATABASE_GUID +/// for the timestamp signature database. +/// +#define EFI_IMAGE_SECURITY_DATABASE2 L"dbt" + +#define SECURE_BOOT_MODE_ENABLE 1 +#define SECURE_BOOT_MODE_DISABLE 0 + +#define SETUP_MODE 1 +#define USER_MODE 0 + +#define DEVICE_AUTH_BOOT_MODE_ENABLE 1 +#define DEVICE_AUTH_BOOT_MODE_DISABLE 0 + +// *********************************************************************** +// Signature Database +// *********************************************************************** +/// +/// The format of a signature database. +/// +#pragma pack(1) + +typedef struct { + /// + /// An identifier which identifies the agent which added the signature to the list. + /// + EFI_GUID SignatureOwner; + /// + /// The format of the signature is defined by the SignatureType. + /// + UINT8 SignatureData[1]; +} EFI_SIGNATURE_DATA; + +typedef struct { + /// + /// Type of the signature. GUID signature types are defined in below. + /// + EFI_GUID SignatureType; + /// + /// Total size of the signature list, including this header. + /// + UINT32 SignatureListSize; + /// + /// Size of the signature header which precedes the array of signatures. + /// + UINT32 SignatureHeaderSize; + /// + /// Size of each signature. + /// + UINT32 SignatureSize; + /// + /// Header before the array of signatures. The format of this header is specified + /// by the SignatureType. + /// UINT8 SignatureHeader[SignatureHeaderSize]; + /// + /// An array of signatures. Each signature is SignatureSize bytes in length. + /// EFI_SIGNATURE_DATA Signatures[][SignatureSize]; + /// +} EFI_SIGNATURE_LIST; + +typedef struct { + /// + /// The SHA256 hash of an X.509 certificate's To-Be-Signed contents. + /// + EFI_SHA256_HASH ToBeSignedHash; + /// + /// The time that the certificate shall be considered to be revoked. + /// + EFI_TIME TimeOfRevocation; +} EFI_CERT_X509_SHA256; + +typedef struct { + /// + /// The SHA384 hash of an X.509 certificate's To-Be-Signed contents. + /// + EFI_SHA384_HASH ToBeSignedHash; + /// + /// The time that the certificate shall be considered to be revoked. + /// + EFI_TIME TimeOfRevocation; +} EFI_CERT_X509_SHA384; + +typedef struct { + /// + /// The SHA512 hash of an X.509 certificate's To-Be-Signed contents. + /// + EFI_SHA512_HASH ToBeSignedHash; + /// + /// The time that the certificate shall be considered to be revoked. + /// + EFI_TIME TimeOfRevocation; +} EFI_CERT_X509_SHA512; + +typedef UINT8 EFI_SM3_HASH[32]; + +typedef struct { + /// + /// The SM3 hash of an X.509 certificate's To-Be-Signed contents. + /// + EFI_SM3_HASH ToBeSignedHash; + /// + /// The time that the certificate shall be considered to be revoked. + /// + EFI_TIME TimeOfRevocation; +} EFI_CERT_X509_SM3; + +#pragma pack() + +/// +/// This identifies a signature containing a SHA-256 hash. The SignatureHeader size shall +/// always be 0. The SignatureSize shall always be 16 (size of SignatureOwner component) + +/// 32 bytes. +/// +#define EFI_CERT_SHA256_GUID \ + { \ + 0xc1c41626, 0x504c, 0x4092, {0xac, 0xa9, 0x41, 0xf9, 0x36, 0x93, 0x43, 0x28} \ + } + +/// +/// This identifies a signature containing an RSA-2048 key. The key (only the modulus +/// since the public key exponent is known to be 0x10001) shall be stored in big-endian +/// order. +/// The SignatureHeader size shall always be 0. The SignatureSize shall always be 16 (size +/// of SignatureOwner component) + 256 bytes. +/// +#define EFI_CERT_RSA2048_GUID \ + { \ + 0x3c5766e8, 0x269c, 0x4e34, {0xaa, 0x14, 0xed, 0x77, 0x6e, 0x85, 0xb3, 0xb6} \ + } + +/// +/// This identifies a signature containing a RSA-2048 signature of a SHA-256 hash. The +/// SignatureHeader size shall always be 0. The SignatureSize shall always be 16 (size of +/// SignatureOwner component) + 256 bytes. +/// +#define EFI_CERT_RSA2048_SHA256_GUID \ + { \ + 0xe2b36190, 0x879b, 0x4a3d, {0xad, 0x8d, 0xf2, 0xe7, 0xbb, 0xa3, 0x27, 0x84} \ + } + +/// +/// This identifies a signature containing a SHA-1 hash. The SignatureSize shall always +/// be 16 (size of SignatureOwner component) + 20 bytes. +/// +#define EFI_CERT_SHA1_GUID \ + { \ + 0x826ca512, 0xcf10, 0x4ac9, {0xb1, 0x87, 0xbe, 0x1, 0x49, 0x66, 0x31, 0xbd} \ + } + +/// +/// This identifies a signature containing a SM3 hash. The SignatureSize shall always +/// be 16 (size of SignatureOwner component) + 32 bytes. +/// +#define EFI_CERT_SM3_GUID \ + { \ + 0x57347f87, 0x7a9b, 0x403a, { 0xb9, 0x3c, 0xdc, 0x4a, 0xfb, 0x7a, 0xe, 0xbc } \ + } + +/// +/// TThis identifies a signature containing a RSA-2048 signature of a SHA-1 hash. The +/// SignatureHeader size shall always be 0. The SignatureSize shall always be 16 (size of +/// SignatureOwner component) + 256 bytes. +/// +#define EFI_CERT_RSA2048_SHA1_GUID \ + { \ + 0x67f8444f, 0x8743, 0x48f1, {0xa3, 0x28, 0x1e, 0xaa, 0xb8, 0x73, 0x60, 0x80} \ + } + +/// +/// This identifies a signature based on an X.509 certificate. If the signature is an X.509 +/// certificate then verification of the signature of an image should validate the public +/// key certificate in the image using certificate path verification, up to this X.509 +/// certificate as a trusted root. The SignatureHeader size shall always be 0. The +/// SignatureSize may vary but shall always be 16 (size of the SignatureOwner component) + +/// the size of the certificate itself. +/// Note: This means that each certificate will normally be in a separate EFI_SIGNATURE_LIST. +/// +#define EFI_CERT_X509_GUID \ + { \ + 0xa5c059a1, 0x94e4, 0x4aa7, {0x87, 0xb5, 0xab, 0x15, 0x5c, 0x2b, 0xf0, 0x72} \ + } + +/// +/// This identifies a signature containing the SM3 hash of an X.509 certificate's To-Be-Signed +/// contents, and a time of revocation. The SignatureHeader size shall always be 0. The +/// SignatureSize shall always be 16 (size of the SignatureOwner component) + 32 bytes for +/// an EFI_CERT_X509_SM3 structure. If the TimeOfRevocation is non-zero, the certificate should +/// be considered to be revoked from that time and onwards, and otherwise the certificate shall +/// be considered to always be revoked. +/// +#define EFI_CERT_X509_SM3_GUID \ + { \ + 0x60d807e5, 0x10b4, 0x49a9, {0x93, 0x31, 0xe4, 0x4, 0x37, 0x88, 0x8d, 0x37 } \ + } + +/// +/// This identifies a signature containing a SHA-224 hash. The SignatureHeader size shall +/// always be 0. The SignatureSize shall always be 16 (size of SignatureOwner component) + +/// 28 bytes. +/// +#define EFI_CERT_SHA224_GUID \ + { \ + 0xb6e5233, 0xa65c, 0x44c9, {0x94, 0x7, 0xd9, 0xab, 0x83, 0xbf, 0xc8, 0xbd} \ + } + +/// +/// This identifies a signature containing a SHA-384 hash. The SignatureHeader size shall +/// always be 0. The SignatureSize shall always be 16 (size of SignatureOwner component) + +/// 48 bytes. +/// +#define EFI_CERT_SHA384_GUID \ + { \ + 0xff3e5307, 0x9fd0, 0x48c9, {0x85, 0xf1, 0x8a, 0xd5, 0x6c, 0x70, 0x1e, 0x1} \ + } + +/// +/// This identifies a signature containing a SHA-512 hash. The SignatureHeader size shall +/// always be 0. The SignatureSize shall always be 16 (size of SignatureOwner component) + +/// 64 bytes. +/// +#define EFI_CERT_SHA512_GUID \ + { \ + 0x93e0fae, 0xa6c4, 0x4f50, {0x9f, 0x1b, 0xd4, 0x1e, 0x2b, 0x89, 0xc1, 0x9a} \ + } + +/// +/// This identifies a signature containing the SHA256 hash of an X.509 certificate's +/// To-Be-Signed contents, and a time of revocation. The SignatureHeader size shall +/// always be 0. The SignatureSize shall always be 16 (size of the SignatureOwner component) +/// + 48 bytes for an EFI_CERT_X509_SHA256 structure. If the TimeOfRevocation is non-zero, +/// the certificate should be considered to be revoked from that time and onwards, and +/// otherwise the certificate shall be considered to always be revoked. +/// +#define EFI_CERT_X509_SHA256_GUID \ + { \ + 0x3bd2a492, 0x96c0, 0x4079, {0xb4, 0x20, 0xfc, 0xf9, 0x8e, 0xf1, 0x03, 0xed } \ + } + +/// +/// This identifies a signature containing the SHA384 hash of an X.509 certificate's +/// To-Be-Signed contents, and a time of revocation. The SignatureHeader size shall +/// always be 0. The SignatureSize shall always be 16 (size of the SignatureOwner component) +/// + 64 bytes for an EFI_CERT_X509_SHA384 structure. If the TimeOfRevocation is non-zero, +/// the certificate should be considered to be revoked from that time and onwards, and +/// otherwise the certificate shall be considered to always be revoked. +/// +#define EFI_CERT_X509_SHA384_GUID \ + { \ + 0x7076876e, 0x80c2, 0x4ee6, {0xaa, 0xd2, 0x28, 0xb3, 0x49, 0xa6, 0x86, 0x5b } \ + } + +/// +/// This identifies a signature containing the SHA512 hash of an X.509 certificate's +/// To-Be-Signed contents, and a time of revocation. The SignatureHeader size shall +/// always be 0. The SignatureSize shall always be 16 (size of the SignatureOwner component) +/// + 80 bytes for an EFI_CERT_X509_SHA512 structure. If the TimeOfRevocation is non-zero, +/// the certificate should be considered to be revoked from that time and onwards, and +/// otherwise the certificate shall be considered to always be revoked. +/// +#define EFI_CERT_X509_SHA512_GUID \ + { \ + 0x446dbf63, 0x2502, 0x4cda, {0xbc, 0xfa, 0x24, 0x65, 0xd2, 0xb0, 0xfe, 0x9d } \ + } + +/// +/// This identifies a signature containing a DER-encoded PKCS #7 version 1.5 [RFC2315] +/// SignedData value. +/// +#define EFI_CERT_TYPE_PKCS7_GUID \ + { \ + 0x4aafd29d, 0x68df, 0x49ee, {0x8a, 0xa9, 0x34, 0x7d, 0x37, 0x56, 0x65, 0xa7} \ + } + +// *********************************************************************** +// Image Execution Information Table Definition +// *********************************************************************** +typedef UINT32 EFI_IMAGE_EXECUTION_ACTION; + +#define EFI_IMAGE_EXECUTION_AUTHENTICATION 0x00000007 +#define EFI_IMAGE_EXECUTION_AUTH_UNTESTED 0x00000000 +#define EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED 0x00000001 +#define EFI_IMAGE_EXECUTION_AUTH_SIG_PASSED 0x00000002 +#define EFI_IMAGE_EXECUTION_AUTH_SIG_NOT_FOUND 0x00000003 +#define EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND 0x00000004 +#define EFI_IMAGE_EXECUTION_POLICY_FAILED 0x00000005 +#define EFI_IMAGE_EXECUTION_INITIALIZED 0x00000008 + +// +// EFI_IMAGE_EXECUTION_INFO is added to EFI System Configuration Table +// and assigned the GUID EFI_IMAGE_SECURITY_DATABASE_GUID. +// +typedef struct { + /// + /// Describes the action taken by the firmware regarding this image. + /// + EFI_IMAGE_EXECUTION_ACTION Action; + /// + /// Size of all of the entire structure. + /// + UINT32 InfoSize; + /// + /// If this image was a UEFI device driver (for option ROM, for example) this is the + /// null-terminated, user-friendly name for the device. If the image was for an application, + /// then this is the name of the application. If this cannot be determined, then a simple + /// NULL character should be put in this position. + /// CHAR16 Name[]; + /// + + /// + /// For device drivers, this is the device path of the device for which this device driver + /// was intended. In some cases, the driver itself may be stored as part of the system + /// firmware, but this field should record the device's path, not the firmware path. For + /// applications, this is the device path of the application. If this cannot be determined, + /// a simple end-of-path device node should be put in this position. + /// EFI_DEVICE_PATH_PROTOCOL DevicePath; + /// + + /// + /// Zero or more image signatures. If the image contained no signatures, + /// then this field is empty. + /// EFI_SIGNATURE_LIST Signature; + /// +} EFI_IMAGE_EXECUTION_INFO; + +typedef struct { + /// + /// Number of EFI_IMAGE_EXECUTION_INFO structures. + /// + UINTN NumberOfImages; + /// + /// Number of image instances of EFI_IMAGE_EXECUTION_INFO structures. + /// + // EFI_IMAGE_EXECUTION_INFO InformationInfo[] +} EFI_IMAGE_EXECUTION_INFO_TABLE; + +extern EFI_GUID gEfiImageSecurityDatabaseGuid; +extern EFI_GUID gEfiCertSha256Guid; +extern EFI_GUID gEfiCertRsa2048Guid; +extern EFI_GUID gEfiCertRsa2048Sha256Guid; +extern EFI_GUID gEfiCertSha1Guid; +extern EFI_GUID gEfiCertRsa2048Sha1Guid; +extern EFI_GUID gEfiCertX509Guid; +extern EFI_GUID gEfiCertSha224Guid; +extern EFI_GUID gEfiCertSha384Guid; +extern EFI_GUID gEfiCertSha512Guid; +extern EFI_GUID gEfiCertX509Sha256Guid; +extern EFI_GUID gEfiCertX509Sha384Guid; +extern EFI_GUID gEfiCertX509Sha512Guid; +extern EFI_GUID gEfiCertPkcs7Guid; +extern EFI_GUID gEfiCertSm3Guid; +extern EFI_GUID gEfiCertX509Sm3Guid; + +#endif diff --git a/src/include/ipxe/efi/Protocol/Hash.h b/src/include/ipxe/efi/Protocol/Hash.h new file mode 100644 index 000000000..ac6dcd193 --- /dev/null +++ b/src/include/ipxe/efi/Protocol/Hash.h @@ -0,0 +1,171 @@ +/** @file + EFI_HASH_SERVICE_BINDING_PROTOCOL as defined in UEFI 2.0. + EFI_HASH_PROTOCOL as defined in UEFI 2.0. + The EFI Hash Service Binding Protocol is used to locate hashing services support + provided by a driver and to create and destroy instances of the EFI Hash Protocol + so that a multiple drivers can use the underlying hashing services. + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __EFI_HASH_PROTOCOL_H__ +#define __EFI_HASH_PROTOCOL_H__ + +FILE_LICENCE ( BSD2_PATENT ); + +#define EFI_HASH_SERVICE_BINDING_PROTOCOL_GUID \ + { \ + 0x42881c98, 0xa4f3, 0x44b0, {0xa3, 0x9d, 0xdf, 0xa1, 0x86, 0x67, 0xd8, 0xcd } \ + } + +#define EFI_HASH_PROTOCOL_GUID \ + { \ + 0xc5184932, 0xdba5, 0x46db, {0xa5, 0xba, 0xcc, 0x0b, 0xda, 0x9c, 0x14, 0x35 } \ + } + +#define EFI_HASH_ALGORITHM_SHA1_GUID \ + { \ + 0x2ae9d80f, 0x3fb2, 0x4095, {0xb7, 0xb1, 0xe9, 0x31, 0x57, 0xb9, 0x46, 0xb6 } \ + } + +#define EFI_HASH_ALGORITHM_SHA224_GUID \ + { \ + 0x8df01a06, 0x9bd5, 0x4bf7, {0xb0, 0x21, 0xdb, 0x4f, 0xd9, 0xcc, 0xf4, 0x5b } \ + } + +#define EFI_HASH_ALGORITHM_SHA256_GUID \ + { \ + 0x51aa59de, 0xfdf2, 0x4ea3, {0xbc, 0x63, 0x87, 0x5f, 0xb7, 0x84, 0x2e, 0xe9 } \ + } + +#define EFI_HASH_ALGORITHM_SHA384_GUID \ + { \ + 0xefa96432, 0xde33, 0x4dd2, {0xae, 0xe6, 0x32, 0x8c, 0x33, 0xdf, 0x77, 0x7a } \ + } + +#define EFI_HASH_ALGORITHM_SHA512_GUID \ + { \ + 0xcaa4381e, 0x750c, 0x4770, {0xb8, 0x70, 0x7a, 0x23, 0xb4, 0xe4, 0x21, 0x30 } \ + } + +#define EFI_HASH_ALGORTIHM_MD5_GUID \ + { \ + 0xaf7c79c, 0x65b5, 0x4319, {0xb0, 0xae, 0x44, 0xec, 0x48, 0x4e, 0x4a, 0xd7 } \ + } + +#define EFI_HASH_ALGORITHM_SHA1_NOPAD_GUID \ + { \ + 0x24c5dc2f, 0x53e2, 0x40ca, {0x9e, 0xd6, 0xa5, 0xd9, 0xa4, 0x9f, 0x46, 0x3b } \ + } + +#define EFI_HASH_ALGORITHM_SHA256_NOPAD_GUID \ + { \ + 0x8628752a, 0x6cb7, 0x4814, {0x96, 0xfc, 0x24, 0xa8, 0x15, 0xac, 0x22, 0x26 } \ + } + +// +// Note: Use of the following algorithms with EFI_HASH_PROTOCOL is deprecated. +// EFI_HASH_ALGORITHM_SHA1_GUID +// EFI_HASH_ALGORITHM_SHA224_GUID +// EFI_HASH_ALGORITHM_SHA256_GUID +// EFI_HASH_ALGORITHM_SHA384_GUID +// EFI_HASH_ALGORITHM_SHA512_GUID +// EFI_HASH_ALGORTIHM_MD5_GUID +// + +typedef struct _EFI_HASH_PROTOCOL EFI_HASH_PROTOCOL; + +typedef UINT8 EFI_MD5_HASH[16]; +typedef UINT8 EFI_SHA1_HASH[20]; +typedef UINT8 EFI_SHA224_HASH[28]; +typedef UINT8 EFI_SHA256_HASH[32]; +typedef UINT8 EFI_SHA384_HASH[48]; +typedef UINT8 EFI_SHA512_HASH[64]; + +typedef union { + EFI_MD5_HASH *Md5Hash; + EFI_SHA1_HASH *Sha1Hash; + EFI_SHA224_HASH *Sha224Hash; + EFI_SHA256_HASH *Sha256Hash; + EFI_SHA384_HASH *Sha384Hash; + EFI_SHA512_HASH *Sha512Hash; +} EFI_HASH_OUTPUT; + +/** + Returns the size of the hash which results from a specific algorithm. + + @param[in] This Points to this instance of EFI_HASH_PROTOCOL. + @param[in] HashAlgorithm Points to the EFI_GUID which identifies the algorithm to use. + @param[out] HashSize Holds the returned size of the algorithm's hash. + + @retval EFI_SUCCESS Hash size returned successfully. + @retval EFI_INVALID_PARAMETER HashSize is NULL or HashAlgorithm is NULL. + @retval EFI_UNSUPPORTED The algorithm specified by HashAlgorithm is not supported + by this driver. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_HASH_GET_HASH_SIZE)( + IN CONST EFI_HASH_PROTOCOL *This, + IN CONST EFI_GUID *HashAlgorithm, + OUT UINTN *HashSize + ); + +/** + Creates a hash for the specified message text. + + @param[in] This Points to this instance of EFI_HASH_PROTOCOL. + @param[in] HashAlgorithm Points to the EFI_GUID which identifies the algorithm to use. + @param[in] Extend Specifies whether to create a new hash (FALSE) or extend the specified + existing hash (TRUE). + @param[in] Message Points to the start of the message. + @param[in] MessageSize The size of Message, in bytes. + @param[in,out] Hash On input, if Extend is TRUE, then this parameter holds a pointer + to a pointer to an array containing the hash to extend. If Extend + is FALSE, then this parameter holds a pointer to a pointer to a + caller-allocated array that will receive the result of the hash + computation. On output (regardless of the value of Extend), the + array will contain the result of the hash computation. + + @retval EFI_SUCCESS Hash returned successfully. + @retval EFI_INVALID_PARAMETER Message or Hash, HashAlgorithm is NULL or MessageSize is 0. + MessageSize is not an integer multiple of block size. + @retval EFI_UNSUPPORTED The algorithm specified by HashAlgorithm is not supported by this + driver. Or, Extend is TRUE, and the algorithm doesn't support extending the hash. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_HASH_HASH)( + IN CONST EFI_HASH_PROTOCOL *This, + IN CONST EFI_GUID *HashAlgorithm, + IN BOOLEAN Extend, + IN CONST UINT8 *Message, + IN UINT64 MessageSize, + IN OUT EFI_HASH_OUTPUT *Hash + ); + +/// +/// This protocol allows creating a hash of an arbitrary message digest +/// using one or more hash algorithms. +/// +struct _EFI_HASH_PROTOCOL { + EFI_HASH_GET_HASH_SIZE GetHashSize; + EFI_HASH_HASH Hash; +}; + +extern EFI_GUID gEfiHashServiceBindingProtocolGuid; +extern EFI_GUID gEfiHashProtocolGuid; +extern EFI_GUID gEfiHashAlgorithmSha1Guid; +extern EFI_GUID gEfiHashAlgorithmSha224Guid; +extern EFI_GUID gEfiHashAlgorithmSha256Guid; +extern EFI_GUID gEfiHashAlgorithmSha384Guid; +extern EFI_GUID gEfiHashAlgorithmSha512Guid; +extern EFI_GUID gEfiHashAlgorithmMD5Guid; +extern EFI_GUID gEfiHashAlgorithmSha1NoPadGuid; +extern EFI_GUID gEfiHashAlgorithmSha256NoPadGuid; + +#endif diff --git a/src/include/ipxe/efi/efi.h b/src/include/ipxe/efi/efi.h index 843c79e2e..ef492849e 100644 --- a/src/include/ipxe/efi/efi.h +++ b/src/include/ipxe/efi/efi.h @@ -245,6 +245,7 @@ extern EFI_GUID efi_usb2_hc_protocol_guid; extern EFI_GUID efi_usb_io_protocol_guid; extern EFI_GUID efi_vlan_config_protocol_guid; +extern EFI_GUID efi_cert_x509_guid; extern EFI_GUID efi_file_info_id; extern EFI_GUID efi_file_system_info_id; diff --git a/src/interface/efi/efi_debug.c b/src/interface/efi/efi_debug.c index 895a712bd..81c442179 100644 --- a/src/interface/efi/efi_debug.c +++ b/src/interface/efi/efi_debug.c @@ -103,6 +103,8 @@ static struct efi_well_known_guid efi_well_known_guids[] = { "BlockIo2" }, { &efi_bus_specific_driver_override_protocol_guid, "BusSpecificDriverOverride" }, + { &efi_cert_x509_guid, + "CertX509" }, { &efi_component_name_protocol_guid, "ComponentName" }, { &efi_component_name2_protocol_guid, diff --git a/src/interface/efi/efi_guid.c b/src/interface/efi/efi_guid.c index 16c1a5738..bd45d05b9 100644 --- a/src/interface/efi/efi_guid.c +++ b/src/interface/efi/efi_guid.c @@ -83,6 +83,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include /** @file * @@ -391,6 +392,9 @@ EFI_GUID efi_usb_io_protocol_guid EFI_GUID efi_vlan_config_protocol_guid = EFI_VLAN_CONFIG_PROTOCOL_GUID; +/** X.509 certificate GUID */ +EFI_GUID efi_cert_x509_guid = EFI_CERT_X509_GUID; + /** File information GUID */ EFI_GUID efi_file_info_id = EFI_FILE_INFO_ID; From 011c778f06fbfa315f8749f39df016d8865422c6 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 11 Mar 2025 11:52:37 +0000 Subject: [PATCH 29/50] [efi] Allow efi_guid_ntoa() to be used in non-EFI builds The debug message transcription of well-known EFI GUIDs does not require any EFI boot services calls. Move this code from efi_debug.c to efi_guid.c, to allow it to be linked in to non-EFI builds. We continue to rely on linker garbage collection to ensure that the code is omitted completely from any non-debug builds. Signed-off-by: Michael Brown --- src/interface/efi/efi_debug.c | 236 ---------------------------------- src/interface/efi/efi_guid.c | 236 ++++++++++++++++++++++++++++++++++ 2 files changed, 236 insertions(+), 236 deletions(-) diff --git a/src/interface/efi/efi_debug.c b/src/interface/efi/efi_debug.c index 81c442179..030c6a93e 100644 --- a/src/interface/efi/efi_debug.c +++ b/src/interface/efi/efi_debug.c @@ -31,9 +31,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ #include -#include #include -#include #include #include #include @@ -47,240 +45,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); static EFI_DEVICE_PATH_TO_TEXT_PROTOCOL *efidpt; EFI_REQUEST_PROTOCOL ( EFI_DEVICE_PATH_TO_TEXT_PROTOCOL, &efidpt ); -/** HttpBootDxe module GUID */ -static EFI_GUID efi_http_boot_dxe_guid = { - 0xecebcb00, 0xd9c8, 0x11e4, - { 0xaf, 0x3d, 0x8c, 0xdc, 0xd4, 0x26, 0xc9, 0x73 } -}; - -/** IScsiDxe module GUID */ -static EFI_GUID efi_iscsi_dxe_guid = { - 0x86cddf93, 0x4872, 0x4597, - { 0x8a, 0xf9, 0xa3, 0x5a, 0xe4, 0xd3, 0x72, 0x5f } -}; - -/** Old IScsi4Dxe module GUID */ -static EFI_GUID efi_iscsi4_dxe_guid = { - 0x4579b72d, 0x7ec4, 0x4dd4, - { 0x84, 0x86, 0x08, 0x3c, 0x86, 0xb1, 0x82, 0xa7 } -}; - -/** UefiPxeBcDxe module GUID */ -static EFI_GUID efi_uefi_pxe_bc_dxe_guid = { - 0xb95e9fda, 0x26de, 0x48d2, - { 0x88, 0x07, 0x1f, 0x91, 0x07, 0xac, 0x5e, 0x3a } -}; - -/** VlanConfigDxe module GUID */ -static EFI_GUID efi_vlan_config_dxe_guid = { - 0xe4f61863, 0xfe2c, 0x4b56, - { 0xa8, 0xf4, 0x08, 0x51, 0x9b, 0xc4, 0x39, 0xdf } -}; - -/** A well-known GUID */ -struct efi_well_known_guid { - /** GUID */ - EFI_GUID *guid; - /** Name */ - const char *name; -}; - -/** Well-known GUIDs */ -static struct efi_well_known_guid efi_well_known_guids[] = { - { &efi_absolute_pointer_protocol_guid, - "AbsolutePointer" }, - { &efi_acpi_table_protocol_guid, - "AcpiTable" }, - { &efi_apple_net_boot_protocol_guid, - "AppleNetBoot" }, - { &efi_arp_protocol_guid, - "Arp" }, - { &efi_arp_service_binding_protocol_guid, - "ArpSb" }, - { &efi_block_io_protocol_guid, - "BlockIo" }, - { &efi_block_io2_protocol_guid, - "BlockIo2" }, - { &efi_bus_specific_driver_override_protocol_guid, - "BusSpecificDriverOverride" }, - { &efi_cert_x509_guid, - "CertX509" }, - { &efi_component_name_protocol_guid, - "ComponentName" }, - { &efi_component_name2_protocol_guid, - "ComponentName2" }, - { &efi_console_control_protocol_guid, - "ConsoleControl" }, - { &efi_device_path_protocol_guid, - "DevicePath" }, - { &efi_driver_binding_protocol_guid, - "DriverBinding" }, - { &efi_dhcp4_protocol_guid, - "Dhcp4" }, - { &efi_dhcp4_service_binding_protocol_guid, - "Dhcp4Sb" }, - { &efi_dhcp6_protocol_guid, - "Dhcp6" }, - { &efi_dhcp6_service_binding_protocol_guid, - "Dhcp6Sb" }, - { &efi_disk_io_protocol_guid, - "DiskIo" }, - { &efi_dns4_protocol_guid, - "Dns4" }, - { &efi_dns4_service_binding_protocol_guid, - "Dns4Sb" }, - { &efi_dns6_protocol_guid, - "Dns6" }, - { &efi_dns6_service_binding_protocol_guid, - "Dns6Sb" }, - { &efi_graphics_output_protocol_guid, - "GraphicsOutput" }, - { &efi_hii_config_access_protocol_guid, - "HiiConfigAccess" }, - { &efi_hii_font_protocol_guid, - "HiiFont" }, - { &efi_http_boot_dxe_guid, - "HttpBootDxe" }, - { &efi_http_protocol_guid, - "Http" }, - { &efi_http_service_binding_protocol_guid, - "HttpSb" }, - { &efi_ip4_protocol_guid, - "Ip4" }, - { &efi_ip4_config_protocol_guid, - "Ip4Config" }, - { &efi_ip4_config2_protocol_guid, - "Ip4Config2" }, - { &efi_ip4_service_binding_protocol_guid, - "Ip4Sb" }, - { &efi_ip6_protocol_guid, - "Ip6" }, - { &efi_ip6_config_protocol_guid, - "Ip6Config" }, - { &efi_ip6_service_binding_protocol_guid, - "Ip6Sb" }, - { &efi_iscsi_dxe_guid, - "IScsiDxe" }, - { &efi_iscsi4_dxe_guid, - "IScsi4Dxe" }, - { &efi_load_file_protocol_guid, - "LoadFile" }, - { &efi_load_file2_protocol_guid, - "LoadFile2" }, - { &efi_loaded_image_protocol_guid, - "LoadedImage" }, - { &efi_loaded_image_device_path_protocol_guid, - "LoadedImageDevicePath"}, - { &efi_managed_network_protocol_guid, - "ManagedNetwork" }, - { &efi_managed_network_service_binding_protocol_guid, - "ManagedNetworkSb" }, - { &efi_mtftp4_protocol_guid, - "Mtftp4" }, - { &efi_mtftp4_service_binding_protocol_guid, - "Mtftp4Sb" }, - { &efi_mtftp6_protocol_guid, - "Mtftp6" }, - { &efi_mtftp6_service_binding_protocol_guid, - "Mtftp6Sb" }, - { &efi_nii_protocol_guid, - "Nii" }, - { &efi_nii31_protocol_guid, - "Nii31" }, - { &efi_pci_io_protocol_guid, - "PciIo" }, - { &efi_pci_root_bridge_io_protocol_guid, - "PciRootBridgeIo" }, - { &efi_pxe_base_code_protocol_guid, - "PxeBaseCode" }, - { &efi_serial_io_protocol_guid, - "SerialIo" }, - { &efi_shim_lock_protocol_guid, - "ShimLock" }, - { &efi_simple_file_system_protocol_guid, - "SimpleFileSystem" }, - { &efi_simple_network_protocol_guid, - "SimpleNetwork" }, - { &efi_simple_pointer_protocol_guid, - "SimplePointer" }, - { &efi_simple_text_input_protocol_guid, - "SimpleTextInput" }, - { &efi_simple_text_input_ex_protocol_guid, - "SimpleTextInputEx" }, - { &efi_simple_text_output_protocol_guid, - "SimpleTextOutput" }, - { &efi_tcg_protocol_guid, - "Tcg" }, - { &efi_tcg2_protocol_guid, - "Tcg2" }, - { &efi_tcp4_protocol_guid, - "Tcp4" }, - { &efi_tcp4_service_binding_protocol_guid, - "Tcp4Sb" }, - { &efi_tcp6_protocol_guid, - "Tcp6" }, - { &efi_tcp6_service_binding_protocol_guid, - "Tcp6Sb" }, - { &efi_tree_protocol_guid, - "TrEE" }, - { &efi_udp4_protocol_guid, - "Udp4" }, - { &efi_udp4_service_binding_protocol_guid, - "Udp4Sb" }, - { &efi_udp6_protocol_guid, - "Udp6" }, - { &efi_udp6_service_binding_protocol_guid, - "Udp6Sb" }, - { &efi_uefi_pxe_bc_dxe_guid, - "UefiPxeBcDxe" }, - { &efi_uga_draw_protocol_guid, - "UgaDraw" }, - { &efi_unicode_collation_protocol_guid, - "UnicodeCollation" }, - { &efi_usb_hc_protocol_guid, - "UsbHc" }, - { &efi_usb2_hc_protocol_guid, - "Usb2Hc" }, - { &efi_usb_io_protocol_guid, - "UsbIo" }, - { &efi_vlan_config_protocol_guid, - "VlanConfig" }, - { &efi_vlan_config_dxe_guid, - "VlanConfigDxe" }, -}; - -/** - * Convert GUID to a printable string - * - * @v guid GUID - * @ret string Printable string - */ -const __attribute__ (( pure )) char * efi_guid_ntoa ( CONST EFI_GUID *guid ) { - union { - union uuid uuid; - EFI_GUID guid; - } u; - unsigned int i; - - /* Sanity check */ - if ( ! guid ) - return NULL; - - /* Check for a match against well-known GUIDs */ - for ( i = 0 ; i < ( sizeof ( efi_well_known_guids ) / - sizeof ( efi_well_known_guids[0] ) ) ; i++ ) { - if ( memcmp ( guid, efi_well_known_guids[i].guid, - sizeof ( *guid ) ) == 0 ) { - return efi_well_known_guids[i].name; - } - } - - /* Convert GUID to standard endianness */ - memcpy ( &u.guid, guid, sizeof ( u.guid ) ); - uuid_mangle ( &u.uuid ); - return uuid_ntoa ( &u.uuid ); -} - /** * Name locate search type * diff --git a/src/interface/efi/efi_guid.c b/src/interface/efi/efi_guid.c index bd45d05b9..bd35b94cc 100644 --- a/src/interface/efi/efi_guid.c +++ b/src/interface/efi/efi_guid.c @@ -23,6 +23,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +#include +#include #include #include #include @@ -400,3 +402,237 @@ EFI_GUID efi_file_info_id = EFI_FILE_INFO_ID; /** File system information GUID */ EFI_GUID efi_file_system_info_id = EFI_FILE_SYSTEM_INFO_ID; + +/** HttpBootDxe module GUID */ +static EFI_GUID efi_http_boot_dxe_guid = { + 0xecebcb00, 0xd9c8, 0x11e4, + { 0xaf, 0x3d, 0x8c, 0xdc, 0xd4, 0x26, 0xc9, 0x73 } +}; + +/** IScsiDxe module GUID */ +static EFI_GUID efi_iscsi_dxe_guid = { + 0x86cddf93, 0x4872, 0x4597, + { 0x8a, 0xf9, 0xa3, 0x5a, 0xe4, 0xd3, 0x72, 0x5f } +}; + +/** Old IScsi4Dxe module GUID */ +static EFI_GUID efi_iscsi4_dxe_guid = { + 0x4579b72d, 0x7ec4, 0x4dd4, + { 0x84, 0x86, 0x08, 0x3c, 0x86, 0xb1, 0x82, 0xa7 } +}; + +/** UefiPxeBcDxe module GUID */ +static EFI_GUID efi_uefi_pxe_bc_dxe_guid = { + 0xb95e9fda, 0x26de, 0x48d2, + { 0x88, 0x07, 0x1f, 0x91, 0x07, 0xac, 0x5e, 0x3a } +}; + +/** VlanConfigDxe module GUID */ +static EFI_GUID efi_vlan_config_dxe_guid = { + 0xe4f61863, 0xfe2c, 0x4b56, + { 0xa8, 0xf4, 0x08, 0x51, 0x9b, 0xc4, 0x39, 0xdf } +}; + +/** A well-known GUID */ +struct efi_well_known_guid { + /** GUID */ + EFI_GUID *guid; + /** Name */ + const char *name; +}; + +/** Well-known GUIDs */ +static struct efi_well_known_guid efi_well_known_guids[] = { + { &efi_absolute_pointer_protocol_guid, + "AbsolutePointer" }, + { &efi_acpi_table_protocol_guid, + "AcpiTable" }, + { &efi_apple_net_boot_protocol_guid, + "AppleNetBoot" }, + { &efi_arp_protocol_guid, + "Arp" }, + { &efi_arp_service_binding_protocol_guid, + "ArpSb" }, + { &efi_block_io_protocol_guid, + "BlockIo" }, + { &efi_block_io2_protocol_guid, + "BlockIo2" }, + { &efi_bus_specific_driver_override_protocol_guid, + "BusSpecificDriverOverride" }, + { &efi_cert_x509_guid, + "CertX509" }, + { &efi_component_name_protocol_guid, + "ComponentName" }, + { &efi_component_name2_protocol_guid, + "ComponentName2" }, + { &efi_console_control_protocol_guid, + "ConsoleControl" }, + { &efi_device_path_protocol_guid, + "DevicePath" }, + { &efi_driver_binding_protocol_guid, + "DriverBinding" }, + { &efi_dhcp4_protocol_guid, + "Dhcp4" }, + { &efi_dhcp4_service_binding_protocol_guid, + "Dhcp4Sb" }, + { &efi_dhcp6_protocol_guid, + "Dhcp6" }, + { &efi_dhcp6_service_binding_protocol_guid, + "Dhcp6Sb" }, + { &efi_disk_io_protocol_guid, + "DiskIo" }, + { &efi_dns4_protocol_guid, + "Dns4" }, + { &efi_dns4_service_binding_protocol_guid, + "Dns4Sb" }, + { &efi_dns6_protocol_guid, + "Dns6" }, + { &efi_dns6_service_binding_protocol_guid, + "Dns6Sb" }, + { &efi_graphics_output_protocol_guid, + "GraphicsOutput" }, + { &efi_hii_config_access_protocol_guid, + "HiiConfigAccess" }, + { &efi_hii_font_protocol_guid, + "HiiFont" }, + { &efi_http_boot_dxe_guid, + "HttpBootDxe" }, + { &efi_http_protocol_guid, + "Http" }, + { &efi_http_service_binding_protocol_guid, + "HttpSb" }, + { &efi_ip4_protocol_guid, + "Ip4" }, + { &efi_ip4_config_protocol_guid, + "Ip4Config" }, + { &efi_ip4_config2_protocol_guid, + "Ip4Config2" }, + { &efi_ip4_service_binding_protocol_guid, + "Ip4Sb" }, + { &efi_ip6_protocol_guid, + "Ip6" }, + { &efi_ip6_config_protocol_guid, + "Ip6Config" }, + { &efi_ip6_service_binding_protocol_guid, + "Ip6Sb" }, + { &efi_iscsi_dxe_guid, + "IScsiDxe" }, + { &efi_iscsi4_dxe_guid, + "IScsi4Dxe" }, + { &efi_load_file_protocol_guid, + "LoadFile" }, + { &efi_load_file2_protocol_guid, + "LoadFile2" }, + { &efi_loaded_image_protocol_guid, + "LoadedImage" }, + { &efi_loaded_image_device_path_protocol_guid, + "LoadedImageDevicePath"}, + { &efi_managed_network_protocol_guid, + "ManagedNetwork" }, + { &efi_managed_network_service_binding_protocol_guid, + "ManagedNetworkSb" }, + { &efi_mtftp4_protocol_guid, + "Mtftp4" }, + { &efi_mtftp4_service_binding_protocol_guid, + "Mtftp4Sb" }, + { &efi_mtftp6_protocol_guid, + "Mtftp6" }, + { &efi_mtftp6_service_binding_protocol_guid, + "Mtftp6Sb" }, + { &efi_nii_protocol_guid, + "Nii" }, + { &efi_nii31_protocol_guid, + "Nii31" }, + { &efi_pci_io_protocol_guid, + "PciIo" }, + { &efi_pci_root_bridge_io_protocol_guid, + "PciRootBridgeIo" }, + { &efi_pxe_base_code_protocol_guid, + "PxeBaseCode" }, + { &efi_serial_io_protocol_guid, + "SerialIo" }, + { &efi_shim_lock_protocol_guid, + "ShimLock" }, + { &efi_simple_file_system_protocol_guid, + "SimpleFileSystem" }, + { &efi_simple_network_protocol_guid, + "SimpleNetwork" }, + { &efi_simple_pointer_protocol_guid, + "SimplePointer" }, + { &efi_simple_text_input_protocol_guid, + "SimpleTextInput" }, + { &efi_simple_text_input_ex_protocol_guid, + "SimpleTextInputEx" }, + { &efi_simple_text_output_protocol_guid, + "SimpleTextOutput" }, + { &efi_tcg_protocol_guid, + "Tcg" }, + { &efi_tcg2_protocol_guid, + "Tcg2" }, + { &efi_tcp4_protocol_guid, + "Tcp4" }, + { &efi_tcp4_service_binding_protocol_guid, + "Tcp4Sb" }, + { &efi_tcp6_protocol_guid, + "Tcp6" }, + { &efi_tcp6_service_binding_protocol_guid, + "Tcp6Sb" }, + { &efi_tree_protocol_guid, + "TrEE" }, + { &efi_udp4_protocol_guid, + "Udp4" }, + { &efi_udp4_service_binding_protocol_guid, + "Udp4Sb" }, + { &efi_udp6_protocol_guid, + "Udp6" }, + { &efi_udp6_service_binding_protocol_guid, + "Udp6Sb" }, + { &efi_uefi_pxe_bc_dxe_guid, + "UefiPxeBcDxe" }, + { &efi_uga_draw_protocol_guid, + "UgaDraw" }, + { &efi_unicode_collation_protocol_guid, + "UnicodeCollation" }, + { &efi_usb_hc_protocol_guid, + "UsbHc" }, + { &efi_usb2_hc_protocol_guid, + "Usb2Hc" }, + { &efi_usb_io_protocol_guid, + "UsbIo" }, + { &efi_vlan_config_protocol_guid, + "VlanConfig" }, + { &efi_vlan_config_dxe_guid, + "VlanConfigDxe" }, +}; + +/** + * Convert GUID to a printable string + * + * @v guid GUID + * @ret string Printable string + */ +const __attribute__ (( pure )) char * efi_guid_ntoa ( CONST EFI_GUID *guid ) { + union { + union uuid uuid; + EFI_GUID guid; + } u; + unsigned int i; + + /* Sanity check */ + if ( ! guid ) + return NULL; + + /* Check for a match against well-known GUIDs */ + for ( i = 0 ; i < ( sizeof ( efi_well_known_guids ) / + sizeof ( efi_well_known_guids[0] ) ) ; i++ ) { + if ( memcmp ( guid, efi_well_known_guids[i].guid, + sizeof ( *guid ) ) == 0 ) { + return efi_well_known_guids[i].name; + } + } + + /* Convert GUID to standard endianness */ + memcpy ( &u.guid, guid, sizeof ( u.guid ) ); + uuid_mangle ( &u.uuid ); + return uuid_ntoa ( &u.uuid ); +} From 26a8fed710900be439e7548308ecca910a9b683c Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 11 Mar 2025 11:55:15 +0000 Subject: [PATCH 30/50] [crypto] Allow for parsing of DER data separate from DER images We currently provide pem_asn1() to allow for parsing of PEM data that is not necessarily contained in an image. Provide an equivalent function der_asn1() to allow for similar parsing of DER data. Signed-off-by: Michael Brown --- src/image/der.c | 69 ++++++++++++++++++++++++++++++++---------- src/include/ipxe/der.h | 5 +++ 2 files changed, 58 insertions(+), 16 deletions(-) diff --git a/src/image/der.c b/src/image/der.c index 9d31c253b..ac4992336 100644 --- a/src/image/der.c +++ b/src/image/der.c @@ -38,32 +38,41 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ /** - * Extract ASN.1 object from image + * Extract ASN.1 object from DER data * - * @v image DER image - * @v offset Offset within image + * @v data DER data + * @v len Length of DER data + * @v offset Offset within data * @v cursor ASN.1 cursor to fill in - * @ret next Offset to next image, or negative error + * @ret next Offset to next object, or negative error * * The caller is responsible for eventually calling free() on the * allocated ASN.1 cursor. */ -static int der_asn1 ( struct image *image, size_t offset __unused, - struct asn1_cursor **cursor ) { - void *data; +int der_asn1 ( userptr_t data, size_t len, size_t offset, + struct asn1_cursor **cursor ) { + size_t remaining; + void *raw; + + /* Sanity check */ + assert ( offset <= len ); + remaining = ( len - offset ); /* Allocate cursor and data buffer */ - *cursor = malloc ( sizeof ( **cursor ) + image->len ); + *cursor = malloc ( sizeof ( **cursor ) + remaining ); if ( ! *cursor ) return -ENOMEM; - data = ( ( ( void * ) *cursor ) + sizeof ( **cursor ) ); + raw = ( ( ( void * ) *cursor ) + sizeof ( **cursor ) ); /* Populate cursor and data buffer */ - (*cursor)->data = data; - (*cursor)->len = image->len; - copy_from_user ( data, image->data, 0, image->len ); + (*cursor)->data = raw; + (*cursor)->len = remaining; + copy_from_user ( raw, data, offset, remaining ); - return image->len; + /* Shrink cursor */ + asn1_shrink_any ( *cursor ); + + return ( offset + (*cursor)->len ); } /** @@ -72,7 +81,7 @@ static int der_asn1 ( struct image *image, size_t offset __unused, * @v image DER image * @ret rc Return status code */ -static int der_probe ( struct image *image ) { +static int der_image_probe ( struct image *image ) { struct asn1_cursor cursor; uint8_t buf[8]; size_t extra; @@ -105,9 +114,37 @@ static int der_probe ( struct image *image ) { return 0; } +/** + * Extract ASN.1 object from DER image + * + * @v image DER image + * @v offset Offset within image + * @v cursor ASN.1 cursor to fill in + * @ret next Offset to next image, or negative error + * + * The caller is responsible for eventually calling free() on the + * allocated ASN.1 cursor. + */ +static int der_image_asn1 ( struct image *image, size_t offset, + struct asn1_cursor **cursor ) { + int next; + int rc; + + /* Extract ASN.1 object */ + if ( ( next = der_asn1 ( image->data, image->len, offset, + cursor ) ) < 0 ) { + rc = next; + DBGC ( image, "DER %s could not extract ASN.1: %s\n", + image->name, strerror ( rc ) ); + return rc; + } + + return next; +} + /** DER image type */ struct image_type der_image_type __image_type ( PROBE_NORMAL ) = { .name = "DER", - .probe = der_probe, - .asn1 = der_asn1, + .probe = der_image_probe, + .asn1 = der_image_asn1, }; diff --git a/src/include/ipxe/der.h b/src/include/ipxe/der.h index c63bd9751..983aeb23c 100644 --- a/src/include/ipxe/der.h +++ b/src/include/ipxe/der.h @@ -9,8 +9,13 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +#include +#include #include +extern int der_asn1 ( userptr_t data, size_t len, size_t offset, + struct asn1_cursor **cursor ); + extern struct image_type der_image_type __image_type ( PROBE_NORMAL ); #endif /* _IPXE_DER_H */ From 5f3ecbde5a68c503ee92becd5ddf427c317c94f1 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 11 Mar 2025 11:58:28 +0000 Subject: [PATCH 31/50] [crypto] Support extracting certificates from EFI signature list images Add support for the EFI signature list image format (as produced by tools such as efisecdb). The parsing code does not require any EFI boot services functions and so may be enabled even in non-EFI builds. We default to enabling it only for EFI builds. Signed-off-by: Michael Brown --- src/config/config_asn1.c | 3 + src/config/defaults/efi.h | 1 + src/config/general.h | 1 + src/image/efi_siglist.c | 253 +++++++++++++++++++++++++++++ src/include/ipxe/efi/efi_siglist.h | 22 +++ src/include/ipxe/errfile.h | 1 + src/tests/efi_siglist_test.c | 167 +++++++++++++++++++ src/tests/tests.c | 1 + 8 files changed, 449 insertions(+) create mode 100644 src/image/efi_siglist.c create mode 100644 src/include/ipxe/efi/efi_siglist.h create mode 100644 src/tests/efi_siglist_test.c diff --git a/src/config/config_asn1.c b/src/config/config_asn1.c index c4419d04d..107f99c1d 100644 --- a/src/config/config_asn1.c +++ b/src/config/config_asn1.c @@ -37,3 +37,6 @@ REQUIRE_OBJECT ( der ); #ifdef IMAGE_PEM REQUIRE_OBJECT ( pem ); #endif +#ifdef IMAGE_EFISIG +REQUIRE_OBJECT ( efi_siglist ); +#endif diff --git a/src/config/defaults/efi.h b/src/config/defaults/efi.h index 607f94c14..d9814eab5 100644 --- a/src/config/defaults/efi.h +++ b/src/config/defaults/efi.h @@ -35,6 +35,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define IMAGE_EFI /* EFI image support */ #define IMAGE_SCRIPT /* iPXE script image support */ +#define IMAGE_EFISIG /* EFI signature list support */ #define SANBOOT_PROTO_ISCSI /* iSCSI protocol */ #define SANBOOT_PROTO_AOE /* AoE protocol */ diff --git a/src/config/general.h b/src/config/general.h index 763a34aa0..c40e4fdae 100644 --- a/src/config/general.h +++ b/src/config/general.h @@ -125,6 +125,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define IMAGE_PNG /* PNG image support */ #define IMAGE_DER /* DER image support */ #define IMAGE_PEM /* PEM image support */ +//#define IMAGE_EFISIG /* EFI signature list image support */ //#define IMAGE_ZLIB /* ZLIB image support */ //#define IMAGE_GZIP /* GZIP image support */ //#define IMAGE_UCODE /* Microcode update image support */ diff --git a/src/image/efi_siglist.c b/src/image/efi_siglist.c new file mode 100644 index 000000000..56c8493d6 --- /dev/null +++ b/src/image/efi_siglist.c @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2025 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * EFI signature lists + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * Find EFI signature list entry + * + * @v data EFI signature list + * @v len Length of EFI signature list + * @v start Starting offset to update + * @v lhdr Signature list header to fill in + * @v dhdr Signature data header to fill in + * @ret rc Return status code + */ +static int efisig_find ( userptr_t data, size_t len, size_t *start, + EFI_SIGNATURE_LIST *lhdr, EFI_SIGNATURE_DATA *dhdr ) { + size_t offset; + size_t remaining; + size_t skip; + size_t dlen; + + /* Scan through signature list */ + offset = 0; + while ( 1 ) { + + /* Read list header */ + assert ( offset <= len ); + remaining = ( len - offset ); + if ( remaining < sizeof ( *lhdr ) ) { + DBGC ( data, "EFISIG [%#zx,%#zx) truncated header " + "at +%#zx\n", *start, len, offset ); + return -EINVAL; + } + copy_from_user ( lhdr, data, offset, sizeof ( *lhdr ) ); + + /* Get length of this signature list */ + if ( remaining < lhdr->SignatureListSize ) { + DBGC ( data, "EFISIG [%#zx,%#zx) truncated list at " + "+%#zx\n", *start, len, offset ); + return -EINVAL; + } + remaining = lhdr->SignatureListSize; + + /* Get length of each signature in list */ + dlen = lhdr->SignatureSize; + if ( dlen < sizeof ( *dhdr ) ) { + DBGC ( data, "EFISIG [%#zx,%#zx) underlength " + "signatures at +%#zx\n", *start, len, offset ); + return -EINVAL; + } + + /* Strip list header (including variable portion) */ + if ( ( remaining < sizeof ( *lhdr ) ) || + ( ( remaining - sizeof ( *lhdr ) ) < + lhdr->SignatureHeaderSize ) ) { + DBGC ( data, "EFISIG [%#zx,%#zx) malformed header at " + "+%#zx\n", *start, len, offset ); + return -EINVAL; + } + skip = ( sizeof ( *lhdr ) + lhdr->SignatureHeaderSize ); + offset += skip; + remaining -= skip; + + /* Read signatures */ + for ( ; remaining ; offset += dlen, remaining -= dlen ) { + + /* Check length */ + if ( remaining < dlen ) { + DBGC ( data, "EFISIG [%#zx,%#zx) truncated " + "at +%#zx\n", *start, len, offset ); + return -EINVAL; + } + + /* Continue until we find the requested signature */ + if ( offset < *start ) + continue; + + /* Read data header */ + copy_from_user ( dhdr, data, offset, sizeof ( *dhdr )); + DBGC2 ( data, "EFISIG [%#zx,%#zx) %s ", + offset, ( offset + dlen ), + efi_guid_ntoa ( &lhdr->SignatureType ) ); + DBGC2 ( data, "owner %s\n", + efi_guid_ntoa ( &dhdr->SignatureOwner ) ); + *start = offset; + return 0; + } + } +} + +/** + * Extract ASN.1 object from EFI signature list + * + * @v data EFI signature list + * @v len Length of EFI signature list + * @v offset Offset within image + * @v cursor ASN.1 cursor to fill in + * @ret next Offset to next image, or negative error + * + * The caller is responsible for eventually calling free() on the + * allocated ASN.1 cursor. + */ +int efisig_asn1 ( userptr_t data, size_t len, size_t offset, + struct asn1_cursor **cursor ) { + EFI_SIGNATURE_LIST lhdr; + EFI_SIGNATURE_DATA dhdr; + int ( * asn1 ) ( userptr_t data, size_t len, size_t offset, + struct asn1_cursor **cursor ); + size_t skip = offsetof ( typeof ( dhdr ), SignatureData ); + int next; + int rc; + + /* Locate signature list entry */ + if ( ( rc = efisig_find ( data, len, &offset, &lhdr, &dhdr ) ) != 0 ) + goto err_entry; + len = ( offset + lhdr.SignatureSize ); + + /* Parse as PEM or DER based on first character */ + asn1 = ( ( dhdr.SignatureData[0] == ASN1_SEQUENCE ) ? + der_asn1 : pem_asn1 ); + DBGC2 ( data, "EFISIG [%#zx,%#zx) extracting %s\n", offset, len, + ( ( asn1 == der_asn1 ) ? "DER" : "PEM" ) ); + next = asn1 ( data, len, ( offset + skip ), cursor ); + if ( next < 0 ) { + rc = next; + DBGC ( data, "EFISIG [%#zx,%#zx) could not extract ASN.1: " + "%s\n", offset, len, strerror ( rc ) ); + goto err_asn1; + } + + /* Check that whole entry was consumed */ + if ( ( ( unsigned int ) next ) != len ) { + DBGC ( data, "EFISIG [%#zx,%#zx) malformed data\n", + offset, len ); + rc = -EINVAL; + goto err_whole; + } + + return len; + + err_whole: + free ( *cursor ); + err_asn1: + err_entry: + return rc; +} + +/** + * Probe EFI signature list image + * + * @v image EFI signature list + * @ret rc Return status code + */ +static int efisig_image_probe ( struct image *image ) { + EFI_SIGNATURE_LIST lhdr; + EFI_SIGNATURE_DATA dhdr; + size_t offset = 0; + unsigned int count = 0; + int rc; + + /* Check file is a well-formed signature list */ + while ( 1 ) { + + /* Find next signature list entry */ + if ( ( rc = efisig_find ( image->data, image->len, &offset, + &lhdr, &dhdr ) ) != 0 ) { + return rc; + } + + /* Skip this entry */ + offset += lhdr.SignatureSize; + count++; + + /* Check if we have reached end of the image */ + if ( offset == image->len ) { + DBGC ( image, "EFISIG %s contains %d signatures\n", + image->name, count ); + return 0; + } + } +} + +/** + * Extract ASN.1 object from EFI signature list image + * + * @v image EFI signature list + * @v offset Offset within image + * @v cursor ASN.1 cursor to fill in + * @ret next Offset to next image, or negative error + * + * The caller is responsible for eventually calling free() on the + * allocated ASN.1 cursor. + */ +static int efisig_image_asn1 ( struct image *image, size_t offset, + struct asn1_cursor **cursor ) { + int next; + int rc; + + /* Extract ASN.1 object */ + if ( ( next = efisig_asn1 ( image->data, image->len, offset, + cursor ) ) < 0 ) { + rc = next; + DBGC ( image, "EFISIG %s could not extract ASN.1: %s\n", + image->name, strerror ( rc ) ); + return rc; + } + + return next; +} + +/** EFI signature list image type */ +struct image_type efisig_image_type __image_type ( PROBE_NORMAL ) = { + .name = "EFISIG", + .probe = efisig_image_probe, + .asn1 = efisig_image_asn1, +}; diff --git a/src/include/ipxe/efi/efi_siglist.h b/src/include/ipxe/efi/efi_siglist.h new file mode 100644 index 000000000..177f28b00 --- /dev/null +++ b/src/include/ipxe/efi/efi_siglist.h @@ -0,0 +1,22 @@ +#ifndef _IPXE_EFI_SIGLIST_H +#define _IPXE_EFI_SIGLIST_H + +/** @file + * + * PEM-encoded ASN.1 data + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include + +extern int efisig_asn1 ( userptr_t data, size_t len, size_t offset, + struct asn1_cursor **cursor ); + +extern struct image_type efisig_image_type __image_type ( PROBE_NORMAL ); + +#endif /* _IPXE_EFI_SIGLIST_H */ diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index b826a4a6f..15bb31b0e 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -323,6 +323,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_archive ( ERRFILE_IMAGE | 0x000a0000 ) #define ERRFILE_zlib ( ERRFILE_IMAGE | 0x000b0000 ) #define ERRFILE_gzip ( ERRFILE_IMAGE | 0x000c0000 ) +#define ERRFILE_efi_siglist ( ERRFILE_IMAGE | 0x000d0000 ) #define ERRFILE_asn1 ( ERRFILE_OTHER | 0x00000000 ) #define ERRFILE_chap ( ERRFILE_OTHER | 0x00010000 ) diff --git a/src/tests/efi_siglist_test.c b/src/tests/efi_siglist_test.c new file mode 100644 index 000000000..12d1ec6ac --- /dev/null +++ b/src/tests/efi_siglist_test.c @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2025 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * EFI signature list self-tests + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include +#include +#include +#include +#include "asn1_test.h" + +/** Define inline data */ +#define DATA(...) { __VA_ARGS__ } + +/** Define inline expected digest */ +#define DIGEST(...) { { __VA_ARGS__ } } + +/** Two certificates, one PEM, one DER, created by efisecdb */ +ASN1 ( efisecdb, &efisig_image_type, + DATA ( 0xa1, 0x59, 0xc0, 0xa5, 0xe4, 0x94, 0xa7, 0x4a, 0x87, 0xb5, + 0xab, 0x15, 0x5c, 0x2b, 0xf0, 0x72, 0x94, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x78, 0x01, 0x00, 0x00, 0xaf, 0x1e, + 0xbb, 0xc0, 0x33, 0x74, 0xa2, 0x4c, 0x93, 0xf2, 0xe9, 0x74, + 0x1b, 0x90, 0x98, 0x6c, 0x30, 0x82, 0x01, 0x64, 0x30, 0x82, + 0x01, 0x0e, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x10, 0x31, 0x0e, 0x30, + 0x0c, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x05, 0x74, 0x65, + 0x73, 0x74, 0x32, 0x30, 0x1e, 0x17, 0x0d, 0x32, 0x35, 0x30, + 0x33, 0x31, 0x31, 0x31, 0x31, 0x31, 0x37, 0x32, 0x36, 0x5a, + 0x17, 0x0d, 0x32, 0x35, 0x30, 0x34, 0x31, 0x30, 0x31, 0x31, + 0x31, 0x37, 0x32, 0x36, 0x5a, 0x30, 0x10, 0x31, 0x0e, 0x30, + 0x0c, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x05, 0x74, 0x65, + 0x73, 0x74, 0x32, 0x30, 0x5c, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, + 0x03, 0x4b, 0x00, 0x30, 0x48, 0x02, 0x41, 0x00, 0xc6, 0x75, + 0x2e, 0xc8, 0x09, 0x37, 0x14, 0xd3, 0xc0, 0xa5, 0x88, 0x3e, + 0x0d, 0xf9, 0x6f, 0x9f, 0xf2, 0xab, 0x3a, 0xe4, 0x6c, 0x0e, + 0x2b, 0x78, 0x3c, 0xe9, 0x1a, 0x52, 0x66, 0xbc, 0x7b, 0x7f, + 0xbe, 0xaa, 0xcd, 0x23, 0x68, 0x76, 0x26, 0x95, 0x45, 0x42, + 0xb5, 0xc6, 0x16, 0x2e, 0x3b, 0x33, 0x9d, 0x82, 0x6e, 0x6a, + 0xcf, 0xa5, 0x72, 0x71, 0x40, 0xff, 0xdc, 0x1d, 0x77, 0xe6, + 0x6f, 0x87, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x53, 0x30, + 0x51, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, + 0x04, 0x14, 0x1c, 0x11, 0x40, 0xcc, 0x63, 0xab, 0xad, 0x6a, + 0xa8, 0x83, 0x17, 0xbb, 0xc5, 0xc6, 0x94, 0x29, 0xe1, 0xad, + 0x4e, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, + 0x18, 0x30, 0x16, 0x80, 0x14, 0x1c, 0x11, 0x40, 0xcc, 0x63, + 0xab, 0xad, 0x6a, 0xa8, 0x83, 0x17, 0xbb, 0xc5, 0xc6, 0x94, + 0x29, 0xe1, 0xad, 0x4e, 0x21, 0x30, 0x0f, 0x06, 0x03, 0x55, + 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, + 0x01, 0xff, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x41, 0x00, + 0x57, 0xa3, 0x3a, 0x9c, 0x83, 0xae, 0x94, 0x4c, 0xcd, 0x06, + 0x86, 0x9b, 0x25, 0x70, 0x87, 0x61, 0xfe, 0xbf, 0xb4, 0xa6, + 0x52, 0x0b, 0x37, 0x37, 0x85, 0xbb, 0xea, 0x79, 0x2b, 0x0b, + 0xc4, 0x29, 0x03, 0x8d, 0xa0, 0x26, 0xc2, 0xb4, 0x25, 0x1c, + 0x87, 0x08, 0xcb, 0x94, 0xee, 0x61, 0x48, 0xa4, 0xe1, 0x77, + 0xa6, 0x24, 0x2d, 0x15, 0x1b, 0x15, 0x62, 0x6a, 0x0f, 0x28, + 0x7c, 0xcc, 0xa6, 0xaf, 0xa1, 0x59, 0xc0, 0xa5, 0xe4, 0x94, + 0xa7, 0x4a, 0x87, 0xb5, 0xab, 0x15, 0x5c, 0x2b, 0xf0, 0x72, + 0x4a, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x02, + 0x00, 0x00, 0xaf, 0x1e, 0xbb, 0xc0, 0x33, 0x74, 0xa2, 0x4c, + 0x93, 0xf2, 0xe9, 0x74, 0x1b, 0x90, 0x98, 0x6c, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x20, 0x43, + 0x45, 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x4d, 0x49, 0x49, 0x42, + 0x5a, 0x44, 0x43, 0x43, 0x41, 0x51, 0x36, 0x67, 0x41, 0x77, + 0x49, 0x42, 0x41, 0x67, 0x49, 0x42, 0x41, 0x54, 0x41, 0x4e, + 0x42, 0x67, 0x6b, 0x71, 0x68, 0x6b, 0x69, 0x47, 0x39, 0x77, + 0x30, 0x42, 0x41, 0x51, 0x73, 0x46, 0x41, 0x44, 0x41, 0x51, + 0x4d, 0x51, 0x34, 0x77, 0x44, 0x41, 0x59, 0x44, 0x56, 0x51, + 0x51, 0x44, 0x44, 0x41, 0x56, 0x30, 0x5a, 0x58, 0x4e, 0x30, + 0x0a, 0x4d, 0x54, 0x41, 0x65, 0x46, 0x77, 0x30, 0x79, 0x4e, + 0x54, 0x41, 0x7a, 0x4d, 0x54, 0x45, 0x78, 0x4d, 0x54, 0x45, + 0x33, 0x4d, 0x44, 0x42, 0x61, 0x46, 0x77, 0x30, 0x79, 0x4e, + 0x54, 0x41, 0x30, 0x4d, 0x54, 0x41, 0x78, 0x4d, 0x54, 0x45, + 0x33, 0x4d, 0x44, 0x42, 0x61, 0x4d, 0x42, 0x41, 0x78, 0x44, + 0x6a, 0x41, 0x4d, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x4d, + 0x4d, 0x42, 0x58, 0x52, 0x6c, 0x0a, 0x63, 0x33, 0x51, 0x78, + 0x4d, 0x46, 0x77, 0x77, 0x44, 0x51, 0x59, 0x4a, 0x4b, 0x6f, + 0x5a, 0x49, 0x68, 0x76, 0x63, 0x4e, 0x41, 0x51, 0x45, 0x42, + 0x42, 0x51, 0x41, 0x44, 0x53, 0x77, 0x41, 0x77, 0x53, 0x41, + 0x4a, 0x42, 0x41, 0x4e, 0x4d, 0x56, 0x4c, 0x35, 0x67, 0x78, + 0x76, 0x6c, 0x35, 0x31, 0x30, 0x32, 0x42, 0x4c, 0x6c, 0x31, + 0x78, 0x79, 0x7a, 0x56, 0x44, 0x6c, 0x4c, 0x77, 0x63, 0x62, + 0x0a, 0x59, 0x72, 0x6e, 0x52, 0x4e, 0x76, 0x53, 0x72, 0x68, + 0x6f, 0x2f, 0x59, 0x61, 0x31, 0x6f, 0x63, 0x31, 0x71, 0x76, + 0x73, 0x75, 0x34, 0x72, 0x71, 0x43, 0x64, 0x2f, 0x30, 0x68, + 0x65, 0x6a, 0x55, 0x6a, 0x4e, 0x66, 0x71, 0x4b, 0x47, 0x64, + 0x79, 0x57, 0x61, 0x49, 0x67, 0x43, 0x45, 0x38, 0x71, 0x78, + 0x4e, 0x50, 0x34, 0x68, 0x32, 0x64, 0x37, 0x4e, 0x72, 0x45, + 0x43, 0x41, 0x77, 0x45, 0x41, 0x0a, 0x41, 0x61, 0x4e, 0x54, + 0x4d, 0x46, 0x45, 0x77, 0x48, 0x51, 0x59, 0x44, 0x56, 0x52, + 0x30, 0x4f, 0x42, 0x42, 0x59, 0x45, 0x46, 0x47, 0x38, 0x46, + 0x4d, 0x78, 0x52, 0x6e, 0x53, 0x6b, 0x36, 0x34, 0x65, 0x79, + 0x42, 0x69, 0x56, 0x43, 0x35, 0x75, 0x67, 0x73, 0x35, 0x63, + 0x4f, 0x77, 0x38, 0x6a, 0x4d, 0x42, 0x38, 0x47, 0x41, 0x31, + 0x55, 0x64, 0x49, 0x77, 0x51, 0x59, 0x4d, 0x42, 0x61, 0x41, + 0x0a, 0x46, 0x47, 0x38, 0x46, 0x4d, 0x78, 0x52, 0x6e, 0x53, + 0x6b, 0x36, 0x34, 0x65, 0x79, 0x42, 0x69, 0x56, 0x43, 0x35, + 0x75, 0x67, 0x73, 0x35, 0x63, 0x4f, 0x77, 0x38, 0x6a, 0x4d, + 0x41, 0x38, 0x47, 0x41, 0x31, 0x55, 0x64, 0x45, 0x77, 0x45, + 0x42, 0x2f, 0x77, 0x51, 0x46, 0x4d, 0x41, 0x4d, 0x42, 0x41, + 0x66, 0x38, 0x77, 0x44, 0x51, 0x59, 0x4a, 0x4b, 0x6f, 0x5a, + 0x49, 0x68, 0x76, 0x63, 0x4e, 0x0a, 0x41, 0x51, 0x45, 0x4c, + 0x42, 0x51, 0x41, 0x44, 0x51, 0x51, 0x41, 0x4a, 0x4d, 0x54, + 0x78, 0x6c, 0x62, 0x4e, 0x43, 0x58, 0x62, 0x6b, 0x2f, 0x73, + 0x6a, 0x79, 0x67, 0x4b, 0x30, 0x39, 0x58, 0x68, 0x50, 0x38, + 0x48, 0x74, 0x4c, 0x6b, 0x45, 0x2b, 0x34, 0x33, 0x6e, 0x61, + 0x67, 0x44, 0x39, 0x4b, 0x52, 0x48, 0x35, 0x53, 0x52, 0x47, + 0x6b, 0x68, 0x45, 0x43, 0x34, 0x50, 0x7a, 0x68, 0x53, 0x31, + 0x0a, 0x52, 0x76, 0x65, 0x34, 0x79, 0x4a, 0x35, 0x50, 0x2b, + 0x4b, 0x4a, 0x74, 0x36, 0x4d, 0x65, 0x78, 0x38, 0x4c, 0x48, + 0x37, 0x79, 0x2b, 0x74, 0x38, 0x61, 0x42, 0x62, 0x79, 0x68, + 0x56, 0x30, 0x47, 0x0a, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x45, + 0x4e, 0x44, 0x20, 0x43, 0x45, 0x52, 0x54, 0x49, 0x46, 0x49, + 0x43, 0x41, 0x54, 0x45, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a ), + DIGEST ( 0x87, 0x95, 0x3b, 0x90, 0xb5, 0x5c, 0xb6, 0x7b, 0xc3, 0xfb, + 0xcb, 0x2c, 0x72, 0xbd, 0x4c, 0x2d, 0xb9, 0x9f, 0x10, 0xda ), + DIGEST ( 0x9b, 0x08, 0xa2, 0x7d, 0x53, 0x35, 0x0a, 0xeb, 0x53, 0xca, + 0x50, 0x66, 0xc0, 0xfd, 0xbd, 0x70, 0x78, 0xf2, 0xa0, 0xc9 ) ); + +/** + * Perform EFI signature list self-test + * + */ +static void efisig_test_exec ( void ) { + + /* Perform tests */ + asn1_ok ( &efisecdb ); +} + +/** EFI signature list self-test */ +struct self_test efisig_test __self_test = { + .name = "efisig", + .exec = efisig_test_exec, +}; diff --git a/src/tests/tests.c b/src/tests/tests.c index 96687423f..865818bdc 100644 --- a/src/tests/tests.c +++ b/src/tests/tests.c @@ -88,3 +88,4 @@ REQUIRE_OBJECT ( uuid_test ); REQUIRE_OBJECT ( editstring_test ); REQUIRE_OBJECT ( p256_test ); REQUIRE_OBJECT ( p384_test ); +REQUIRE_OBJECT ( efi_siglist_test ); From d6ee9a9242f7f2c3ea1fad6959ff16481b34f6b3 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 12 Mar 2025 14:15:16 +0000 Subject: [PATCH 32/50] [cpio] Fix calculation of name lengths in CPIO headers Commit 12ea8c4 ("[cpio] Allow for construction of parent directories as needed") introduced a regression in constructing CPIO archive headers for relative paths (e.g. simple filenames with no leading slash). Fix by counting the number of path components rather than the number of path separators, and add some test cases to cover CPIO header construction. Signed-off-by: Michael Brown --- src/core/cpio.c | 16 ++- src/tests/cpio_test.c | 263 ++++++++++++++++++++++++++++++++++++++++++ src/tests/tests.c | 1 + 3 files changed, 274 insertions(+), 6 deletions(-) create mode 100644 src/tests/cpio_test.c diff --git a/src/core/cpio.c b/src/core/cpio.c index 4a7061960..63ac28ee6 100644 --- a/src/core/cpio.c +++ b/src/core/cpio.c @@ -63,14 +63,15 @@ static unsigned int cpio_max ( struct image *image ) { const char *name = cpio_name ( image ); unsigned int max = 0; char c; + char p; /* Check for existence of CPIO filename */ if ( ! name ) return 0; - /* Count number of path separators */ - while ( ( ( c = *(name++) ) ) && ( c != ' ' ) ) { - if ( c == '/' ) + /* Count number of path components */ + for ( p = '/' ; ( ( ( c = *(name++) ) ) && ( c != ' ' ) ) ; p = c ) { + if ( ( p == '/' ) && ( c != '/' ) ) max++; } @@ -88,13 +89,16 @@ static size_t cpio_name_len ( struct image *image, unsigned int depth ) { const char *name = cpio_name ( image ); size_t len; char c; + char p; - /* Sanity check */ + /* Sanity checks */ assert ( name != NULL ); + assert ( depth > 0 ); /* Calculate length up to specified path depth */ - for ( len = 0 ; ( ( ( c = name[len] ) ) && ( c != ' ' ) ) ; len++ ) { - if ( ( c == '/' ) && ( depth-- == 0 ) ) + for ( len = 0, p = '/' ; ( ( ( c = name[len] ) ) && ( c != ' ' ) ) ; + len++, p = c ) { + if ( ( c == '/' ) && ( p != '/' ) && ( --depth == 0 ) ) break; } diff --git a/src/tests/cpio_test.c b/src/tests/cpio_test.c new file mode 100644 index 000000000..3498c0f95 --- /dev/null +++ b/src/tests/cpio_test.c @@ -0,0 +1,263 @@ +/* + * Copyright (C) 2025 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * CPIO self-tests + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include +#include +#include + +/** A CPIO test */ +struct cpio_test { + /** Test name */ + const char *name; + /** Image length */ + size_t len; + /** Image command line */ + const char *cmdline; + /** Expected CPIO headers */ + const uint8_t *expected; + /** Length of expected CPIO headers */ + size_t expected_len; + /** Expected number of CPIO headers */ + unsigned int expected_count; +}; + +/** Define an expected CPIO header */ +#define CPIO_HEADER( mode, filesize, namesize, pname ) \ + "070701" "00000000" mode "00000000" "00000000" "00000001" \ + "00000000" filesize "00000000" "00000000" "00000000" "00000000" \ + namesize "00000000" pname + +/** Define a one-byte padding */ +#define PAD1 "\0" + +/** Define a two-byte padding */ +#define PAD2 "\0\0" + +/** Define a three-byte padding */ +#define PAD3 "\0\0\0" + +/** Define four-byte padding */ +#define PAD4 "\0\0\0\0" + +/** Define a CPIO test */ +#define CPIO_TEST( NAME, LEN, CMDLINE, COUNT, EXPECTED ) \ + static const uint8_t NAME ## _expected[] = EXPECTED; \ + static struct cpio_test NAME = { \ + .name = #NAME, \ + .len = LEN, \ + .cmdline = CMDLINE, \ + .expected = NAME ## _expected, \ + .expected_len = ( sizeof ( NAME ## _expected ) \ + - 1 /* NUL */ ), \ + .expected_count = COUNT, \ + }; + +/** + * Report a CPIO test result + * + * @v test CPIO test + * @v file Test code file + * @v line Test code line + */ +static void cpio_okx ( struct cpio_test *test, const char *file, + unsigned int line ) { + struct cpio_header cpio; + struct image *image; + uint8_t *data; + size_t len; + size_t cpio_len; + unsigned int i; + unsigned int j; + + DBGC ( test, "CPIO len %#zx cmdline \"%s\"\n", + test->len, test->cmdline ); + DBGC2_HDA ( test, 0, test->expected, test->expected_len ); + + /* Sanity check */ + okx ( ( test->expected_len % CPIO_ALIGN ) == 0, file, line ); + + /* Construct dummy image */ + image = alloc_image ( NULL ); + okx ( image != NULL, file, line ); + okx ( image_set_name ( image, test->name ) == 0, file, line ); + okx ( image_set_len ( image, test->len ) == 0, file, line ); + okx ( image_set_cmdline ( image, test->cmdline ) == 0, file, line ); + + /* Calculate length of CPIO headers */ + len = 0; + for ( i = 0 ; ( cpio_len = cpio_header ( image, i, &cpio ) ) ; i++ ) { + okx ( cpio_len >= sizeof ( cpio ), file, line ); + len += ( cpio_len + cpio_pad_len ( cpio_len ) ); + okx ( cpio_pad_len ( cpio_len ) > 0, file, line ); + okx ( ( len % CPIO_ALIGN ) == 0, file, line ); + } + okx ( i == test->expected_count, file, line ); + okx ( len == test->expected_len, file, line ); + + /* Allocate space for CPIO headers */ + data = zalloc ( len ); + okx ( data != NULL, file, line ); + + /* Construct CPIO headers */ + len = 0; + for ( i = 0 ; ( cpio_len = cpio_header ( image, i, &cpio ) ) ; i++ ) { + memcpy ( ( data + len ), &cpio, sizeof ( cpio ) ); + memcpy ( ( data + len + sizeof ( cpio ) ), cpio_name ( image ), + ( cpio_len - sizeof ( cpio ) ) ); + DBGC ( test, "CPIO hdr %d: ", i ); + for ( j = 0 ; j < cpio_len ; j++ ) { + if ( ( j <= sizeof ( cpio ) && ! ( ( j + 2 ) % 8 ) ) ) + DBGC ( test, " " ); + DBGC ( test, "%c", data[ len + j ] ); + } + DBGC ( test, "\n" ); + len += ( cpio_len + cpio_pad_len ( cpio_len ) ); + } + okx ( i == test->expected_count, file, line ); + okx ( len == test->expected_len, file, line ); + + /* Verify constructed CPIO headers */ + DBGC2_HDA ( test, 0, data, len ); + okx ( memcmp ( data, test->expected, test->expected_len ) == 0, + file, line ); + + /* Free constructed headers */ + free ( data ); + + /* Drop reference to dummy image */ + image_put ( image ); +} +#define cpio_ok( test ) cpio_okx ( test, __FILE__, __LINE__ ) + +/* Image with no command line */ +CPIO_TEST ( no_cmdline, 42, NULL, 0, "" ); + +/* Image with empty command line */ +CPIO_TEST ( empty_cmdline, 154, "", 0, "" ); + +/* All slashes */ +CPIO_TEST ( all_slashes, 64, "////", 0, "" ); + +/* Simple filename */ +CPIO_TEST ( simple, 0x69, "wimboot", 1, + CPIO_HEADER ( "000081a4", "00000069", "00000008", + "wimboot" PAD3 ) ); + +/* Initial slash */ +CPIO_TEST ( init_slash, 0x273, "/wimboot", 1, + CPIO_HEADER ( "000081a4", "00000273", "00000009", + "/wimboot" PAD2 ) ); + +/* Initial slashes */ +CPIO_TEST ( init_slashes, 0x94, "///initscript", 1, + CPIO_HEADER ( "000081a4", "00000094", "0000000e", + "///initscript" PAD1 ) ); + +/* Full path */ +CPIO_TEST ( path, 0x341, "/usr/share/oem/config.ign", 1, + CPIO_HEADER ( "000081a4", "00000341", "0000001a", + "/usr/share/oem/config.ign" PAD1 ) ); + +/* Full path, mkdir=0 */ +CPIO_TEST ( path_mkdir_0, 0x341, "/usr/share/oem/config.ign mkdir=0", 1, + CPIO_HEADER ( "000081a4", "00000341", "0000001a", + "/usr/share/oem/config.ign" PAD1 ) ); + +/* Full path, mkdir=1 */ +CPIO_TEST ( path_mkdir_1, 0x341, "/usr/share/oem/config.ign mkdir=1", 2, + CPIO_HEADER ( "000041ed", "00000000", "0000000f", + "/usr/share/oem" PAD4 ) + CPIO_HEADER ( "000081a4", "00000341", "0000001a", + "/usr/share/oem/config.ign" PAD1 ) ); + +/* Full path, mkdir=2 */ +CPIO_TEST ( path_mkdir_2, 0x341, "/usr/share/oem/config.ign mkdir=2", 3, + CPIO_HEADER ( "000041ed", "00000000", "0000000b", + "/usr/share" PAD4 ) + CPIO_HEADER ( "000041ed", "00000000", "0000000f", + "/usr/share/oem" PAD4 ) + CPIO_HEADER ( "000081a4", "00000341", "0000001a", + "/usr/share/oem/config.ign" PAD1 ) ); + +/* Full path, mkdir=-1 */ +CPIO_TEST ( path_mkdir_all, 0x341, "/usr/share/oem/config.ign mkdir=-1", 4, + CPIO_HEADER ( "000041ed", "00000000", "00000005", + "/usr" PAD2 ) + CPIO_HEADER ( "000041ed", "00000000", "0000000b", + "/usr/share" PAD4 ) + CPIO_HEADER ( "000041ed", "00000000", "0000000f", + "/usr/share/oem" PAD4 ) + CPIO_HEADER ( "000081a4", "00000341", "0000001a", + "/usr/share/oem/config.ign" PAD1 ) ); + +/* Custom mode */ +CPIO_TEST ( mode, 39, "/sbin/init mode=755", 1, + CPIO_HEADER ( "000081ed", "00000027", "0000000b", + "/sbin/init" PAD4 ) ); + +/* Chaos */ +CPIO_TEST ( chaos, 73, "///etc//init.d///runthings mode=700 mkdir=99", 3, + CPIO_HEADER ( "000041ed", "00000000", "00000007", + "///etc" PAD4 ) + CPIO_HEADER ( "000041ed", "00000000", "0000000f", + "///etc//init.d" PAD4 ) + CPIO_HEADER ( "000081c0", "00000049", "0000001b", + "///etc//init.d///runthings" PAD4 ) ); + +/** + * Perform CPIO self-test + * + */ +static void cpio_test_exec ( void ) { + + cpio_ok ( &no_cmdline ); + cpio_ok ( &empty_cmdline ); + cpio_ok ( &all_slashes ); + cpio_ok ( &simple ); + cpio_ok ( &init_slash ); + cpio_ok ( &init_slashes ); + cpio_ok ( &path ); + cpio_ok ( &path_mkdir_0 ); + cpio_ok ( &path_mkdir_1 ); + cpio_ok ( &path_mkdir_2 ); + cpio_ok ( &path_mkdir_all ); + cpio_ok ( &mode ); + cpio_ok ( &chaos ); +} + +/** CPIO self-test */ +struct self_test cpio_test __self_test = { + .name = "cpio", + .exec = cpio_test_exec, +}; diff --git a/src/tests/tests.c b/src/tests/tests.c index 865818bdc..447eec314 100644 --- a/src/tests/tests.c +++ b/src/tests/tests.c @@ -89,3 +89,4 @@ REQUIRE_OBJECT ( editstring_test ); REQUIRE_OBJECT ( p256_test ); REQUIRE_OBJECT ( p384_test ); REQUIRE_OBJECT ( efi_siglist_test ); +REQUIRE_OBJECT ( cpio_test ); From da3024d257d73d2c40e65c6fb9db5a2a6bc86dbb Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 12 Mar 2025 14:25:05 +0000 Subject: [PATCH 33/50] [cpio] Allow for the construction of pure directories Allow for the possibility of creating empty directories (without having to include a dummy file inside the directory) using a zero-length image and a CPIO filename with a trailing slash, such as: initrd emptyfile /usr/share/oem/ Signed-off-by: Michael Brown --- src/core/cpio.c | 11 +++++------ src/tests/cpio_test.c | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/core/cpio.c b/src/core/cpio.c index 63ac28ee6..9f5d772e9 100644 --- a/src/core/cpio.c +++ b/src/core/cpio.c @@ -179,17 +179,16 @@ size_t cpio_header ( struct image *image, unsigned int index, /* Get filename length */ name_len = cpio_name_len ( image, depth ); - /* Calculate mode and length */ - if ( depth < max ) { - /* Directory */ + /* Set directory mode or file mode as appropriate */ + if ( name[name_len] == '/' ) { mode = ( CPIO_MODE_DIR | CPIO_DEFAULT_DIR_MODE ); - len = 0; } else { - /* File */ mode |= CPIO_MODE_FILE; - len = image->len; } + /* Set length on final header */ + len = ( ( depth < max ) ? 0 : image->len ); + /* Construct CPIO header */ memset ( cpio, '0', sizeof ( *cpio ) ); memcpy ( cpio->c_magic, CPIO_MAGIC, sizeof ( cpio->c_magic ) ); diff --git a/src/tests/cpio_test.c b/src/tests/cpio_test.c index 3498c0f95..7eb8b2c74 100644 --- a/src/tests/cpio_test.c +++ b/src/tests/cpio_test.c @@ -221,6 +221,20 @@ CPIO_TEST ( path_mkdir_all, 0x341, "/usr/share/oem/config.ign mkdir=-1", 4, CPIO_HEADER ( "000081a4", "00000341", "0000001a", "/usr/share/oem/config.ign" PAD1 ) ); +/* Simple directory */ +CPIO_TEST ( dir, 0, "/opt/", 1, + CPIO_HEADER ( "000041ed", "00000000", "00000005", + "/opt" PAD2 ) ); + +/* Directory tree */ +CPIO_TEST ( tree, 0, "/opt/oem/scripts/ mkdir=-1", 3, + CPIO_HEADER ( "000041ed", "00000000", "00000005", + "/opt" PAD2 ) + CPIO_HEADER ( "000041ed", "00000000", "00000009", + "/opt/oem" PAD2 ) + CPIO_HEADER ( "000041ed", "00000000", "00000011", + "/opt/oem/scripts" PAD2 ) ); + /* Custom mode */ CPIO_TEST ( mode, 39, "/sbin/init mode=755", 1, CPIO_HEADER ( "000081ed", "00000027", "0000000b", @@ -252,6 +266,8 @@ static void cpio_test_exec ( void ) { cpio_ok ( &path_mkdir_1 ); cpio_ok ( &path_mkdir_2 ); cpio_ok ( &path_mkdir_all ); + cpio_ok ( &dir ); + cpio_ok ( &tree ); cpio_ok ( &mode ); cpio_ok ( &chaos ); } From 2a901a33dffb0b8cf25d54312a73e39db7d15f33 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 13 Mar 2025 13:56:27 +0000 Subject: [PATCH 34/50] [efi] Add EFI_GLOBAL_VARIABLE as a well-known GUID Signed-off-by: Michael Brown --- src/include/ipxe/efi/efi.h | 1 + src/interface/efi/efi_guid.c | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/src/include/ipxe/efi/efi.h b/src/include/ipxe/efi/efi.h index ef492849e..862a38e5c 100644 --- a/src/include/ipxe/efi/efi.h +++ b/src/include/ipxe/efi/efi.h @@ -248,6 +248,7 @@ extern EFI_GUID efi_vlan_config_protocol_guid; extern EFI_GUID efi_cert_x509_guid; extern EFI_GUID efi_file_info_id; extern EFI_GUID efi_file_system_info_id; +extern EFI_GUID efi_global_variable; extern EFI_HANDLE efi_image_handle; extern EFI_LOADED_IMAGE_PROTOCOL *efi_loaded_image; diff --git a/src/interface/efi/efi_guid.c b/src/interface/efi/efi_guid.c index bd35b94cc..ae78c068e 100644 --- a/src/interface/efi/efi_guid.c +++ b/src/interface/efi/efi_guid.c @@ -85,6 +85,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include /** @file @@ -403,6 +404,9 @@ EFI_GUID efi_file_info_id = EFI_FILE_INFO_ID; /** File system information GUID */ EFI_GUID efi_file_system_info_id = EFI_FILE_SYSTEM_INFO_ID; +/** Global variable GUID */ +EFI_GUID efi_global_variable = EFI_GLOBAL_VARIABLE; + /** HttpBootDxe module GUID */ static EFI_GUID efi_http_boot_dxe_guid = { 0xecebcb00, 0xd9c8, 0x11e4, @@ -489,6 +493,8 @@ static struct efi_well_known_guid efi_well_known_guids[] = { "Dns6" }, { &efi_dns6_service_binding_protocol_guid, "Dns6Sb" }, + { &efi_global_variable, + "GlobalVar" }, { &efi_graphics_output_protocol_guid, "GraphicsOutput" }, { &efi_hii_config_access_protocol_guid, From aa49ce5b1dce3dfbf97bf67ef95524e4710c99f5 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 13 Mar 2025 13:40:16 +0000 Subject: [PATCH 35/50] [efi] Add TLS authentication header and GUID definitions Add the TlsAuthentication.h header from EDK2's NetworkPkg, along with a GUID definition for EFI_TLS_CA_CERTIFICATE_GUID. It is unclear whether or not the TlsCaCertificate variable is intended to be a UEFI standard. Its presence in NetworkPkg (rather than MdePkg) suggests not, but the choice of EFI_TLS_CA_CERTIFICATE_GUID (rather than e.g. EDKII_TLS_CA_CERTIFICATE_GUID) suggests that it is intended to be included in future versions of the standard. Signed-off-by: Michael Brown --- src/include/ipxe/efi/Guid/TlsAuthentication.h | 25 +++++++++++++++++++ src/include/ipxe/efi/efi.h | 1 + src/include/ipxe/efi/import.pl | 3 ++- src/interface/efi/efi_guid.c | 6 +++++ 4 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 src/include/ipxe/efi/Guid/TlsAuthentication.h diff --git a/src/include/ipxe/efi/Guid/TlsAuthentication.h b/src/include/ipxe/efi/Guid/TlsAuthentication.h new file mode 100644 index 000000000..f1e1b4f40 --- /dev/null +++ b/src/include/ipxe/efi/Guid/TlsAuthentication.h @@ -0,0 +1,25 @@ +/** @file + This file defines TlsCaCertificate variable. + +Copyright (c) 2016, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __TLS_AUTHENTICATION_H__ +#define __TLS_AUTHENTICATION_H__ + +FILE_LICENCE ( BSD2_PATENT ); + +// Private variable for CA Certificate configuration +// +#define EFI_TLS_CA_CERTIFICATE_GUID \ + { \ + 0xfd2340D0, 0x3dab, 0x4349, { 0xa6, 0xc7, 0x3b, 0x4f, 0x12, 0xb4, 0x8e, 0xae } \ + } + +#define EFI_TLS_CA_CERTIFICATE_VARIABLE L"TlsCaCertificate" + +extern EFI_GUID gEfiTlsCaCertificateGuid; + +#endif diff --git a/src/include/ipxe/efi/efi.h b/src/include/ipxe/efi/efi.h index 862a38e5c..486c6070e 100644 --- a/src/include/ipxe/efi/efi.h +++ b/src/include/ipxe/efi/efi.h @@ -249,6 +249,7 @@ extern EFI_GUID efi_cert_x509_guid; extern EFI_GUID efi_file_info_id; extern EFI_GUID efi_file_system_info_id; extern EFI_GUID efi_global_variable; +extern EFI_GUID efi_tls_ca_certificate_guid; extern EFI_HANDLE efi_image_handle; extern EFI_LOADED_IMAGE_PROTOCOL *efi_loaded_image; diff --git a/src/include/ipxe/efi/import.pl b/src/include/ipxe/efi/import.pl index 0a7669f43..346d45e5f 100755 --- a/src/include/ipxe/efi/import.pl +++ b/src/include/ipxe/efi/import.pl @@ -118,7 +118,8 @@ pod2usage ( 1 ) unless @ARGV == 1; my $edktop = shift; # Identify edk import directories -my $edkdirs = [ "MdePkg/Include", "MdeModulePkg/Include" ]; +my $edkdirs = [ "MdePkg/Include", "MdeModulePkg/Include", + "NetworkPkg/Include" ]; foreach my $edkdir ( @$edkdirs ) { die "Directory \"$edktop\" does not appear to contain the EFI EDK2 " ."(missing \"$edkdir\")\n" unless -d catdir ( $edktop, $edkdir ); diff --git a/src/interface/efi/efi_guid.c b/src/interface/efi/efi_guid.c index ae78c068e..9d9c9ef91 100644 --- a/src/interface/efi/efi_guid.c +++ b/src/interface/efi/efi_guid.c @@ -87,6 +87,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include /** @file * @@ -407,6 +408,9 @@ EFI_GUID efi_file_system_info_id = EFI_FILE_SYSTEM_INFO_ID; /** Global variable GUID */ EFI_GUID efi_global_variable = EFI_GLOBAL_VARIABLE; +/** TLS CA certificate variable GUID */ +EFI_GUID efi_tls_ca_certificate_guid = EFI_TLS_CA_CERTIFICATE_GUID; + /** HttpBootDxe module GUID */ static EFI_GUID efi_http_boot_dxe_guid = { 0xecebcb00, 0xd9c8, 0x11e4, @@ -583,6 +587,8 @@ static struct efi_well_known_guid efi_well_known_guids[] = { "Tcp6" }, { &efi_tcp6_service_binding_protocol_guid, "Tcp6Sb" }, + { &efi_tls_ca_certificate_guid, + "TlsCaCert" }, { &efi_tree_protocol_guid, "TrEE" }, { &efi_udp4_protocol_guid, From ddc2d928d25e3c87ba6153b96446113d59221b94 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 13 Mar 2025 14:04:26 +0000 Subject: [PATCH 36/50] [efi] Accept and trust CA certificates in the TlsCaCertificates variable UEFI's built-in HTTPS boot mechanism requires the trusted CA certificates to be provided via the TlsCaCertificates variable. (There is no equivalent of the iPXE cross-signing mechanism, so it is not possible for UEFI to automatically use public CA certificates.) Users who have configured UEFI HTTPS boot to use a custom root of trust (e.g. a private CA certificate) may find it useful to have iPXE automatically pick up and use this same root of trust, so that iPXE can seamlessly fetch files via HTTPS from the same servers that were trusted by UEFI HTTPS boot, in addition to servers that iPXE can validate through other means such as cross-signed certificates. Parse the TlsCaCertificates variable at startup, add any certificates to the certificate store, and mark these certificates as trusted. There are no access restrictions on modifying the TlsCaCertificates variable: anybody with access to write UEFI variables is permitted to change the root of trust. The UEFI security model assumes that anyone with access to run code prior to ExitBootServices() or with access to modify UEFI variables from within a loaded operating system is supposed to be able to change the system's root of trust for TLS. Any certificates parsed from TlsCaCertificates will show up in the output of "certstat", and may be discarded using "certfree" if unwanted. Support for parsing TlsCaCertificates is enabled by default in EFI builds, but may be disabled in config/general.h if needed. As with the ${trust} setting, the contents of the TlsCaCertificates variable will be ignored if iPXE has been compiled with an explicit root of trust by specifying TRUST=... on the build command line. Signed-off-by: Michael Brown --- src/config/config_certs.c | 36 ++++++ src/config/defaults/efi.h | 2 + src/config/general.h | 6 + src/crypto/certstore.c | 6 + src/crypto/rootcert.c | 3 + src/crypto/x509.c | 6 +- src/include/ipxe/errfile.h | 1 + src/include/ipxe/rootcert.h | 1 + src/include/ipxe/x509.h | 3 + src/interface/efi/efi_cacert.c | 200 +++++++++++++++++++++++++++++++++ 10 files changed, 261 insertions(+), 3 deletions(-) create mode 100644 src/config/config_certs.c create mode 100644 src/interface/efi/efi_cacert.c diff --git a/src/config/config_certs.c b/src/config/config_certs.c new file mode 100644 index 000000000..a325d132c --- /dev/null +++ b/src/config/config_certs.c @@ -0,0 +1,36 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** @file + * + * Certificate source configuration + * + */ + +PROVIDE_REQUIRING_SYMBOL(); + +#ifdef CERTS_EFI +REQUIRE_OBJECT ( efi_cacert ); +#endif diff --git a/src/config/defaults/efi.h b/src/config/defaults/efi.h index d9814eab5..a0e52e7a7 100644 --- a/src/config/defaults/efi.h +++ b/src/config/defaults/efi.h @@ -53,6 +53,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define EFI_SETTINGS /* EFI variable settings */ +#define CERTS_EFI /* EFI certificate sources */ + #if defined ( __i386__ ) || defined ( __x86_64__ ) #define IOAPI_X86 #define ENTROPY_RDRAND diff --git a/src/config/general.h b/src/config/general.h index c40e4fdae..7ac77bfe6 100644 --- a/src/config/general.h +++ b/src/config/general.h @@ -174,6 +174,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define SHIM_CMD /* EFI shim command (or dummy command) */ //#define USB_CMD /* USB commands */ +/* + * Certificate sources + * + */ +//#undef CERTS_EFI /* EFI certificate sources */ + /* * ROM-specific options * diff --git a/src/crypto/certstore.c b/src/crypto/certstore.c index 31797c4cd..86f67a0af 100644 --- a/src/crypto/certstore.c +++ b/src/crypto/certstore.c @@ -266,3 +266,9 @@ static int certstore_apply_settings ( void ) { struct settings_applicator certstore_applicator __settings_applicator = { .apply = certstore_apply_settings, }; + +/* Drag in objects via certificate store */ +REQUIRING_SYMBOL ( certstore ); + +/* Drag in alternative certificate sources */ +REQUIRE_OBJECT ( config_certs ); diff --git a/src/crypto/rootcert.c b/src/crypto/rootcert.c index 0835ff071..e2b817c57 100644 --- a/src/crypto/rootcert.c +++ b/src/crypto/rootcert.c @@ -58,6 +58,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); 0xed, 0x1a, #endif +/** Flag indicating if root of trust may be overridden at runtime */ +const int allow_trust_override = ALLOW_TRUST_OVERRIDE; + /** Root certificate fingerprints */ static const uint8_t fingerprints[] = { TRUSTED }; diff --git a/src/crypto/x509.c b/src/crypto/x509.c index 4101c8094..acb27411a 100644 --- a/src/crypto/x509.c +++ b/src/crypto/x509.c @@ -1323,9 +1323,9 @@ int x509_is_valid ( struct x509_certificate *cert, struct x509_root *root ) { * @v issuer Issuing X.509 certificate (or NULL) * @v root Root certificate list */ -static void x509_set_valid ( struct x509_certificate *cert, - struct x509_certificate *issuer, - struct x509_root *root ) { +void x509_set_valid ( struct x509_certificate *cert, + struct x509_certificate *issuer, + struct x509_root *root ) { unsigned int max_path_remaining; /* Sanity checks */ diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 15bb31b0e..c704dd44d 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -428,6 +428,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_usb_cmd ( ERRFILE_OTHER | 0x00640000 ) #define ERRFILE_usb_settings ( ERRFILE_OTHER | 0x00650000 ) #define ERRFILE_weierstrass ( ERRFILE_OTHER | 0x00660000 ) +#define ERRFILE_efi_cacert ( ERRFILE_OTHER | 0x00670000 ) /** @} */ diff --git a/src/include/ipxe/rootcert.h b/src/include/ipxe/rootcert.h index d4be2e1bc..d1a69723d 100644 --- a/src/include/ipxe/rootcert.h +++ b/src/include/ipxe/rootcert.h @@ -11,6 +11,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include +extern const int allow_trust_override; extern struct x509_root root_certificates; #endif /* _IPXE_ROOTCERT_H */ diff --git a/src/include/ipxe/x509.h b/src/include/ipxe/x509.h index e71cee8a3..e8cd0f303 100644 --- a/src/include/ipxe/x509.h +++ b/src/include/ipxe/x509.h @@ -421,6 +421,9 @@ extern int x509_certificate ( const void *data, size_t len, struct x509_certificate **cert ); extern int x509_is_valid ( struct x509_certificate *cert, struct x509_root *root ); +extern void x509_set_valid ( struct x509_certificate *cert, + struct x509_certificate *issuer, + struct x509_root *root ); extern int x509_validate ( struct x509_certificate *cert, struct x509_certificate *issuer, time_t time, struct x509_root *root ); diff --git a/src/interface/efi/efi_cacert.c b/src/interface/efi/efi_cacert.c new file mode 100644 index 000000000..5cc268b0e --- /dev/null +++ b/src/interface/efi/efi_cacert.c @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2025 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * EFI CA certificates + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** List of EFI CA certificates */ +static struct x509_chain efi_cacerts = { + .refcnt = REF_INIT ( ref_no_free ), + .links = LIST_HEAD_INIT ( efi_cacerts.links ), +}; + +/** + * Retrieve EFI CA certificate + * + * @v data TlsCaCertificate variable data + * @v len Length of TlsCaCertificate + * @v offset Offset within data + * @v next Next offset, or negative error + */ +static int efi_cacert ( void *data, size_t len, size_t offset ) { + struct asn1_cursor *cursor; + struct x509_certificate *cert; + int next; + int rc; + + /* Extract ASN.1 object */ + next = efisig_asn1 ( virt_to_user ( data ), len, offset, &cursor ); + if ( next < 0 ) { + rc = next; + DBGC ( &efi_cacerts, "EFICA could not parse at +%#zx: %s\n", + offset, strerror ( rc ) ); + goto err_asn1; + } + + /* Append to list of EFI CA certificates */ + if ( ( rc = x509_append_raw ( &efi_cacerts, cursor->data, + cursor->len ) ) != 0 ) { + DBGC ( &efi_cacerts, "EFICA could not append at +%#zx: %s\n", + offset, strerror ( rc ) ); + goto err_append; + } + cert = x509_last ( &efi_cacerts ); + DBGC ( &efi_cacerts, "EFICA found certificate %s\n", + x509_name ( cert ) ); + + /* Mark certificate as valid (i.e. trusted) if permitted */ + if ( allow_trust_override ) { + DBGC ( &efi_cacerts, "EFICA trusting certificate %s\n", + x509_name ( cert ) ); + x509_set_valid ( cert, NULL, &root_certificates ); + } + + /* Free ASN.1 object */ + free ( cursor ); + + return next; + + err_append: + free ( cursor ); + err_asn1: + return rc; +} + +/** + * Retrieve all EFI CA certificates + * + * @ret rc Return status code + */ +static int efi_cacert_all ( void ) { + EFI_RUNTIME_SERVICES *rs = efi_systab->RuntimeServices; + EFI_GUID *guid = &efi_tls_ca_certificate_guid; + static CHAR16 *wname = EFI_TLS_CA_CERTIFICATE_VARIABLE; + int offset = 0; + UINT32 attrs; + UINTN size; + void *data; + EFI_STATUS efirc; + int rc; + + /* Get variable length */ + size = 0; + if ( ( efirc = rs->GetVariable ( wname, guid, &attrs, &size, + NULL ) ) != EFI_BUFFER_TOO_SMALL ) { + rc = -EEFI ( efirc ); + DBGC ( &efi_cacerts, "EFICA could not get %ls size: %s\n", + wname, strerror ( rc ) ); + goto err_len; + } + + /* Allocate temporary buffer */ + data = malloc ( size ); + if ( ! data ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Read variable */ + if ( ( efirc = rs->GetVariable ( wname, guid, &attrs, &size, + data ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( &efi_cacerts, "EFICA could not read %ls: %s\n", + wname, strerror ( rc ) ); + goto err_get; + } + + /* Parse certificates */ + while ( ( ( size_t ) offset ) < size ) { + offset = efi_cacert ( data, size, offset ); + if ( offset < 0 ) { + rc = offset; + goto err_cacert; + } + } + + /* Success */ + rc = 0; + + err_cacert: + err_get: + free ( data ); + err_alloc: + err_len: + return rc; +} + +/** + * Initialise EFI CA certificates + * + */ +static void efi_cacert_init ( void ) { + int rc; + + /* Initialise all certificates */ + if ( ( rc = efi_cacert_all() ) != 0 ) { + DBGC ( &efi_cacert, "EFICA could not initialise: %s\n", + strerror ( rc ) ); + /* Nothing we can do at this point */ + return; + } +} + +/** EFI CA certificates initialisation function */ +struct init_fn efi_cacert_init_fn __init_fn ( INIT_LATE ) = { + .initialise = efi_cacert_init, +}; + +/** + * Discard any EFI CA certificates + * + */ +static void efi_cacert_shutdown ( int booting __unused ) { + + /* Drop our references to the certificates */ + DBGC ( &efi_cacert, "EFICA discarding certificates\n" ); + x509_truncate ( &efi_cacerts, NULL ); + assert ( list_empty ( &efi_cacerts.links ) ); +} + +/** EFI CA certificates shutdown function */ +struct startup_fn efi_cacert_shutdown_fn __startup_fn ( STARTUP_NORMAL ) = { + .name = "efi_cacert", + .shutdown = efi_cacert_shutdown, +}; From d1133956d10779369ee1077d05a83c4aa3d70f1f Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 14 Mar 2025 12:46:02 +0000 Subject: [PATCH 37/50] [contrib] Update bochsrc.txt to work with current versions Signed-off-by: Michael Brown --- contrib/vm/bochsrc.txt | 427 +++++++++++++++++++++-------------------- 1 file changed, 214 insertions(+), 213 deletions(-) diff --git a/contrib/vm/bochsrc.txt b/contrib/vm/bochsrc.txt index feda98595..19db76636 100644 --- a/contrib/vm/bochsrc.txt +++ b/contrib/vm/bochsrc.txt @@ -25,12 +25,12 @@ plugin_ctrl: unmapped=1, biosdev=1, speaker=1, e1000=1, parallel=1, serial=1 # allows you to change all the settings that control Bochs's behavior. # Depending on the platform there are up to 3 choices of configuration # interface: a text mode version called "textconfig" and two graphical versions -# called "win32config" and "wx". The text mode version uses stdin/stdout and -# is always compiled in, unless Bochs is compiled for wx only. The choice -# "win32config" is only available on win32 and it is the default there. -# The choice "wx" is only available when you use "--with-wx" on the configure -# command. If you do not write a config_interface line, Bochs will -# choose a default for you. +# called "win32config" and "wx". The text mode version uses stdin/stdout or +# gui console (if available / runtime config) and is always compiled in, unless +# Bochs is compiled for wx only. The choice "win32config" is only available on +# win32/win64 and it is the default on these platforms. The choice "wx" is only +# available when Bochs is compiled with wxWidgets support. If you do not write +# a config_interface line, Bochs will choose a default for you. # # NOTE: if you use the "wx" configuration interface, you must also use # the "wx" display library. @@ -73,12 +73,14 @@ plugin_ctrl: unmapped=1, biosdev=1, speaker=1, e1000=1, parallel=1, serial=1 # "cmdmode" - call a headerbar button handler after pressing F7 (sdl, sdl2, # win32, x) # "fullscreen" - startup in fullscreen mode (sdl, sdl2) -# "gui_debug" - use GTK debugger gui (sdl, sdl2, x) / Win32 debugger gui (sdl, -# sdl2, win32) # "hideIPS" - disable IPS output in status bar (rfb, sdl, sdl2, term, vncsrv, # win32, wx, x) # "nokeyrepeat" - turn off host keyboard repeat (sdl, sdl2, win32, x) +# "no_gui_console" - use system console instead of builtin gui console +# (rfb, sdl, sdl2, vncsrv, x) # "timeout" - time (in seconds) to wait for client (rfb, vncsrv) +# "gui_debug" - This option is DEPRECATED, use command line option '-dbg_gui' +# instead. It also supports the 'globalini' extension # # See the examples below for other currently supported options. # Setting up options without specifying display library is also supported. @@ -113,9 +115,12 @@ plugin_ctrl: unmapped=1, biosdev=1, speaker=1, e1000=1, parallel=1, serial=1 # # CPU configurations that can be selected: # ----------------------------------------------------------------- +# i386 Intel 386SX +# i486dx4 Intel 486DX4 # pentium Intel Pentium (P54C) # pentium_mmx Intel Pentium MMX # amd_k6_2_chomper AMD-K6(tm) 3D processor (Chomper) +# athlon_xp AMD Athlon(tm) XP Processor # p2_klamath Intel Pentium II (Klamath) # p3_katmai Intel Pentium III (Katmai) # p4_willamette Intel(R) Pentium(R) 4 (Willamette) @@ -136,6 +141,26 @@ plugin_ctrl: unmapped=1, biosdev=1, speaker=1, e1000=1, parallel=1, serial=1 # corei7_ivy_bridge_3770k Intel(R) Core(TM) i7-3770K CPU (Ivy Bridge) # corei7_haswell_4770 Intel(R) Core(TM) i7-4770 CPU (Haswell) # broadwell_ult Intel(R) Processor 5Y70 CPU (Broadwell) +# corei7_skylake_x Intel(R) Core(TM) i7-7800X CPU (Skylake) +# corei3_cnl Intel(R) Core(TM) i3-8121U CPU (Cannonlake) +# corei7_icelake_u QuadCore Intel Core i7-1065G7 (IceLake) +# tigerlake 11th Gen Intel(R) Core(TM) i5-1135G7 (TigerLake) +# sapphire_rapids Intel(R) Xeon(R) w9-3475X (Sappire Rapids) +# arrow_lake 15th Gen Intel(R) Core(TM) Ultra 5 245K (ArrowLake) +# +# ADD_FEATURES: +# Enable one of more CPU feature in the CPU configuration selected by MODEL. +# Could be useful for testing CPU with newer imaginary configurations by +# adding a specific feature or set of features to existing MODEL. The list +# of features to add supplied through space or comma separated string. +# +# EXCLUDE_FEATURES: +# Disable one of more CPU feature from CPU configuration selected by MODEL. +# Could be useful for testing CPU without a specific feature or set of +# features. When experiening issues booting a modern OS it could be useful +# to disable CPU features(s) to see if they responsible for the failures. +# The list of features to exclude supplied through space or comma separated +# string. # # COUNT: # Set the number of processors:cores per processor:threads per core when @@ -196,151 +221,6 @@ plugin_ctrl: unmapped=1, biosdev=1, speaker=1, e1000=1, parallel=1, serial=1 cpu: model=core2_penryn_t9600, count=1, ips=50000000, reset_on_triple_fault=1, ignore_bad_msrs=1, msrs="msrs.def" cpu: cpuid_limit_winnt=0 -#======================================================================= -# CPUID: -# -# This defines features and functionality supported by Bochs emulated CPU. -# The option has no offect if CPU model was selected in CPU option. -# -# MMX: -# Select MMX instruction set support. -# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 5. -# -# APIC: -# Select APIC configuration (LEGACY/XAPIC/XAPIC_EXT/X2APIC). -# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 5. -# -# SEP: -# Select SYSENTER/SYSEXIT instruction set support. -# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6. -# -# SIMD: -# Select SIMD instructions support. -# Any of NONE/SSE/SSE2/SSE3/SSSE3/SSE4_1/SSE4_2/AVX/AVX2/AVX512 -# could be selected. -# -# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6. -# The AVX choises exists only if Bochs compiled with --enable-avx option. -# -# SSE4A: -# Select AMD SSE4A instructions support. -# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6. -# -# MISALIGNED_SSE: -# Select AMD Misaligned SSE mode support. -# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6. -# -# AES: -# Select AES instruction set support. -# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6. -# -# SHA: -# Select SHA instruction set support. -# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6. -# -# MOVBE: -# Select MOVBE Intel(R) Atom instruction support. -# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6. -# -# ADX: -# Select ADCX/ADOX instructions support. -# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6. -# -# XSAVE: -# Select XSAVE extensions support. -# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6. -# -# XSAVEOPT: -# Select XSAVEOPT instruction support. -# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6. -# -# AVX_F16C: -# Select AVX float16 convert instructions support. -# This option exists only if Bochs compiled with --enable-avx option. -# -# AVX_FMA: -# Select AVX fused multiply add (FMA) instructions support. -# This option exists only if Bochs compiled with --enable-avx option. -# -# BMI: -# Select BMI1/BMI2 instructions support. -# This option exists only if Bochs compiled with --enable-avx option. -# -# XOP: -# Select AMD XOP instructions support. -# This option exists only if Bochs compiled with --enable-avx option. -# -# FMA4: -# Select AMD four operand FMA instructions support. -# This option exists only if Bochs compiled with --enable-avx option. -# -# TBM: -# Select AMD Trailing Bit Manipulation (TBM) instructions support. -# This option exists only if Bochs compiled with --enable-avx option. -# -# X86-64: -# Enable x86-64 and long mode support. -# This option exists only if Bochs compiled with x86-64 support. -# -# 1G_PAGES: -# Enable 1G page size support in long mode. -# This option exists only if Bochs compiled with x86-64 support. -# -# PCID: -# Enable Process-Context Identifiers (PCID) support in long mode. -# This option exists only if Bochs compiled with x86-64 support. -# -# FSGSBASE: -# Enable GS/GS BASE access instructions support in long mode. -# This option exists only if Bochs compiled with x86-64 support. -# -# SMEP: -# Enable Supervisor Mode Execution Protection (SMEP) support. -# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6. -# -# SMAP: -# Enable Supervisor Mode Access Prevention (SMAP) support. -# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6. -# -# MWAIT: -# Select MONITOR/MWAIT instructions support. -# This option exists only if Bochs compiled with --enable-monitor-mwait. -# -# VMX: -# Select VMX extensions emulation support. -# This option exists only if Bochs compiled with --enable-vmx option. -# -# SVM: -# Select AMD SVM (Secure Virtual Machine) extensions emulation support. -# This option exists only if Bochs compiled with --enable-svm option. -# -# VENDOR_STRING: -# Set the CPUID vendor string returned by CPUID(0x0). This should be a -# twelve-character ASCII string. -# -# BRAND_STRING: -# Set the CPUID vendor string returned by CPUID(0x80000002 .. 0x80000004). -# This should be at most a forty-eight-character ASCII string. -# -# LEVEL: -# Set emulated CPU level information returned by CPUID. Default value is -# determined by configure option --enable-cpu-level. Currently supported -# values are 5 (for Pentium and similar processors) and 6 (for P6 and -# later processors). -# -# FAMILY: -# Set model information returned by CPUID. Default family value determined -# by configure option --enable-cpu-level. -# -# MODEL: -# Set model information returned by CPUID. Default model value is 3. -# -# STEPPING: -# Set stepping information returned by CPUID. Default stepping value is 3. -#======================================================================= -#cpuid: x86_64=1, mmx=1, sep=1, simd=sse4_2, apic=xapic, aes=1, movbe=1, xsave=1 -#cpuid: family=6, model=0x1a, stepping=5 - #======================================================================= # MEMORY # Set the amount of physical memory you want to emulate. @@ -357,8 +237,14 @@ cpu: cpuid_limit_winnt=0 # memory pool. You will be warned (by FATAL PANIC) in case guest already # used all allocated host memory and wants more. # +# BLOCK_SIZE: +# Memory block size select granularity of host memory allocation. Very +# large memory configurations might requre larger memory blocks which +# configurations with small memory might want memory block smaller. +# Default memory block size is 128K. +# #======================================================================= -memory: guest=512, host=256 +memory: guest=512, host=256, block_size=512 #======================================================================= # ROMIMAGE: @@ -368,28 +254,50 @@ memory: guest=512, host=256 # starting at address 0xfffe0000, and it is exactly 128k long. The legacy # version of the Bochs BIOS is usually loaded starting at address 0xffff0000, # and it is exactly 64k long. -# You can use the environment variable $BXSHARE to specify the location -# of the BIOS. # The usage of external large BIOS images (up to 512k) at memory top is # now supported, but we still recommend to use the BIOS distributed with Bochs. -# The start address is optional, since it can be calculated from image size. -# The Bochs BIOS currently supports only the option "fastboot" to skip the -# boot menu delay. +# +# FILE +# Name of the BIOS image file. You can use the environment variable $BXSHARE +# to specify the location of the BIOS. +# +# ADDRESS +# The start address is optional, since it can be calculated from image size. +# +# OPTIONS +# The Bochs BIOS currently only supports the option "fastboot" to skip the +# boot menu delay. +# +# FLASH_DATA +# This parameter defines the file name for the flash BIOS config space loaded +# at startup if existing and saved on exit if modified. The Bochs BIOS doesn't +# use this feature yet. +# +# Please note that if you use the BIOS-bochs-legacy romimage BIOS option, +# you cannot use a PCI enabled VGA ROM BIOS. +# Please note that if you use a SeaBIOS binary in romimage BIOS option, +# you must use a PCI enabled VGA ROM BIOS. #======================================================================= #romimage: file=$BXSHARE/BIOS-bochs-latest, options=fastboot +#romimage: file=$BXSHARE/BIOS-bochs-legacy #romimage: file=$BXSHARE/bios.bin-1.13.0 # http://www.seabios.org/SeaBIOS +#romimage: file=$BXSHARE/i440fx.bin, flash_data=escd.bin +#romimage: file=asus_p6np5.bin, flash_data=escd.bin #romimage: file=mybios.bin, address=0xfff80000 # 512k at memory top romimage: file=bochs/bios/BIOS-bochs-latest #======================================================================= # VGAROMIMAGE # You now need to load a VGA ROM BIOS into C0000. +# Please note that if you use the BIOS-bochs-legacy romimage BIOS option, +# you cannot use a PCI enabled VGA ROM BIOS option such as the cirrus +# option shown below. #======================================================================= -#vgaromimage: file=$BXSHARE/VGABIOS-lgpl-latest -#vgaromimage: file=bios/VGABIOS-lgpl-latest-cirrus +#vgaromimage: file=$BXSHARE/VGABIOS-lgpl-latest.bin +#vgaromimage: file=bios/VGABIOS-lgpl-latest-cirrus.bin #vgaromimage: file=$BXSHARE/vgabios-cirrus.bin-1.13.0 # http://www.seabios.org/SeaVGABIOS #vgaromimage: file=bios/VGABIOS-elpin-2.40 -vgaromimage: file=bochs/bios/VGABIOS-lgpl-latest +vgaromimage: file=bochs/bios/VGABIOS-lgpl-latest.bin #======================================================================= # OPTROMIMAGE[1-4]: @@ -406,7 +314,7 @@ vgaromimage: file=bochs/bios/VGABIOS-lgpl-latest #optromimage2: file=optionalrom.bin, address=0xd1000 #optromimage3: file=optionalrom.bin, address=0xd2000 #optromimage4: file=optionalrom.bin, address=0xd3000 -optromimage1: file=../../src/bin/intel.rom, address=0xcb000 +optromimage1: file=../../src/bin/intel.rom, address=0xc8000 #optramimage1: file=/path/file1.img, address=0x0010000 #optramimage2: file=/path/file2.img, address=0x0020000 @@ -426,7 +334,9 @@ optromimage1: file=../../src/bin/intel.rom, address=0xcb000 # UPDATE_FREQ # This parameter specifies the number of display updates per second. # The VGA update timer by default uses the realtime engine with a value -# of 5. This parameter can be changed at runtime. +# of 10 (valid: 1 to 75). This parameter can be changed at runtime. +# The special value 0 enables support for using the frame rate of the +# emulated graphics device. # # REALTIME # If set to 1 (default), the VGA timer is based on realtime, otherwise it @@ -440,6 +350,10 @@ optromimage1: file=../../src/bin/intel.rom, address=0xcb000 # the monitor EDID data. By default the 'builtin' values for 'Bochs Screen' # are used. Other choices are 'disabled' (no DDC emulation) and 'file' # (read monitor EDID from file / path name separated with a colon). +# +# VBE_MEMSIZE +# With this parameter the size of the memory for the Bochs VBE extension +# can be defined. Valid values are 4, 8, 16 and 32 MB (default is 16 MB). # Examples: # vga: extension=cirrus, update_freq=10, ddc=builtin #======================================================================= @@ -488,6 +402,8 @@ optromimage1: file=../../src/bin/intel.rom, address=0xcb000 # KEYMAP: # This enables a remap of a physical localized keyboard to a # virtualized us keyboard, as the PC architecture expects. +# Using a language specifier instead of a file name is also supported. +# A keymap is also required by the paste feature. # # USER_SHORTCUT: # This defines the keyboard shortcut to be sent when you press the "user" @@ -504,7 +420,7 @@ optromimage1: file=../../src/bin/intel.rom, address=0xcb000 # keyboard: keymap=gui/keymaps/x11-pc-de.map # keyboard: user_shortcut=ctrl-alt-del #======================================================================= -#keyboard: type=mf, serial_delay=250 +#keyboard: type=mf, serial_delay=150 #======================================================================= # MOUSE: @@ -529,8 +445,8 @@ optromimage1: file=../../src/bin/intel.rom, address=0xcb000 # TOGGLE: # The default method to toggle the mouse capture at runtime is to press the # CTRL key and the middle mouse button ('ctrl+mbutton'). This option allows -# to change the method to 'ctrl+f10' (like DOSBox), 'ctrl+alt' (like QEMU) -# or 'f12'. +# to change the method to 'ctrl+f10' (like DOSBox), 'ctrl+alt' (legacy QEMU), +# 'ctrl+alt+g' (QEMU current) or 'f12'. # # Examples: # mouse: enabled=1 @@ -567,7 +483,8 @@ mouse: enabled=0 # PCI chipset. These options can be specified as comma-separated values. # By default the "Bochs i440FX" chipset enables the ACPI and HPET devices, but # original i440FX doesn't support them. The options 'noacpi' and 'nohpet' make -# it possible to disable them. +# it possible to disable them. The option 'noagp' disables the incomplete AGP +# subsystem of the i440BX chipset. # # Example: # pci: enabled=1, chipset=i440fx, slot1=pcivga, slot2=ne2k, advopts=noacpi @@ -772,10 +689,11 @@ ata3: enabled=0, ioaddr1=0x168, ioaddr2=0x360, irq=9 # This defines the boot sequence. Now you can specify up to 3 boot drives, # which can be 'floppy', 'disk', 'cdrom' or 'network' (boot ROM). # Legacy 'a' and 'c' are also supported. +# The new boot choice 'usb' is only supported by the i440fx.bin BIOS. # Examples: # boot: floppy # boot: cdrom, disk -# boot: network, disk +# boot: network, usb, disk # boot: cdrom, floppy, disk #======================================================================= #boot: floppy @@ -929,15 +847,15 @@ parport1: enabled=1, file="parport.out" # waveoutdrv: # This defines the driver to be used for the waveout feature. # Possible values are 'file' (all wave data sent to file), 'dummy' (no -# output) and the platform-dependant drivers 'alsa', 'oss', 'osx', 'sdl' -# and 'win'. +# output), 'pulse', 'sdl' (both cross-platform) and the platform-dependant +# drivers 'alsa', 'oss', 'osx' and 'win'. # waveout: # This defines the device to be used for wave output (if necessary) or # the output file for the 'file' driver. # waveindrv: # This defines the driver to be used for the wavein feature. -# Possible values are 'dummy' (recording silence) and platform-dependent -# drivers 'alsa', 'oss', 'sdl' and 'win'. +# Possible values are 'dummy' (recording silence), 'pulse', 'sdl' (both +# cross-platform) and platform-dependent drivers 'alsa', 'oss' and 'win'. # wavein: # This defines the device to be used for wave input (if necessary). # midioutdrv: @@ -967,6 +885,7 @@ parport1: enabled=1, file="parport.out" # the Beep() function. The 'gui' mode forwards the beep to the related # gui methods (currently only used by the Carbon gui). #======================================================================= +#speaker: enabled=1, mode=sound, volume=15 speaker: enabled=1, mode=system #======================================================================= @@ -1000,14 +919,15 @@ speaker: enabled=1, mode=system # log: The file to write the sb16 emulator messages to. # dmatimer: # microseconds per second for a DMA cycle. Make it smaller to fix -# non-continuous sound. 750000 is usually a good value. This needs a -# reasonably correct setting for the IPS parameter of the CPU option. +# non-continuous sound. 1000000 is usually a good value. This needs a +# reasonably correct setting for the IPS parameter of the CPU option +# and also depends on the clock sync setting. # # Examples for output modes: # sb16: midimode=2, midifile="output.mid", wavemode=1 # MIDI to file # sb16: midimode=1, wavemode=3, wavefile="output.wav" # wave to file and device #======================================================================= -#sb16: midimode=1, wavemode=1, loglevel=2, log=sb16.log, dmatimer=600000 +#sb16: midimode=1, wavemode=1, loglevel=2, log=sb16.log, dmatimer=900000 #======================================================================= # ES1370: @@ -1069,7 +989,8 @@ speaker: enabled=1, mode=system # # BOOTROM: The bootrom value is optional, and is the name of the ROM image # to load. Note that this feature is only implemented for the PCI version of -# the NE2000. +# the NE2000. For the ISA version using one of the 'optromimage[1-4]' options +# must be used instead of this one. # # If you don't want to make connections to any physical networks, # you can use the following 'ethmod's to simulate a virtual network. @@ -1137,34 +1058,40 @@ e1000: enabled=1, mac=52:54:00:12:34:56, ethmod=tuntap, ethdev=/dev/net/tun:tap0 # the numeric keypad to the USB device instead of the PS/2 keyboard. If the # keyboard is selected, all key events are sent to the USB device. # -# To connect a 'flat' mode image as a USB hardisk you can use the 'disk' device -# with the path to the image separated with a colon. To use other disk image modes -# similar to ATA disks the syntax 'disk:mode:filename' must be used (see below). +# To connect a disk image as a USB hardisk you can use the 'disk' device. Use +# the 'path' option in the optionsX parameter to specify the path to the image +# separated with a colon. To use other disk image modes similar to ATA disks +# the syntax 'path:mode:filename' must be used (see below). # -# To emulate a USB cdrom you can use the 'cdrom' device name and the path to -# an ISO image or raw device name also separated with a colon. An option to -# insert/eject media is available in the runtime configuration. +# To emulate a USB cdrom you can use the 'cdrom' device and the path to an +# ISO image or raw device name can be set with the 'path' option in the +# optionsX parameter also separated with a colon. An option to insert/eject +# media is available in the runtime configuration. # -# To emulate a USB floppy you can use the 'floppy' device with the path to the -# image separated with a colon. To use the VVFAT image mode similar to the -# legacy floppy the syntax 'floppy:vvfat:directory' must be used (see below). +# To emulate a USB floppy you can use the 'floppy' device and the path to a +# floppy image can be set with the 'path' option in the optionsX parameter +# separated with a colon. To use the VVFAT image mode similar to the legacy +# floppy the syntax 'path:vvfat:directory' must be used (see below). # An option to insert/eject media is available in the runtime configuration. # # The device name 'hub' connects an external hub with max. 8 ports (default: 4) -# to the root hub. To specify the number of ports you have to add the value -# separated with a colon. Connecting devices to the external hub ports is only -# available in the runtime configuration. +# to the root hub. To specify the number of ports you have to use the 'ports' +# option in the optionsX parameter with the value separated with a colon. +# Connecting devices to the external hub ports is only available in the runtime +# configuration. # # The device 'printer' emulates the HP Deskjet 920C printer. The PCL data is -# sent to a file specified in bochsrc.txt. The current code appends the PCL -# code to the file if the file already existed. The output file can be -# changed at runtime. +# sent to a file specified in the 'file' option with the optionsX parameter. +# The current code appends the PCL code to the file if the file already existed. +# The output file can be changed at runtime. # # The optionsX parameter can be used to assign specific options to the device -# connected to the corresponding USB port. Currently this feature is used to -# set the speed reported by device ('low', 'full', 'high' or 'super'). The -# available speed choices depend on both HC and device. The option 'debug' turns -# on debug output for the device at connection time. +# connected to the corresponding USB port. The option 'speed' can be used to set +# the speed reported by device ('low', 'full', 'high' or 'super'). The available +# speed choices depend on both HC and device. The option 'debug' turns on debug +# output for the device at connection time. The option 'pcap' turns on packet +# logging in PCAP format. +# # For the USB 'disk' device the optionsX parameter can be used to specify an # alternative redolog file (journal) of some image modes. For 'vvfat' mode USB # disks the optionsX parameter can be used to specify the disk size (range @@ -1174,15 +1101,23 @@ e1000: enabled=1, mac=52:54:00:12:34:56, ethmod=tuntap, ethdev=/dev/net/tun:tap0 # supported (can fix hw detection in some guest OS). The USB floppy also # accepts the parameter "write_protected" with valid values 0 and 1 to select # the access mode (default is 0). +# +# For a high- or super-speed USB 'disk' device the optionsX parameter can include +# the 'proto:bbb' or 'proto:uasp' parameter specifying to use either the bulk-only +# Protocol (default) or the USB Attached SCSI Protocol. If no such parameter +# is given, the 'bbb' protocol is used. A Guest that doesn't support UASP +# should revert to bbb even if the 'uasp' attribute is given. See the usb_ehci: +# or usb_xhci: section below for an example. (Only 1 LUN is available at this time) #======================================================================= #usb_uhci: enabled=1 -#usb_uhci: enabled=1, port1=mouse, port2=disk:usbstick.img -#usb_uhci: enabled=1, port1=hub:7, port2=disk:growing:usbdisk.img -#usb_uhci: enabled=1, port2=disk:undoable:usbdisk.img, options2=journal:redo.log -#usb_uhci: enabled=1, port2=disk:usbdisk2.img, options2=sect_size:1024 -#usb_uhci: enabled=1, port2=disk:vvfat:vvfat, options2="debug,speed:full" -#usb_uhci: enabled=1, port1=printer:printdata.bin, port2=cdrom:image.iso -#usb_uhci: enabled=1, port2=floppy:vvfat:diskette, options2="model:teac" +#usb_uhci: port1=mouse, port2=disk, options2="path:usbstick.img" +#usb_uhci: port1=hub, options1="ports:6, pcap:outfile.pcap" +#usb_uhci: port2=disk, options2="path:undoable:usbdisk.img, journal:u.redolog" +#usb_uhci: port2=disk, options2=""path:usbdisk2.img, sect_size:1024" +#usb_uhci: port2=disk, options2="path:vvfat:vvfat, debug, speed:full" +#usb_uhci: port2=cdrom, options2="path:image.iso" +#usb_uhci: port1=printer, options1="file:printdata.bin" +#usb_uhci: port2=floppy, options2="path:vvfat:diskette, model:teac" #======================================================================= # USB_OHCI: @@ -1200,19 +1135,61 @@ e1000: enabled=1, mac=52:54:00:12:34:56, ethmod=tuntap, ethdev=/dev/net/tun:tap0 # 6-port hub. The portX parameter accepts the same device types with the # same syntax as the UHCI controller (see above). The optionsX parameter is # also available on EHCI. +# The HC will default to three UHCI companion controllers, but you can specify +# either UHCI or OHCI. Each companion controller will be evenly divided +# with 2 ports each, the first 2 on the first companion, and so on. #======================================================================= #usb_ehci: enabled=1 +#usb_ehci: enabled=1, companion=uhci +#usb_ehci: enabled=1, companion=ohci +#usb_ehci: port1=disk, options1="speed:high, path:hdd.img, proto:bbb" +#usb_ehci: port1=disk, options1="speed:high, path:hdd.img, proto:uasp" #======================================================================= # USB_XHCI: # This option controls the presence of the USB xHCI host controller with a -# 4-port hub. The portX parameter accepts the same device types with the -# same syntax as the UHCI controller (see above). The optionsX parameter is -# also available on xHCI. NOTE: port 1 and 2 are USB3 and only support -# super-speed devices, but port 3 and 4 are USB2 and support speed settings -# low, full and high. +# default 4-port hub. The portX parameter accepts the same device types +# with the same syntax as the UHCI controller (see above). The optionsX +# parameter is also available on xHCI. +# +# The xHCI emulation allows you to set the number of ports used with a range +# of 2 to 10, requiring an even numbered count. +# +# NOTE: The first half of the ports, (ports 1 and 2 on a 4-port hub) are +# USB3 only and support super-speed devices. The second half ports (ports +# 3 and 4) are USB2 and support speed settings of low, full, or high. +# The xHCI also allows for different host controllers using the model= +# parameter. Currently, the two allowed options are "uPD720202" and +# "uPD720201". The first defaults to 2 sockets (4 ports) and the later +# defaults to 4 sockets (8 ports). #======================================================================= -#usb_xhci: enabled=1 +#usb_xhci: enabled=1 # defaults to the uPD720202 w/4 ports +#usb_xhci: enabled=1, n_ports=6 # defaults to the uPD720202 w/6 ports +#usb_xhci: enabled=1, model=uPD720202 # defaults to 4 ports +#usb_xhci: enabled=1, model=uPD720202, n_ports=6 # change to 6 ports +#usb_xhci: enabled=1, model=uPD720201 # defaults to 8 ports +#usb_xhci: enabled=1, model=uPD720201, n_ports=10 # change to 10 ports +#usb_xhci: port1=disk, options1="speed:super, path:hdd.img, proto:bbb" +#usb_xhci: port1=disk, options1="speed:super, path:hdd.img, proto:uasp" +#usb_xhci: port3=disk, options3="speed:high, path:hdd.img, proto:uasp" + +#======================================================================= +# USB Debugger: +# This is the experimental USB Debugger for the Windows Platform. +# Specify a type (none, uhci, ohci, ehci, xhci) and one or more triggers. +# (Currently, only xhci is supported, with some uhci in the process) +# Triggers: +# reset: will break and load the debugger on a port reset +# enable: will break and load the debugger on a port enable +# doorbell: will break and load the debugger a xHCI Command Ring addition +# event: will break and load the debugger on a xHCI Event Ring addition +# data: will break and load the debugger on a xHCI Data Ring addition +# start_frame: will break and load the debugger on start of frame +# (this is different for each controller type) +# non_exist: will break and load the debugger on a non-existant port access +# (experimental and is under development) +#======================================================================= +#usb_debug: type=xhci, reset=1, enable=1, start_frame=1, doorbell=1, event=1, data=1, non_exist=1 #======================================================================= # PCIDEV: @@ -1232,11 +1209,20 @@ e1000: enabled=1, mac=52:54:00:12:34:56, ethmod=tuntap, ethdev=/dev/net/tun:tap0 #======================================================================= # MAGIC_BREAK: # This enables the "magic breakpoint" feature when using the debugger. -# The useless cpu instruction XCHG BX, BX causes Bochs to enter the +# The useless cpu instructions XCHG %REGW, %REGW causes Bochs to enter the # debugger mode. This might be useful for software development. # -# Example: +# You can specify multiple at once: +# +# cx dx bx sp bp si di +# +# Example for breaking on "XCHGW %DI, %DI" or "XCHG %SP, %SP" execution +# magic_break: enabled=1 di sp +# +# If nothing is specified, the default will be used: XCHGW %BX, %BX # magic_break: enabled=1 +# +# Note: Windows XP ntldr can cause problems with XCHGW %BX, %BX #======================================================================= magic_break: enabled=1 @@ -1262,12 +1248,27 @@ magic_break: enabled=1 # very early when writing BIOS or OS code for example, without having to # bother with setting up a serial port or etc. Reading from port 0xE9 will # will return 0xe9 to let you know if the feature is available. -# Leave this 0 unless you have a reason to use it. +# Leave this 0 unless you have a reason to use it. By enabling the +# 'all_rings' option, you can utilize the port e9 hack from ring3. # # Example: # port_e9_hack: enabled=1 +# port_e9_hack: enabled=1, all_rings=1 #======================================================================= -port_e9_hack: enabled=1 +port_e9_hack: enabled=1, all_rings=1 + +#======================================================================= +# IODEBUG: +# I/O Interface to Bochs Debugger plugin allows the code running inside +# Bochs to monitor memory ranges, trace individual instructions, and +# observe register values during execution. By enabling the 'all_rings' +# option, you can utilize the iodebug ports from ring3. For more +# information, refer to "Advanced debugger usage" documentation. +# +# Example: +# iodebug: all_rings=1 +#======================================================================= +#iodebug: all_rings=1 #======================================================================= # fullscreen: ONLY IMPLEMENTED ON AMIGA From 8840de40962cabf84847919220a29f3c7d1be13e Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 14 Mar 2025 14:09:26 +0000 Subject: [PATCH 38/50] [pic8259] Return previous state when enabling or disabling IRQs Return the previous interrupt enabled state from enable_irq() and disable_irq(), to allow callers to more easily restore this state. Signed-off-by: Michael Brown --- src/arch/x86/include/pic8259.h | 47 +++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/src/arch/x86/include/pic8259.h b/src/arch/x86/include/pic8259.h index dbec5fd2c..0dc59cf27 100644 --- a/src/arch/x86/include/pic8259.h +++ b/src/arch/x86/include/pic8259.h @@ -47,9 +47,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /* Macros to enable/disable IRQs */ #define IMR_REG(x) ( (x) < IRQ_PIC_CUTOFF ? PIC1_IMR : PIC2_IMR ) #define IMR_BIT(x) ( 1 << ( (x) % IRQ_PIC_CUTOFF ) ) -#define irq_enabled(x) ( ( inb ( IMR_REG(x) ) & IMR_BIT(x) ) == 0 ) -#define enable_irq(x) outb ( inb( IMR_REG(x) ) & ~IMR_BIT(x), IMR_REG(x) ) -#define disable_irq(x) outb ( inb( IMR_REG(x) ) | IMR_BIT(x), IMR_REG(x) ) /* Macros for acknowledging IRQs */ #define ICR_REG( irq ) ( (irq) < IRQ_PIC_CUTOFF ? PIC1_ICR : PIC2_ICR ) @@ -63,6 +60,50 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define IRQ_MAX 15 #define IRQ_NONE -1U +/** + * Check if interrupt is enabled + * + * @v irq Interrupt number + * @ret enabled Interrupt is currently enabled + */ +static inline __attribute__ (( always_inline )) int +irq_enabled ( unsigned int irq ) { + int imr = inb ( IMR_REG ( irq ) ); + int mask = IMR_BIT ( irq ); + + return ( ( imr & mask ) == 0 ); +} + +/** + * Enable interrupt + * + * @v irq Interrupt number + * @ret enabled Interrupt was previously enabled + */ +static inline __attribute__ (( always_inline )) int +enable_irq ( unsigned int irq ) { + int imr = inb ( IMR_REG ( irq ) ); + int mask = IMR_BIT ( irq ); + + outb ( ( imr & ~mask ), IMR_REG ( irq ) ); + return ( ( imr & mask ) == 0 ); +} + +/** + * Disable interrupt + * + * @v irq Interrupt number + * @ret enabled Interrupt was previously enabled + */ +static inline __attribute__ (( always_inline )) int +disable_irq ( unsigned int irq ) { + int imr = inb ( IMR_REG ( irq ) ); + int mask = IMR_BIT ( irq ); + + outb ( ( imr | mask ), IMR_REG ( irq ) ); + return ( ( imr & mask ) == 0 ); +} + /* Function prototypes */ void send_eoi ( unsigned int irq ); From 829e2d1f299c7c0b15a5e9e07479f6e3aec121cf Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 14 Mar 2025 14:11:48 +0000 Subject: [PATCH 39/50] [rng] Restore state of IRQ 8 and PIE when disabling entropy gathering Legacy IRQ 8 appears to be enabled by default on some platforms. If iPXE selects the RTC entropy source, this will currently result in the RTC IRQ 8 being unconditionally disabled. This can break assumptions made by BIOSes or subsequent bootloaders: in particular, the FreeBSD loader may lock up at the point of starting its default 10-second countdown when it calls INT 15,86. Fix by restoring the previous state of IRQ 8 instead of disabling it unconditionally. Note that we do not need to disable IRQ 8 around the point of hooking (or unhooking) the ISR, since this code will be executing in iPXE's normal state of having interrupts disabled anyway. Also restore the previous state of the RTC periodic interrupt enable, rather than disabling it unconditionally. Signed-off-by: Michael Brown --- src/arch/x86/interface/pcbios/rtc_entropy.c | 33 ++++++++++++++++----- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/src/arch/x86/interface/pcbios/rtc_entropy.c b/src/arch/x86/interface/pcbios/rtc_entropy.c index 8f47ff6b8..7c98019b6 100644 --- a/src/arch/x86/interface/pcbios/rtc_entropy.c +++ b/src/arch/x86/interface/pcbios/rtc_entropy.c @@ -53,6 +53,12 @@ extern void rtc_isr ( void ); /** Previous RTC interrupt handler */ static struct segoff rtc_old_handler; +/** Previous RTC interrupt enabled state */ +static uint8_t rtc_irq_enabled; + +/** Previous RTC periodic interrupt enabled state */ +static uint8_t rtc_int_enabled; + /** Flag set by RTC interrupt handler */ extern volatile uint8_t __text16 ( rtc_flag ); #define rtc_flag __use_text16 ( rtc_flag ) @@ -107,8 +113,9 @@ static void rtc_unhook_isr ( void ) { /** * Enable RTC interrupts * + * @ret enabled Periodic interrupt was previously enabled */ -static void rtc_enable_int ( void ) { +static int rtc_enable_int ( void ) { uint8_t status_b; /* Clear any stale pending interrupts via status register C */ @@ -124,6 +131,9 @@ static void rtc_enable_int ( void ) { /* Re-enable NMI and reset to default address */ outb ( CMOS_DEFAULT_ADDRESS, CMOS_ADDRESS ); inb ( CMOS_DATA ); /* Discard; may be needed on some platforms */ + + /* Return previous state */ + return ( status_b & RTC_STATUS_B_PIE ); } /** @@ -198,8 +208,11 @@ static int rtc_entropy_enable ( void ) { /* Hook ISR and enable RTC interrupts */ rtc_hook_isr(); - enable_irq ( RTC_IRQ ); - rtc_enable_int(); + rtc_irq_enabled = enable_irq ( RTC_IRQ ); + rtc_int_enabled = rtc_enable_int(); + DBGC ( &rtc_flag, "RTC had IRQ%d %sabled, interrupt %sabled\n", + RTC_IRQ, ( rtc_irq_enabled ? "en" : "dis" ), + ( rtc_int_enabled ? "en" : "dis" ) ); /* Check that RTC interrupts are working */ if ( ( rc = rtc_entropy_check() ) != 0 ) @@ -223,8 +236,10 @@ static int rtc_entropy_enable ( void ) { return 0; err_check: - rtc_disable_int(); - disable_irq ( RTC_IRQ ); + if ( ! rtc_int_enabled ) + rtc_disable_int(); + if ( ! rtc_irq_enabled ) + disable_irq ( RTC_IRQ ); rtc_unhook_isr(); err_no_tsc: return rc; @@ -236,9 +251,11 @@ static int rtc_entropy_enable ( void ) { */ static void rtc_entropy_disable ( void ) { - /* Disable RTC interrupts and unhook ISR */ - rtc_disable_int(); - disable_irq ( RTC_IRQ ); + /* Restore RTC interrupt state and unhook ISR */ + if ( ! rtc_int_enabled ) + rtc_disable_int(); + if ( ! rtc_irq_enabled ) + disable_irq ( RTC_IRQ ); rtc_unhook_isr(); } From 42a29d56812fdf1a434f9093eaefa5634c1beb2e Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 17 Mar 2025 11:39:41 +0000 Subject: [PATCH 40/50] [crypto] Update cmsdetach to work with python-asn1 version 3.0.0 The python-asn1 documentation indicates that end of file may be detected either by obtaining a True value from .eof() or by obtaining a None value from .peek(), but does not mention any way to detect the end of a constructed tag (rather than the end of the overall file). We currently use .eof() to detect the end of a constructed tag, based on the observed behaviour of the library. The behaviour of .eof() changed between versions 2.8.0 and 3.0.0, such that .eof() no longer returns True at the end of a constructed tag. Switch to testing for a None value returned from .peek() to determine when we have reached the end of a constructed tag, since this works on both newer and older versions. Continue to treat .eof() as a necessary but not sufficient condition for reaching the overall end of file, to maintain compatibility with older versions. Signed-off-by: Michael Brown --- contrib/crypto/cmsdetach | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/contrib/crypto/cmsdetach b/contrib/crypto/cmsdetach index 007500996..fdd888958 100755 --- a/contrib/crypto/cmsdetach +++ b/contrib/crypto/cmsdetach @@ -47,22 +47,21 @@ datastack = [ ] stack = [] while stack or not decoder.eof(): - if decoder.eof(): + tag = decoder.peek() + if tag is None: encoder.leave() decoder.leave() stack.pop() + elif tag.typ == asn1.Types.Constructed: + encoder.enter(nr=tag.nr, cls=tag.cls) + decoder.enter() + stack.append(tag.nr) else: - tag = decoder.peek() - if tag.typ == asn1.Types.Constructed: - encoder.enter(nr=tag.nr, cls=tag.cls) - decoder.enter() - stack.append(tag.nr) + (tag, value) = decoder.read() + if stack == datastack and tag.nr == 0: + data = value else: - (tag, value) = decoder.read() - if stack == datastack and tag.nr == 0: - data = value - else: - encoder.write(value, nr=tag.nr, cls=tag.cls) + encoder.write(value, nr=tag.nr, cls=tag.cls) envelope = encoder.output() if data is None: parser.error("Input file does not contain any encrypted data") From 8ea8411f0d4b5260f91054b904115224714edf25 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 18 Mar 2025 12:49:19 +0000 Subject: [PATCH 41/50] [efi] Add EFI_RNG_PROTOCOL_GUID as a well-known GUID Signed-off-by: Michael Brown --- src/include/ipxe/efi/efi.h | 1 + src/interface/efi/efi_guid.c | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/src/include/ipxe/efi/efi.h b/src/include/ipxe/efi/efi.h index 486c6070e..41e1aa945 100644 --- a/src/include/ipxe/efi/efi.h +++ b/src/include/ipxe/efi/efi.h @@ -219,6 +219,7 @@ extern EFI_GUID efi_nii31_protocol_guid; extern EFI_GUID efi_pci_io_protocol_guid; extern EFI_GUID efi_pci_root_bridge_io_protocol_guid; extern EFI_GUID efi_pxe_base_code_protocol_guid; +extern EFI_GUID efi_rng_protocol_guid; extern EFI_GUID efi_serial_io_protocol_guid; extern EFI_GUID efi_shim_lock_protocol_guid; extern EFI_GUID efi_simple_file_system_protocol_guid; diff --git a/src/interface/efi/efi_guid.c b/src/interface/efi/efi_guid.c index 9d9c9ef91..191ce5094 100644 --- a/src/interface/efi/efi_guid.c +++ b/src/interface/efi/efi_guid.c @@ -63,6 +63,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include #include @@ -296,6 +297,10 @@ EFI_GUID efi_pci_root_bridge_io_protocol_guid EFI_GUID efi_pxe_base_code_protocol_guid = EFI_PXE_BASE_CODE_PROTOCOL_GUID; +/** Random number generator protocol GUID */ +EFI_GUID efi_rng_protocol_guid + = EFI_RNG_PROTOCOL_GUID; + /** Serial I/O protocol GUID */ EFI_GUID efi_serial_io_protocol_guid = EFI_SERIAL_IO_PROTOCOL_GUID; @@ -559,6 +564,8 @@ static struct efi_well_known_guid efi_well_known_guids[] = { "PciRootBridgeIo" }, { &efi_pxe_base_code_protocol_guid, "PxeBaseCode" }, + { &efi_rng_protocol_guid, + "Rng" }, { &efi_serial_io_protocol_guid, "SerialIo" }, { &efi_shim_lock_protocol_guid, From 6e4196baff241434d07b74e244fff3d469218f77 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 18 Mar 2025 13:50:11 +0000 Subject: [PATCH 42/50] [efi] Prescroll the display after a failed wrapped ExitBootServices() call On some systems (observed with an HP Elitebook 840 G10), writing console output that happens to cause the display to scroll will modify the system memory map. This causes builds with DEBUG=efi_wrap to typically fail to boot, since the debug output from the wrapped ExitBootServices() call itself is sufficient to change the memory map and therefore cause ExitBootServices() to fail due to an invalid memory map key. Work around these UEFI firmware bugs by prescrolling the display after a failed ExitBootServices() attempt, in order to minimise the chance that further scrolling will happen during the subsequent attempt. Signed-off-by: Michael Brown --- src/interface/efi/efi_wrap.c | 44 ++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/interface/efi/efi_wrap.c b/src/interface/efi/efi_wrap.c index c2fab6647..cbec48a00 100644 --- a/src/interface/efi/efi_wrap.c +++ b/src/interface/efi/efi_wrap.c @@ -40,6 +40,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Colour for debug messages */ #define colour &efi_systab +/** Number of lines to prescroll when needed */ +#define EFI_WRAP_PRESCROLL 16 + /** * Convert EFI status code to text * @@ -195,6 +198,38 @@ static const char * efi_timer_delay ( EFI_TIMER_DELAY type ) { } } +/** + * Pre-scroll display to create space for output lines + * + * @v lines Number of lines required + * @ret efirc EFI status code + */ +static int efi_prescroll ( unsigned int lines ) { + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *conout = efi_systab->ConOut; + UINTN columns; + UINTN rows; + UINTN space; + EFI_STATUS efirc; + + /* Get number of rows and columns */ + if ( ( efirc = conout->QueryMode ( conout, conout->Mode->Mode, + &columns, &rows ) ) != 0 ) + return efirc; + + /* Calculate available space */ + space = ( rows - conout->Mode->CursorRow - 1 ); + + /* Scroll to create space */ + while ( space++ < lines ) + conout->OutputString ( conout, L"\n" ); + + /* Move cursor to start of space */ + conout->SetCursorPosition ( conout, 0, + ( conout->Mode->CursorRow - lines ) ); + + return 0; +} + /** * Dump information about a loaded image * @@ -783,6 +818,15 @@ efi_exit_boot_services_wrapper ( EFI_HANDLE image_handle, UINTN map_key ) { if ( efirc != 0 ) { DBGC ( colour, "ExitBootServices ( ... ) = %s -> %p\n", efi_status ( efirc ), retaddr ); + /* On some systems, scrolling the output will cause + * the system memory map to change (and so cause + * ExitBootServices() to fail). + * + * After the first failed attempt, prescroll the + * screen to maximise the chance of the subsequent + * attempt succeeding. + */ + efi_prescroll ( EFI_WRAP_PRESCROLL ); } return efirc; } From 37ea181d8b007120bfd70629c6fdffc30145e310 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 18 Mar 2025 16:13:55 +0000 Subject: [PATCH 43/50] [efi] Ignore path separator characters in virtual filenames The virtual filesystem that we provide to expose downloaded images will erroneously interpret filenames with redundant path separators such as ".\filename" as an attempt to open the directory, rather than an attempt to open "filename". This shows up most obviously when chainloading from one iPXE into another iPXE, when the inner iPXE may end up attempting to open ".\autoexec.ipxe" from the outer iPXE's virtual filesystem. (The erroneously opened file will have a zero length and will therefore be ignored, but is still confusing.) Fix by discarding any dot or backslash characters after a potential initial backslash. This is very liberal and will accept some syntactically invalid paths, but this is acceptable since our virtual filesystem does not implement directories anyway. Signed-off-by: Michael Brown --- src/interface/efi/efi_file.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/interface/efi/efi_file.c b/src/interface/efi/efi_file.c index e10c2ec4e..48fccdbe1 100644 --- a/src/interface/efi/efi_file.c +++ b/src/interface/efi/efi_file.c @@ -388,8 +388,12 @@ efi_file_open ( EFI_FILE_PROTOCOL *this, EFI_FILE_PROTOCOL **new, name++; } + /* Strip redundant path separator characters */ + while ( ( *name == '\\' ) || ( *name == '.' ) ) + name++; + /* Allow root directory itself to be opened */ - if ( ( name[0] == '\0' ) || ( name[0] == '.' ) ) + if ( ! *name ) return efi_file_open_fixed ( &efi_file_root, wname, new ); /* Fail unless opening from the root */ From f68c8b09e39f1837ea6344f465d62e4b2c62a1c9 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 19 Mar 2025 16:20:27 +0000 Subject: [PATCH 44/50] [efi] Fix debug wrappers for CloseEvent() and CheckEvent() The debug wrappers for CloseEvent() and CheckEvent() are currently both calling SignalEvent() instead (presumably due to copy-paste errors). Astonishingly, this has generally not prevented a successful boot in the (very rare) case that DEBUG=efi_wrap is enabled. Fix the wrappers to call the intended functions. Signed-off-by: Michael Brown --- src/interface/efi/efi_wrap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/interface/efi/efi_wrap.c b/src/interface/efi/efi_wrap.c index cbec48a00..252bc2624 100644 --- a/src/interface/efi/efi_wrap.c +++ b/src/interface/efi/efi_wrap.c @@ -513,7 +513,7 @@ efi_close_event_wrapper ( EFI_EVENT event ) { EFI_STATUS efirc; DBGC ( colour, "CloseEvent ( %p ) ", event ); - efirc = bs->SignalEvent ( event ); + efirc = bs->CloseEvent ( event ); DBGC ( colour, "= %s -> %p\n", efi_status ( efirc ), retaddr ); return efirc; } @@ -528,7 +528,7 @@ efi_check_event_wrapper ( EFI_EVENT event ) { EFI_STATUS efirc; DBGCP ( colour, "CheckEvent ( %p ) ", event ); - efirc = bs->SignalEvent ( event ); + efirc = bs->CheckEvent ( event ); DBGCP ( colour, "= %s -> %p\n", efi_status ( efirc ), retaddr ); return efirc; } From 1a602c92ac865762a96125081fc9173b95aa50a0 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 20 Mar 2025 12:25:26 +0000 Subject: [PATCH 45/50] [efi] Allow wrapping the global boot services table in situ When DEBUG=efi_wrap is enabled, we construct a patched copy of the boot services table and patch the global system table to point to this copy. This ensures that any subsequently loaded EFI binaries will call our wrappers. Previously loaded EFI binaries will typically have cached the boot services table pointer (in the gBS variable used by EDK2 code), and therefore will not pick up the updated pointer and so will not call our wrappers. In most cases, this is what we want to happen: we are interested in tracing the calls issued by the newly loaded binary and we do not want to be distracted by the high volume of boot services calls issued by existing UEFI drivers. In some circumstances (such as when a badly behaved OEM driver is causing the system to lock up during the ExitBootServices() call), it can be very useful to be able to patch the global boot services table in situ, so that we can trace calls issued by existing drivers. Restructure the wrapping code to allow wrapping to be enabled or disabled at any time, and to allow for patching the global boot services table in situ. Signed-off-by: Michael Brown --- src/image/efi_image.c | 3 +- src/include/ipxe/efi/efi_wrap.h | 7 +- src/interface/efi/efi_wrap.c | 214 ++++++++++++++++++++------------ 3 files changed, 144 insertions(+), 80 deletions(-) diff --git a/src/image/efi_image.c b/src/image/efi_image.c index 104753a85..d2171e7de 100644 --- a/src/image/efi_image.c +++ b/src/image/efi_image.c @@ -268,7 +268,7 @@ static int efi_image_exec ( struct image *image ) { efi_snp_release(); /* Wrap calls made by the loaded image (for debugging) */ - efi_wrap ( handle ); + efi_wrap_image ( handle ); /* Reset console since image will probably use it */ console_reset(); @@ -291,6 +291,7 @@ static int efi_image_exec ( struct image *image ) { rc = 0; err_start_image: + efi_unwrap(); efi_snp_claim(); err_open_protocol: /* If there was no error, then the image must have been diff --git a/src/include/ipxe/efi/efi_wrap.h b/src/include/ipxe/efi/efi_wrap.h index 2747a9e33..d1e970bde 100644 --- a/src/include/ipxe/efi/efi_wrap.h +++ b/src/include/ipxe/efi/efi_wrap.h @@ -10,7 +10,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include -extern EFI_BOOT_SERVICES * efi_wrap_bs ( void ); -extern void efi_wrap ( EFI_HANDLE handle ); +extern void efi_wrap_bs ( EFI_BOOT_SERVICES *wrapped ); +extern void efi_wrap_systab ( int global ); +extern void efi_unwrap ( void ); + +extern void efi_wrap_image ( EFI_HANDLE handle ); #endif /* _IPXE_EFI_WRAP_H */ diff --git a/src/interface/efi/efi_wrap.c b/src/interface/efi/efi_wrap.c index 252bc2624..8891f464b 100644 --- a/src/interface/efi/efi_wrap.c +++ b/src/interface/efi/efi_wrap.c @@ -43,6 +43,18 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Number of lines to prescroll when needed */ #define EFI_WRAP_PRESCROLL 16 +/** Public EFI system table pointer */ +static EFI_SYSTEM_TABLE *efi_systab_pub; + +/** Private EFI system table used while wrapping is active */ +static EFI_SYSTEM_TABLE efi_systab_priv; + +/** Original EFI boot services table pointer */ +static EFI_BOOT_SERVICES *efi_bs_orig; + +/** Backup of original EFI boot services table */ +static EFI_BOOT_SERVICES efi_bs_copy; + /** * Convert EFI status code to text * @@ -1217,93 +1229,141 @@ efi_create_event_ex_wrapper ( UINT32 type, EFI_TPL notify_tpl, } /** - * Build boot services table wrapper + * Wrap a boot services table * - * @ret bs Wrapped boot services table + * @v wrapper Boot services table to wrap */ -EFI_BOOT_SERVICES * efi_wrap_bs ( void ) { - static EFI_BOOT_SERVICES efi_bs_wrapper; +void efi_wrap_bs ( EFI_BOOT_SERVICES *wrapper ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - /* Build boot services table wrapper */ - memcpy ( &efi_bs_wrapper, bs, sizeof ( efi_bs_wrapper ) ); - efi_bs_wrapper.RaiseTPL = efi_raise_tpl_wrapper; - efi_bs_wrapper.RestoreTPL = efi_restore_tpl_wrapper; - efi_bs_wrapper.AllocatePages = efi_allocate_pages_wrapper; - efi_bs_wrapper.FreePages = efi_free_pages_wrapper; - efi_bs_wrapper.GetMemoryMap = efi_get_memory_map_wrapper; - efi_bs_wrapper.AllocatePool = efi_allocate_pool_wrapper; - efi_bs_wrapper.FreePool = efi_free_pool_wrapper; - efi_bs_wrapper.CreateEvent = efi_create_event_wrapper; - efi_bs_wrapper.SetTimer = efi_set_timer_wrapper; - efi_bs_wrapper.WaitForEvent = efi_wait_for_event_wrapper; - efi_bs_wrapper.SignalEvent = efi_signal_event_wrapper; - efi_bs_wrapper.CloseEvent = efi_close_event_wrapper; - efi_bs_wrapper.CheckEvent = efi_check_event_wrapper; - efi_bs_wrapper.InstallProtocolInterface - = efi_install_protocol_interface_wrapper; - efi_bs_wrapper.ReinstallProtocolInterface - = efi_reinstall_protocol_interface_wrapper; - efi_bs_wrapper.UninstallProtocolInterface - = efi_uninstall_protocol_interface_wrapper; - efi_bs_wrapper.HandleProtocol = efi_handle_protocol_wrapper; - efi_bs_wrapper.RegisterProtocolNotify - = efi_register_protocol_notify_wrapper; - efi_bs_wrapper.LocateHandle = efi_locate_handle_wrapper; - efi_bs_wrapper.LocateDevicePath = efi_locate_device_path_wrapper; - efi_bs_wrapper.InstallConfigurationTable - = efi_install_configuration_table_wrapper; - efi_bs_wrapper.LoadImage = efi_load_image_wrapper; - efi_bs_wrapper.StartImage = efi_start_image_wrapper; - efi_bs_wrapper.Exit = efi_exit_wrapper; - efi_bs_wrapper.UnloadImage = efi_unload_image_wrapper; - efi_bs_wrapper.ExitBootServices = efi_exit_boot_services_wrapper; - efi_bs_wrapper.GetNextMonotonicCount - = efi_get_next_monotonic_count_wrapper; - efi_bs_wrapper.Stall = efi_stall_wrapper; - efi_bs_wrapper.SetWatchdogTimer = efi_set_watchdog_timer_wrapper; - efi_bs_wrapper.ConnectController - = efi_connect_controller_wrapper; - efi_bs_wrapper.DisconnectController - = efi_disconnect_controller_wrapper; - efi_bs_wrapper.OpenProtocol = efi_open_protocol_wrapper; - efi_bs_wrapper.CloseProtocol = efi_close_protocol_wrapper; - efi_bs_wrapper.OpenProtocolInformation - = efi_open_protocol_information_wrapper; - efi_bs_wrapper.ProtocolsPerHandle - = efi_protocols_per_handle_wrapper; - efi_bs_wrapper.LocateHandleBuffer - = efi_locate_handle_buffer_wrapper; - efi_bs_wrapper.LocateProtocol = efi_locate_protocol_wrapper; - efi_bs_wrapper.InstallMultipleProtocolInterfaces - = efi_install_multiple_protocol_interfaces_wrapper; - efi_bs_wrapper.UninstallMultipleProtocolInterfaces - = efi_uninstall_multiple_protocol_interfaces_wrapper; - efi_bs_wrapper.CreateEventEx = efi_create_event_ex_wrapper; - - return &efi_bs_wrapper; -} - -/** - * Wrap the calls made by a loaded image - * - * @v handle Image handle - */ -void efi_wrap ( EFI_HANDLE handle ) { - static EFI_SYSTEM_TABLE efi_systab_copy; - /* Do nothing unless debugging is enabled */ if ( ! DBG_LOG ) return; - /* Construct modified system table */ - if ( efi_systab != &efi_systab_copy ) { - memcpy ( &efi_systab_copy, efi_systab, - sizeof ( efi_systab_copy ) ); - efi_systab->BootServices = efi_wrap_bs(); - efi_systab = &efi_systab_copy; + /* Build boot services table wrapper */ + memcpy ( wrapper, bs, sizeof ( *wrapper ) ); + wrapper->RaiseTPL = efi_raise_tpl_wrapper; + wrapper->RestoreTPL = efi_restore_tpl_wrapper; + wrapper->AllocatePages = efi_allocate_pages_wrapper; + wrapper->FreePages = efi_free_pages_wrapper; + wrapper->GetMemoryMap = efi_get_memory_map_wrapper; + wrapper->AllocatePool = efi_allocate_pool_wrapper; + wrapper->FreePool = efi_free_pool_wrapper; + wrapper->CreateEvent = efi_create_event_wrapper; + wrapper->SetTimer = efi_set_timer_wrapper; + wrapper->WaitForEvent = efi_wait_for_event_wrapper; + wrapper->SignalEvent = efi_signal_event_wrapper; + wrapper->CloseEvent = efi_close_event_wrapper; + wrapper->CheckEvent = efi_check_event_wrapper; + wrapper->InstallProtocolInterface + = efi_install_protocol_interface_wrapper; + wrapper->ReinstallProtocolInterface + = efi_reinstall_protocol_interface_wrapper; + wrapper->UninstallProtocolInterface + = efi_uninstall_protocol_interface_wrapper; + wrapper->HandleProtocol = efi_handle_protocol_wrapper; + wrapper->RegisterProtocolNotify = efi_register_protocol_notify_wrapper; + wrapper->LocateHandle = efi_locate_handle_wrapper; + wrapper->LocateDevicePath = efi_locate_device_path_wrapper; + wrapper->InstallConfigurationTable + = efi_install_configuration_table_wrapper; + wrapper->LoadImage = efi_load_image_wrapper; + wrapper->StartImage = efi_start_image_wrapper; + wrapper->Exit = efi_exit_wrapper; + wrapper->UnloadImage = efi_unload_image_wrapper; + wrapper->ExitBootServices = efi_exit_boot_services_wrapper; + wrapper->GetNextMonotonicCount = efi_get_next_monotonic_count_wrapper; + wrapper->Stall = efi_stall_wrapper; + wrapper->SetWatchdogTimer = efi_set_watchdog_timer_wrapper; + wrapper->ConnectController = efi_connect_controller_wrapper; + wrapper->DisconnectController = efi_disconnect_controller_wrapper; + wrapper->OpenProtocol = efi_open_protocol_wrapper; + wrapper->CloseProtocol = efi_close_protocol_wrapper; + wrapper->OpenProtocolInformation + = efi_open_protocol_information_wrapper; + wrapper->ProtocolsPerHandle = efi_protocols_per_handle_wrapper; + wrapper->LocateHandleBuffer = efi_locate_handle_buffer_wrapper; + wrapper->LocateProtocol = efi_locate_protocol_wrapper; + wrapper->InstallMultipleProtocolInterfaces + = efi_install_multiple_protocol_interfaces_wrapper; + wrapper->UninstallMultipleProtocolInterfaces + = efi_uninstall_multiple_protocol_interfaces_wrapper; + wrapper->CreateEventEx = efi_create_event_ex_wrapper; +} + +/** + * Wrap the public EFI system table + * + * @v global Patch global boot services table in-place + */ +void efi_wrap_systab ( int global ) { + static EFI_BOOT_SERVICES local; + EFI_BOOT_SERVICES *wrapper; + + /* Do nothing unless debugging is enabled */ + if ( ! DBG_LOG ) + return; + + /* Preserve original system and boot services tables */ + if ( ! efi_systab_pub ) { + efi_systab_pub = efi_systab; + efi_bs_orig = efi_systab_pub->BootServices; + memcpy ( &efi_bs_copy, efi_bs_orig, sizeof ( efi_bs_copy ) ); } + /* Construct and use private system table */ + if ( efi_systab != &efi_systab_priv ) { + memcpy ( &efi_systab_priv, efi_systab_pub, + sizeof ( efi_systab_priv ) ); + efi_systab_priv.BootServices = &efi_bs_copy; + efi_systab = &efi_systab_priv; + } + + /* Wrap global or local boot services table as applicable */ + wrapper = ( global ? efi_bs_orig : &local ); + efi_wrap_bs ( wrapper ); + efi_systab_pub->BootServices = wrapper; + DBGC ( colour, "WRAP installed %s wrappers\n", + ( global ? "global" : "local" ) ); +} + +/** + * Remove boot services table wrapper + * + */ +void efi_unwrap ( void ) { + + /* Do nothing unless debugging is enabled */ + if ( ! DBG_LOG ) + return; + + /* Do nothing if wrapping was never enabled */ + if ( ! efi_systab_pub ) + return; + + /* Restore original system and boot services tables */ + memcpy ( efi_bs_orig, &efi_bs_copy, sizeof ( *efi_bs_orig ) ); + efi_systab_pub->BootServices = efi_bs_orig; + + /* Switch back to using public system table */ + efi_systab = efi_systab_pub; + DBGC ( colour, "WRAP uninstalled wrappers\n" ); +} + +/** + * Wrap calls made by a newly loaded image + * + * @v handle Image handle + */ +void efi_wrap_image ( EFI_HANDLE handle ) { + + /* Do nothing unless debugging is enabled */ + if ( ! DBG_LOG ) + return; + /* Dump image information */ efi_dump_image ( handle ); + + /* Patch public system table */ + efi_wrap_systab ( 0 ); } From 7cda3dbf94936f265ab017841d0236bb62f51efa Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 20 Mar 2025 14:18:02 +0000 Subject: [PATCH 46/50] [efi] Attempt to retrieve driver name from image handle for debug messages Not all drivers will install the driver binding protocol on the image handle. Accommodate these drivers by attempting to retrieve the driver name via the component name protocol(s) located on the driver binding's ImageHandle, as well as on the driver handle itself. Signed-off-by: Michael Brown --- src/interface/efi/efi_debug.c | 91 +++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/src/interface/efi/efi_debug.c b/src/interface/efi/efi_debug.c index 030c6a93e..1b778805e 100644 --- a/src/interface/efi/efi_debug.c +++ b/src/interface/efi/efi_debug.c @@ -38,6 +38,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include @@ -320,6 +321,90 @@ static const char * efi_driver_name2 ( EFI_COMPONENT_NAME2_PROTOCOL *wtf ) { return name; } +/** + * Get driver binding name + * + * @v binding Driver binding protocol + * @ret name Driver name, or NULL + */ +static const char * efi_binding_name ( EFI_DRIVER_BINDING_PROTOCOL *binding ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + union { + EFI_COMPONENT_NAME_PROTOCOL *name; + void *interface; + } u; + EFI_HANDLE image; + const char *name; + EFI_STATUS efirc; + + /* Sanity check */ + if ( ! binding ) { + DBG ( "[NULL DriverBinding]" ); + return NULL; + } + + /* Try to open component name protocol on image handle */ + image = binding->ImageHandle; + if ( ( efirc = bs->OpenProtocol ( image, + &efi_component_name_protocol_guid, + &u.interface, efi_image_handle, image, + EFI_OPEN_PROTOCOL_GET_PROTOCOL)) !=0){ + DBG ( "[DriverBinding no ComponentName]" ); + return NULL; + } + + /* Try to get name from component name protocol */ + name = efi_driver_name ( u.name ); + + /* Close component name protocol */ + bs->CloseProtocol ( image, &efi_component_name_protocol_guid, + efi_image_handle, image ); + + return name; +} + +/** + * Get driver binding name + * + * @v binding Driver binding protocol + * @ret name Driver name, or NULL + */ +static const char * efi_binding_name2 ( EFI_DRIVER_BINDING_PROTOCOL *binding ){ + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_HANDLE image; + union { + EFI_COMPONENT_NAME2_PROTOCOL *name2; + void *interface; + } u; + const char *name; + EFI_STATUS efirc; + + /* Sanity check */ + if ( ! binding ) { + DBG ( "[NULL DriverBinding]" ); + return NULL; + } + + /* Try to open component name protocol on image handle */ + image = binding->ImageHandle; + if ( ( efirc = bs->OpenProtocol ( image, + &efi_component_name2_protocol_guid, + &u.interface, efi_image_handle, image, + EFI_OPEN_PROTOCOL_GET_PROTOCOL)) !=0){ + DBG ( "[DriverBinding no ComponentName2]" ); + return NULL; + } + + /* Try to get name from component name protocol */ + name = efi_driver_name2 ( u.name2 ); + + /* Close component name protocol */ + bs->CloseProtocol ( image, &efi_component_name2_protocol_guid, + efi_image_handle, image ); + + return name; +} + /** * Get PE/COFF debug filename * @@ -547,6 +632,12 @@ static struct efi_handle_name_type efi_handle_name_types[] = { /* Driver name (via obsolete original ComponentName protocol) */ EFI_HANDLE_NAME_TYPE ( &efi_component_name_protocol_guid, efi_driver_name ), + /* Driver name (for driver binding handles) */ + EFI_HANDLE_NAME_TYPE ( &efi_driver_binding_protocol_guid, + efi_binding_name2 ), + /* Driver name (via obsolete original ComponentName protocol) */ + EFI_HANDLE_NAME_TYPE ( &efi_driver_binding_protocol_guid, + efi_binding_name ), /* PE/COFF debug filename (for image handles) */ EFI_HANDLE_NAME_TYPE ( &efi_loaded_image_protocol_guid, efi_pecoff_debug_name ), From 5d64469a9e6aa6126643cfb33355441496c84c3a Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 20 Mar 2025 14:20:57 +0000 Subject: [PATCH 47/50] [efi] Prefer driver name to device path for debug messages The driver name is usually more informative for debug messages than the device path from which a driver was loaded. Try using the various mechanisms for obtaining a driver name before trying the device path. Signed-off-by: Michael Brown --- src/interface/efi/efi_debug.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/interface/efi/efi_debug.c b/src/interface/efi/efi_debug.c index 1b778805e..9e28067de 100644 --- a/src/interface/efi/efi_debug.c +++ b/src/interface/efi/efi_debug.c @@ -623,9 +623,6 @@ struct efi_handle_name_type { /** EFI handle name types */ static struct efi_handle_name_type efi_handle_name_types[] = { - /* Device path */ - EFI_HANDLE_NAME_TYPE ( &efi_device_path_protocol_guid, - efi_devpath_text ), /* Driver name (for driver image handles) */ EFI_HANDLE_NAME_TYPE ( &efi_component_name2_protocol_guid, efi_driver_name2 ), @@ -650,6 +647,9 @@ static struct efi_handle_name_type efi_handle_name_types[] = { /* Handle's loaded image file path (for image handles) */ EFI_HANDLE_NAME_TYPE ( &efi_loaded_image_protocol_guid, efi_loaded_image_filepath_name ), + /* Device path */ + EFI_HANDLE_NAME_TYPE ( &efi_device_path_protocol_guid, + efi_devpath_text ), /* Our standard input file handle */ EFI_HANDLE_NAME_TYPE ( &efi_simple_text_input_protocol_guid, efi_conin_name ), From be5bf0aa7a0d76d8426a04c88d1ec6e7c5293ff1 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 20 Mar 2025 14:30:34 +0000 Subject: [PATCH 48/50] [efi] Show image address range in veto debug messages When hunting down a misbehaving OEM driver to add it to the veto list, it can be very useful to know the address ranges used by each driver. Add this information to the verbose debug messages. Signed-off-by: Michael Brown --- src/interface/efi/efi_veto.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/interface/efi/efi_veto.c b/src/interface/efi/efi_veto.c index 37aa9a379..1b2332041 100644 --- a/src/interface/efi/efi_veto.c +++ b/src/interface/efi/efi_veto.c @@ -569,9 +569,6 @@ static int efi_veto_find ( EFI_HANDLE driver, const char *manufacturer, EFI_STATUS efirc; int rc; - DBGC2 ( &efi_vetoes, "EFIVETO checking %s\n", - efi_handle_name ( driver ) ); - /* Mark as not vetoed */ memset ( veto, 0, sizeof ( *veto ) ); @@ -621,6 +618,9 @@ static int efi_veto_find ( EFI_HANDLE driver, const char *manufacturer, } /* Check vetoes */ + DBGC2 ( &efi_vetoes, "EFIVETO checking %s [%p,%p)\n", + efi_handle_name ( driver ), loaded.loaded->ImageBase, + ( loaded.loaded->ImageBase + loaded.loaded->ImageSize ) ); for ( i = 0 ; i < ( sizeof ( efi_vetoes ) / sizeof ( efi_vetoes[0] ) ) ; i++ ) { if ( efi_vetoes[i].veto ( binding.binding, loaded.loaded, From 756e3907fdca0a9b4fe18a618eb35895d0208b86 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 20 Mar 2025 14:35:11 +0000 Subject: [PATCH 49/50] [efi] Get veto candidate driver name from image handle Allow for drivers that do not install the driver binding protocol on the image handle by opening the component name protocol on the driver binding's ImageHandle rather than on the driver handle itself. Signed-off-by: Michael Brown --- src/interface/efi/efi_veto.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/interface/efi/efi_veto.c b/src/interface/efi/efi_veto.c index 1b2332041..4cf4e846d 100644 --- a/src/interface/efi/efi_veto.c +++ b/src/interface/efi/efi_veto.c @@ -598,10 +598,10 @@ static int efi_veto_find ( EFI_HANDLE driver, const char *manufacturer, goto err_loaded; } - /* Open component name protocol, if present*/ + /* Open component name protocol, if present */ if ( ( efirc = bs->OpenProtocol ( - driver, &efi_component_name_protocol_guid, - &wtf.interface, efi_image_handle, driver, + image, &efi_component_name_protocol_guid, + &wtf.interface, efi_image_handle, image, EFI_OPEN_PROTOCOL_GET_PROTOCOL ) ) != 0 ) { /* Ignore failure; is not required to be present */ wtf.interface = NULL; @@ -641,8 +641,8 @@ static int efi_veto_find ( EFI_HANDLE driver, const char *manufacturer, /* Close protocols */ if ( wtf.wtf ) { - bs->CloseProtocol ( driver, &efi_component_name_protocol_guid, - efi_image_handle, driver ); + bs->CloseProtocol ( image, &efi_component_name_protocol_guid, + efi_image_handle, image ); } bs->CloseProtocol ( image, &efi_loaded_image_protocol_guid, efi_image_handle, image ); From 02ecb23d106fe849899597b79f9db03a5d4673d9 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 20 Mar 2025 14:38:20 +0000 Subject: [PATCH 50/50] [efi] Get veto candidate driver name via either component name protocol Attempt to get the veto candidate driver name from both the current and obsolete versions of the component name protocol. Signed-off-by: Michael Brown --- src/interface/efi/efi_veto.c | 41 +++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/src/interface/efi/efi_veto.c b/src/interface/efi/efi_veto.c index 4cf4e846d..17bbf7af4 100644 --- a/src/interface/efi/efi_veto.c +++ b/src/interface/efi/efi_veto.c @@ -29,6 +29,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include /** @file @@ -46,14 +47,12 @@ struct efi_veto_candidate { * * @v binding Driver binding protocol * @v loaded Loaded image protocol - * @v wtf Component name protocol, if present * @v manufacturer Manufacturer name, if present - * @v name Driver name (in "eng" language), if present + * @v name Driver name, if present * @ret vetoed Driver is to be vetoed */ int ( * veto ) ( EFI_DRIVER_BINDING_PROTOCOL *binding, EFI_LOADED_IMAGE_PROTOCOL *loaded, - EFI_COMPONENT_NAME_PROTOCOL *wtf, const char *manufacturer, const CHAR16 *name ); }; @@ -394,7 +393,6 @@ static int efi_veto_driver ( struct efi_veto *veto ) { * * @v binding Driver binding protocol * @v loaded Loaded image protocol - * @v wtf Component name protocol, if present * @v manufacturer Manufacturer name, if present * @v name Driver name, if present * @ret vetoed Driver is to be vetoed @@ -402,7 +400,6 @@ static int efi_veto_driver ( struct efi_veto *veto ) { static int efi_veto_ip4config ( EFI_DRIVER_BINDING_PROTOCOL *binding __unused, EFI_LOADED_IMAGE_PROTOCOL *loaded __unused, - EFI_COMPONENT_NAME_PROTOCOL *wtf __unused, const char *manufacturer, const CHAR16 *name ) { static const CHAR16 ip4cfg[] = L"IP4 CONFIG Network Service Driver"; static const char *dell = "Dell Inc."; @@ -427,7 +424,6 @@ efi_veto_ip4config ( EFI_DRIVER_BINDING_PROTOCOL *binding __unused, * * @v binding Driver binding protocol * @v loaded Loaded image protocol - * @v wtf Component name protocol, if present * @v manufacturer Manufacturer name, if present * @v name Driver name, if present * @ret vetoed Driver is to be vetoed @@ -435,7 +431,6 @@ efi_veto_ip4config ( EFI_DRIVER_BINDING_PROTOCOL *binding __unused, static int efi_veto_hp_xhci ( EFI_DRIVER_BINDING_PROTOCOL *binding __unused, EFI_LOADED_IMAGE_PROTOCOL *loaded __unused, - EFI_COMPONENT_NAME_PROTOCOL *wtf __unused, const char *manufacturer, const CHAR16 *name ) { static const CHAR16 xhci[] = L"Usb Xhci Driver"; static const char *hp = "HP"; @@ -468,7 +463,6 @@ efi_veto_hp_xhci ( EFI_DRIVER_BINDING_PROTOCOL *binding __unused, * * @v binding Driver binding protocol * @v loaded Loaded image protocol - * @v wtf Component name protocol, if present * @v manufacturer Manufacturer name, if present * @v name Driver name, if present * @ret vetoed Driver is to be vetoed @@ -476,7 +470,6 @@ efi_veto_hp_xhci ( EFI_DRIVER_BINDING_PROTOCOL *binding __unused, static int efi_veto_vmware_uefipxebc ( EFI_DRIVER_BINDING_PROTOCOL *binding __unused, EFI_LOADED_IMAGE_PROTOCOL *loaded __unused, - EFI_COMPONENT_NAME_PROTOCOL *wtf __unused, const char *manufacturer, const CHAR16 *name ) { static const CHAR16 uefipxebc[] = L"UEFI PXE Base Code Driver"; static const char *vmware = "VMware, Inc."; @@ -499,14 +492,12 @@ efi_veto_vmware_uefipxebc ( EFI_DRIVER_BINDING_PROTOCOL *binding __unused, * * @v binding Driver binding protocol * @v loaded Loaded image protocol - * @v wtf Component name protocol, if present * @v manufacturer Manufacturer name, if present * @v name Driver name, if present * @ret vetoed Driver is to be vetoed */ static int efi_veto_dhcp6 ( EFI_DRIVER_BINDING_PROTOCOL *binding __unused, EFI_LOADED_IMAGE_PROTOCOL *loaded __unused, - EFI_COMPONENT_NAME_PROTOCOL *wtf __unused, const char *manufacturer __unused, const CHAR16 *name ) { static const CHAR16 dhcp6[] = L"DHCP6 Protocol Driver"; @@ -559,6 +550,10 @@ static int efi_veto_find ( EFI_HANDLE driver, const char *manufacturer, EFI_LOADED_IMAGE_PROTOCOL *loaded; void *interface; } loaded; + union { + EFI_COMPONENT_NAME2_PROTOCOL *wtf2; + void *interface; + } wtf2; union { EFI_COMPONENT_NAME_PROTOCOL *wtf; void *interface; @@ -599,6 +594,15 @@ static int efi_veto_find ( EFI_HANDLE driver, const char *manufacturer, } /* Open component name protocol, if present */ + if ( ( efirc = bs->OpenProtocol ( + image, &efi_component_name2_protocol_guid, + &wtf2.interface, efi_image_handle, image, + EFI_OPEN_PROTOCOL_GET_PROTOCOL ) ) != 0 ) { + /* Ignore failure; is not required to be present */ + wtf2.interface = NULL; + } + + /* Open obsolete component name protocol, if present */ if ( ( efirc = bs->OpenProtocol ( image, &efi_component_name_protocol_guid, &wtf.interface, efi_image_handle, image, @@ -608,9 +612,12 @@ static int efi_veto_find ( EFI_HANDLE driver, const char *manufacturer, } /* Get driver name, if available */ - if ( wtf.wtf && - ( ( efirc = wtf.wtf->GetDriverName ( wtf.wtf, "eng", - &name ) == 0 ) ) ) { + if ( ( wtf2.wtf2 && + ( ( efirc = wtf2.wtf2->GetDriverName ( wtf2.wtf2, "en", + &name ) == 0 ) ) ) || + ( wtf.wtf && + ( ( efirc = wtf.wtf->GetDriverName ( wtf.wtf, "eng", + &name ) == 0 ) ) ) ) { /* Driver has a name */ } else { /* Ignore failure; name is not required to be present */ @@ -624,7 +631,7 @@ static int efi_veto_find ( EFI_HANDLE driver, const char *manufacturer, for ( i = 0 ; i < ( sizeof ( efi_vetoes ) / sizeof ( efi_vetoes[0] ) ) ; i++ ) { if ( efi_vetoes[i].veto ( binding.binding, loaded.loaded, - wtf.wtf, manufacturer, name ) ) { + manufacturer, name ) ) { DBGC ( driver, "EFIVETO %s is vetoed (%s)\n", efi_handle_name ( driver ), efi_vetoes[i].name ); @@ -644,6 +651,10 @@ static int efi_veto_find ( EFI_HANDLE driver, const char *manufacturer, bs->CloseProtocol ( image, &efi_component_name_protocol_guid, efi_image_handle, image ); } + if ( wtf2.wtf2 ) { + bs->CloseProtocol ( image, &efi_component_name2_protocol_guid, + efi_image_handle, image ); + } bs->CloseProtocol ( image, &efi_loaded_image_protocol_guid, efi_image_handle, image ); err_loaded: