diff --git a/.gitignore b/.gitignore index ec85677f..afb8ed85 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ TestsUser/Prelinked/Prelinked TestsUser/Prelinked/Result.xml TestsUser/Serialized/Serialized TestsUser/Smbios/Smbios +Utilities/RsaTool/RsaTool Utilities/EfiResTool/EfiResTool Utilities/AppleEfiSignTool/AppleEfiSignTool Utilities/readlabel/readlabel diff --git a/Include/Library/OcAppleChunklistLib.h b/Include/Library/OcAppleChunklistLib.h index 96cbf698..fda9b506 100644 --- a/Include/Library/OcAppleChunklistLib.h +++ b/Include/Library/OcAppleChunklistLib.h @@ -53,7 +53,7 @@ OcAppleChunklistInitializeContext ( BOOLEAN OcAppleChunklistVerifySignature ( IN OUT OC_APPLE_CHUNKLIST_CONTEXT *Context, - IN RSA_PUBLIC_KEY *PublicKey + IN CONST OC_RSA_PUBLIC_KEY *PublicKey ); /** diff --git a/Include/Library/OcAppleImg4Lib.h b/Include/Library/OcAppleImg4Lib.h new file mode 100644 index 00000000..149aa8e2 --- /dev/null +++ b/Include/Library/OcAppleImg4Lib.h @@ -0,0 +1,50 @@ +/** @file + Copyright (C) 2019, Download-Fritz. All rights reserved. + + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef OC_APPLE_IMG4_LIB_H +#define OC_APPLE_IMG4_LIB_H + +/** + Verify the signature of ImageBuffer against Type of its IMG4 Manifest. + + @param[in] This The pointer to the current protocol instance. + @param[in] ObjType The IMG4 object type to validate against. + @param[in] ImageBuffer The buffer to validate. + @param[in] ImageSize The size, in bytes, of ImageBuffer. + @param[in] SbMode The requested IMG4 Secure Boot mode. + @param[in] ManifestBuffer The buffer of the IMG4 Manifest. + @param[in] ManifestSize The size, in bytes, of ManifestBuffer. + @param[out] HashDigest On output, a pointer to ImageBuffer's digest. + @param[out] DigestSize On output, the size, in bytes, of *HashDigest. + + @retval EFI_SUCCESS ImageBuffer is correctly signed. + @retval EFI_INVALID_PARAMETER One or more required parameters are NULL. + @retval EFI_OUT_OF_RESOURCES Not enough resources are available. + @retval EFI_SECURITY_VIOLATION ImageBuffer's signature is invalid. + +**/ +EFI_STATUS +EFIAPI +AppleImg4Verify ( + IN APPLE_IMG4_VERIFICATION_PROTOCOL *This, + IN UINT32 ObjType, + IN CONST VOID *ImageBuffer, + IN UINTN ImageSize, + IN UINT8 SbMode, + IN CONST VOID *ManifestBuffer, + IN UINTN ManifestSize, + OUT UINT8 **HashDigest OPTIONAL, + OUT UINTN *DigestSize OPTIONAL + ); + +#endif // OC_APPLE_IMG4_LIB_H diff --git a/Include/Library/OcAppleKeysLib.h b/Include/Library/OcAppleKeysLib.h index 18a3b9d4..a824f574 100644 --- a/Include/Library/OcAppleKeysLib.h +++ b/Include/Library/OcAppleKeysLib.h @@ -20,14 +20,9 @@ #define NUM_OF_PK 2 -OC_STATIC_ASSERT ( - sizeof (RSA_PUBLIC_KEY) == 520, - "Incompatible configured RSA key size" - ); - typedef struct APPLE_PK_ENTRY_ { - UINT8 Hash[SHA256_DIGEST_SIZE]; - UINT8 PublicKey[sizeof (RSA_PUBLIC_KEY)]; + UINT8 Hash[SHA256_DIGEST_SIZE]; + CONST OC_RSA_PUBLIC_KEY *PublicKey; } APPLE_PK_ENTRY; extern CONST APPLE_PK_ENTRY PkDataBase[NUM_OF_PK]; diff --git a/Include/Library/OcCryptoLib.h b/Include/Library/OcCryptoLib.h index 6d2503da..9a2bd894 100644 --- a/Include/Library/OcCryptoLib.h +++ b/Include/Library/OcCryptoLib.h @@ -19,13 +19,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #ifndef OC_CRYPTO_LIB_H #define OC_CRYPTO_LIB_H -// -// Default to 2048-bit key length for RSA. -// -#ifndef CONFIG_RSA_KEY_BIT_SIZE -#define CONFIG_RSA_KEY_BIT_SIZE 2048 -#define CONFIG_RSA_KEY_SIZE (CONFIG_RSA_KEY_BIT_SIZE / 8) -#endif +#include // // Default to 128-bit key length for AES. @@ -43,6 +37,8 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #define SHA384_DIGEST_SIZE 48 #define SHA512_DIGEST_SIZE 64 +#define OC_MAX_SHA_DIGEST_SIZE SHA512_DIGEST_SIZE + // // Block sizes. // @@ -53,7 +49,6 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. // // Derived parameters. // -#define RSANUMWORDS (CONFIG_RSA_KEY_SIZE / sizeof (UINT32)) #define AES_BLOCK_SIZE 16 // @@ -70,22 +65,16 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #endif // -// For now abort on anything but 2048, but we can support 1024 and 4096 at least. +// Possible RSA algorithm types supported by OcCryptoLib +// for RSA digital signature verification +// PcdOcCryptoAllowedSigHashTypes MUST be kept in sync with changes! // -#if CONFIG_RSA_KEY_BIT_SIZE != 2048 || CONFIG_RSA_KEY_SIZE != 256 -#error "Only RSA-2048 is supported" -#endif - -#pragma pack(push, 1) - -typedef struct RSA_PUBLIC_KEY_ { - UINT32 Size; - UINT32 N0Inv; - UINT32 N[RSANUMWORDS]; - UINT32 Rr[RSANUMWORDS]; -} RSA_PUBLIC_KEY; - -#pragma pack(pop) +typedef enum OC_SIG_HASH_TYPE_ { + OcSigHashTypeSha256, + OcSigHashTypeSha384, + OcSigHashTypeSha512, + OcSigHashTypeMax +} OC_SIG_HASH_TYPE; typedef struct AES_CONTEXT_ { UINT8 RoundKey[AES_KEY_EXP_SIZE]; @@ -123,15 +112,43 @@ typedef struct SHA512_CONTEXT_ { typedef SHA512_CONTEXT SHA384_CONTEXT; +#pragma pack(push, 1) + +/// +/// The structure describing the RSA Public Key format. +/// The exponent is always 65537. +/// +typedef PACKED struct { + /// + /// The number of 64-bit values of N and RSqrMod each. + /// + UINT16 NumQwords; + /// + /// Padding for 64-bit alignment. Must be 0 to allow future extensions. + /// + UINT8 Reserved[6]; + /// + /// The Montgomery Inverse in 64-bit space: -1 / N[0] mod 2^64. + /// + UINT64 N0Inv; +} OC_RSA_PUBLIC_KEY_HDR; + +typedef PACKED struct { + /// + /// The RSA Public Key header structure. + /// + OC_RSA_PUBLIC_KEY_HDR Hdr; + /// + /// The Modulus and Montgomery's R^2 mod N in little endian byte order. + /// + UINT64 Data[]; +} OC_RSA_PUBLIC_KEY; + +#pragma pack(pop) + // // Functions prototypes // -BOOLEAN -RsaVerify ( - RSA_PUBLIC_KEY *Key, - UINT8 *Signature, - UINT8 *Sha256 - ); VOID AesInitCtxIv ( @@ -148,7 +165,7 @@ AesSetCtxIv ( // // Data size MUST be mutiple of AES_BLOCK_SIZE; -// Suggest https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme +// Suggest https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for Padding scheme // NOTES: you need to set Iv in Context via AesInitCtxIv() or AesSetCtxIv() // no Iv should ever be reused with the same key // @@ -169,7 +186,7 @@ AesCbcDecryptBuffer ( // // Same function for encrypting as for decrypting. // Iv is incremented for every block, and used after encryption as XOR-compliment for output -// Suggesting https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme +// Suggesting https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for Padding scheme // NOTES: you need to set Iv in Context via AesInitCtxIv() or AesSetCtxIv() // no Iv should ever be reused with the same key // @@ -305,6 +322,107 @@ Sha384 ( UINTN Len ); +/** + Verifies Data against Hash with the appropiate SHA2 algorithm for HashSize. + + @param[in] Data The data to verify the hash of. + @param[in] DataSize The, in bytes, of Data. + @param[in] Hash The reference hash to verify against. + @param[in] HashSize The size, in bytes, of Hash. + + @return 0 All HashSize bytes of the two buffers are identical. + @retval Non-zero If HashSize is not a valid SHA2 digest size, -1. Otherwise, + the first mismatched byte in Data's hash subtracted from + the first mismatched byte in Hash. + +**/ +INTN +SigVerifyShaHashBySize ( + IN CONST VOID *Data, + IN UINTN DataSize, + IN CONST UINT8 *Hash, + IN UINTN HashSize + ); + +/** + Verify a RSA PKCS1.5 signature against an expected hash. + The exponent is always 65537 as per the format specification. + + @param[in] Key The RSA Public Key. + @param[in] Signature The RSA signature to be verified. + @param[in] SignatureSize Size, in bytes, of Signature. + @param[in] Hash The Hash digest of the signed data. + @param[in] HashSize Size, in bytes, of Hash. + @param[in] Algorithm The RSA algorithm used. + + @returns Whether the signature has been successfully verified as valid. + +**/ +BOOLEAN +RsaVerifySigHashFromKey ( + IN CONST OC_RSA_PUBLIC_KEY *Key, + IN CONST UINT8 *Signature, + IN UINTN SignatureSize, + IN CONST UINT8 *Hash, + IN UINTN HashSize, + IN OC_SIG_HASH_TYPE Algorithm + ); + +/** + Verify RSA PKCS1.5 signed data against its signature. + The modulus' size must be a multiple of the configured BIGNUM word size. + This will be true for any conventional RSA, which use two's potencies. + + @param[in] Modulus The RSA modulus byte array. + @param[in] ModulusSize The size, in bytes, of Modulus. + @param[in] Exponent The RSA exponent. + @param[in] Signature The RSA signature to be verified. + @param[in] SignatureSize Size, in bytes, of Signature. + @param[in] Data The signed data to verify. + @param[in] DataSize Size, in bytes, of Data. + @param[in] Algorithm The RSA algorithm used. + + @returns Whether the signature has been successfully verified as valid. + +**/ +BOOLEAN +RsaVerifySigDataFromData ( + IN CONST UINT8 *Modulus, + IN UINTN ModulusSize, + IN UINT32 Exponent, + IN CONST UINT8 *Signature, + IN UINTN SignatureSize, + IN CONST UINT8 *Data, + IN UINTN DataSize, + IN OC_SIG_HASH_TYPE Algorithm + ); + +/** + Verify RSA PKCS1.5 signed data against its signature. + The modulus' size must be a multiple of the configured BIGNUM word size. + This will be true for any conventional RSA, which use two's potencies. + The exponent is always 65537 as per the format specification. + + @param[in] Key The RSA Public Key. + @param[in] Signature The RSA signature to be verified. + @param[in] SignatureSize Size, in bytes, of Signature. + @param[in] Data The signed data to verify. + @param[in] DataSize Size, in bytes, of Data. + @param[in] Algorithm The RSA algorithm used. + + @returns Whether the signature has been successfully verified as valid. + +**/ +BOOLEAN +RsaVerifySigDataFromKey ( + IN CONST OC_RSA_PUBLIC_KEY *Key, + IN CONST UINT8 *Signature, + IN UINTN SignatureSize, + IN CONST UINT8 *Data, + IN UINTN DataSize, + IN OC_SIG_HASH_TYPE Algorithm + ); + /** Performs a cryptographically secure comparison of the contents of two buffers. diff --git a/Include/Library/OcMiscLib.h b/Include/Library/OcMiscLib.h index 10220521..f4cb435f 100755 --- a/Include/Library/OcMiscLib.h +++ b/Include/Library/OcMiscLib.h @@ -18,6 +18,11 @@ #include #include +/** + The size, in Bits, of one Byte. +**/ +#define OC_CHAR_BIT 8 + /** Convert seconds to microseconds for use in e.g. gBS->Stall. **/ diff --git a/Include/Library/OcStorageLib.h b/Include/Library/OcStorageLib.h index df700fe0..b0034c68 100644 --- a/Include/Library/OcStorageLib.h +++ b/Include/Library/OcStorageLib.h @@ -20,14 +20,6 @@ #include #include -/** - Ensure that we actually use RSA 2048. -**/ -OC_STATIC_ASSERT ( - CONFIG_RSA_KEY_BIT_SIZE == 2048 && CONFIG_RSA_KEY_SIZE == 256, - "Unsupported RSA key size" - ); - /** Storage vault file containing a dictionary with SHA-256 hashes for all files. **/ @@ -104,7 +96,7 @@ OcStorageInitFromFs ( OUT OC_STORAGE_CONTEXT *Context, IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FileSystem, IN CONST CHAR16 *Path, - IN RSA_PUBLIC_KEY *StorageKey OPTIONAL + IN OC_RSA_PUBLIC_KEY *StorageKey OPTIONAL ); /** diff --git a/Library/OcAppleChunklistLib/OcAppleChunklistLib.c b/Library/OcAppleChunklistLib/OcAppleChunklistLib.c index b561b56c..ad8afb58 100644 --- a/Library/OcAppleChunklistLib/OcAppleChunklistLib.c +++ b/Library/OcAppleChunklistLib/OcAppleChunklistLib.c @@ -99,7 +99,7 @@ OcAppleChunklistInitializeContext ( BOOLEAN OcAppleChunklistVerifySignature ( IN OUT OC_APPLE_CHUNKLIST_CONTEXT *Context, - IN RSA_PUBLIC_KEY *PublicKey + IN CONST OC_RSA_PUBLIC_KEY *PublicKey ) { BOOLEAN Result; @@ -107,10 +107,13 @@ OcAppleChunklistVerifySignature ( ASSERT (Context != NULL); ASSERT (Context->Signature != NULL); - Result = RsaVerify ( + Result = RsaVerifySigHashFromKey ( PublicKey, Context->Signature->Signature, - Context->Hash + sizeof (Context->Signature->Signature), + Context->Hash, + sizeof (Context->Hash), + OcSigHashTypeSha256 ); DEBUG_CODE ( if (Result) { diff --git a/Library/OcAppleImageVerificationLib/OcAppleImageVerification.c b/Library/OcAppleImageVerificationLib/OcAppleImageVerification.c index 3fd6ff7a..1af7cb3d 100644 --- a/Library/OcAppleImageVerificationLib/OcAppleImageVerification.c +++ b/Library/OcAppleImageVerificationLib/OcAppleImageVerification.c @@ -617,7 +617,7 @@ VerifyApplePeImageSignature ( { UINTN Index = 0; APPLE_SIGNATURE_CONTEXT *SignatureContext = NULL; - RSA_PUBLIC_KEY *Pk = NULL; + OC_RSA_PUBLIC_KEY *Pk = NULL; // // Build context if not present @@ -680,7 +680,7 @@ VerifyApplePeImageSignature ( // // PublicKey valid. Extract prepared publickey from database // - Pk = (RSA_PUBLIC_KEY *) PkDataBase[Index].PublicKey; + Pk = (OC_RSA_PUBLIC_KEY *) PkDataBase[Index].PublicKey; } } @@ -694,7 +694,7 @@ VerifyApplePeImageSignature ( // // Verify signature // - if (RsaVerify (Pk, SignatureContext->Signature, Context->PeImageHash) == 1 ) { + if (RsaVerifySigHashFromKey (Pk, SignatureContext->Signature, sizeof (SignatureContext->Signature), Context->PeImageHash, sizeof (Context->PeImageHash), OcSigHashTypeSha256) == 1 ) { DEBUG ((DEBUG_INFO, "Signature verified!\n")); FreePool (SignatureContext); FreePool (Context); diff --git a/Library/OcAppleImg4Lib/OcAppleImg4Lib.c b/Library/OcAppleImg4Lib/OcAppleImg4Lib.c new file mode 100644 index 00000000..1cbf09cd --- /dev/null +++ b/Library/OcAppleImg4Lib/OcAppleImg4Lib.c @@ -0,0 +1,322 @@ +/** @file + Copyright (C) 2019, Download-Fritz. All rights reserved. + + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include "libDER/oids.h" +#include "libDERImg4/libDERImg4.h" +#include "libDERImg4/Img4oids.h" + +GLOBAL_REMOVE_IF_UNREFERENCED const uint8_t *DERImg4RootCertificate = gAppleX86SecureBootRootCaCert; +GLOBAL_REMOVE_IF_UNREFERENCED const size_t *DERImg4RootCertificateSize = &gAppleX86SecureBootRootCaCertSize; + +bool +DERImg4VerifySignature ( + DERByte *Modulus, + DERSize ModulusSize, + uint32_t Exponent, + const uint8_t *Signature, + size_t SignatureSize, + uint8_t *Data, + size_t DataSize, + const DERItem *AlgoOid + ) +{ + OC_SIG_HASH_TYPE AlgoType; + + ASSERT (Modulus != NULL); + ASSERT (ModulusSize > 0); + ASSERT (Signature != NULL); + ASSERT (SignatureSize > 0); + ASSERT (Data != NULL); + ASSERT (DataSize > 0); + ASSERT (AlgoOid != NULL); + + if (DEROidCompare (AlgoOid, &oidSha512Rsa)) { + AlgoType = OcSigHashTypeSha512; + } else if (DEROidCompare (AlgoOid, &oidSha384Rsa)) { + AlgoType = OcSigHashTypeSha384; + } else if (DEROidCompare (AlgoOid, &oidSha256Rsa)) { + AlgoType = OcSigHashTypeSha256; + } else { + return false; + } + + return RsaVerifySigDataFromData ( + Modulus, + ModulusSize, + Exponent, + Signature, + SignatureSize, + Data, + DataSize, + AlgoType + ); +} + +VOID +InternalRetrieveHwInfo ( + OUT DERImg4Environment *Environment + ) +{ + EFI_STATUS Status; + UINTN DataSize; + // + // FIXME: Retrieve these values from trusted storage and expose the variables. + // + ASSERT (Environment != NULL); + + ZeroMem (Environment, sizeof (*Environment)); + + DataSize = sizeof (Environment->ecid); + Status = gRT->GetVariable ( + L"ApECID", + &gAppleSecureBootVariableGuid, + NULL, + &DataSize, + &Environment->ecid + ); + if (EFI_ERROR (Status) || DataSize != sizeof (Environment->ecid)) { + Environment->ecid = 0; + } + + DataSize = sizeof (Environment->chipId); + Status = gRT->GetVariable ( + L"ApChipID", + &gAppleSecureBootVariableGuid, + NULL, + &DataSize, + &Environment->chipId + ); + if (EFI_ERROR (Status) || DataSize != sizeof (Environment->chipId)) { + Environment->chipId = 0; + } + + DataSize = sizeof (Environment->boardId); + Status = gRT->GetVariable ( + L"ApBoardID", + &gAppleSecureBootVariableGuid, + NULL, + &DataSize, + &Environment->boardId + ); + if (EFI_ERROR (Status) || DataSize != sizeof (Environment->boardId)) { + Environment->boardId = 0; + } + + Environment->certificateEpoch = 2; + + DataSize = sizeof (Environment->securityDomain); + Status = gRT->GetVariable ( + L"ApSecurityDomain", + &gAppleSecureBootVariableGuid, + NULL, + &DataSize, + &Environment->securityDomain + ); + if (EFI_ERROR (Status) || DataSize != sizeof (Environment->securityDomain)) { + Environment->securityDomain = 1; + } + + DataSize = sizeof (Environment->productionStatus); + Status = gRT->GetVariable ( + L"ApProductionStatus", + &gAppleSecureBootVariableGuid, + NULL, + &DataSize, + &Environment->productionStatus + ); + if (EFI_ERROR (Status) + || DataSize != sizeof (Environment->productionStatus)) { + Environment->productionStatus = TRUE; + } + + DataSize = sizeof (Environment->securityMode); + Status = gRT->GetVariable ( + L"ApSecurityMode", + &gAppleSecureBootVariableGuid, + NULL, + &DataSize, + &Environment->securityMode + ); + if (EFI_ERROR (Status) || DataSize != sizeof (Environment->securityMode)) { + Environment->securityMode = TRUE; + } + + DataSize = sizeof (Environment->effectiveProductionStatus); + Status = gRT->GetVariable ( + L"EffectiveProductionStatus", + &gAppleSecureBootVariableGuid, + NULL, + &DataSize, + &Environment->effectiveProductionStatus + ); + if (EFI_ERROR (Status) + || DataSize != sizeof (Environment->effectiveProductionStatus)) { + Environment->effectiveProductionStatus = TRUE; + } + + DataSize = sizeof (Environment->effectiveSecurityMode); + Status = gRT->GetVariable ( + L"EffectiveSecurityMode", + &gAppleSecureBootVariableGuid, + NULL, + &DataSize, + &Environment->effectiveSecurityMode + ); + if (EFI_ERROR (Status) + || DataSize != sizeof (Environment->effectiveSecurityMode)) { + Environment->effectiveSecurityMode = TRUE; + } + + DataSize = sizeof (Environment->internalUseOnlyUnit); + Status = gRT->GetVariable ( + L"InternalUseOnlyUnit", + &gAppleSecureBootVariableGuid, + NULL, + &DataSize, + &Environment->internalUseOnlyUnit + ); + if (EFI_ERROR (Status) + || DataSize != sizeof (Environment->internalUseOnlyUnit)) { + Environment->internalUseOnlyUnit = FALSE; + } + + // CHANGE: HardwareModel is unused. +} + +/** + Verify the signature of ImageBuffer against Type of its IMG4 Manifest. + + @param[in] This The pointer to the current protocol instance. + @param[in] ObjType The IMG4 object type to validate against. + @param[in] ImageBuffer The buffer to validate. + @param[in] ImageSize The size, in bytes, of ImageBuffer. + @param[in] SbMode The requested IMG4 Secure Boot mode. + @param[in] ManifestBuffer The buffer of the IMG4 Manifest. + @param[in] ManifestSize The size, in bytes, of ManifestBuffer. + @param[out] HashDigest On output, a pointer to ImageBuffer's digest. + @param[out] DigestSize On output, the size, in bytes, of *HashDigest. + + @retval EFI_SUCCESS ImageBuffer is correctly signed. + @retval EFI_INVALID_PARAMETER One or more required parameters are NULL. + @retval EFI_OUT_OF_RESOURCES Not enough resources are available. + @retval EFI_SECURITY_VIOLATION ImageBuffer's signature is invalid. + +**/ +EFI_STATUS +EFIAPI +AppleImg4Verify ( + IN APPLE_IMG4_VERIFICATION_PROTOCOL *This, + IN UINT32 ObjType, + IN CONST VOID *ImageBuffer, + IN UINTN ImageSize, + IN UINT8 SbMode, + IN CONST VOID *ManifestBuffer, + IN UINTN ManifestSize, + OUT UINT8 **HashDigest OPTIONAL, + OUT UINTN *DigestSize OPTIONAL + ) +{ + DERReturn DerResult; + INTN CmpResult; + + DERImg4Environment EnvInfo; + DERImg4ManifestInfo ManInfo; + + if ((ImageBuffer == NULL || ImageSize == 0) + || (ManifestBuffer == NULL || ManifestSize == 0) + || (HashDigest == NULL && DigestSize != NULL) + || (HashDigest != NULL && DigestSize == NULL) + || (SbMode != AppleImg4SbModeMedium + && SbMode != AppleImg4SbModeFull)) { + return EFI_INVALID_PARAMETER; + } + + DerResult = DERImg4ParseManifest ( + &ManInfo, + ManifestBuffer, + ManifestSize, + ObjType + ); + if (DerResult != DR_Success) { + return EFI_SECURITY_VIOLATION; + } + // + // As ManInfo.imageDigest is a buffer of static size, the bounds check to + // retrieve it acts as implicit sanitizing of ManInfo.imageDigestSize which + // can be considered trusted at this point. + // + CmpResult = SigVerifyShaHashBySize ( + ImageBuffer, + ImageSize, + ManInfo.imageDigest, + ManInfo.imageDigestSize + ); + if (CmpResult != 0) { + return EFI_SECURITY_VIOLATION; + } + + InternalRetrieveHwInfo (&EnvInfo); + + if (SbMode == AppleImg4SbModeMedium) { + if (ManInfo.hasEcid + || !ManInfo.hasXugs || ManInfo.environment.xugs != EnvInfo.xugs + || (ManInfo.environment.internalUseOnlyUnit + && ManInfo.environment.internalUseOnlyUnit != EnvInfo.internalUseOnlyUnit)) { + return EFI_SECURITY_VIOLATION; + } + } else if (SbMode == AppleImg4SbModeFull) { + if (!ManInfo.hasEcid || ManInfo.environment.ecid != EnvInfo.ecid + || ManInfo.hasXugs + || ManInfo.environment.internalUseOnlyUnit) { + return EFI_SECURITY_VIOLATION; + } + } + + if ((ManInfo.environment.boardId != EnvInfo.boardId) + || (ManInfo.environment.chipId != EnvInfo.chipId) + || (ManInfo.environment.certificateEpoch < EnvInfo.certificateEpoch) + || (ManInfo.environment.productionStatus != EnvInfo.productionStatus && !ManInfo.environment.productionStatus) + || (ManInfo.environment.securityMode != EnvInfo.securityMode && !ManInfo.environment.securityMode) + || (ManInfo.environment.securityDomain != EnvInfo.securityDomain) + || (ManInfo.hasEffectiveProductionStatus + && (ManInfo.environment.effectiveProductionStatus != EnvInfo.effectiveProductionStatus && !ManInfo.environment.effectiveProductionStatus)) + || (ManInfo.hasEffectiveSecurityMode + && (ManInfo.environment.effectiveSecurityMode != EnvInfo.effectiveSecurityMode && !ManInfo.environment.effectiveSecurityMode)) + ) { + return EFI_SECURITY_VIOLATION; + } + + if (HashDigest != NULL) { + *HashDigest = AllocateCopyPool (ManInfo.imageDigestSize, ManInfo.imageDigest); + if (*HashDigest == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + *DigestSize = ManInfo.imageDigestSize; + } + + return EFI_SUCCESS; +} diff --git a/Library/OcAppleImg4Lib/OcAppleImg4Lib.inf b/Library/OcAppleImg4Lib/OcAppleImg4Lib.inf index 4d33895f..7b2059bd 100644 --- a/Library/OcAppleImg4Lib/OcAppleImg4Lib.inf +++ b/Library/OcAppleImg4Lib/OcAppleImg4Lib.inf @@ -55,3 +55,4 @@ libDERImg4/Img4oids.h libDERImg4/libDERImg4.h libDERImg4/libDERImg4_config.h + OcAppleImg4Lib.c diff --git a/Library/OcAppleImg4Lib/libDERImg4/DER_Img4Manifest.c b/Library/OcAppleImg4Lib/libDERImg4/DER_Img4Manifest.c index 30a3d3ad..d9eda471 100644 --- a/Library/OcAppleImg4Lib/libDERImg4/DER_Img4Manifest.c +++ b/Library/OcAppleImg4Lib/libDERImg4/DER_Img4Manifest.c @@ -1002,6 +1002,10 @@ DERImg4ManifestDecodeProperty ( return DR_BufOverflow; } + if (Property.valueItem.length == 0) { + return DR_DecodeError; + } + DERMemmove ( ManInfo->imageDigest, Property.valueItem.data, diff --git a/Library/OcAppleImg4Lib/libDERImg4/libDERImg4_config.h b/Library/OcAppleImg4Lib/libDERImg4/libDERImg4_config.h index e739bfdf..fa5e4480 100644 --- a/Library/OcAppleImg4Lib/libDERImg4/libDERImg4_config.h +++ b/Library/OcAppleImg4Lib/libDERImg4/libDERImg4_config.h @@ -18,6 +18,23 @@ extern "C" { #endif +#define DER_IMG4_MAX_DIGEST_SIZE 64 + +extern const uint8_t *DERImg4RootCertificate; +extern const size_t *DERImg4RootCertificateSize; + +bool +DERImg4VerifySignature ( + DERByte *Modulus, + DERSize ModulusSize, + uint32_t Exponent, + const uint8_t *Signature, + size_t SignatureSize, + uint8_t *Data, + size_t DataSize, + const DERItem *AlgoOid + ); + /* ---------------------- Do not edit below this line ---------------------- */ #ifndef DER_IMG4_MAN_CERT_CHAIN_MAX diff --git a/Library/OcAppleKeysLib/OcAppleKeysLib.c b/Library/OcAppleKeysLib/OcAppleKeysLib.c index 8e7eebb9..c818179d 100644 --- a/Library/OcAppleKeysLib/OcAppleKeysLib.c +++ b/Library/OcAppleKeysLib/OcAppleKeysLib.c @@ -12,39 +12,64 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ -#include +#include -CONST APPLE_PK_ENTRY PkDataBase[NUM_OF_PK] = { +#include +#include + +#pragma pack(push, 1) + +/// +/// The data size of a 2048 Bits RSA Public Key. Includes N and R^2 mod N. +/// +#define OC_RSA_PK_2048_NUM_BYTES (2 * (2048 / OC_CHAR_BIT)) + +typedef PACKED struct { + /// + /// RSA Public Key header structure. + /// + OC_RSA_PUBLIC_KEY_HDR Hdr; + /// + /// The Modulus and Montgomery's R^2 mod N in little endian byte order. + /// + union { + UINT8 Bytes[OC_RSA_PK_2048_NUM_BYTES]; + UINT64 Qwords[OC_RSA_PK_2048_NUM_BYTES / sizeof (UINT64)]; + } Data; +} OC_RSA_PUBLIC_KEY_2048; + +#pragma pack(pop) + +// +// Verify the structure size equals to the header plus two times the key size +// (N and R^2 mod N). +// +OC_STATIC_ASSERT ( + sizeof (OC_RSA_PUBLIC_KEY_2048) == sizeof (OC_RSA_PUBLIC_KEY_HDR) + OC_RSA_PK_2048_NUM_BYTES, + "The 2048-bit RSA PK struct is malformed." + ); + +STATIC CONST OC_RSA_PUBLIC_KEY_2048 mPkDb1 = { + { ARRAY_SIZE (mPkDb1.Data.Qwords) / 2, { 0 }, 0xb9a584a4e7cd16d1 }, + /** + CFFD3E6B FE66EC75 F44B7E2E 0ED26398 08A98D10 AC378E55 + 1CAA0E1C 1D85EF6C D51C758C 751816BF 599FBEDA EF4D6B0C + EBA31024 7357CDE1 05696D2E F6A36FE8 540A010E 96311C7E + 1F1971E8 34C3A237 EFF5FFCC 1262FDA4 B399EE9A 29CCCBFC + 1E767165 3F3F2F61 08DAACA3 372D465A C518D154 56A315FC + 555F417D F0095974 755F5150 E667EDA8 823162E0 8CF831F7 + AF5831CF 3F0D92CA BAD076C1 3F74ED92 4CC8A3DE CCD11863 + 9120D1CB 2E85B06B DB155AE2 DA144006 44B63635 CD9AF896 + C172DF2B 4ED961FE 1F467BEA F0EB0BCE EDA1DF17 7D5112CA + 299E9EC8 6F5E85D4 79213FB1 F9DD5B93 8C57DE9A 52FF62A7 + E1431EA9 250EE129 4338CDD9 CA48E7C3 + **/ { - // - // PublicKey hash - // { - 0xc7, 0xa1, 0xb9, 0x36, 0x28, 0x80, 0xde, 0x69, 0x57, 0x62, 0xb7, 0xb6, - 0x5b, 0xec, 0x6b, 0xf1, 0x56, 0xa5, 0x5c, 0xf9, 0x24, 0x7f, 0x22, 0xef, - 0x78, 0x62, 0x35, 0x53, 0x7f, 0x95, 0x2b, 0x45 - }, - // - // Original PublicKey - // - /** - CFFD3E6B FE66EC75 F44B7E2E 0ED26398 08A98D10 AC378E55 - 1CAA0E1C 1D85EF6C D51C758C 751816BF 599FBEDA EF4D6B0C - EBA31024 7357CDE1 05696D2E F6A36FE8 540A010E 96311C7E - 1F1971E8 34C3A237 EFF5FFCC 1262FDA4 B399EE9A 29CCCBFC - 1E767165 3F3F2F61 08DAACA3 372D465A C518D154 56A315FC - 555F417D F0095974 755F5150 E667EDA8 823162E0 8CF831F7 - AF5831CF 3F0D92CA BAD076C1 3F74ED92 4CC8A3DE CCD11863 - 9120D1CB 2E85B06B DB155AE2 DA144006 44B63635 CD9AF896 - C172DF2B 4ED961FE 1F467BEA F0EB0BCE EDA1DF17 7D5112CA - 299E9EC8 6F5E85D4 79213FB1 F9DD5B93 8C57DE9A 52FF62A7 - E1431EA9 250EE129 4338CDD9 CA48E7C3 - **/ - // - // PublicKey in format specified by Chromium project implementation - // - { - 0x40, 0x00, 0x00, 0x00, 0xd1, 0x16, 0xcd, 0xe7, 0xcf, 0xfd, 0x3e, 0x6b, + // + // N + // + 0xcf, 0xfd, 0x3e, 0x6b, 0xfe, 0x66, 0xec, 0x75, 0xf4, 0x4b, 0x7e, 0x2e, 0x0e, 0xd2, 0x63, 0x98, 0x08, 0xa9, 0x8d, 0x10, 0xac, 0x37, 0x8e, 0x55, 0x1c, 0xaa, 0x0e, 0x1c, 0x1d, 0x85, 0xef, 0x6c, 0xd5, 0x1c, 0x75, 0x8c, 0x75, 0x18, 0x16, 0xbf, @@ -66,6 +91,9 @@ CONST APPLE_PK_ENTRY PkDataBase[NUM_OF_PK] = { 0x6f, 0x5e, 0x85, 0xd4, 0x79, 0x21, 0x3f, 0xb1, 0xf9, 0xdd, 0x5b, 0x93, 0x8c, 0x57, 0xde, 0x9a, 0x52, 0xff, 0x62, 0xa7, 0xe1, 0x43, 0x1e, 0xa9, 0x25, 0x0e, 0xe1, 0x29, 0x43, 0x38, 0xcd, 0xd9, 0xca, 0x48, 0xe7, 0xc3, + // + // R^2 mod N + // 0x4f, 0x4b, 0x25, 0x95, 0x56, 0xa1, 0xa9, 0x4f, 0x33, 0x4f, 0x0f, 0xdc, 0xcd, 0x7c, 0xb6, 0xab, 0x41, 0xa8, 0xfe, 0x2c, 0x24, 0x94, 0xad, 0x39, 0x7f, 0x5a, 0x6d, 0x82, 0x40, 0x42, 0x32, 0xf9, 0xbb, 0x27, 0xc1, 0x17, @@ -89,37 +117,30 @@ CONST APPLE_PK_ENTRY PkDataBase[NUM_OF_PK] = { 0xe1, 0x72, 0xc3, 0x6c, 0x5f, 0xe4, 0x66, 0x0d, 0x4e, 0x08, 0x65, 0x9c, 0x46, 0xc5, 0x7b, 0x04 } - }, + } +}; + +STATIC CONST OC_RSA_PUBLIC_KEY_2048 mPkDb2 = { + { ARRAY_SIZE (mPkDb2.Data.Qwords) / 2, { 0 }, 0x646a020e9ed45d13 }, + /** + E50AC288 2D44B7E3 3B67C51D AC639329 DA0363BD EAB5179F + F88E460E E703D6CE 30383B25 0DE0E2FF 39386811 512A1A3B + 7531294F 9C512D28 FF56E5CD EB989342 B3A1A866 B41382A6 + 2FB17D5D 3E97454A E19EB3C8 FA42F103 1DA21EEC 8D33BCE7 + 8C4E3C55 EFF8A596 3A3CDAB7 2F475061 30FC1978 03DB6CA5 + A0FDC594 82EC0A3F 408364E3 4FAE9FD6 D5EBF633 357E3E7A + 3BA491D3 34A62604 B9CBF382 63BFF475 5168DB6D FBBA8669 + B7DB7280 218C4EAC 561CBF2F 932EAD6B 41159573 D7FF75B1 + 9D65195F 0A41DC32 ED6F6D55 51716B6F 744ACD2D 3EE64FDE + 29742594 5ED2FF45 2B8CC72B C5D4C987 D0D1DC0A FC543E28 + F7A49046 8F434122 58F91106 8253C0B6 + **/ { - // - // PublicKey hash - // { - 0x74, 0x61, 0x89, 0x8e, 0x6e, 0x62, 0x96, 0x2e, 0xdd, 0x64, 0x44, 0x71, - 0x45, 0xf0, 0xd9, 0xd0, 0x2b, 0xcc, 0x95, 0x19, 0x49, 0x20, 0x46, 0x67, - 0x1e, 0x1f, 0xcd, 0xdd, 0x18, 0xdc, 0x9b, 0x8b - }, - // - // Original PublicKey - // - /** - E50AC288 2D44B7E3 3B67C51D AC639329 DA0363BD EAB5179F - F88E460E E703D6CE 30383B25 0DE0E2FF 39386811 512A1A3B - 7531294F 9C512D28 FF56E5CD EB989342 B3A1A866 B41382A6 - 2FB17D5D 3E97454A E19EB3C8 FA42F103 1DA21EEC 8D33BCE7 - 8C4E3C55 EFF8A596 3A3CDAB7 2F475061 30FC1978 03DB6CA5 - A0FDC594 82EC0A3F 408364E3 4FAE9FD6 D5EBF633 357E3E7A - 3BA491D3 34A62604 B9CBF382 63BFF475 5168DB6D FBBA8669 - B7DB7280 218C4EAC 561CBF2F 932EAD6B 41159573 D7FF75B1 - 9D65195F 0A41DC32 ED6F6D55 51716B6F 744ACD2D 3EE64FDE - 29742594 5ED2FF45 2B8CC72B C5D4C987 D0D1DC0A FC543E28 - F7A49046 8F434122 58F91106 8253C0B6 - **/ - // - // PublicKey in format specified by Chromium project implementation - // - { - 0x40, 0x00, 0x00, 0x00, 0x13, 0x5d, 0xd4, 0x9e, 0xe5, 0x0a, 0xc2, 0x88, + // + // N + // + 0xe5, 0x0a, 0xc2, 0x88, 0x2d, 0x44, 0xb7, 0xe3, 0x3b, 0x67, 0xc5, 0x1d, 0xac, 0x63, 0x93, 0x29, 0xda, 0x03, 0x63, 0xbd, 0xea, 0xb5, 0x17, 0x9f, 0xf8, 0x8e, 0x46, 0x0e, 0xe7, 0x03, 0xd6, 0xce, 0x30, 0x38, 0x3b, 0x25, 0x0d, 0xe0, 0xe2, 0xff, @@ -141,6 +162,9 @@ CONST APPLE_PK_ENTRY PkDataBase[NUM_OF_PK] = { 0x5e, 0xd2, 0xff, 0x45, 0x2b, 0x8c, 0xc7, 0x2b, 0xc5, 0xd4, 0xc9, 0x87, 0xd0, 0xd1, 0xdc, 0x0a, 0xfc, 0x54, 0x3e, 0x28, 0xf7, 0xa4, 0x90, 0x46, 0x8f, 0x43, 0x41, 0x22, 0x58, 0xf9, 0x11, 0x06, 0x82, 0x53, 0xc0, 0xb6, + // + // R^2 mod N + // 0xa7, 0x3e, 0xe4, 0x73, 0x82, 0xbe, 0x69, 0xc7, 0x7c, 0x75, 0x84, 0xea, 0xb2, 0x84, 0xaf, 0xff, 0x16, 0x2f, 0xe4, 0xce, 0x22, 0x68, 0xf6, 0x35, 0x42, 0x90, 0xd7, 0x3a, 0x15, 0xee, 0x79, 0x8a, 0x54, 0x99, 0x93, 0x6e, @@ -167,6 +191,34 @@ CONST APPLE_PK_ENTRY PkDataBase[NUM_OF_PK] = { } }; +// +// The pointer casts are safe because the structs are compatible. +// +CONST APPLE_PK_ENTRY PkDataBase[NUM_OF_PK] = { + { + // + // PublicKey hash + // + { + 0xc7, 0xa1, 0xb9, 0x36, 0x28, 0x80, 0xde, 0x69, 0x57, 0x62, 0xb7, 0xb6, + 0x5b, 0xec, 0x6b, 0xf1, 0x56, 0xa5, 0x5c, 0xf9, 0x24, 0x7f, 0x22, 0xef, + 0x78, 0x62, 0x35, 0x53, 0x7f, 0x95, 0x2b, 0x45 + }, + (CONST OC_RSA_PUBLIC_KEY *)&mPkDb1 + }, + { + // + // PublicKey hash + // + { + 0x74, 0x61, 0x89, 0x8e, 0x6e, 0x62, 0x96, 0x2e, 0xdd, 0x64, 0x44, 0x71, + 0x45, 0xf0, 0xd9, 0xd0, 0x2b, 0xcc, 0x95, 0x19, 0x49, 0x20, 0x46, 0x67, + 0x1e, 0x1f, 0xcd, 0xdd, 0x18, 0xdc, 0x9b, 0x8b + }, + (CONST OC_RSA_PUBLIC_KEY *)&mPkDb2 + } +}; + GLOBAL_REMOVE_IF_UNREFERENCED CONST UINT8 gAppleX86SecureBootRootCaCert[] = { 0x30, 0x82, 0x05, 0x62, 0x30, 0x82, 0x03, 0x4A, 0xA0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x08, 0x25, 0x2D, 0x14, 0x97, 0x18, 0x5C, 0x6A, 0xA0, 0x30, 0x0D, 0x06, diff --git a/Library/OcAppleSecureBootLib/OcAppleSecureBootLib.c b/Library/OcAppleSecureBootLib/OcAppleSecureBootLib.c index e42dd0e2..a9a23b7f 100644 --- a/Library/OcAppleSecureBootLib/OcAppleSecureBootLib.c +++ b/Library/OcAppleSecureBootLib/OcAppleSecureBootLib.c @@ -86,6 +86,9 @@ InternalGetHardwareModel ( OUT CHAR8 Model[16] ) { + // + // FIXME: Retrieve this value from trusted storage and expose the variable. + // EFI_STATUS Status; UINTN DataSize; CHAR8 HwModel[16]; @@ -114,6 +117,9 @@ InternalGetApEcid ( OUT UINT64 *Ecid ) { + // + // FIXME: Retrieve this value from trusted storage and expose the variable. + // EFI_STATUS Status; UINTN DataSize; UINT64 ApEcid; diff --git a/Library/OcBootManagementLib/DmgBootSupport.c b/Library/OcBootManagementLib/DmgBootSupport.c index 6f6127f2..55440b71 100644 --- a/Library/OcBootManagementLib/DmgBootSupport.c +++ b/Library/OcBootManagementLib/DmgBootSupport.c @@ -159,14 +159,14 @@ InternalGetDiskImageBootFile ( if ((Policy & OC_LOAD_TRUST_APPLE_V1_KEY) != 0) { Result = OcAppleChunklistVerifySignature ( &ChunklistContext, - (RSA_PUBLIC_KEY *)&PkDataBase[0].PublicKey + PkDataBase[0].PublicKey ); } if (!Result && ((Policy & OC_LOAD_TRUST_APPLE_V2_KEY) != 0)) { Result = OcAppleChunklistVerifySignature ( &ChunklistContext, - (RSA_PUBLIC_KEY *)&PkDataBase[1].PublicKey + PkDataBase[1].PublicKey ); } diff --git a/Library/OcCryptoLib/BigNumLib.h b/Library/OcCryptoLib/BigNumLib.h new file mode 100644 index 00000000..fb8d2b78 --- /dev/null +++ b/Library/OcCryptoLib/BigNumLib.h @@ -0,0 +1,134 @@ +/** + This library performs arbitrary precision arithmetic operations. + For more details, please refer to the source files and function headers. + + Copyright (C) 2019, Download-Fritz. All rights reserved. + +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef BIG_NUM_LIB_H +#define BIG_NUM_LIB_H + +#include + +/// +/// A BIGNUM word. This is at best an integer of the platform's natural size +/// to optimize memory accesses and arithmetic operation count. +/// +typedef UINTN OC_BN_WORD; +// +// Declarations regarding the Word size. +// +#define OC_BN_WORD_SIZE (sizeof (OC_BN_WORD)) +#define OC_BN_WORD_NUM_BITS (OC_BN_WORD_SIZE * OC_CHAR_BIT) +// +// Declarations regarding the maximum size of OC_BN structures. +// +typedef UINT16 OC_BN_NUM_WORDS; +typedef UINT32 OC_BN_NUM_BITS; +#define OC_BN_MAX_SIZE MAX_UINT16 +#define OC_BN_MAX_LEN (OC_BN_MAX_SIZE / OC_BN_WORD_SIZE) + +// +// Primitives +// + +/** + Assigns A the value 0. + + @param[in,out] A The number to assign to. + @param[in] NumWords The number of Words of A. + +**/ +VOID +BigNumAssign0 ( + IN OUT OC_BN_WORD *A, + IN OC_BN_NUM_WORDS NumWords + ); + +/** + Parses a data array into a number. The buffer size must be a multiple of the + Word size. The length of Result must precisely fit the required size. + + @param[in,out] Result The buffer to store the result in. + @param[in] NumWords The number of Words of Result. + @param[in] Buffer The buffer to parse. + @param[in] BufferSize The size, in bytes, of Buffer. + +**/ +VOID +BigNumParseBuffer ( + IN OUT OC_BN_WORD *Result, + IN OC_BN_NUM_WORDS NumWords, + IN CONST UINT8 *Buffer, + IN UINTN BufferSize + ); + +/** + Swaps the byte order of Word. + + @param[in] Word The Word to swap. + + @returns The byte-swapped value of Word. + +**/ +OC_BN_WORD +BigNumSwapWord ( + IN OC_BN_WORD Word + ); + +// +// Montgomery arithmetics +// + +/** + Calculates the Montgomery Inverse and Rē mod N. + + @param[in,out] RSqrMod The buffer to return R^2 mod N into. + @param[in] NumWords The number of Words of RSqrMod and N. + @param[in] N The Montgomery Modulus. + + @returns The Montgomery Inverse of N. + +**/ +OC_BN_WORD +BigNumCalculateMontParams ( + IN OUT OC_BN_WORD *RSqrMod, + IN OC_BN_NUM_WORDS NumWords, + IN CONST OC_BN_WORD *N + ); + +/** + Caulculates the exponentiation of A with B mod N. + + @param[in,out] Result The buffer to return the result into. + @param[in] NumWords The number of Words of Result, A, N and RSqrMod. + @param[in] A The base. + @param[in] B The exponent. + @param[in] N The modulus. + @param[in] N0Inv The Montgomery Inverse of N. + @param[in] RSqrMod Montgomery's R^2 mod N. + + @returns Whether the operation was completes successfully. + +**/ +BOOLEAN +BigNumPowMod ( + IN OUT OC_BN_WORD *Result, + IN OC_BN_NUM_WORDS NumWords, + IN CONST OC_BN_WORD *A, + IN UINT32 B, + IN CONST OC_BN_WORD *N, + IN OC_BN_WORD N0Inv, + IN CONST OC_BN_WORD *RSqrMod + ); + +#endif // BIG_NUM_LIB_H diff --git a/Library/OcCryptoLib/BigNumLibInternal.h b/Library/OcCryptoLib/BigNumLibInternal.h new file mode 100644 index 00000000..ed7432f1 --- /dev/null +++ b/Library/OcCryptoLib/BigNumLibInternal.h @@ -0,0 +1,129 @@ +/** + Copyright (C) 2019, Download-Fritz. All rights reserved. + +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef BIG_NUM_LIB_INTERNAL_H +#define BIG_NUM_LIB_INTERNAL_H + +#include "BigNumLib.h" + +/** + Calculates the product of A and B. + + @param[out] Hi Buffer in which the high Word of the result is returned. + @param[in] A The multiplicant. + @param[in] B The multiplier. + + @returns The low Word of the result. + +**/ +OC_BN_WORD +BigNumWordMul ( + OUT OC_BN_WORD *Hi, + IN OC_BN_WORD A, + IN OC_BN_WORD B + ); + +/** + Calulates the difference of A and B. + A must have the same precision as B. Result must have a precision at most as + bit as A and B. + + @param[in,out] Result The buffer to return the result into. + @param[in] NumWords The number of Words of Result, minimum number of + Words of A and B. + @param[in] A The minuend. + @param[in] B The subtrahend. + +**/ +VOID +BigNumSub ( + IN OUT OC_BN_WORD *Result, + IN OC_BN_NUM_WORDS NumWordsResult, + IN CONST OC_BN_WORD *A, + IN CONST OC_BN_WORD *B + ); + +/** + Calculates the binary union of A and (Value << Exponent). + + @param[in,out] A The number to OR with and store the result into. + @param[in] NumWords The number of Words of A. + @param[in] Value The Word value to OR with. + @param[in] Exponent The Word shift exponent. + +**/ +VOID +BigNumOrWord ( + IN OUT OC_BN_WORD *A, + IN OC_BN_NUM_WORDS NumWords, + IN OC_BN_WORD Value, + IN UINTN Exponent + ); + +/** + Calculates the remainder of A and B. + + @param[in,out] Result The buffer to return the result into. + @param[in] NumWordsRest The number of Words of Result and B. + @param[in] A The dividend. + @param[in] NumWordsA The number of Words of A. + @param[in] B The divisor. + + @returns Whether the operation was completes successfully. + +**/ +BOOLEAN +BigNumMod ( + IN OUT OC_BN_WORD *Result, + IN OC_BN_NUM_WORDS NumWordsRest, + IN CONST OC_BN_WORD *A, + IN OC_BN_NUM_WORDS NumWordsA, + IN CONST OC_BN_WORD *B + ); + +/** + Returns the relative order of A and B. A and B must have the same precision. + + @param[in] A The first number to compare. + @param[in] NumWords The number of Words to compare. + @param[in] B The second number to compare. + + @retval < 0 A is lower than B. + @retval 0 A is as big as B. + @retval > 0 A is greater than B. + +**/ +INTN +BigNumCmp ( + IN CONST OC_BN_WORD *A, + IN OC_BN_NUM_WORDS NumWords, + IN CONST OC_BN_WORD *B + ); + +/** + Returns the number of significant bits in a number. + Logically matches OpenSSL's BN_num_bits. + + @param[in] A The number to gather the number of significant bits of. + @param[in] NumWords The number of Words of A. + + @returns The number of significant bits in A. + +**/ +OC_BN_NUM_BITS +BigNumSignificantBits ( + IN CONST OC_BN_WORD *A, + IN UINTN NumWords + ); + +#endif // BIG_NUM_LIB_INTERNAL_H diff --git a/Library/OcCryptoLib/BigNumMontgomery.c b/Library/OcCryptoLib/BigNumMontgomery.c new file mode 100644 index 00000000..61d52d53 --- /dev/null +++ b/Library/OcCryptoLib/BigNumMontgomery.c @@ -0,0 +1,659 @@ +/** + This library performs arbitrary precision Montgomery operations. + All results are returned into caller-provided buffers. The caller is + responsible to ensure the buffers can hold the full result of the operation. + + https://chromium.googlesource.com/chromiumos/platform/ec/+/master/common/rsa.c + has served as a template for several algorithmic ideas. + + This code is not to be considered general-purpose but solely to support + cryptographic operations such as RSA encryption. As such, there are arbitrary + limitations, such as requirement of equal precision, to limit the complexity + of the operations to the bare minimum required to support such use caes. + + SECURITY: Currently, no security measures have been taken. This code is + vulnerable to both timing and side channel attacks for value + leakage. However, its current purpose is the verification of public + binaries with public certificates, for which this is perfectly + acceptable, especially in regards to performance. + + Copyright (C) 2019, Download-Fritz. All rights reserved. + +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include + +#include +#include +#include +#include +#include + +#include "BigNumLibInternal.h" + +/** + Calculates the Montgomery Inverse -1 / A mod 2^#Bits(Word). + This algorithm is based on the Extended Euclidean Algorithm, which returns + 1 / A mod 2^#Bits(Word). + + @param[in] A The number to calculate the Montgomery Inverse of. + + @retval 0 The Montgomery Inverse of A could not be computed. + @retval other The Montgomery Inverse of A. + +**/ +STATIC +OC_BN_WORD +BigNumMontInverse ( + IN CONST OC_BN_WORD *A + ) +{ + OC_BN_WORD Dividend; + OC_BN_WORD Divisor; + OC_BN_WORD X; + OC_BN_WORD Y; + OC_BN_WORD Mod; + OC_BN_WORD Div; + OC_BN_WORD Tmp; + + ASSERT (A != NULL); + // + // We cannot compute the Montgomery Inverse of 0. + // + if (A[0] == 0) { + return 0; + } + + // + // The initial divisor 2^Bits(Word) obviously cannot be represented as a + // variable value. For this reason, the first two iterations of the loop are + // unrolled. 2^Bits(Word) is represented as 0 as those two values are + // congruent modulo 2^Bits(Word), which is the variable storage space. + // The modulo operation is therefor implemented as a subtraction loop. + // + + // + // === INITIALISATION === + // + // Divisor = 1U << sizeof(A->Words[0]) * OC_CHAR_BIT; + // Dividend = A->Words[0]; + // + // Y = 1; + // X = 0; + // + // === LOOP UNROLL 1) === + // + // Div = Dividend / Divisor; // => 0 + // Mod = Dividend % Divisor; // => A->Words[0] + // + // Dividend = Divisor; // => 2^Bits(Word) + // Divisor = Mod; // => A->Words[0] + // + Dividend = 0; + Divisor = A[0]; + // + // Tmp = Y - Div * X; // => 1 + // Y = X; // => 0 + // X = Tmp; // => 1 + // + // === LOOP UNROLL 2) === + // + // Div = Dividend / Divisor; + // Mod = Dividend % Divisor; + // + Div = 0; + do { + Dividend -= Divisor; + ++Div; + } while (Dividend >= Divisor); + + Mod = Dividend; + // + // Dividend = Divisor; + // Divisor = Mod; + // + Dividend = Divisor; + Divisor = Mod; + // + // Tmp = Y - Div * X; // => -Div + // Y = X; // => 1 + // X = Tmp; // => -Div + // + Y = 1; + X = (OC_BN_WORD)0U - Div; + + while (Divisor != 0) { + // + // FIXME: This needs a good source for an algorithm specification. + // + Div = Dividend / Divisor; + Mod = Dividend % Divisor; + + Dividend = Divisor; + Divisor = Mod; + + Tmp = Y - Div * X; + Y = X; + X = Tmp; + } + // + // When the loop ends, Dividend contains the Greatest Common Divisor. + // If it is not 1, we cannot compute the Montgomery Inverse. + // + if (Dividend != 1) { + return 0; + } + // + // As per above, Y is 1 / A mod 2^#Bits(Word), so invert the result to yield + // -1 / A mod 2^#Bits(Word). + // + return (OC_BN_WORD)0U - Y; +} + +OC_BN_WORD +BigNumCalculateMontParams ( + IN OUT OC_BN_WORD *RSqrMod, + IN OC_BN_NUM_WORDS NumWords, + IN CONST OC_BN_WORD *N + ) +{ + OC_BN_WORD N0Inv; + UINT32 NumBits; + UINTN SizeRSqr; + OC_BN_NUM_WORDS NumWordsRSqr; + OC_BN_WORD *RSqr; + + ASSERT (RSqrMod != NULL); + ASSERT (NumWords > 0); + ASSERT (N != NULL); + // + // Calculate the Montgomery Inverse. + // This is a reduced approach of the algorithmic -1 / N mod 2^#Bits(N), + // where the modulus is reduced from 2^#Bits(N) to 2^#Bits(Word). This + // reduces N to N[0] and yields an inverse valid in the Word domain. + // + N0Inv = BigNumMontInverse (N); + if (N0Inv == 0) { + return 0; + } + + NumBits = BigNumSignificantBits (N, NumWords); + + OC_STATIC_ASSERT ( + OC_BN_MAX_SIZE * OC_CHAR_BIT <= ((MAX_UINTN - 1) / 2) - (OC_CHAR_BIT - 1), + "An overflow verification must be added" + ); + // + // Considering NumBits can at most be MAX_UINT16 * OC_CHAR_BIT, this cannot + // overflow. OC_CHAR_BIT-1 is added to achieve rounding towards the next Byte + // boundary. + // + SizeRSqr = ALIGN_VALUE ( + ((2 * (NumBits + 1) + (OC_CHAR_BIT - 1)) / OC_CHAR_BIT), + OC_BN_WORD_SIZE + ); + if (SizeRSqr > OC_BN_MAX_SIZE) { + return 0; + } + + RSqr = AllocatePool (SizeRSqr); + if (RSqr == NULL) { + return 0; + } + NumWordsRSqr = (OC_BN_NUM_WORDS)(SizeRSqr / OC_BN_WORD_SIZE); + // + // Calculate Montgomery's R^2 mod N. + // + BigNumAssign0 (RSqr, NumWordsRSqr); + // + // 2 * NumBits cannot overflow as per above. + // + BigNumOrWord (RSqr, NumWordsRSqr, 1, 2 * NumBits); + BigNumMod (RSqrMod, NumWords, RSqr, NumWordsRSqr, N); + + FreePool (RSqr); + + return N0Inv; +} + +/** + Calculates the sum of C and the product of A and B. + + @param[out] Hi Buffer in which the high Word of the result is returned. + @param[in] C The addend. + @param[in] A The multiplicant. + @param[in] B The multiplier. + + @returns The low Word of the result. + +**/ +STATIC +OC_BN_WORD +BigNumWordAddMul ( + OUT OC_BN_WORD *Hi, + IN OC_BN_WORD C, + IN OC_BN_WORD A, + IN OC_BN_WORD B + ) +{ + OC_BN_WORD ResHi; + OC_BN_WORD ResLo; + + ASSERT (Hi != NULL); + + ResLo = BigNumWordMul (&ResHi, A, B) + C; + if (ResLo < C) { + ++ResHi; + } + + *Hi = ResHi; + return ResLo; +} + +/** + Calculates the sum of C, the product of A and B, and Carry. + + @param[out] Hi Buffer in which the high Word of the result is returned. + @param[in] C The addend. + @param[in] A The multiplicant. + @param[in] B The multiplier. + @param[in] Carry The carry of the previous multiplication. + + @returns The low Word of the result. + +**/ +STATIC +OC_BN_WORD +BigNumWordAddMulCarry ( + OUT OC_BN_WORD *Hi, + IN OC_BN_WORD C, + IN OC_BN_WORD A, + IN OC_BN_WORD B, + IN OC_BN_WORD Carry + ) +{ + OC_BN_WORD MulResHi; + OC_BN_WORD MulResLo; + + ASSERT (Hi != NULL); + + MulResLo = BigNumWordAddMul (&MulResHi, C, A, B); + MulResLo += Carry; + if (MulResLo < Carry) { + ++MulResHi; + } + + *Hi = MulResHi; + return MulResLo; +} + +/** + Calculates a row of the product of A and B mod N. + + @param[in,out] Result The result buffer. + @param[in] NumWords The number of Words of Result, B and N. + @param[in] AWord The current row's Word of the multiplicant. + @param[in] B The multiplier. + @param[in] N The modulus. + @param[in] N0Inv The Montgomery Inverse of N. + +**/ +STATIC +VOID +BigNumMontMulRow ( + IN OUT OC_BN_WORD *Result, + IN OC_BN_NUM_WORDS NumWords, + IN OC_BN_WORD AWord, + IN CONST OC_BN_WORD *B, + IN CONST OC_BN_WORD *N, + IN OC_BN_WORD N0Inv + ) +{ + UINTN CompIndex; + + OC_BN_WORD CCurMulHi; + OC_BN_WORD CCurMulLo; + OC_BN_WORD CCurMontHi; + OC_BN_WORD CCurMontLo; + OC_BN_WORD TFirst; + + ASSERT (Result != NULL); + ASSERT (NumWords > 0); + ASSERT (B != NULL); + ASSERT (N != NULL); + ASSERT (N0Inv != 0); + // + // Standard multiplication + // C = C + A*B + // + CCurMulLo = BigNumWordAddMul ( + &CCurMulHi, + Result[0], + AWord, + B[0] + ); + // + // Montgomery Reduction preparation + // As N_first is reduced mod 2^#Bits (word), we're operating in this domain + // and reduce both C and the result as well. + // t_first = C * N_first + // + TFirst = CCurMulLo * N0Inv; + // + // Montgomery Reduction + // 1. C = C + t_first * N + // 2. In the first step, only the carries are actually used, which implies + // division by R. + // + CCurMontLo = BigNumWordAddMul (&CCurMontHi, CCurMulLo, TFirst, N[0]); + + for (CompIndex = 1; CompIndex < NumWords; ++CompIndex) { + // + // Standard multiplication + // C = C + A*B + carry + // + CCurMulLo = BigNumWordAddMulCarry ( + &CCurMulHi, + Result[CompIndex], + AWord, + B[CompIndex], + CCurMulHi + ); + // + // Montgomery Reduction + // 1. C = C + t_first * N + carry + // + CCurMontLo = BigNumWordAddMulCarry ( + &CCurMontHi, + CCurMulLo, + TFirst, + N[CompIndex], + CCurMontHi + ); + // + // 2. The index shift translates to a bitshift equivalent to the division + // by R = 2^#Bits (word). + // C = C / R + // + Result[CompIndex - 1] = CCurMontLo; + } + // + // Assign the most significant byte the remaining carrys. + // + CCurMulLo = CCurMulHi + CCurMontHi; + Result[CompIndex - 1] = CCurMulLo; + // + // If the result has wrapped around, C >= N is true and we reduce mod N. + // + if (CCurMulLo < CCurMulHi) { + // + // The discarded most significant word must be the last borrow of the + // subtraction, as C_actual = (CCurMul >> WORD_BITS)||C. + // + BigNumSub (Result, NumWords, Result, N); + } +} + +/** + Calculates the Montgomery product of A and B mod N. + + @param[in,out] Result The result buffer. + @param[in] NumWords The number of Words of Result, A, B and N. + @param[in] A The multiplicant. + @param[in] B The multiplier. + @param[in] N The modulus. + @param[in] N0Inv The Montgomery Inverse of N. + +**/ +STATIC +VOID +BigNumMontMul ( + IN OUT OC_BN_WORD *Result, + IN OC_BN_NUM_WORDS NumWords, + IN CONST OC_BN_WORD *A, + IN CONST OC_BN_WORD *B, + IN CONST OC_BN_WORD *N, + IN OC_BN_WORD N0Inv + ) +{ + UINTN RowIndex; + + ASSERT (Result != NULL); + ASSERT (NumWords > 0); + ASSERT (A != NULL); + ASSERT (B != NULL); + ASSERT (N != NULL); + ASSERT (N0Inv != 0); + + ZeroMem (Result, (UINTN)NumWords * OC_BN_WORD_SIZE); + // + // RowIndex is used as an index into the words of A. Because this domain + // operates in mod 2^#Bits (word), 'row results' do not require multiplication + // as the positional factor is stripped by the word-size modulus. + // + for (RowIndex = 0; RowIndex < NumWords; ++RowIndex) { + BigNumMontMulRow (Result, NumWords, A[RowIndex], B, N, N0Inv); + } + // + // As this implementation only reduces mod N on overflow and not for every + // yes-instance of C >= N, any sequence of Montgomery Multiplications must be + // completed with a final reduction step. + // +} + +/** + This is an optimized version of the call + BigNumMontMulRow (C, 0, A, N, N0Inv) + + Calculates a row of the product of 0 and A mod N. + + @param[in,out] Result The result buffer. + @param[in] NumWords The number of Words of Result and N. + @param[in] N The modulus. + @param[in] N0Inv The Montgomery Inverse of N. + +**/ +STATIC +VOID +BigNumMontMulRow0 ( + IN OUT OC_BN_WORD *Result, + IN OC_BN_NUM_WORDS NumWords, + IN CONST OC_BN_WORD *N, + IN OC_BN_WORD N0Inv + ) +{ + UINTN CompIndex; + + OC_BN_WORD CCurMontHi; + OC_BN_WORD CCurMontLo; + OC_BN_WORD TFirst; + + ASSERT (Result != NULL); + ASSERT (NumWords > 0); + ASSERT (N != NULL); + ASSERT (N0Inv != 0); + // + // Montgomery Reduction preparation + // As N_first is reduced mod 2^#Bits (word), we reduce C as well. + // Due to the reduction, the high bits are discarded safely. + // t_first = C * N_first + // + TFirst = Result[0] * N0Inv; + // + // Montgomery Reduction + // 1. C = C + t_first * N + // 2. In the first step, only the carries are actually used, so the + // division by R can be omited. + // + CCurMontLo = BigNumWordAddMul (&CCurMontHi, Result[0], TFirst, N[0]); + for (CompIndex = 1; CompIndex < NumWords; ++CompIndex) { + // + // Montgomery Reduction + // 1. C = C + t_first * N + carry + // + CCurMontLo = BigNumWordAddMulCarry ( + &CCurMontHi, + Result[CompIndex], + TFirst, + N[CompIndex], + CCurMontHi + ); + // + // 2. The index shift translates to a bitshift equivalent to the division + // by R = 2^#Bits (word). + // C = C / R + // + Result[CompIndex - 1] = CCurMontLo; + } + // + // Assign the most significant byte the remaining carry. + // + Result[CompIndex - 1] = CCurMontHi; +} + +/** + This is an optimized version of the call + BigNumMontMul (C, 1, A, N, N0Inv) + + @param[in,out] Result The result buffer. + @param[in] NumWords The number of Words of Result, A and N. + @param[in] A The multiplicant. + @param[in] N The modulus. + @param[in] N0Inv The Montgomery Inverse of N. + +**/ +STATIC +VOID +BigNumMontMul1 ( + IN OUT OC_BN_WORD *Result, + IN OC_BN_NUM_WORDS NumWords, + IN CONST OC_BN_WORD *A, + IN CONST OC_BN_WORD *N, + IN OC_BN_WORD N0Inv + ) +{ + UINTN RowIndex; + + ASSERT (Result != NULL); + ASSERT (NumWords > 0); + ASSERT (A != NULL); + ASSERT (N != NULL); + ASSERT (N0Inv != 0); + + ZeroMem (Result, (UINTN)NumWords * OC_BN_WORD_SIZE); + // + // Perform the entire standard multiplication and one Montgomery Reduction. + // + BigNumMontMulRow (Result, NumWords, 1, A, N, N0Inv); + // + // Perform the remaining Montgomery Reductions. + // + for (RowIndex = 1; RowIndex < NumWords; ++RowIndex) { + BigNumMontMulRow0 (Result, NumWords, N, N0Inv); + } + // + // As this implementation only reduces mod N on overflow and not for every + // yes-instance of C >= N, any sequence of Montgomery Multiplications must be + // completed with a final reduction step. + // +} + +BOOLEAN +BigNumPowMod ( + IN OUT OC_BN_WORD *Result, + IN OC_BN_NUM_WORDS NumWords, + IN CONST OC_BN_WORD *A, + IN UINT32 B, + IN CONST OC_BN_WORD *N, + IN OC_BN_WORD N0Inv, + IN CONST OC_BN_WORD *RSqrMod + ) +{ + OC_BN_WORD *ATmp; + + UINTN Index; + + ASSERT (Result != NULL); + ASSERT (NumWords > 0); + ASSERT (A != NULL); + ASSERT (N != NULL); + ASSERT (N0Inv != 0); + ASSERT (RSqrMod != NULL); + // + // Currently, only the most frequent exponents are supported. + // + if (B != 0x10001 && B != 3) { + DEBUG ((DEBUG_INFO, "OCCR: Unsupported exponent: %x\n", B)); + return FALSE; + } + + ATmp = AllocatePool ((UINTN)NumWords * OC_BN_WORD_SIZE); + if (ATmp == NULL) { + DEBUG ((DEBUG_INFO, "OCCR: Memory allocation failure in ModPow\n")); + return FALSE; + } + // + // Convert A into the Montgomery Domain. + // ATmp = MM (A, R^2 mod N) + // + BigNumMontMul (ATmp, NumWords, A, RSqrMod, N, N0Inv); + + if (B == 0x10001) { + // + // Squaring the intermediate results 16 times yields A'^ (2^16). + // + for (Index = 0; Index < 16; Index += 2) { + // + // Result = MM (ATmp, ATmp) + // + BigNumMontMul (Result, NumWords, ATmp, ATmp, N, N0Inv); + // + // ATmp = MM (Result, Result) + // + BigNumMontMul (ATmp, NumWords, Result, Result, N, N0Inv); + } + // + // Because A is not within the Montgomery Domain, this implies another + // division by R, which takes the result out of the Montgomery Domain. + // C = MM (ATmp, A) + // + BigNumMontMul (Result, NumWords, ATmp, A, N, N0Inv); + } else { + // + // Result = MM (ATmp, ATmp) + // + BigNumMontMul (Result, NumWords, ATmp, ATmp, N, N0Inv); + // + // ATmp = MM (Result, ATmp) + // + BigNumMontMul (ATmp, NumWords, Result, ATmp, N, N0Inv); + // + // Perform a Montgomery Multiplication with 1, which effectively is a + // division by R, taking the result out of the Montgomery Domain. + // C = MM (ATmp, 1) + // TODO: Is this needed or can we just multiply with A above? + // + BigNumMontMul1 (Result, NumWords, ATmp, N, N0Inv); + } + // + // The Montgomery Multiplications above only ensure the result is mod N when + // it does not fit within #Bits(N). For N != 0, which is an obvious + // requirement, #Bits(N) can only ever fit values smaller than 2*N, so the + // result is at most one modulus too large. + // C = C mod N + // + if (BigNumCmp (Result, NumWords, N) >= 0){ + BigNumSub (Result, NumWords, Result, N); + } + + FreePool (ATmp); + return TRUE; +} diff --git a/Library/OcCryptoLib/BigNumPrimitives.c b/Library/OcCryptoLib/BigNumPrimitives.c new file mode 100644 index 00000000..67e44675 --- /dev/null +++ b/Library/OcCryptoLib/BigNumPrimitives.c @@ -0,0 +1,903 @@ +/** + This library performs unsigned arbitrary precision arithmetic operations. + All results are returned into caller-provided buffers. The caller is + responsible to ensure the buffers can hold a value of the precision it + desires. Too large results will be truncated without further notification for + public APIs. + + https://github.com/kokke/tiny-bignum-c has served as a template for several + algorithmic ideas. + + This code is not to be considered general-purpose but solely to support + cryptographic operations such as RSA encryption. As such, there are arbitrary + limitations, such as requirement of equal precision, to limit the complexity + of the operations to the bare minimum required to support such use caes. + + SECURITY: Currently, no security measures have been taken. This code is + vulnerable to both timing and side channel attacks for value + leakage. However, its current purpose is the verification of public + binaries with public certificates, for which this is perfectly + acceptable, especially in regards to performance. + + Copyright (C) 2019, Download-Fritz. All rights reserved. + +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include + +#include +#include +#include +#include +#include +#include + +#include "BigNumLibInternal.h" + +#define OC_BN_MAX_VAL ((OC_BN_WORD)0U - 1U) + +OC_STATIC_ASSERT ( + OC_BN_WORD_SIZE == sizeof (UINT32) || OC_BN_WORD_SIZE == sizeof (UINT64), + "OC_BN_WORD_SIZE and OC_BN_WORD_NUM_BITS usages must be adapted." + ); + +OC_BN_WORD +BigNumSwapWord ( + IN OC_BN_WORD Word + ) +{ + if (OC_BN_WORD_SIZE == sizeof (UINT32)) { + return (OC_BN_WORD)SwapBytes32 ((UINT32)Word); + } + + if (OC_BN_WORD_SIZE == sizeof (UINT64)) { + return (OC_BN_WORD)SwapBytes64 ((UINT64)Word); + } + + ASSERT (FALSE); +} + +/** + Shifts A left by Exponent Words. + + @param[in,out] Result The buffer to return the result into. + @param[in] NumWordsResult The number of Words of Result. + @param[in,out] A The number to be word-shifted. + @param[in] NumWordsA The number of Words of A. + @param[in] Exponent The Word shift exponent. + +**/ +STATIC +VOID +BigNumLeftShiftWords ( + IN OUT OC_BN_WORD *Result, + IN OC_BN_NUM_WORDS NumWordsResult, + IN CONST OC_BN_WORD *A, + IN OC_BN_NUM_WORDS NumWordsA, + IN UINTN Exponent + ) +{ + ASSERT (Result != NULL); + ASSERT (NumWordsResult > 0); + ASSERT (A != NULL); + ASSERT (NumWordsA > 0); + ASSERT (Exponent < NumWordsResult); + ASSERT (NumWordsResult - Exponent >= NumWordsA); + + CopyMem (&Result[Exponent], A, (NumWordsResult - Exponent) * OC_BN_WORD_SIZE); + ZeroMem (Result, Exponent * OC_BN_WORD_SIZE); + if (NumWordsResult - Exponent > NumWordsA) { + ZeroMem ( + &Result[NumWordsA + Exponent], + (NumWordsResult - Exponent - NumWordsA) * OC_BN_WORD_SIZE + ); + } +} + +/** + Shifts A left by Exponent Bits for 0 < Exponent < #Bits(Word). + + @param[in,out] Result The buffer to return the result into. + @param[in] NumWordsResult The number of Words of Result. + @param[in] A The base. + @param[in] NumWordsA The number of Words of A. + @param[in] NumWords The Word shift exponent. + @param[in] NumBits The Bit shift exponent. + +**/ +STATIC +VOID +BigNumLeftShiftWordsAndBits ( + IN OUT OC_BN_WORD *Result, + IN OC_BN_NUM_WORDS NumWordsResult, + IN CONST OC_BN_WORD *A, + IN OC_BN_NUM_WORDS NumWordsA, + IN UINTN NumWords, + IN UINT8 NumBits + ) +{ + UINTN Index; + + ASSERT (Result != NULL); + ASSERT (NumWordsResult > 0); + ASSERT (A != NULL); + ASSERT (NumWordsA > 0); + ASSERT (NumWordsResult >= NumWords); + // + // NumBits must not be 0 because a shift of a Word by its Bit width or + // larger is Undefined Behaviour. + // + ASSERT (NumBits > 0); + ASSERT (NumBits < OC_BN_WORD_NUM_BITS); + // + // This, assuming below, is required to avoid overflows, which purely + // internal calls should never produce. + // + ASSERT (NumWordsResult - NumWords > NumWordsA); + // + // This is not an algorithmic requirement, but BigNumLeftShiftWords shall be + // called if TRUE. + // + ASSERT (NumWords > 0); + + for (Index = (NumWordsA - 1); Index > 0; --Index) { + Result[Index + NumWords] = (A[Index] << NumBits) | (A[Index - 1] >> (OC_BN_WORD_NUM_BITS - NumBits)); + } + // + // Handle the edge-cases at the beginning and the end of the value. + // + Result[NumWords] = A[0] << NumBits; + Result[NumWordsA + NumWords] = A[NumWordsA - 1] >> (OC_BN_WORD_NUM_BITS - NumBits); + // + // Zero everything outside of the previously set ranges. + // + ZeroMem (&Result[NumWordsA + NumWords + 1], (NumWordsResult - NumWords - NumWordsA - 1) * OC_BN_WORD_SIZE); + ZeroMem (Result, NumWords * OC_BN_WORD_SIZE); +} + +/** + Shifts A left by Exponent Bits for 0 < Exponent < #Bits(Word). + + @param[in,out] A The base. + @param[in] NumWords The number of Words of A. + @param[in] Exponent The Bit shift exponent. + +**/ +STATIC +VOID +BigNumLeftShiftBitsSmall ( + IN OUT OC_BN_WORD *A, + IN OC_BN_NUM_WORDS NumWords, + IN UINT8 Exponent + ) +{ + UINTN Index; + + ASSERT (A != NULL); + ASSERT (NumWords > 0); + // + // Exponent must not be 0 because a shift of a Word by its Bit width or + // larger is Undefined Behaviour. + // + ASSERT (Exponent > 0); + ASSERT (Exponent < OC_BN_WORD_NUM_BITS); + + for (Index = (NumWords - 1); Index > 0; --Index) { + A[Index] = (A[Index] << Exponent) | (A[Index - 1] >> (OC_BN_WORD_NUM_BITS - Exponent)); + } + A[0] <<= Exponent; +} + +/** + Shifts A right by Exponent Bits for 0 < Exponent < #Bits(Word). + + @param[in,out] A The base. + @param[in] NumWords The number of Words of A. + @param[in] Exponent The Bit shift exponent. + +**/ +STATIC +VOID +BigNumRightShiftBitsSmall ( + IN OUT OC_BN_WORD *A, + IN OC_BN_NUM_WORDS NumWords, + IN UINT8 Exponent + ) +{ + UINTN Index; + + ASSERT (A != NULL); + ASSERT (NumWords > 0); + // + // Exponent must not be 0 because a shift of a Word by its Bit width or + // larger is Undefined Behaviour. + // + ASSERT (Exponent > 0); + ASSERT (Exponent < OC_BN_WORD_NUM_BITS); + + for (Index = 0; Index < (NumWords - 1U); ++Index) { + A[Index] = (A[Index] >> Exponent) | (A[Index + 1] << (OC_BN_WORD_NUM_BITS - Exponent)); + } + A[Index] >>= Exponent; +} + +OC_BN_WORD +BigNumWordMul64 ( + OUT OC_BN_WORD *Hi, + IN OC_BN_WORD A, + IN OC_BN_WORD B + ); + +/** + Calculates the product of A and B. + + @param[out] Hi Buffer in which the high Word of the result is returned. + @param[in] A The multiplicant. + @param[in] B The multiplier. + + @returns The low Word of the result. + +**/ +OC_BN_WORD +BigNumWordMul ( + OUT OC_BN_WORD *Hi, + IN OC_BN_WORD A, + IN OC_BN_WORD B + ) +{ + UINT64 Result64; + + ASSERT (Hi != NULL); + + if (OC_BN_WORD_SIZE == sizeof (UINT32)) { + Result64 = (UINT64)A * B; + // + // FIXME: The subtraction in the shift is a dirty hack to shut up MSVC. + // + *Hi = (OC_BN_WORD)(RShiftU64 (Result64, OC_BN_WORD_NUM_BITS)); + return (OC_BN_WORD)Result64; + } + + if (OC_BN_WORD_SIZE == sizeof (UINT64)) { + return BigNumWordMul64 (Hi, A, B); + } +} + +/** + Assigns A to Result. + + @param[in,out] Result The buffer to store the result in. + @param[in] NumWordsResult The number of Words of Result. + @param[in] A The number to assign. + @param[in] NumWordsA The number of Words of A. + +**/ +STATIC +VOID +BigNumAssign ( + IN OUT OC_BN_WORD *Result, + IN OC_BN_NUM_WORDS NumWordsResult, + IN CONST OC_BN_WORD *A, + IN OC_BN_NUM_WORDS NumWordsA + ) +{ + UINTN NumWordsCopy; + + ASSERT (Result != NULL); + ASSERT (NumWordsResult > 0); + ASSERT (A != NULL); + ASSERT (NumWordsA > 0); + + if (NumWordsResult > NumWordsA) { + ZeroMem ( + &Result[NumWordsA], + (NumWordsResult - NumWordsA) * OC_BN_WORD_SIZE + ); + NumWordsCopy = NumWordsA; + } else { + NumWordsCopy = NumWordsResult; + } + + CopyMem (Result, A, NumWordsCopy * OC_BN_WORD_SIZE); +} + +VOID +BigNumAssign0 ( + IN OUT OC_BN_WORD *A, + IN OC_BN_NUM_WORDS NumWords + ) +{ + ASSERT (A != NULL); + ASSERT (NumWords > 0); + + ZeroMem (A, NumWords * OC_BN_WORD_SIZE); +} + +VOID +BigNumSub ( + IN OUT OC_BN_WORD *Result, + IN OC_BN_NUM_WORDS NumWords, + IN CONST OC_BN_WORD *A, + IN CONST OC_BN_WORD *B + ) +{ + OC_BN_WORD TmpResult; + OC_BN_WORD Tmp1; + OC_BN_WORD Tmp2; + UINTN Index; + UINT8 Borrow; + + ASSERT (Result != NULL); + ASSERT (NumWords > 0); + ASSERT (A != NULL); + ASSERT (B != NULL); + // + // As the same indices are ever accessed at a step, the index is always + // increased per step, the preexisting values in c are unused and all are + // are set, it is safe to call this function with c = a or c = b + // ATTENTION: This might conflict with future "top" optimizations + // + Borrow = 0; + for (Index = 0; Index < NumWords; ++Index) { + Tmp1 = A[Index]; + Tmp2 = B[Index] + Borrow; + TmpResult = (Tmp1 - Tmp2); + // + // When a subtraction wraps around, the result must be bigger than either + // operand. + // + Borrow = (Tmp2 < Borrow) | (Tmp1 < TmpResult); + Result[Index] = TmpResult; + } +} + +/** + Propagates multiplicative Carry from the result at Index - 1 within Result. + + @param[in,out] A The number to propagate Carry in. + @param[in] NumWords The number of Words of A. + @param[in] Index The index from which on to add Carry. + @param[in] Carry The carry from the multiplication of Index - 1. + +**/ +STATIC +VOID +BigNumMulPropagateCarry ( + IN OUT OC_BN_WORD *A, + IN OC_BN_NUM_WORDS NumWords, + IN UINTN Index, + IN OC_BN_WORD Carry + ) +{ + OC_BN_WORD Tmp; + + ASSERT (A != NULL); + ASSERT (NumWords > 0); + + for (; Index < NumWords && Carry != 0; ++Index) { + Tmp = A[Index] + Carry; + // + // When an addition wraps around, the result must be smaller than either + // operand. + // + Carry = (Tmp < Carry); + A[Index] = Tmp; + } +} + +/** + Returns the number of significant bits in a Word. + + @param[in] Word The word to gather the number of significant bits of. + + @returns The number of significant bits in Word. + +**/ +STATIC +UINT8 +BigNumSignificantBitsWord ( + IN OC_BN_WORD Word + ) +{ + UINT8 NumBits; + OC_BN_WORD Mask; + // + // The values we are receiving are very likely large, thus this approach + // should be reasonably fast. + // + NumBits = OC_BN_WORD_NUM_BITS; + Mask = (OC_BN_WORD)1U << (OC_BN_WORD_NUM_BITS - 1); + while ((Word & Mask) == 0) { + --NumBits; + Mask >>= 1U; + } + + return NumBits; +} + +/** + Returns the most significant word index of A. + + @param[in] A The number to gather the most significant Word index of. + @param[in] NumWords The number of Words of A. + + @returns The index of the most significant Word in A. + +**/ +STATIC +OC_BN_NUM_WORDS +BigNumMostSignificantWord ( + IN CONST OC_BN_WORD *A, + IN OC_BN_NUM_WORDS NumWords + ) +{ + OC_BN_NUM_WORDS Index; + + ASSERT (A != NULL); + ASSERT (NumWords > 0); + + Index = NumWords; + do { + --Index; + if (A[Index] != 0) { + return Index; + } + } while (Index != 0); + + return 0; +} + +OC_BN_NUM_BITS +BigNumSignificantBits ( + IN CONST OC_BN_WORD *A, + IN UINTN NumWords + ) +{ + OC_BN_NUM_BITS Index; + + ASSERT (A != NULL); + ASSERT (NumWords > 0); + + Index = BigNumMostSignificantWord (A, NumWords); + return ((Index * OC_BN_WORD_NUM_BITS) + BigNumSignificantBitsWord (A[Index])); +} + +/** + Calculates the product of A and B. + + @param[in,out] Result The buffer to store the result in. + @param[in] NumWordsResult The number of Words of Result. + @param[in] A The multiplicant. + @param[in] NumWordsA The number of Words of A. + @param[in] B The multiplier. + @param[in] NumWordsB The number of Words of B. + +**/ +STATIC +VOID +BigNumMul ( + IN OUT OC_BN_WORD *Result, + IN OC_BN_NUM_WORDS NumWordsResult, + IN CONST OC_BN_WORD *A, + IN OC_BN_NUM_WORDS NumWordsA, + IN CONST OC_BN_WORD *B, + IN OC_BN_NUM_WORDS NumWordsB + ) +{ + // + // Given a better modulo function, this is subject for removal. + // This algorithm is based on a space-optimised version of the conventional + // Long Multiplication. + // https://en.wikipedia.org/wiki/Multiplication_algorithm#Optimizing_space_complexity + // + OC_BN_WORD CurWord; + OC_BN_WORD MulHi; + OC_BN_WORD MulLo; + OC_BN_WORD CurCarry; + + UINTN LengthA; + UINTN LengthB; + UINTN LengthTmp; + + UINTN IndexRes; + UINTN IndexA; + UINTN IndexB; + + ASSERT (Result != NULL); + ASSERT (NumWordsResult > 0); + ASSERT (A != NULL); + ASSERT (NumWordsA > 0); + ASSERT (B != NULL); + ASSERT (NumWordsB > 0); + ASSERT (A != Result); + ASSERT (B != Result); + + BigNumAssign0 (Result, NumWordsResult); + // + // These additions cannot overflow because NumWords fits UINTN. + // + LengthA = BigNumMostSignificantWord (A, NumWordsA) + 1; + LengthB = BigNumMostSignificantWord (B, NumWordsB) + 1; + // + // This cannot overflow for sane outputs due to the address space limitation. + // + ASSERT (LengthA + LengthB > LengthA); + // + // This is required to avoid overflows, which purely internal calls should + // never produce. + // + ASSERT (LengthA + LengthB - 1 < NumWordsResult); + + CurCarry = 0; + for (IndexRes = 0; IndexRes < LengthA + LengthB - 1; ++IndexRes) { + // + // Add the carry from the last iteration. + // + CurWord = Result[IndexRes] + CurCarry; + CurCarry = (CurWord < CurCarry); + // + // When IndexB is out of bounds for B, the value at the requested position + // would be 0 and hence no calculation would be performed. + // + if (IndexRes < LengthB) { + IndexA = 0; + LengthTmp = IndexRes + 1; + IndexB = IndexRes; + } else { + IndexA = IndexRes - LengthB + 1; + LengthTmp = LengthA; + IndexB = LengthB - 1; + } + + for (; IndexA < LengthTmp; ++IndexA, --IndexB) { + // + // No arithmetics need to be performed when both operands are 0. + // + if ((A[IndexA] | B[IndexB]) == 0) { + continue; + } + + MulLo = BigNumWordMul (&MulHi, A[IndexA], B[IndexB]); + // + // FIXME: + // This is hard to read and probably not optimal, however during + // simplification of various operations, it turned out multiplication + // itself is only required as part of the current modulo implementation. + // Instead of cleaning and tweaking this algorithm, an optimised modulo + // algorithm that does not depend on this multiplication algorithm should + // be found and imported, rendering this function subject for removal. + // + CurCarry += MulHi; + if (CurCarry < MulHi) { + // + // If the current Carry overflows, propagate a carry of maximum value + // upwards and start counting anew. + // + BigNumMulPropagateCarry ( + Result, + NumWordsResult, + IndexRes + 1, + OC_BN_MAX_VAL + ); + ++CurCarry; + } + + CurWord += MulLo; + if (CurWord < MulLo) { + CurCarry += 1; + if (CurCarry < 1) { + // + // If the current Carry overflows, propagate a carry of maximum value + // upwards and start counting anew. + // + BigNumMulPropagateCarry ( + Result, + NumWordsResult, + IndexRes + 1, + OC_BN_MAX_VAL + ); + ++CurCarry; + } + } + } + + Result[IndexRes] = CurWord; + } + // + // Set the MSB to the carry of the last iteration. + // + Result[IndexRes] = CurCarry; +} + +VOID +BigNumOrWord ( + IN OUT OC_BN_WORD *A, + IN OC_BN_NUM_WORDS NumWords, + IN OC_BN_WORD Value, + IN UINTN Exponent + ) +{ + UINTN WordIndex; + UINT8 NumBits; + + ASSERT (A != NULL); + ASSERT (NumWords > 0); + ASSERT (Exponent / OC_BN_WORD_NUM_BITS < NumWords); + + WordIndex = Exponent / OC_BN_WORD_NUM_BITS; + if (WordIndex < NumWords) { + NumBits = Exponent % OC_BN_WORD_NUM_BITS; + A[WordIndex] |= (Value << NumBits); + } +} + +INTN +BigNumCmp ( + IN CONST OC_BN_WORD *A, + IN OC_BN_NUM_WORDS NumWords, + IN CONST OC_BN_WORD *B + ) +{ + UINTN Index; + + ASSERT (A != NULL); + ASSERT (NumWords > 0); + ASSERT (B != NULL); + + Index = NumWords; + do { + --Index; + if (A[Index] > B[Index]) { + return 1; + } else if (A[Index] < B[Index]) { + return -1; + } + } while (Index != 0); + + return 0; +} + +/** + Calculates the left-shift of A by Exponent Bits. + + @param[in,out] Result The buffer to return the result into. + @param[in] NumWordsResult The number of Words of Result. + @param[in] A The number to shift. + @param[in] NumWordsA The number of Words of A. + @param[in] Exponent The amount of Bits to shift by. + +**/ +STATIC +VOID +BigNumLeftShift ( + IN OUT OC_BN_WORD *Result, + IN OC_BN_NUM_WORDS NumWordsResult, + IN CONST OC_BN_WORD *A, + IN OC_BN_NUM_WORDS NumWordsA, + IN UINTN Exponent + ) +{ + UINTN NumWords; + UINT8 NumBits; + + ASSERT (Result != NULL); + ASSERT (NumWordsResult > 0); + ASSERT (A != NULL); + ASSERT (NumWordsA > 0); + + NumWords = Exponent / OC_BN_WORD_NUM_BITS; + NumBits = Exponent % OC_BN_WORD_NUM_BITS; + + if (NumBits != 0) { + BigNumLeftShiftWordsAndBits ( + Result, + NumWordsResult, + A, + NumWordsA, + NumWords, + NumBits + ); + } else { + BigNumLeftShiftWords (Result, NumWordsResult, A, NumWordsA, NumWords); + } +} + +/** + Calculates the quotient of A and B. + + @param[in,out] Result The buffer to return the result into. + @param[in] NumWordsRest The number of Words of Result, A, DenonBuf and + DividenBuf. + @param[in] A The dividend. + @param[in] B The divisor. + @param[in] NumWordsB The number of Words of B. + @param[in] DenomBuf A temporary denominator buffer. + @param[in] DividendBuf A temporary dividend buffer. + + @returns Whether the operation was completes successfully. + +**/ +STATIC +VOID +BigNumDiv ( + IN OUT OC_BN_WORD *Result, + IN OC_BN_NUM_WORDS NumWordsRest, + IN CONST OC_BN_WORD *A, + IN CONST OC_BN_WORD *B, + IN OC_BN_NUM_WORDS NumWordsB, + IN OC_BN_WORD *DenomBuf, + IN OC_BN_WORD *DividendBuf + ) +{ + // + // As for multiplication, this is subject for removal. + // + UINTN CurBitIndex; + BOOLEAN Overflow; + + UINT32 NumBitsA; + UINT32 NumBitsB; + + ASSERT (Result != NULL); + ASSERT (NumWordsRest > 0); + ASSERT (A != NULL); + ASSERT (B != NULL); + ASSERT (NumWordsB > 0); + ASSERT (DenomBuf != NULL); + ASSERT (DividendBuf != NULL); + // + // Use an integer of natural size to store the current Bit index as 'current' + // is always a 2's potency. While a BIGNUM can theoretically hold a + // 2's potency eight times larger than what can represent as Bit index with a + // natural integer (Bytes vs Bits), this cannot happen within this function + // as 'a' aligned to the next 2's potency would need to be just as big for + // this to be the case. This cannot happen due to the address space + // limitation. + // + CurBitIndex = 0; + Overflow = FALSE; + // + // Shift b to the left so it has the same amount of significant bits as a. + // This would, without this speedup, be done on per-bit basis by the loop + // below. + // + NumBitsA = BigNumSignificantBits (A, NumWordsRest); + NumBitsB = BigNumSignificantBits (B, NumWordsB); + if (NumBitsA > NumBitsB) { + CurBitIndex = NumBitsA - NumBitsB; // int Current = 1 << (numBitsA - numBitsB); + BigNumLeftShift (DenomBuf, NumWordsRest, B, NumWordsB, CurBitIndex); // Denom = B << CurBitIndex + } else { + CurBitIndex = 0; // int Current = 1; + BigNumAssign (DenomBuf, NumWordsRest, B, NumWordsB); // Denom = B + } + + while (BigNumCmp (DenomBuf, NumWordsRest, A) <= 0) { // while (Denom <= a) { + if (DenomBuf[NumWordsRest - 1] > (OC_BN_MAX_VAL / 2U)) { + Overflow = TRUE; + break; + } + ++CurBitIndex; // Current <<= 1; + BigNumLeftShiftBitsSmall (DenomBuf, NumWordsRest, 1); // Denom <<= 1; + } + if (!Overflow) { + BigNumRightShiftBitsSmall (DenomBuf, NumWordsRest, 1); // Denom >>= 1; + --CurBitIndex; // Current >>= 1; + } + BigNumAssign0 (Result, NumWordsRest); // int Result = 0; + BigNumAssign (DividendBuf, NumWordsRest, A, NumWordsRest); // Dividend = A + // + // currentBitIndex cannot add-wraparound to reach this value as reasoned in + // the comment before. + // + while (CurBitIndex != (0ULL - 1ULL)) { // while (Current != 0) + if (BigNumCmp (DividendBuf, NumWordsRest, DenomBuf) >= 0) { // if (Dividend >= Denom) + BigNumSub (DividendBuf, NumWordsRest, DividendBuf, DenomBuf); // Dividend -= denom; + BigNumOrWord (Result, NumWordsRest, 1, CurBitIndex); // Result |= current; + } + --CurBitIndex; // Current >>= 1; + BigNumRightShiftBitsSmall (DenomBuf, NumWordsRest, 1); // Denom >>= 1; + } // return Result; +} + +BOOLEAN +BigNumMod ( + IN OUT OC_BN_WORD *Result, + IN OC_BN_NUM_WORDS NumWordsRest, + IN CONST OC_BN_WORD *A, + IN OC_BN_NUM_WORDS NumWordsA, + IN CONST OC_BN_WORD *B + ) +{ + // + // FIXME: + // The algorithm is rather expensive and slow. It utilitises the current + // suboptimal multiplication and division algorithms. An optimised algorithm + // should be imported and formerly mentioned functions be removed as they'd + // be dead code. + // + UINTN TempsBnSize; + + VOID *Memory; + OC_BN_WORD *TmpDiv; + OC_BN_WORD *TmpMod; + OC_BN_WORD *TmpDenom; + OC_BN_WORD *TmpDividend; + + ASSERT (Result != NULL); + ASSERT (NumWordsRest > 0); + ASSERT (A != NULL); + ASSERT (NumWordsA > 0); + ASSERT (B != NULL); + ASSERT (NumWordsA >= NumWordsRest); + + OC_STATIC_ASSERT ( + OC_BN_MAX_SIZE <= MAX_UINTN / 4, + "An overflow verification must be added" + ); + + TempsBnSize = NumWordsA * OC_BN_WORD_SIZE; + Memory = AllocatePool (4 * TempsBnSize); + if (Memory == NULL) { + return FALSE; + } + + TmpDiv = (OC_BN_WORD *)Memory; + TmpMod = (OC_BN_WORD *)((UINTN)TmpDiv + TempsBnSize); + TmpDenom = (OC_BN_WORD *)((UINTN)TmpMod + TempsBnSize); + TmpDividend = (OC_BN_WORD *)((UINTN)TmpDenom + TempsBnSize); + + BigNumDiv (TmpDiv, NumWordsA, A, B, NumWordsRest, TmpDenom, TmpDividend); + BigNumMul (TmpMod, NumWordsA, TmpDiv, NumWordsA, B, NumWordsRest); + BigNumSub (Result, NumWordsRest, A, TmpMod); + + FreePool (Memory); + return TRUE; +} + +VOID +BigNumParseBuffer ( + IN OUT OC_BN_WORD *Result, + IN OC_BN_NUM_WORDS NumWords, + IN CONST UINT8 *Buffer, + IN UINTN BufferSize + ) +{ + UINTN Index; + OC_BN_WORD Tmp; + + ASSERT (Result != NULL); + ASSERT (NumWords * OC_BN_WORD_SIZE == BufferSize); + ASSERT (Buffer != NULL); + ASSERT (BufferSize > 0); + ASSERT ((BufferSize % OC_BN_WORD_SIZE) == 0); + + for (Index = OC_BN_WORD_SIZE; Index <= BufferSize; Index += OC_BN_WORD_SIZE) { + if (OC_BN_WORD_SIZE == sizeof (UINT32)) { + Tmp = (OC_BN_WORD)( + ((UINT32)Buffer[(BufferSize - Index) + 0] << 24U) | + ((UINT32)Buffer[(BufferSize - Index) + 1] << 16U) | + ((UINT32)Buffer[(BufferSize - Index) + 2] << 8U) | + ((UINT32)Buffer[(BufferSize - Index) + 3] << 0U)); + } else if (OC_BN_WORD_SIZE == sizeof (UINT64)) { + Tmp = (OC_BN_WORD)( + ((UINT64)Buffer[(BufferSize - Index) + 0] << 56U) | + ((UINT64)Buffer[(BufferSize - Index) + 1] << 48U) | + ((UINT64)Buffer[(BufferSize - Index) + 2] << 40U) | + ((UINT64)Buffer[(BufferSize - Index) + 3] << 32U) | + ((UINT64)Buffer[(BufferSize - Index) + 4] << 24U) | + ((UINT64)Buffer[(BufferSize - Index) + 5] << 16U) | + ((UINT64)Buffer[(BufferSize - Index) + 6] << 8U) | + ((UINT64)Buffer[(BufferSize - Index) + 7] << 0U)); + } + + Result[(Index / OC_BN_WORD_SIZE) - 1] = Tmp; + } +} diff --git a/Library/OcCryptoLib/IA32/BigNumWordMul64.c b/Library/OcCryptoLib/IA32/BigNumWordMul64.c new file mode 100644 index 00000000..480ae252 --- /dev/null +++ b/Library/OcCryptoLib/IA32/BigNumWordMul64.c @@ -0,0 +1,64 @@ +/** @file + Copyright (C) 2019, Download-Fritz. All rights reserved. + +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ +#include + +#include +#include + +#include "../BigNumLib.h" + +OC_BN_WORD +BigNumWordMul64 ( + OUT OC_BN_WORD *Hi, + IN OC_BN_WORD A, + IN OC_BN_WORD B + ) +{ + ASSERT (OC_BN_WORD_SIZE == sizeof (UINT64)); + ASSERT (Hi != NULL); + // + // This is to be used when the used compiler lacks support for both the + // __int128 type and a suiting intrinsic to perform the calculation. + // + // Source: https://stackoverflow.com/a/31662911 + // + CONST OC_BN_WORD SubWordShift = OC_BN_WORD_NUM_BITS / 2; + CONST OC_BN_WORD SubWordMask = ((OC_BN_WORD)1U << SubWordShift) - 1; + + OC_BN_WORD ALo; + OC_BN_WORD AHi; + OC_BN_WORD BLo; + OC_BN_WORD BHi; + + OC_BN_WORD P0; + OC_BN_WORD P1; + OC_BN_WORD P2; + OC_BN_WORD P3; + + OC_BN_WORD Cy; + + ALo = A & SubWordMask; + AHi = A >> SubWordShift; + BLo = B & SubWordMask; + BHi = B >> SubWordShift; + + P0 = ALo * BLo; + P1 = ALo * BHi; + P2 = AHi * BLo; + P3 = AHi * BHi; + + Cy = (((P0 >> SubWordShift) + (P1 & SubWordMask) + (P2 & SubWordMask)) >> SubWordShift) & SubWordMask; + + *Hi = P3 + (P1 >> SubWordShift) + (P2 >> SubWordShift) + Cy; + return P0 + (P1 << SubWordShift) + (P2 << SubWordShift); +} diff --git a/Library/OcCryptoLib/OcCryptoLib.inf b/Library/OcCryptoLib/OcCryptoLib.inf index e62a0d77..51f49d89 100644 --- a/Library/OcCryptoLib/OcCryptoLib.inf +++ b/Library/OcCryptoLib/OcCryptoLib.inf @@ -25,17 +25,31 @@ # -# VALID_ARCHITECTURES = X64 +# VALID_ARCHITECTURES = IA32 X64 # [Sources] Aes.c - Rsa2048Sha256.c + RsaDigitalSign.c Md5.c Sha1.c Sha2.c SecureMem.c PasswordHash.c + BigNumLib.h + BigNumLibInternal.h + BigNumPrimitives.c + BigNumMontgomery.c + +[Sources.IA32] + IA32/BigNumWordMul64.c + +[Sources.X64] + IA32/BigNumWordMul64.c + +[FixedPcd] + gOcSupportPkgTokenSpaceGuid.PcdOcCryptoAllowedRsaModuli + gOcSupportPkgTokenSpaceGuid.PcdOcCryptoAllowedSigHashTypes [Packages] MdePkg/MdePkg.dec diff --git a/Library/OcCryptoLib/Rsa2048Sha256.c b/Library/OcCryptoLib/Rsa2048Sha256.c deleted file mode 100644 index 3df1803c..00000000 --- a/Library/OcCryptoLib/Rsa2048Sha256.c +++ /dev/null @@ -1,332 +0,0 @@ -/** @file - -OcCryptoLib - -Copyright (c) 2018, savvas - -All rights reserved. - -This program and the accompanying materials -are licensed and made available under the terms and conditions of the BSD License -which accompanies this distribution. The full text of the license may be found at -http://opensource.org/licenses/bsd-license.php - -THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, -WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. - -**/ - -/** - Copyright (c) 2014 The Chromium OS Authors. All rights reserved. - Use of this source code is governed by a BSD-style license that can be - found in the LICENSE file. - - Implementation of RSA signature verification which uses a pre-processed key - for computation. -**/ - -#ifdef EFIAPI -#include -#endif - -#include - -/** - PKCS#1 padding (from the RSA PKCS#1 v2.1 standard) - - The DER-encoded padding is defined as follows : - 0x00 || 0x01 || PS || 0x00 || T - - T: DER Encoded DigestInfo value which depends on the hash function used, - for SHA-256: - (0x)30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 || H. - - Length(T) = 51 octets for SHA-256 - - PS: octet string consisting of {Length(RSA Key) - Length(T) - 3} 0xFF - **/ -#define PKCS_PAD_SIZE (CONFIG_RSA_KEY_SIZE - SHA256_DIGEST_SIZE) - -STATIC UINT8 mSha256Tail[] = { - 0x00, 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, - 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, - 0x05, 0x00, 0x04, 0x20 -}; - -UINT64 -Mula32 ( - UINT32 A, - UINT32 B, - UINT32 C - ) -{ - UINT64 Ret = A; - - Ret *= B; - Ret += C; - return Ret; -} - -UINT64 -Mulaa32 ( - UINT32 A, - UINT32 B, - UINT32 C, - UINT32 D - ) -{ - UINT64 Ret = A; - - Ret *= B; - Ret += C; - Ret += D; - return Ret; -} - -// -// A[] -= Mod -// -STATIC -VOID -SubMod ( - RSA_PUBLIC_KEY *Key, - UINT32 *A - ) -{ - INT64 B = 0; - UINT32 Index = 0; - for (Index = 0; Index < RSANUMWORDS; ++Index) { - B += (UINT64) A[Index] - Key->N[Index]; - A[Index] = (UINT32) B; - B >>= 32; - } -} - -// -// Return A[] >= Mod -// -STATIC -INT32 -GeMod ( - RSA_PUBLIC_KEY *Key, - CONST UINT32 *A - ) -{ - UINT32 Index = 0; - - for (Index = RSANUMWORDS; Index;) { - --Index; - if (A[Index] < Key->N[Index]) - return 0; - if (A[Index] > Key->N[Index]) - return 1; - } - return 1; -} - -// -// Montgomery c[] += a * b[] / R % mod -// -STATIC -VOID -MontMulAdd ( - RSA_PUBLIC_KEY *Key, - UINT32 *C, - UINT32 Aa, - UINT32 *Bb - ) -{ - UINT64 A,B; - UINT32 D0, Index; - - A = Mula32 (Aa, Bb[0], C[0]); - D0 = (UINT32) A * Key->N0Inv; - B = Mula32 (D0, Key->N[0], (UINT32) A); - - for (Index = 1; Index < RSANUMWORDS; ++Index) { - A = Mulaa32 (Aa, Bb[Index], C[Index], (UINT32) (A >> 32)); - B = Mulaa32 (D0, Key->N[Index], (UINT32) A, (UINT32) (B >> 32)); - C[Index - 1] = (UINT32) B; - } - - A = (A >> 32) + (B >> 32); - C[Index - 1] = (UINT32) A; - - if (A >> 32) { - SubMod (Key, C); - } -} - -// -// Montgomery c[] = a[] * b[] / R % mod -// -STATIC -VOID -MontMul ( - RSA_PUBLIC_KEY *Key, - UINT32 *C, - UINT32 *A, - UINT32 *B - ) -{ - UINT32 Index; - - ZeroMem (C, CONFIG_RSA_KEY_SIZE); - - for (Index = 0; Index < RSANUMWORDS; ++Index) - MontMulAdd (Key, C, A[Index], B); -} - -/** - In-place public exponentiation. - Exponent depends on the configuration (65537 (default), or 3). - - @param Key Key to use in signing - @param InOut Input and output big-endian byte array - **/ -STATIC -VOID -ModPow ( - RSA_PUBLIC_KEY *Key, - UINT8 *InOut - ) -{ - UINT32 *A = NULL; - UINT32 *Ar = NULL; - UINT32 *Aar = NULL; - UINT32 *Aaa = NULL; - INT32 Index = 0; - UINT32 Tmp = 0; - - UINT32 Workbuf32[RSANUMWORDS * 3]; - - A = Workbuf32; - Ar = A + RSANUMWORDS; - Aar = Ar + RSANUMWORDS; - - // - // Re-use location - // - Aaa = Aar; - - // - // Convert from big endian byte array to little endian word array - // - for (Index = 0; Index < (INT32) RSANUMWORDS; ++Index) { - Tmp = - ((UINT32)InOut[((RSANUMWORDS - 1 - Index) * 4) + 0] << 24) | - ((UINT32)InOut[((RSANUMWORDS - 1 - Index) * 4) + 1] << 16) | - ((UINT32)InOut[((RSANUMWORDS - 1 - Index) * 4) + 2] << 8) | - ((UINT32)InOut[((RSANUMWORDS - 1 - Index) * 4) + 3] << 0); - A[Index] = Tmp; - } - - MontMul (Key, Ar, A, Key->Rr); - // - // Exponent 65537 - // - for (Index = 0; Index < 16; Index += 2) { - MontMul (Key, Aar, Ar, Ar); - MontMul (Key, Ar, Aar, Aar); - } - MontMul (Key, Aaa, Ar, A); - - if (GeMod (Key, Aaa)){ - SubMod (Key, Aaa); - } - - // - // Convert to bigendian byte array - // - for (Index = (INT32) RSANUMWORDS - 1; Index >= 0; --Index) { - Tmp = Aaa[Index]; - - *InOut++ = (UINT8) (Tmp >> 24); - *InOut++ = (UINT8) (Tmp >> 16); - *InOut++ = (UINT8) (Tmp >> 8); - *InOut++ = (UINT8) (Tmp >> 0); - } -} - -/** - * Check PKCS#1 padding bytes - * - * @param sig Signature to verify - * @return 0 if the padding is correct. - */ -STATIC -INT32 -CheckPadding ( - UINT8 *Sig - ) -{ - UINT8 *Ptr = NULL; - INT32 Result = 0; - UINT32 Index = 0; - - Ptr = Sig; - // - // First 2 bytes are always 0x00 0x01 - // - Result |= *Ptr++ ^ 0x00; - Result |= *Ptr++ ^ 0x01; - // - // Then 0xff bytes until the tail - // - for (Index = 0; Index < PKCS_PAD_SIZE - sizeof (mSha256Tail) - 2; Index++) - Result |= *Ptr++ ^ 0xff; - // - // Check the tail - // - Result |= CompareMem (Ptr, mSha256Tail, sizeof (mSha256Tail)); - return Result != 0; -} - -/** - Verify a SHA256WithRSA PKCS#1 v1.5 signature against an expected - SHA256 hash. - - @param Key RSA public key - @param Signature RSA signature - @param Sha256 SHA-256 digest of the content to verify - - @return FALSE on failure, TRUE on success. - **/ -BOOLEAN -RsaVerify ( - RSA_PUBLIC_KEY *Key, - UINT8 *Signature, - UINT8 *Sha256 - ) -{ - UINT8 Buf[CONFIG_RSA_KEY_SIZE]; - - // - // Copy input to local workspace - // - CopyMem (Buf, Signature, CONFIG_RSA_KEY_SIZE); - - // - // In-place exponentiation - // - ModPow (Key, Buf); - - // - // Check the PKCS#1 padding - // - if (CheckPadding (Buf) != 0) { - return FALSE; - } - - // - // Check the digest - // - if (CompareMem (Buf + PKCS_PAD_SIZE, Sha256, SHA256_DIGEST_SIZE) != 0) { - return FALSE; - } - - // - // All checked out OK - // - return TRUE; -} diff --git a/Library/OcCryptoLib/RsaDigitalSign.c b/Library/OcCryptoLib/RsaDigitalSign.c new file mode 100644 index 00000000..b7457c41 --- /dev/null +++ b/Library/OcCryptoLib/RsaDigitalSign.c @@ -0,0 +1,614 @@ +/** @file + This library performs RSA-based cryptography operations. + + SECURITY: Currently, no security measures have been taken. This code is + vulnerable to both timing and side channel attacks for value + leakage. However, its current purpose is the verification of public + binaries with public certificates, for which this is perfectly + acceptable, especially in regards to performance. + + Copyright (C) 2019, Download-Fritz. All rights reserved. + +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "BigNumLib.h" + +// +// RFC 3447, 9.2 EMSA-PKCS1-v1_5, Notes 1. +// +STATIC CONST UINT8 mPkcsDigestEncodingPrefixSha256[] = { + 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, + 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 +}; + +STATIC CONST UINT8 mPkcsDigestEncodingPrefixSha384[] = { + 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, + 0x02, 0x02, 0x05, 0x00, 0x04, 0x30 +}; + +STATIC CONST UINT8 mPkcsDigestEncodingPrefixSha512[] = { + 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, + 0x02, 0x03, 0x05, 0x00, 0x04, 0x40 +}; + +/** + Returns whether the RSA modulus size is allowed. + + @param[in] ModulusSize The size, in bytes, of the RSA modulus. + +**/ +STATIC +BOOLEAN +InternalRsaModulusSizeIsAllowed ( + IN UINT16 ModulusSize + ) +{ + // + // Verify ModulusSize is a two's potency. + // + if ((ModulusSize & (ModulusSize - 1U)) != 0) { + return FALSE; + } + + return (PcdGet16 (PcdOcCryptoAllowedRsaModuli) & ModulusSize) != 0; +} + +/** + Returns whether the signature hashing algorithm is allowed. + + @param[in] Type The signature hashing algorithm type. + +**/ +STATIC +BOOLEAN +InternalSigHashTypeIsAllowed ( + IN OC_SIG_HASH_TYPE Type + ) +{ + return (PcdGet16 (PcdOcCryptoAllowedSigHashTypes) & (1U << Type)) != 0; +} + +INTN +SigVerifyShaHashBySize ( + IN CONST VOID *Data, + IN UINTN DataSize, + IN CONST UINT8 *Hash, + IN UINTN HashSize + ) +{ + OC_SIG_HASH_TYPE Hashtype; + UINT8 DataDigest[OC_MAX_SHA_DIGEST_SIZE]; + + ASSERT (Data != NULL); + ASSERT (DataSize > 0); + ASSERT (Hash != NULL); + ASSERT (HashSize > 0); + ASSERT (HashSize <= sizeof (DataDigest)); + + switch (HashSize) { + case SHA512_DIGEST_SIZE: + { + Hashtype = OcSigHashTypeSha512; + Sha512 (DataDigest, Data, DataSize); + break; + } + + case SHA384_DIGEST_SIZE: + { + Hashtype = OcSigHashTypeSha384; + Sha384 (DataDigest, Data, DataSize); + break; + } + + case SHA256_DIGEST_SIZE: + { + Hashtype = OcSigHashTypeSha256; + Sha256 (DataDigest, Data, DataSize); + break; + } + + default: + { + return -1; + } + } + + if (!InternalSigHashTypeIsAllowed (Hashtype)) { + return -1; + } + + return CompareMem (DataDigest, Hash, HashSize); +} + +/** + Verify a RSA PKCS1.5 signature against an expected hash. + + @param[in] N The RSA modulus. + @param[in] N0Inv The Montgomery Inverse of N. + @param[in] RSqrMod Montgomery's R^2 mod N. + @param[in] NumWords The number of Words of N and RSqrMod. + @param[in] Exponent The RSA exponent. + @param[in] Signature The RSA signature to be verified. + @param[in] SignatureSize Size, in bytes, of Signature. + @param[in] Hash The Hash digest of the signed data. + @param[in] HashSize Size, in bytes, of Hash. + @param[in] Algorithm The RSA algorithm used. + + @returns Whether the signature has been successfully verified as valid. + +**/ +STATIC +BOOLEAN +RsaVerifySigHashFromProcessed ( + IN CONST OC_BN_WORD *N, + IN UINTN NumWords, + IN OC_BN_WORD N0Inv, + IN CONST OC_BN_WORD *RSqrMod, + IN UINT32 Exponent, + IN CONST UINT8 *Signature, + IN UINTN SignatureSize, + IN CONST UINT8 *Hash, + IN UINTN HashSize, + IN OC_SIG_HASH_TYPE Algorithm + ) +{ + BOOLEAN Result; + INTN CmpResult; + + UINTN ModulusSize; + + VOID *Memory; + OC_BN_WORD *EncryptedSigNum; + OC_BN_WORD *DecryptedSigNum; + + CONST UINT8 *Padding; + UINTN PaddingSize; + UINTN DigestSize; + UINTN Index; + + OC_BN_WORD Tmp; + + ASSERT (N != NULL); + ASSERT (NumWords > 0); + ASSERT (RSqrMod != NULL); + ASSERT (Signature != NULL); + ASSERT (SignatureSize > 0); + ASSERT (Hash != NULL); + ASSERT (HashSize > 0); + + OC_STATIC_ASSERT ( + OcSigHashTypeSha512 == OcSigHashTypeMax - 1, + "New switch cases have to be added for every introduced algorithm." + ); + + if (NumWords > OC_BN_MAX_LEN) { + return FALSE; + } + + if (!InternalSigHashTypeIsAllowed (Algorithm)) { + return FALSE; + } + + switch (Algorithm) { + case OcSigHashTypeSha256: + { + ASSERT (HashSize == SHA256_DIGEST_SIZE); + + Padding = mPkcsDigestEncodingPrefixSha256; + PaddingSize = sizeof (mPkcsDigestEncodingPrefixSha256); + break; + } + + case OcSigHashTypeSha384: + { + ASSERT (HashSize == SHA384_DIGEST_SIZE); + + Padding = mPkcsDigestEncodingPrefixSha384; + PaddingSize = sizeof (mPkcsDigestEncodingPrefixSha384); + break; + } + + case OcSigHashTypeSha512: + { + ASSERT (HashSize == SHA512_DIGEST_SIZE); + + Padding = mPkcsDigestEncodingPrefixSha512; + PaddingSize = sizeof (mPkcsDigestEncodingPrefixSha512); + break; + } + + default: + { + ASSERT (FALSE); + } + } + // + // Verify the Signature size matches the Modulus size. + // This implicitly verifies it's a multiple of the Word size. + // + ModulusSize = NumWords * OC_BN_WORD_SIZE; + if (!InternalRsaModulusSizeIsAllowed (ModulusSize)) { + return FALSE; + } + + if (SignatureSize != ModulusSize) { + DEBUG ((DEBUG_INFO, "OCCR: Signature length does not match key length")); + return FALSE; + } + + Memory = AllocatePool (2 * ModulusSize); + if (Memory == NULL) { + DEBUG ((DEBUG_INFO, "OCCR: Memory allocation failure\n")); + return FALSE; + } + + EncryptedSigNum = Memory; + DecryptedSigNum = (OC_BN_WORD *)((UINTN)EncryptedSigNum + ModulusSize); + + BigNumParseBuffer ( + EncryptedSigNum, + (OC_BN_NUM_WORDS)NumWords, + Signature, + SignatureSize + ); + + Result = BigNumPowMod ( + DecryptedSigNum, + (OC_BN_NUM_WORDS)NumWords, + EncryptedSigNum, + Exponent, + N, + N0Inv, + RSqrMod + ); + if (!Result) { + FreePool (Memory); + return FALSE; + } + // + // Convert the result to a big-endian byte array. + // Re-use EncryptedSigNum as it is not required anymore. + // FIXME: Doing this as part of the comparison could speed up the process + // and clean up the code. + // + Index = NumWords; + while (Index > 0) { + --Index; + Tmp = BigNumSwapWord ( + DecryptedSigNum[NumWords - 1 - Index] + ); + EncryptedSigNum[Index] = Tmp; + } + Signature = (UINT8 *)EncryptedSigNum; + + // + // From RFC 3447, 9.2 EMSA-PKCS1-v1_5: + // + // 5. Concatenate PS, the DER encoding T, and other padding to form the + // encoded message EM as + // + // EM = 0x00 || 0x01 || PS || 0x00 || T. + // + + // + // 3. If emLen < tLen + 11, output "intended encoded message length too + // short" and stop. + // + // The additions cannot overflow because both PaddingSize and HashSize are + // sane at this point. + // + DigestSize = PaddingSize + HashSize; + if (SignatureSize < DigestSize + 11) { + FreePool (Memory); + return FALSE; + } + + if (Signature[0] != 0x00 || Signature[1] != 0x01) { + FreePool (Memory); + return FALSE; + } + // + // 4. Generate an octet string PS consisting of emLen - tLen - 3 octets with + // hexadecimal value 0xff. The length of PS will be at least 8 octets. + // + // The additions and subtractions cannot overflow as per 3. + // + for (Index = 2; Index < SignatureSize - DigestSize - 3 + 2; ++Index) { + if (Signature[Index] != 0xFF) { + FreePool (Memory); + return FALSE; + } + } + + if (Signature[Index] != 0x00) { + FreePool (Memory); + return FALSE; + } + + ++Index; + + CmpResult = CompareMem (&Signature[Index], Padding, PaddingSize); + if (CmpResult != 0) { + FreePool (Memory); + return FALSE; + } + + Index += PaddingSize; + + CmpResult = CompareMem (&Signature[Index], Hash, HashSize); + if (CmpResult != 0) { + FreePool (Memory); + return FALSE; + } + // + // The code above must have covered the entire Signature range. + // + ASSERT (Index + HashSize == SignatureSize); + + FreePool (Memory); + return TRUE; +} + +/** + Verify RSA PKCS1.5 signed data against its signature. + The modulus' size must be a multiple of the configured BIGNUM word size. + This will be true for any conventional RSA, which use two's potencies. + + @param[in] N The RSA modulus. + @param[in] NumWords The number of Words of N and RSqrMod. + @param[in] N0Inv The Montgomery Inverse of N. + @param[in] RSqrMod Montgomery's R^2 mod N. + @param[in] Exponent The RSA exponent. + @param[in] Signature The RSA signature to be verified. + @param[in] SignatureSize Size, in bytes, of Signature. + @param[in] Data The signed data to verify. + @param[in] DataSize Size, in bytes, of Data. + @param[in] Algorithm The RSA algorithm used. + + @returns Whether the signature has been successfully verified as valid. + +**/ +STATIC +BOOLEAN +RsaVerifySigDataFromProcessed ( + IN CONST OC_BN_WORD *N, + IN UINTN NumWords, + IN OC_BN_WORD N0Inv, + IN CONST OC_BN_WORD *RSqrMod, + IN UINT32 Exponent, + IN CONST UINT8 *Signature, + IN UINTN SignatureSize, + IN CONST UINT8 *Data, + IN UINTN DataSize, + IN OC_SIG_HASH_TYPE Algorithm + ) +{ + UINT8 Hash[OC_MAX_SHA_DIGEST_SIZE]; + UINTN HashSize; + + ASSERT (N != NULL); + ASSERT (NumWords > 0); + ASSERT (RSqrMod != NULL); + ASSERT (Exponent > 0); + ASSERT (Signature != NULL); + ASSERT (SignatureSize > 0); + ASSERT (Data != NULL); + ASSERT (DataSize > 0); + + OC_STATIC_ASSERT ( + OcSigHashTypeSha512 == OcSigHashTypeMax - 1, + "New switch cases have to be added for every introduced algorithm." + ); + + switch (Algorithm) { + case OcSigHashTypeSha256: + { + Sha256 (Hash, Data, DataSize); + HashSize = SHA256_DIGEST_SIZE; + break; + } + + case OcSigHashTypeSha384: + { + Sha384 (Hash, Data, DataSize); + HashSize = SHA384_DIGEST_SIZE; + break; + } + + case OcSigHashTypeSha512: + { + Sha512 (Hash, Data, DataSize); + HashSize = SHA512_DIGEST_SIZE; + break; + } + + default: + { + // + // New switch cases have to be added for every introduced algorithm. + // + ASSERT (FALSE); + return FALSE; + } + } + + return RsaVerifySigHashFromProcessed ( + N, + NumWords, + N0Inv, + RSqrMod, + Exponent, + Signature, + SignatureSize, + Hash, + HashSize, + Algorithm + ); +} + +BOOLEAN +RsaVerifySigDataFromData ( + IN CONST UINT8 *Modulus, + IN UINTN ModulusSize, + IN UINT32 Exponent, + IN CONST UINT8 *Signature, + IN UINTN SignatureSize, + IN CONST UINT8 *Data, + IN UINTN DataSize, + IN OC_SIG_HASH_TYPE Algorithm + ) +{ + UINTN ModulusNumWords; + + VOID *Memory; + OC_BN_WORD *N; + OC_BN_WORD *RSqrMod; + + OC_BN_WORD N0Inv; + BOOLEAN Result; + + ASSERT (Modulus != NULL); + ASSERT (ModulusSize > 0); + ASSERT (Exponent > 0); + ASSERT (Signature != NULL); + ASSERT (SignatureSize > 0); + ASSERT (Data != NULL); + ASSERT (DataSize > 0); + + ModulusNumWords = ModulusSize / OC_BN_WORD_SIZE; + if (ModulusNumWords > OC_BN_MAX_LEN + || (ModulusSize % OC_BN_WORD_SIZE) != 0) { + return FALSE; + } + + OC_STATIC_ASSERT ( + OC_BN_MAX_SIZE <= MAX_UINTN / 2, + "An overflow verification must be added" + ); + + Memory = AllocatePool (2 * ModulusSize); + if (Memory == NULL) { + return FALSE; + } + + N = (OC_BN_WORD *)Memory; + RSqrMod = (OC_BN_WORD *)((UINTN)N + ModulusSize); + + BigNumParseBuffer (N, ModulusNumWords, Modulus, ModulusSize); + + N0Inv = BigNumCalculateMontParams (RSqrMod, ModulusNumWords, N); + if (N0Inv == 0) { + FreePool (Memory); + return FALSE; + } + + Result = RsaVerifySigDataFromProcessed ( + N, + ModulusNumWords, + N0Inv, + RSqrMod, + Exponent, + Signature, + SignatureSize, + Data, + DataSize, + Algorithm + ); + + FreePool (Memory); + return Result; +} + +BOOLEAN +RsaVerifySigHashFromKey ( + IN CONST OC_RSA_PUBLIC_KEY *Key, + IN CONST UINT8 *Signature, + IN UINTN SignatureSize, + IN CONST UINT8 *Hash, + IN UINTN HashSize, + IN OC_SIG_HASH_TYPE Algorithm + ) +{ + ASSERT (Key != NULL); + ASSERT (Signature != NULL); + ASSERT (SignatureSize > 0); + ASSERT (Hash != NULL); + ASSERT (HashSize > 0); + + OC_STATIC_ASSERT ( + OC_BN_WORD_SIZE <= 8, + "The parentheses need to be changed to avoid truncation." + ); + // + // When OC_BN_WORD is not UINT64, this violates the strict aliasing rule. + // However, due to packed-ness and byte order, this is perfectly safe. + // + return RsaVerifySigHashFromProcessed ( + (OC_BN_WORD *)Key->Data, + Key->Hdr.NumQwords * (8 / OC_BN_WORD_SIZE), + (OC_BN_WORD)Key->Hdr.N0Inv, + (OC_BN_WORD *)&Key->Data[Key->Hdr.NumQwords], + 0x10001, + Signature, + SignatureSize, + Hash, + HashSize, + Algorithm + ); +} + +BOOLEAN +RsaVerifySigDataFromKey ( + IN CONST OC_RSA_PUBLIC_KEY *Key, + IN CONST UINT8 *Signature, + IN UINTN SignatureSize, + IN CONST UINT8 *Data, + IN UINTN DataSize, + IN OC_SIG_HASH_TYPE Algorithm + ) +{ + ASSERT (Key != NULL); + ASSERT (Signature != NULL); + ASSERT (SignatureSize > 0); + ASSERT (Data != NULL); + ASSERT (DataSize > 0); + + OC_STATIC_ASSERT ( + OC_BN_WORD_SIZE <= 8, + "The parentheses need to be changed to avoid truncation." + ); + // + // When OC_BN_WORD is not UINT64, this violates the strict aliasing rule. + // However, due to packed-ness and byte order, this is perfectly safe. + // + return RsaVerifySigDataFromProcessed ( + (OC_BN_WORD *)Key->Data, + Key->Hdr.NumQwords * (8 / OC_BN_WORD_SIZE), + (OC_BN_WORD)Key->Hdr.N0Inv, + (OC_BN_WORD *)&Key->Data[Key->Hdr.NumQwords], + 0x10001, + Signature, + SignatureSize, + Data, + DataSize, + Algorithm + ); +} diff --git a/Library/OcCryptoLib/X64/BigNumWordMul64.c b/Library/OcCryptoLib/X64/BigNumWordMul64.c new file mode 100644 index 00000000..1e6a7a02 --- /dev/null +++ b/Library/OcCryptoLib/X64/BigNumWordMul64.c @@ -0,0 +1,49 @@ +/** @file + Copyright (C) 2019, Download-Fritz. All rights reserved. + +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ +#include + +#include +#include + +#include "../BigNumLib.h" + +#if defined(_MSC_VER) && !defined(__clang__) + #include + #pragma intrinsic(_umul128) +#endif + +OC_BN_WORD +BigNumWordMul64 ( + OUT OC_BN_WORD *Hi, + IN OC_BN_WORD A, + IN OC_BN_WORD B + ) +{ + ASSERT (OC_BN_WORD_SIZE == sizeof (UINT64)); + ASSERT (Hi != NULL); + +#if !defined(_MSC_VER) || defined(__clang__) + // + // Clang and GCC support the __int128 type for edk2 builds. + // + unsigned __int128 Result = (unsigned __int128)A * B; + *Hi = (OC_BN_WORD)(Result >> OC_BN_WORD_NUM_BITS); + return (OC_BN_WORD)Result; +#else + // + // MSVC does not support the __int128 type for edk2 builds, but a proprietary + // intrinsic function declared above. + // + return _umul128 (A, B, Hi); +#endif +} diff --git a/Library/OcStorageLib/OcStorageLib.c b/Library/OcStorageLib/OcStorageLib.c index 10c80b25..70a6077d 100644 --- a/Library/OcStorageLib/OcStorageLib.c +++ b/Library/OcStorageLib/OcStorageLib.c @@ -123,14 +123,13 @@ STATIC EFI_STATUS OcStorageInitializeVault ( IN OUT OC_STORAGE_CONTEXT *Context, - IN VOID *Vault OPTIONAL, + IN VOID *Vault OPTIONAL, IN UINT32 VaultSize, - IN RSA_PUBLIC_KEY *StorageKey OPTIONAL, - IN VOID *Signature OPTIONAL + IN OC_RSA_PUBLIC_KEY *StorageKey OPTIONAL, + IN VOID *Signature OPTIONAL, + IN UINT32 SignatureSize OPTIONAL ) { - UINT8 Digest[SHA256_DIGEST_SIZE]; - if (Signature != NULL && Vault == NULL) { DEBUG ((DEBUG_ERROR, "OCS: Missing vault with signature\n")); return EFI_SECURITY_VIOLATION; @@ -144,9 +143,7 @@ OcStorageInitializeVault ( if (Signature != NULL) { ASSERT (StorageKey != NULL); - Sha256 (Digest, Vault, VaultSize); - - if (!RsaVerify (StorageKey, Signature, Digest)) { + if (!RsaVerifySigDataFromKey (StorageKey, Signature, SignatureSize, Vault, VaultSize, OcSigHashTypeSha256)) { DEBUG ((DEBUG_ERROR, "OCS: Invalid vault signature\n")); return EFI_SECURITY_VIOLATION; } @@ -219,7 +216,7 @@ OcStorageInitFromFs ( OUT OC_STORAGE_CONTEXT *Context, IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FileSystem, IN CONST CHAR16 *Path, - IN RSA_PUBLIC_KEY *StorageKey OPTIONAL + IN OC_RSA_PUBLIC_KEY *StorageKey OPTIONAL ) { EFI_STATUS Status; @@ -227,6 +224,7 @@ OcStorageInitFromFs ( VOID *Vault; VOID *Signature; UINT32 DataSize; + UINT32 SignatureSize; ZeroMem (Context, sizeof (*Context)); @@ -253,11 +251,13 @@ OcStorageInitFromFs ( return Status; } + SignatureSize = 0; + if (StorageKey) { Signature = OcStorageReadFileUnicode ( Context, OC_STORAGE_VAULT_SIGNATURE_PATH, - &DataSize + &SignatureSize ); if (Signature == NULL) { @@ -265,18 +265,6 @@ OcStorageInitFromFs ( OcStorageFree (Context); return EFI_SECURITY_VIOLATION; } - - if (DataSize != CONFIG_RSA_KEY_SIZE) { - DEBUG (( - DEBUG_ERROR, - "OCS: Vault signature size mismatch: %u vs %u\n", - DataSize, - CONFIG_RSA_KEY_SIZE - )); - FreePool (Signature); - OcStorageFree (Context); - return EFI_SECURITY_VIOLATION; - } } else { Signature = NULL; } @@ -288,7 +276,7 @@ OcStorageInitFromFs ( &DataSize ); - Status = OcStorageInitializeVault (Context, Vault, DataSize, StorageKey, Signature); + Status = OcStorageInitializeVault (Context, Vault, DataSize, StorageKey, Signature, SignatureSize); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_INFO, "OCS: Vault init failure %p (%u) - %r\n", Vault, DataSize, Status)); diff --git a/OcSupportPkg.dec b/OcSupportPkg.dec index 0be863a8..17391e99 100644 --- a/OcSupportPkg.dec +++ b/OcSupportPkg.dec @@ -80,6 +80,18 @@ # @Prompt Initialize the console to the specified mode on entry. gOcSupportPkgTokenSpaceGuid.PcdConsoleControlEntryMode|0|UINT8|0x00000100 + ## Defines the allowed OcCryptoLib RSA moduli by OR'ing the two's potencies in bytes.

+ # @Prompt Allow these RSA moduli for cryptographic usage. + gOcSupportPkgTokenSpaceGuid.PcdOcCryptoAllowedRsaModuli|0x300|UINT16|0x00000500 + + ## Defines the allowed OcCryptoLib signature hashing algorihtms by OR'ing the + # types' Bit indices.

+ # Bit 0 - OcSigHashTypeSha256 + # Bit 1 - OcSigHashTypeSha384 + # Bit 2 - OcSigHashTypeSha512 + # @Prompt Allow these signature hashing algorithms for cryptographic usage. + gOcSupportPkgTokenSpaceGuid.PcdOcCryptoAllowedSigHashTypes|0x07|UINT16|0x00000501 + [LibraryClasses] ## @libraryclass OcAcpiLib|Include/Library/OcAcpiLib.h @@ -105,6 +117,9 @@ ## @libraryclass OcAppleImageVerificationLib|Include/Library/OcAppleImageVerificationLib.h + ## @libraryclass + OcAppleImg4Lib|Include/Library/OcAppleImg4Lib.h + ## @libraryclass OcAppleKernelLib|Include/Library/OcAppleKernelLib.h diff --git a/Tests/CryptoTest/CryptoTest.c b/Tests/CryptoTest/CryptoTest.c index 15161604..09fb26c6 100644 --- a/Tests/CryptoTest/CryptoTest.c +++ b/Tests/CryptoTest/CryptoTest.c @@ -47,10 +47,13 @@ TestRsa2048Sha256Verify ( SIGNED_DATA_LEN ); - SignatureVerified = RsaVerify ( - (RSA_PUBLIC_KEY *) Rsa2048Sha256Sample.PublicKey, + SignatureVerified = RsaVerifySigHashFromKey ( + (OC_RSA_PUBLIC_KEY *) Rsa2048Sha256Sample.PublicKey, Rsa2048Sha256Sample.Signature, - DataSha256Hash + sizeof (Rsa2048Sha256Sample.Signature), + DataSha256Hash, + sizeof (DataSha256Hash), + OcSigHashTypeSha256 ); if (SignatureVerified) { diff --git a/TestsUser/DiskImage/DiskImage.c b/TestsUser/DiskImage/DiskImage.c index 86d6855c..d5fbe0fd 100644 --- a/TestsUser/DiskImage/DiskImage.c +++ b/TestsUser/DiskImage/DiskImage.c @@ -15,6 +15,8 @@ rm -rf DICT fuzz*.log ; mkdir DICT ; UBSAN_OPTIONS='halt_on_error=1' ./DiskImage **/ +EFI_GUID gOcVendorVariableGuid; + uint8_t *readFile(const char *str, long *size) { FILE *f = fopen(str, "rb"); @@ -116,7 +118,7 @@ int main (int argc, char *argv[]) { goto ContinueDmgLoop; } - Result = OcAppleChunklistVerifySignature (&ChunklistContext, (RSA_PUBLIC_KEY *)PkDataBase[0].PublicKey); + Result = OcAppleChunklistVerifySignature (&ChunklistContext, PkDataBase[0].PublicKey); if (!Result) { printf ("Chunklist signature verification error\n"); goto ContinueDmgLoop; diff --git a/TestsUser/Img4/Img4.c b/TestsUser/Img4/Img4.c new file mode 100644 index 00000000..62aa920a --- /dev/null +++ b/TestsUser/Img4/Img4.c @@ -0,0 +1,172 @@ +#include +#include +#include + +#include + +#include +#include + +#include "libDER/oids.h" +#include "libDERImg4/Img4oids.h" +#include "libDERImg4/libDERImg4.h" + +#ifdef FUZZING_TEST +#define main no_main +#endif + +EFI_GUID gAppleSecureBootVariableGuid; + +void InternalDebugEnvInfo ( + const DERImg4Environment *Env + ) +{ + printf ( + "\nEcid: %llx\n" + "BoardId: %x\n" + "ChipId: %x\n" + "CertificateEpoch: %x\n" + "SecurityDomain: %x\n" + "ProductionStatus: %x\n" + "SecurityMode: %x\n" + "EffectiveProductionStatus: %x\n" + "EffectiveSecurityMode:%x \n" + "InternalUseOnlyUnit: %x\n" + "Xugs: %x\n\n", + (unsigned long long)Env->ecid, + Env->boardId, + Env->chipId, + Env->certificateEpoch, + Env->securityDomain, + Env->productionStatus, + Env->securityMode, + Env->effectiveProductionStatus, + Env->effectiveSecurityMode, + Env->internalUseOnlyUnit, + Env->xugs + ); +} + +uint8_t *readFile(const char *str, uint32_t *size) { + FILE *f = fopen(str, "rb"); + + if (!f) return NULL; + + fseek(f, 0, SEEK_END); + long fsize = ftell(f); + fseek(f, 0, SEEK_SET); + + uint8_t *string = malloc(fsize); + fread(string, fsize, 1, f); + fclose(f); + *size = fsize; + + return string; +} + +int verifyImg4 (char *imageName, char *manifestName, char *type) +{ + void *Manifest, *Image; + uint32_t ManSize, ImgSize; + DERImg4ManifestInfo ManInfo; + + Manifest = readFile (manifestName, &ManSize); + if (Manifest == NULL) { + printf ("\n!!! read error !!!\n"); + return -1; + } + + DERReturn r = DERImg4ParseManifest ( + &ManInfo, + Manifest, + ManSize, + SIGNATURE_32 (type[3], type[2], type[1], type[0]) + ); + free (Manifest); + if (r != DR_Success) { + printf ("\n !!! DERImg4ParseManifest failed - %d !!!\n", r); + return -1; + } + + InternalDebugEnvInfo (&ManInfo.environment); + + Image = readFile (imageName, &ImgSize); + if (Image == NULL) { + printf ("\n!!! read error !!!\n"); + return -1; + } + + INTN CmpResult = SigVerifyShaHashBySize ( + Image, + ImgSize, + ManInfo.imageDigest, + ManInfo.imageDigestSize + ); + + free (Image); + + if (CmpResult != 0) { + printf ("\n!!! digest mismatch !!!\n"); + return -1; + } + + return 0; +} + +int main (int argc, char *argv[]) +{ + if (argc < 2 || (argc % 3) != 1) { + printf ("Img4 ([image path] [manifest path] [object type])*\n"); + return -1; + } + + int r = 0; + for (int i = 1; i < (argc - 1); i += 3) { + if (strlen (argv[i + 2]) != 4) { + printf ("Object types require exactly 4 characters.\n"); + return -1; + } + + r = verifyImg4 ( + argv[i + 0], + argv[i + 1], + argv[i + 2] + ); + if (r != 0) { + return r; + } + } + + return r; +} + +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + const uint32_t signatures[] = { + APPLE_SB_OBJ_EFIBOOT, + APPLE_SB_OBJ_EFIBOOT_DEBUG, + APPLE_SB_OBJ_EFIBOOT_BASE, + APPLE_SB_OBJ_MUPD, + APPLE_SB_OBJ_HPMU, + APPLE_SB_OBJ_THOU, + APPLE_SB_OBJ_GPUU, + APPLE_SB_OBJ_ETHU, + APPLE_SB_OBJ_SDFU, + APPLE_SB_OBJ_DTHU + }; + + if (Data == NULL || Size == 0) { + return 0; + } + + DERImg4ManifestInfo ManInfo; + for (unsigned int i = 0; i < ARRAY_SIZE (signatures); ++i) { + DERImg4ParseManifest ( + &ManInfo, + Data, + Size, + signatures[i] + ); + } + + return 0; +} diff --git a/TestsUser/Include/Base.h b/TestsUser/Include/Base.h index f9bd41b3..72bce708 100644 --- a/TestsUser/Include/Base.h +++ b/TestsUser/Include/Base.h @@ -65,8 +65,6 @@ typedef char CHAR8; typedef unsigned short CHAR16; typedef signed char INT8; typedef unsigned char UINT8; -typedef ssize_t INTN; -typedef size_t UINTN; typedef bool BOOLEAN; typedef int32_t INT32; typedef uint32_t UINT32; @@ -74,6 +72,12 @@ typedef int64_t INT64; typedef uint64_t UINT64; typedef int16_t INT16; typedef uint16_t UINT16; +#if SIZE_MAX == UINT64_MAX +typedef INT64 INTN; +#elif SIZE_MAX == UINT32_MAX +typedef INT32 INTN; +#endif +typedef size_t UINTN; typedef size_t EFI_STATUS; typedef UINTN RETURN_STATUS; typedef UINT64 EFI_PHYSICAL_ADDRESS; @@ -1884,4 +1888,7 @@ GetCurrentMemoryMap ( return NULL; } +#define _PCD_GET_MODE_16_PcdOcCryptoAllowedSigHashTypes (1U | 2U | 4U) // SHA256, SHA384, SHA512 +#define _PCD_GET_MODE_16_PcdOcCryptoAllowedRsaModuli (512U | 256U) + #endif diff --git a/TestsUser/RsaPreprocess/RsaPreprocess.c b/TestsUser/RsaPreprocess/RsaPreprocess.c new file mode 100644 index 00000000..2885f77b --- /dev/null +++ b/TestsUser/RsaPreprocess/RsaPreprocess.c @@ -0,0 +1,79 @@ +#include + +#include +#include + +#include + +uint8_t *readFile(const char *str, uint32_t *size) { + FILE *f = fopen(str, "rb"); + + if (!f) return NULL; + + fseek(f, 0, SEEK_END); + long fsize = ftell(f); + fseek(f, 0, SEEK_SET); + + uint8_t *string = malloc(fsize); + fread(string, fsize, 1, f); + fclose(f); + + *size = fsize; + + return string; +} + +int verifyRsa (CONST OC_RSA_PUBLIC_KEY *PublicKey, char *Name) +{ + OC_BN_WORD N0Inv; + UINTN ModulusSize = PublicKey->Hdr.NumQwords * sizeof (UINT64); + + OC_BN_WORD *RSqrMod = malloc(ModulusSize); + if (RSqrMod == NULL) { + printf ("memory allocation error!\n"); + return -1; + } + + N0Inv = BigNumCalculateMontParams ( + RSqrMod, + ModulusSize / OC_BN_WORD_SIZE, + PublicKey->Data + ); + + printf ( + "%s: results: %d %d\n", + Name, + memcmp ( + RSqrMod, + &PublicKey->Data[PublicKey->Hdr.NumQwords], + ModulusSize + ), + N0Inv != PublicKey->Hdr.N0Inv + ); + + free(RSqrMod); + return 0; +} + +int main(int argc, char *argv[]) { + unsigned int Index; + OC_RSA_PUBLIC_KEY *PublicKey; + uint32_t PkSize; + + for (Index = 1; Index < argc; ++Index) { + PublicKey = (OC_RSA_PUBLIC_KEY *)readFile (argv[Index], &PkSize); + if (PublicKey == NULL) { + printf ("read error\n"); + return -1; + } + + verifyRsa (PublicKey, argv[Index]); + free (PublicKey); + } + + for (Index = 0; Index < ARRAY_SIZE (PkDataBase); ++Index) { + verifyRsa (PkDataBase[Index].PublicKey, "inbuilt"); + } + + return 0; +} diff --git a/Utilities/AppleEfiSignTool/AppleEfiBinary.c b/Utilities/AppleEfiSignTool/AppleEfiBinary.c index 9c71363a..ea50e074 100644 --- a/Utilities/AppleEfiSignTool/AppleEfiBinary.c +++ b/Utilities/AppleEfiSignTool/AppleEfiBinary.c @@ -18,9 +18,10 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include "AppleEfiPeImage.h" #include "AppleEfiFatBinary.h" +#include #include -#ifdef DEBUG +#ifndef NDEBUG # define DEBUG_PRINT(x) printf x #else # define DEBUG_PRINT(x) do {} while (0) @@ -428,7 +429,7 @@ VerifyApplePeImageSignature ( uint8_t SigBe[256]; uint8_t CalcucatedHash[32]; uint8_t PkHash[32]; - RSA_PUBLIC_KEY *Pk = NULL; + const OC_RSA_PUBLIC_KEY *Pk = NULL; APPLE_PE_COFF_LOADER_IMAGE_CONTEXT *Context = NULL; Context = malloc (sizeof (APPLE_PE_COFF_LOADER_IMAGE_CONTEXT)); @@ -479,7 +480,7 @@ VerifyApplePeImageSignature ( // // PublicKey valid. Extract prepared publickey from database // - Pk = (RSA_PUBLIC_KEY *) PkDataBase[Index].PublicKey; + Pk = PkDataBase[Index].PublicKey; } } @@ -491,7 +492,7 @@ VerifyApplePeImageSignature ( // // Verify signature // - if (RsaVerify (Pk, SigBe, CalcucatedHash) == 1 ) { + if (RsaVerifySigHashFromKey (Pk, SigBe, sizeof (SigBe), CalcucatedHash, sizeof (CalcucatedHash), OcSigHashTypeSha256) == 1 ) { puts ("Signature verified!\n"); return 0; } diff --git a/Utilities/AppleEfiSignTool/Edk2PeImage.h b/Utilities/AppleEfiSignTool/Edk2PeImage.h index 92b9e55f..6d1bb709 100644 --- a/Utilities/AppleEfiSignTool/Edk2PeImage.h +++ b/Utilities/AppleEfiSignTool/Edk2PeImage.h @@ -68,13 +68,6 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #define EFI_IMAGE_OS2_SIGNATURE_LE SIGNATURE_16('L', 'E') #define EFI_IMAGE_NT_SIGNATURE SIGNATURE_32('P', 'E', '\0', '\0') -typedef struct { - uint32_t Data1; - uint16_t Data2; - uint16_t Data3; - uint8_t Data4[8]; -} EFI_GUID; - /// /// PE images can start with an optional DOS header, so if an image is run /// under DOS it can print an error message. diff --git a/Utilities/AppleEfiSignTool/IncludeDummy/Base.h b/Utilities/AppleEfiSignTool/IncludeDummy/Base.h new file mode 100644 index 00000000..e69de29b diff --git a/Utilities/AppleEfiSignTool/IncludeDummy/Library/BaseLib.h b/Utilities/AppleEfiSignTool/IncludeDummy/Library/BaseLib.h new file mode 100644 index 00000000..e69de29b diff --git a/Utilities/AppleEfiSignTool/IncludeDummy/Library/BaseMemoryLib.h b/Utilities/AppleEfiSignTool/IncludeDummy/Library/BaseMemoryLib.h new file mode 100644 index 00000000..e69de29b diff --git a/Utilities/AppleEfiSignTool/IncludeDummy/Library/DebugLib.h b/Utilities/AppleEfiSignTool/IncludeDummy/Library/DebugLib.h new file mode 100644 index 00000000..e69de29b diff --git a/Utilities/AppleEfiSignTool/IncludeDummy/Library/MemoryAllocationLib.h b/Utilities/AppleEfiSignTool/IncludeDummy/Library/MemoryAllocationLib.h new file mode 100644 index 00000000..e69de29b diff --git a/Utilities/AppleEfiSignTool/IncludeDummy/Library/PcdLib.h b/Utilities/AppleEfiSignTool/IncludeDummy/Library/PcdLib.h new file mode 100644 index 00000000..e69de29b diff --git a/Utilities/AppleEfiSignTool/IncludeDummy/Uefi.h b/Utilities/AppleEfiSignTool/IncludeDummy/Uefi.h new file mode 100644 index 00000000..e69de29b diff --git a/Utilities/AppleEfiSignTool/Makefile b/Utilities/AppleEfiSignTool/Makefile index 5e3d2a85..158325d5 100644 --- a/Utilities/AppleEfiSignTool/Makefile +++ b/Utilities/AppleEfiSignTool/Makefile @@ -1,6 +1,6 @@ CC ?= gcc -CFLAGS=-c -Wall -Wextra -pedantic -O3 -DDEBUG -I../../Include -include UefiCompat.h -OBJS=AppleEfiBinary.o Sha2.o Rsa2048Sha256.o OcAppleKeysLib.o main.o +CFLAGS=-c -Wall -Wextra -pedantic -O3 -I../../Include -IIncludeDummy -include UefiCompat.h +OBJS=AppleEfiBinary.o Sha2.o BigNumWordMul64.o BigNumPrimitives.o BigNumMontgomery.o RsaDigitalSign.o OcAppleKeysLib.o main.o all: AppleEfiSignTool @@ -10,8 +10,17 @@ AppleEfiSignTool: $(OBJS) Sha2.o: $(CC) $(CFLAGS) ../../Library/OcCryptoLib/Sha2.c -o $@ -Rsa2048Sha256.o: - $(CC) $(CFLAGS) ../../Library/OcCryptoLib/Rsa2048Sha256.c -o $@ +BigNumWordMul64.o: + $(CC) $(CFLAGS) ../../Library/OcCryptoLib/IA32/BigNumWordMul64.c -o $@ + +BigNumPrimitives.o: + $(CC) $(CFLAGS) ../../Library/OcCryptoLib/BigNumPrimitives.c -o $@ + +BigNumMontgomery.o: + $(CC) $(CFLAGS) ../../Library/OcCryptoLib/BigNumMontgomery.c -o $@ + +RsaDigitalSign.o: + $(CC) $(CFLAGS) -include OcCryptoConfig.h ../../Library/OcCryptoLib/RsaDigitalSign.c -o $@ OcAppleKeysLib.o: $(CC) $(CFLAGS) ../../Library/OcAppleKeysLib/OcAppleKeysLib.c -o $@ diff --git a/Utilities/AppleEfiSignTool/OcCryptoConfig.h b/Utilities/AppleEfiSignTool/OcCryptoConfig.h new file mode 100644 index 00000000..90cdd350 --- /dev/null +++ b/Utilities/AppleEfiSignTool/OcCryptoConfig.h @@ -0,0 +1,11 @@ +#ifndef OC_CRYPTO_CONFIG_H +#define OC_CRYPTO_CONFIG_H + +#include + +#define _PCD_GET_MODE_16_PcdOcCryptoAllowedSigHashTypes \ + (1U << OcSigHashTypeSha256) | (1U << OcSigHashTypeSha384) | (1U << OcSigHashTypeSha512) + +#define _PCD_GET_MODE_16_PcdOcCryptoAllowedRsaModuli (512U | 256U) + +#endif // OC_CRYPTO_CONFIG_H diff --git a/Utilities/AppleEfiSignTool/UefiCompat.h b/Utilities/AppleEfiSignTool/UefiCompat.h index e2a3f22a..15a103cf 100644 --- a/Utilities/AppleEfiSignTool/UefiCompat.h +++ b/Utilities/AppleEfiSignTool/UefiCompat.h @@ -1,12 +1,18 @@ +// +// FIXME: Fix up TestsUser/Include/Base.h and unify. +// #ifndef UEFI_COMPAT_H #define UEFI_COMPAT_H +#include #include #include #include #include #include +typedef char CHAR8; +typedef uint16_t CHAR16; typedef int8_t INT8; typedef int16_t INT16; typedef int32_t INT32; @@ -20,14 +26,65 @@ typedef void VOID; typedef intptr_t INTN; typedef size_t UINTN; +typedef UINTN RETURN_STATUS; +typedef RETURN_STATUS EFI_STATUS; + +struct EFI_SYSTEM_TABLE_; +typedef struct EFI_SYSTEM_TABLE_ EFI_SYSTEM_TABLE; + +typedef struct { + uint32_t Data1; + uint16_t Data2; + uint16_t Data3; + uint8_t Data4[8]; +} EFI_GUID; + #define CONST const #define STATIC static #define TRUE true #define FALSE false #define GLOBAL_REMOVE_IF_UNREFERENCED +#define PACKED +#define IN +#define OUT +#define OPTIONAL +#define EFIAPI + +#define MAX_INT8 INT8_MAX +#define MAX_INT16 INT16_MAX +#define MAX_INT32 INT32_MAX +#define MAX_INT64 INT64_MAX +#define MAX_INTN INT64_MAX +#define MAX_UINT8 UINT8_MAX +#define MAX_UINT16 UINT16_MAX +#define MAX_UINT32 UINT32_MAX +#define MAX_UINT64 UINT64_MAX +#define MAX_UINTN SIZE_MAX + +#define ALIGN_VALUE(Value, Alignment) ((Value) + (((Alignment) - (Value)) & ((Alignment) - 1))) +#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) +#define PcdGet16(TokenName) _PCD_GET_MODE_16_##TokenName + +#define ASSERT assert +#define DEBUG(...) + +#if !defined(_MSC_VER) || defined (__clang__) +#define SwapBytes16 __builtin_bswap16 +#define SwapBytes32 __builtin_bswap32 +#define SwapBytes64 __builtin_bswap64 +#else +#define SwapBytes16 _byteswap_ushort +#define SwapBytes32 _byteswap_ulong +#define SwapBytes64 _byteswap_uint64 +#endif +#define RShiftU64(A, B) ((UINT64)(A) >> (UINTN)(B)) +#define RShiftL64(A, B) ((UINT64)(A) << (UINTN)(B)) #define ZeroMem(Dst, Size) (memset)((Dst), 0, (Size)) #define CopyMem(Dst, Src, Size) (memcpy)((Dst), (Src), (Size)) #define CompareMem(One, Two, Size) (memcmp)((One),(Two),(Size)) +#define AllocatePool(Size) (malloc)(Size) +#define FreePool(Ptr) (free)(Ptr) + #endif // UEFI_COMPAT_H diff --git a/Utilities/CreateVault/RsaTool b/Utilities/CreateVault/RsaTool index 66587216..a20585f2 100755 Binary files a/Utilities/CreateVault/RsaTool and b/Utilities/CreateVault/RsaTool differ diff --git a/Utilities/CreateVault/sign.command b/Utilities/CreateVault/sign.command index 7bf040e9..818031d5 100755 --- a/Utilities/CreateVault/sign.command +++ b/Utilities/CreateVault/sign.command @@ -11,7 +11,12 @@ fi cd "$(/usr/bin/dirname "$0")" || abort "Failed to enter working directory!" -OCPath=../../EFI/OC +OCPath="$1" + +if [ "$OCPath" = "" ]; then + OCPath=../../EFI/OC +fi + KeyPath="${OCPath}/Keys" OCBin="${OCPath}/OpenCore.efi" RootCA="${KeyPath}/ca.pem" @@ -70,7 +75,7 @@ if [ "${off}" -le 16 ]; then abort "${OCBin} is borked" fi -/bin/dd of="${OCBin}" if="${PubKey}" bs=1 seek="${off}" count=520 conv=notrunc || abort "Failed to bin-patch ${OCBin}" +/bin/dd of="${OCBin}" if="${PubKey}" bs=1 seek="${off}" count=528 conv=notrunc || abort "Failed to bin-patch ${OCBin}" echo "All done!" exit 0 diff --git a/Utilities/RsaTool/Makefile b/Utilities/RsaTool/Makefile index 56bb87d0..c969a823 100644 --- a/Utilities/RsaTool/Makefile +++ b/Utilities/RsaTool/Makefile @@ -1,6 +1,6 @@ CC ?= gcc CFLAGS=-Wall -Wextra -pedantic -O3 -I/usr/local/opt/openssl/include -I/opt/local/include -LDFLAGS=-L/usr/local/opt/openssl/lib -lcrypto +LDFLAGS=-L/usr/local/opt/openssl/lib -L/opt/local/lib -lcrypto all: RsaTool diff --git a/Utilities/RsaTool/RsaTool.c b/Utilities/RsaTool/RsaTool.c index 3781cae4..b4685932 100644 --- a/Utilities/RsaTool/RsaTool.c +++ b/Utilities/RsaTool/RsaTool.c @@ -73,24 +73,25 @@ static void write_data(void* context, void* data, size_t size) { /* Pre-processes and outputs RSA public key to standard out. */ static void output(RSA* key, t_data_printer printer, void *printer_ctx) { - int i, nwords; + uint64_t i, nwords; const BIGNUM *key_n; BIGNUM *N = NULL; - BIGNUM *Big1 = NULL, *Big2 = NULL, *Big32 = NULL, *BigMinus1 = NULL; + BIGNUM *Big1 = NULL, *Big2 = NULL, *Big64 = NULL, *BigMinus1 = NULL; BIGNUM *B = NULL; BIGNUM *N0inv= NULL, *R = NULL, *RR = NULL, *RRTemp = NULL, *NnumBits = NULL; BIGNUM *n = NULL, *rr = NULL; BN_CTX *bn_ctx = BN_CTX_new(); - uint32_t n0invout; - /* Output size of RSA key in 32-bit words */ - nwords = RSA_size(key) / 4; + uint64_t n0invout; + /* Output size of RSA key in 64-bit words */ + nwords = RSA_size(key) / 8; + if (nwords > UINT16_MAX) return; printer(printer_ctx, &nwords, sizeof(nwords)); /* Initialize BIGNUMs */ RSA_get0_key(key, &key_n, NULL, NULL); N = BN_dup(key_n); Big1 = BN_new(); Big2 = BN_new(); - Big32 = BN_new(); + Big64 = BN_new(); BigMinus1 = BN_new(); N0inv= BN_new(); R = BN_new(); @@ -101,14 +102,16 @@ static void output(RSA* key, t_data_printer printer, void *printer_ctx) { rr = BN_new(); BN_set_word(Big1, 1L); BN_set_word(Big2, 2L); - BN_set_word(Big32, 32L); + BN_set_word(Big64, 64L); BN_sub(BigMinus1, Big1, Big2); B = BN_new(); - BN_exp(B, Big2, Big32, bn_ctx); /* B = 2^32 */ - /* Calculate and output N0inv = -1 / N[0] mod 2^32 */ + BN_exp(B, Big2, Big64, bn_ctx); /* B = 2^64 */ + /* Calculate and output N0inv = -1 / N[0] mod 2^64 */ BN_mod_inverse(N0inv, N, B, bn_ctx); BN_sub(N0inv, B, N0inv); - n0invout = BN_get_word(N0inv); + n0invout = (uint64_t) BN_get_word(N0inv); + BN_rshift(N0inv, N0inv, 32); + n0invout |= (uint64_t) BN_get_word(N0inv) << 32ULL; printer(printer_ctx, &n0invout, sizeof(n0invout)); /* Calculate R = 2^(# of key bits) */ BN_set_word(NnumBits, BN_num_bits(N)); @@ -118,20 +121,20 @@ static void output(RSA* key, t_data_printer printer, void *printer_ctx) { BN_mul(RRTemp, RR, R, bn_ctx); BN_mod(RR, RRTemp, N, bn_ctx); /* Write out modulus as little endian array of integers. */ - for (i = 0; i < nwords; ++i) { + for (i = 0; i < nwords*2; ++i) { uint32_t nout; BN_mod(n, N, B, bn_ctx); /* n = N mod B */ nout = BN_get_word(n); - printer(printer_ctx, &nout, sizeof(nout)); BN_rshift(N, N, 32); /* N = N/B */ + printer(printer_ctx, &nout, sizeof(nout)); } /* Write R^2 as little endian array of integers. */ - for (i = 0; i < nwords; ++i) { + for (i = 0; i < nwords*2; ++i) { uint32_t rrout; BN_mod(rr, RR, B, bn_ctx); /* rr = RR mod B */ rrout = BN_get_word(rr); - printer(printer_ctx, &rrout, sizeof(rrout)); BN_rshift(RR, RR, 32); /* RR = RR/B */ + printer(printer_ctx, &rrout, sizeof(rrout)); } /* print terminator */ printer(printer_ctx, NULL, 0); @@ -139,7 +142,7 @@ static void output(RSA* key, t_data_printer printer, void *printer_ctx) { BN_free(N); BN_free(Big1); BN_free(Big2); - BN_free(Big32); + BN_free(Big64); BN_free(BigMinus1); BN_free(N0inv); BN_free(R); diff --git a/Utilities/RsaTool/openssl_compat.h b/Utilities/RsaTool/openssl_compat.h index 72cec6ca..d12b81ef 100644 --- a/Utilities/RsaTool/openssl_compat.h +++ b/Utilities/RsaTool/openssl_compat.h @@ -21,6 +21,12 @@ #include #include +#if !defined(HAVE_RSA_GET0_KEY) && defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x1000100fL +#define HAVE_RSA_GET0_KEY +#define HAVE_RSA_SET0_KEY +#define EVP_MD_CTX_cleanup EVP_MD_CTX_free +#endif + #ifndef HAVE_RSA_GET0_KEY /** * Get the RSA parameters