diff --git a/.github/workflows/actions-ci.yml b/.github/workflows/actions-ci.yml index 21ea696785..e20c527cd9 100644 --- a/.github/workflows/actions-ci.yml +++ b/.github/workflows/actions-ci.yml @@ -255,17 +255,57 @@ jobs: cxx-compiler: g++-${{ matrix.gccversion }} options: FIPS=${{ matrix.fips }} CMAKE_BUILD_TYPE=Release - name: Build Project + # TODO: Re-enable gcc-14/FIPS build once delocator updated + if: ${{ !( matrix.gccversion == '14' && matrix.fips == '1' ) }} run: cmake --build ./build --target all - name: Run tests + # TODO: Re-enable gcc-14/FIPS build once delocator updated + if: ${{ !( matrix.gccversion == '14' && matrix.fips == '1' ) }} run: cmake --build ./build --target run_tests + gcc-13-pedantic: + if: github.repository_owner == 'aws' + needs: [ sanity-test-run ] + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v3 + - name: Setup CMake + uses: threeal/cmake-action@v1.3.0 + with: + generator: Ninja + c-compiler: gcc-13 + cxx-compiler: g++-13 + options: CMAKE_BUILD_TYPE=Release CMAKE_C_FLAGS=-pedantic CMAKE_CXX_FLAGS=-pedantic + - name: Build Crypto + run: cmake --build ./build --target crypto + - name: Build SSL + run: cmake --build ./build --target ssl + + clang-18-pedantic: + if: github.repository_owner == 'aws' + needs: [ sanity-test-run ] + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v3 + - name: Setup CMake + uses: threeal/cmake-action@v1.3.0 + with: + generator: Ninja + c-compiler: clang-18 + cxx-compiler: clang++-18 + options: CMAKE_BUILD_TYPE=Release CMAKE_C_FLAGS=-pedantic CMAKE_CXX_FLAGS=-pedantic + - name: Build Crypto + run: cmake --build ./build --target crypto + - name: Build SSL + run: cmake --build ./build --target ssl + clang-ubuntu-2004-sanity: if: github.repository_owner == 'aws' needs: [sanity-test-run] strategy: fail-fast: false matrix: - gccversion: + clangversion: - "10" - "11" - "12" @@ -282,8 +322,8 @@ jobs: uses: threeal/cmake-action@v1.3.0 with: generator: Ninja - c-compiler: clang-${{ matrix.gccversion }} - cxx-compiler: clang++-${{ matrix.gccversion }} + c-compiler: clang-${{ matrix.clangversion }} + cxx-compiler: clang++-${{ matrix.clangversion }} options: FIPS=${{ matrix.fips }} CMAKE_BUILD_TYPE=Release - name: Build Project run: cmake --build ./build --target all @@ -296,7 +336,7 @@ jobs: strategy: fail-fast: false matrix: - gccversion: + clangversion: - "13" - "14" - "15" @@ -313,8 +353,8 @@ jobs: uses: threeal/cmake-action@v1.3.0 with: generator: Ninja - c-compiler: clang-${{ matrix.gccversion }} - cxx-compiler: clang++-${{ matrix.gccversion }} + c-compiler: clang-${{ matrix.clangversion }} + cxx-compiler: clang++-${{ matrix.clangversion }} options: FIPS=${{ matrix.fips }} CMAKE_BUILD_TYPE=Release - name: Build Project run: cmake --build ./build --target all @@ -327,7 +367,7 @@ jobs: strategy: fail-fast: false matrix: - gccversion: + clangversion: - "16" - "17" - "18" @@ -344,8 +384,8 @@ jobs: uses: threeal/cmake-action@v1.3.0 with: generator: Ninja - c-compiler: clang-${{ matrix.gccversion }} - cxx-compiler: clang++-${{ matrix.gccversion }} + c-compiler: clang-${{ matrix.clangversion }} + cxx-compiler: clang++-${{ matrix.clangversion }} options: FIPS=${{ matrix.fips }} CMAKE_BUILD_TYPE=Release - name: Build Project run: cmake --build ./build --target all diff --git a/.gitignore b/.gitignore index 941a5e2413..eae71e6471 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,4 @@ symbols.txt .fleet/ .cache/ /CMakePresets.json +/compile_commands.json diff --git a/CMakeLists.txt b/CMakeLists.txt index b640b3ffcf..3b66ce6fe8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -358,8 +358,9 @@ if(GCC OR CLANG) set(C_CXX_FLAGS "${C_CXX_FLAGS} -Wall -fvisibility=hidden -fno-common") endif() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wunused -Wcomment -Wchar-subscripts -Wuninitialized -Wshadow") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wwrite-strings -Wformat-security -Wunused-result") - set(C_CXX_FLAGS "${C_CXX_FLAGS} -Wvla -Wtype-limits -Wno-unused-parameter") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wwrite-strings -Wformat-security -Wunused-result -Wno-overlength-strings") + set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -Wno-newline-eof") + set(C_CXX_FLAGS "${C_CXX_FLAGS} -Wno-c11-extensions -Wvla -Wtype-limits -Wno-unused-parameter") endif() set(C_CXX_FLAGS "${C_CXX_FLAGS} -Werror -Wformat=2 -Wsign-compare -Wmissing-field-initializers -Wwrite-strings") diff --git a/crypto/fipsmodule/FIPS.md b/crypto/fipsmodule/FIPS.md index b514b1a717..13f905fd45 100644 --- a/crypto/fipsmodule/FIPS.md +++ b/crypto/fipsmodule/FIPS.md @@ -1,175 +1,142 @@ -# FIPS 140-2 +# FIPS 140-3 -BoringSSL as a whole is not FIPS validated. However, there is a core library (called BoringCrypto) that has been FIPS validated. This document contains some notes about the design of the FIPS module and some documentation on performing FIPS-related tasks. This is not a substitute for reading the offical Security Policy. - -Please note that we cannot answer questions about FIPS, nor about using BoringSSL in a FIPS-compliant manner. Please consult with an [accredited CMVP lab](http://csrc.nist.gov/groups/STM/testing_labs/) on these subjects. +A submodule of AWS-LC, referred to here as the “FIPS module”, is periodically submitted for FIPS validation from the National Institute for Standards and Technology (NIST). This document contains notes about the design of our FIPS module and documentation on performing certain FIPS-related tasks. This document is not a substitute for reading our latest official [FIPS Security Policy](https://csrc.nist.gov/CSRC/media/projects/cryptographic-module-validation-program/documents/security-policies/140sp4631.pdf). Please note that we cannot answer questions about FIPS, nor about using AWS-LC in a FIPS-compliant manner. Please consult with an [accredited CMVP lab](http://csrc.nist.gov/groups/STM/testing_labs/) or a compliance specialist on these topics. ## Validations -BoringCrypto has undergone the following validations: +NIST has awarded the FIPS module of AWS-LC its validation certificate as a Federal Information Processing Standards (FIPS) 140-3, level 1, cryptographic module. -1. 2017-06-15: certificate [#2964](https://csrc.nist.gov/Projects/Cryptographic-Module-Validation-Program/Certificate/2964), [security policy](./policydocs/BoringCrypto-Security-Policy-20170615.docx) (in docx format). -1. 2018-07-30: certificate [#3318](https://csrc.nist.gov/Projects/Cryptographic-Module-Validation-Program/Certificate/3318), [security policy](./policydocs/BoringCrypto-Security-Policy-20180730.docx) (in docx format). -1. 2019-08-08: certificate [#3678](https://csrc.nist.gov/Projects/Cryptographic-Module-Validation-Program/Certificate/3678), [security policy](./policydocs/BoringCrypto-Security-Policy-20190808.docx) (in docx format). -1. 2019-10-20: certificate [#3753](https://csrc.nist.gov/Projects/Cryptographic-Module-Validation-Program/Certificate/3753), [security policy](./policydocs/BoringCrypto-Android-Security-Policy-20191020.docx) (in docx format). -1. 2021-01-28: certificate [#4156](https://csrc.nist.gov/Projects/Cryptographic-Module-Validation-Program/Certificate/4156), [security policy](./policydocs/BoringCrypto-Android-Security-Policy-20210319.docx) (in docx format). -1. 2021-04-29: certificate [#4407](https://csrc.nist.gov/projects/cryptographic-module-validation-program/certificate/4407). +1. AWS-LC-FIPS v1.0: certificate [#4631](https://csrc.nist.gov/projects/cryptographic-module-validation-program/certificate/4631), [security policy](https://csrc.nist.gov/CSRC/media/projects/cryptographic-module-validation-program/documents/security-policies/140sp4631.pdf) -## Running ACVP tests +NIST has also awarded SP 800-90B validation certificate for our CPU Jitter Entropy Source. -See `util/fipstools/acvp/ACVP.md` for details of how ACVP testing is done. - -## Breaking known-answer and continuous tests - -Each known-answer test (KAT) uses a unique, random input value. `util/fipstools/break-kat.go` contains a listing of those values and can be used to corrupt a given test in a binary. Since changes to the KAT input values will invalidate the integrity test, `BORINGSSL_FIPS_BREAK_TESTS` can be defined in `fips_break_tests.h` to disable it for the purposes of testing. - -Some FIPS tests cannot be broken by replacing a known string in the binary. For those, when `BORINGSSL_FIPS_BREAK_TESTS` is defined, the environment variable `BORINGSSL_FIPS_BREAK_TEST` can be set to one of a number of values in order to break the corresponding test: +1. 2023-09-14: entropy certificate [#E77](https://csrc.nist.gov/projects/cryptographic-module-validation-program/entropy-validations/certificate/77), [public use document](https://csrc.nist.gov/CSRC/media/projects/cryptographic-module-validation-program/documents/entropy/E77_PublicUse.pdf) -1. `RSA_PWCT` -1. `ECDSA_PWCT` +### Modules in Process -## Breaking the integrity test +The modules below have been tested by an accredited lab and have been submitted to NIST for FIPS 140-3 validation. -The utility in `util/fipstools/break-hash.go` can be used to corrupt the FIPS module inside a binary and thus trigger a failure of the integrity test. Note that the binary must not be stripped, otherwise the utility will not be able to find the FIPS module. +* AWS-LC-FIPS v2.0 (dynamic library): [Review Pending](https://csrc.nist.gov/Projects/Cryptographic-Module-Validation-Program/Modules-In-Process/Modules-In-Process-List) - [Draft security policy](https://github.com/aws/aws-lc/blob/fips-2022-11-02/crypto/fipsmodule/policydocs/DRAFT-140-3-AmazonSecurityPolicy-2.0.0-dynamic.pdf) +* AWS-LC-FIPS v2.0 (static library): [Review Pending](https://csrc.nist.gov/Projects/Cryptographic-Module-Validation-Program/Modules-In-Process/Modules-In-Process-List) - [Draft security policy](https://github.com/aws/aws-lc/blob/fips-2022-11-02/crypto/fipsmodule/policydocs/DRAFT-140-3-AmazonSecurityPolicy-2.0.0-static.pdf) ## RNG design -FIPS 140-2 requires that one of its PRNGs be used (which they call DRBGs). In BoringCrypto, we use CTR-DRBG with AES-256 exclusively and `RAND_bytes` (the primary interface for the rest of the system to get random data) takes its output from there. +FIPS 140-3 requires the use of one of its Deterministic Random Bit Generators (DRBGs, also called Pseudo-Random Number Generators, PRNGs). In AWS-LC, we use CTR-DRBG with AES-256 exclusively from which `RAND_bytes` produces its output. The DRBG state is kept in a thread-local structure and is seeded using the configured entropy source. The CTR-DRBG is periodically reseeded from the entropy source during calls to `RAND_bytes`. The rest of the library obtains random data from `RAND_bytes`. -The DRBG state is kept in a thread-local structure and is seeded using the configured entropy source. +The FIPS DRBGs allow “additional input” to be fed into a given call. We use this feature to be as robust as possible against state duplication from process forks and VM copies. A caller may supply bytes that will be XOR'd into the generated “additional input” using `RAND_bytes_with_additional_data`. -The CTR-DRBG is reseeded every 4096 calls to `RAND_bytes`. Thus the process will randomly crash about every 2¹³⁵ calls. +FIPS requires that RNG state be zeroed when the process exits. In order to implement this, all per-thread RNG states are tracked in a linked list and a [destructor function](https://github.com/aws/aws-lc/blob/eb58525ce1704c5183af9aa28f50945c11fe5a0d/crypto/fipsmodule/rand/rand.c#L251) is included which clears them. In order for this to be safe in the presence of threads, a lock is used to stop all other threads from using the RNG once this process has begun. Thus the main thread exiting may cause other threads to deadlock, and drawing on entropy in a destructor function may also deadlock. -The FIPS PRNGs allow “additional input” to be fed into a given call. We use this feature to be as robust as possible to state duplication from process forks and VM copies: on Intel CPUs with RDRAND, for every call we read 32 bytes of “additional data” from RDRAND, which means that cloned states will diverge at the next call to `RAND_bytes`. This is called “prediction resistance” by FIPS, but we do *not* claim this property in a FIPS context because we don't implement it the way they want. +## AWS-LC-FIPS v1.0 Entropy Source -There is a second interface to the RNG which allows the caller to supply bytes that will be XORed into the generated additional data (`RAND_bytes_with_additional_data`). This is used in the ECDSA code to include the message and private key in the generation of *k*, the ECDSA nonce. This allows ECDSA to be robust to entropy failures while still following the FIPS rules. +The AWS-LC-FIPS v1.0 entropy source is CPU Jitter Entropy ([version 3.1.0](https://github.com/aws/aws-lc/blob/fips-2021-10-20/third_party/jitterentropy/README.md)). For technical details on how CPU Jitter generates entropy, please refer to the [Jitter RNG homepage](https://www.chronox.de/jent/index.html). -FIPS requires that RNG state be zeroed when the process exits. In order to implement this, all per-thread RNG states are tracked in a linked list and a destructor function is included which clears them. In order for this to be safe in the presence of threads, a lock is used to stop all other threads from using the RNG once this process has begun. Thus the main thread exiting may cause other threads to deadlock, and drawing on entropy in a destructor function may also deadlock. +## AWS-LC-FIPS v2.0 Entropy Source -## Entropy sources +The AWS-LC-FIPS v2.0 module uses passive entropy by default and the specific entropy gathering mechanism depends on the operating environment installed on. CPU Jitter Entropy ([version 3.4.0](https://github.com/aws/aws-lc/blob/fips-2022-11-02/third_party/jitterentropy/README.md)) can still be enabled by using the `ENABLE_FIPS_ENTROPY_CPU_JITTER=ON` flag. -By default, entropy is sourced using a "Passive" method where the specific entropy source depends on the OE. Seeding and reseeding material for the DRBG is sourced from the specific entropy source. +## Breaking known-answer and continuous tests -In FIPS mode, when the CPU Jitter entropy source is used, we do a 3x oversampling (the CPU Jitter default setting). Note that the CPU Jitter is not the default entropy source (see subsection below). +Each known-answer test (KAT) uses a unique, random input value. `util/fipstools/break-kat.go` contains a listing of those values and can be used to corrupt a given test in a binary. Since changes to the KAT input values will invalidate the integrity test, `BORINGSSL_FIPS_BREAK_TESTS` can be defined in `fips_break_tests.h` to disable it for the purposes of testing. -### Modify entropy source - not recommended +Some FIPS tests cannot be broken by replacing a known string in the binary. For those, when `BORINGSSL_FIPS_BREAK_TESTS` is defined, the environment variable `BORINGSSL_FIPS_BREAK_TEST` can be set to one of a number of values in order to break the corresponding test: -Modifying the entropy source can invalidate your validation status. Changing the entropy is **not** recommended. +1. `RSA_PWCT` +2. `ECDSA_PWCT` -It is possible to modify the entropy method at build-time. Using `ENABLE_FIPS_ENTROPY_CPU_JITTER=ON`, the entropy source is switched to [CPU Jitter](https://github.com/smuellerDD/jitterentropy-library). +## Running ACVP tests -CPU Jitter is less performant than the default method and can incur up to a 2-digit millisecond latency when queried. You can test CPU Jitter entropy on your system using `bssl speed -filter Jitter`. +See `util/fipstools/acvp/ACVP.md` for details of how ACVP testing is done. ## Integrity Test -FIPS-140 mandates that a module calculate an HMAC of its own code in a constructor function and compare the result to a known-good value. Typical code produced by a C compiler includes large numbers of relocations: places in the machine code where the linker needs to resolve and inject the final value of a symbolic expression. These relocations mean that the bytes that make up any specific bit of code generally aren't known until the final link has completed. - -Additionally, because of shared libraries and ASLR, some relocations can only be resolved at run-time, and thus targets of those relocations vary even after the final link. +FIPS-140 mandates that a module calculate an HMAC of its own code in a constructor function and compare the result to a known-good value. Typical code produced by a C compiler includes large numbers of relocations: places in the machine code where the linker needs to resolve and inject the final value of a symbolic expression. These relocations mean that the bytes that make up any specific bit of code generally aren't known until the final link has completed. Additionally, because of shared libraries and [ASLR](https://en.wikipedia.org/wiki/Address_space_layout_randomization), some relocations can only be resolved at run-time, and thus targets of those relocations vary even after the final link. -BoringCrypto is linked (often statically) into a large number of binaries. It would be a significant cost if each of these binaries had to be post-processed in order to calculate the known-good HMAC value. We would much prefer if the value could be calculated, once, when BoringCrypto itself is compiled. +AWS-LC is linked (often statically) into a large number of binaries. It would be a significant cost if each of these binaries had to be post-processed in order to calculate the known-good HMAC value. We would much prefer if the value could be calculated, once, when AWS-LC itself is compiled. In order for the value to be calculated before the final link, there can be no relocations in the hashed code and data. -In order for the value to be calculated before the final link, there can be no relocations in the hashed code and data. This document describes how we build C and assembly code in order to produce a binary file containing all the code and data for the FIPS module without that code having any relocations. - -There are two build configurations supported: static and shared. The shared build produces `libcrypto.so`, which includes the FIPS module and is significantly more straightforward and so is described first: +We describe below how we build C and assembly code in order to produce a binary file containing the code and data for the FIPS module without that code having any relocations. There are two build configurations supported: **static** and **shared**. The shared build produces `libcrypto.so`, which includes the FIPS module and is significantly more straightforward and so is described first. ### Linux Shared build -First, all the C source files for the module are compiled as a single unit by compiling a single source file that `#include`s them all (this is `bcm.c`). This, along with some assembly sources, comprise the FIPS module. +First, all the C source files for the module are compiled as a single unit by compiling a single source file that `#include`s them all (this is `[bcm.c](https://github.com/aws/aws-lc/blob/main/crypto/fipsmodule/bcm.c)`). This, along with some assembly sources, comprise the FIPS module. -The object files resulting from compiling (or assembling) those files is linked in partial-linking mode with a linker script that causes the linker to insert symbols marking the beginning and end of the text and rodata sections. The linker script also discards other types of data sections to ensure that no unhashed data is used by the module. +The object files resulting from compiling/assembling those files are linked in partial-linking mode with a [linker script](https://github.com/aws/aws-lc/blob/main/crypto/fipsmodule/gcc_fips_shared.lds) that causes the linker to insert symbols marking the beginning and end of the text and rodata sections. The linker script also discards other types of data sections to ensure that no unhashed data is used by the module. -One source of such data are `rel.ro` sections, which contain data that includes function pointers. Since these function pointers are absolute, they are written by the dynamic linker at run-time and so we must eliminate them. The pattern that causes them is when we have a static `EVP_MD` or `EVP_CIPHER` object thus, inside the module, this pattern is changed to instead reserve space in the BSS for the object, and to add a `CRYPTO_once_t` to protect its initialisation. +One source of unhashable data is `rel.ro` sections, which contain data that includes function pointers. Since these function pointers are absolute, they are written by the dynamic linker at run-time and so must be eliminated. The pattern that causes them is when we have a static `EVP_MD` or `EVP_CIPHER` object thus, inside the module, this pattern is changed to instead reserve space in the [BSS](https://en.wikipedia.org/wiki/.bss) for the object, and to add a `CRYPTO_once_t` to protect its initialization. -Once the partially-linked result is linked again, with other parts of libcrypto, to produce `libcrypto.so`, the contents of the module are fixed, as required. The module code uses the linker-added symbols to find the its code and data at run-time and hashes them upon initialisation. The result is compared against a value stored inside `libcrypto.so`, but outside of the module. That value will, initially, be incorrect, but `inject-hash.go` can inject the correct value. +Once the partially-linked result is linked again with other parts of libcrypto, to produce `libcrypto.so`, the contents of the module are fixed, as required. The module code uses the linker-added symbols to find its code and data at run-time and hashes them upon initialization. The result is compared against a value stored inside `libcrypto.so`, but outside of the module. That value will, initially, be incorrect, but `inject-hash.go` injects the correct value. ### Windows Shared build The Shared Windows FIPS integrity test differs in two key ways: + 1. How the start and end of the module are marked 2. How the correct integrity hash is calculated -Microsoft Visual C compiler (MSVC) does not support linker scripts which add symbols to mark the start and end of the text and rodata sections on Linux. Instead, fips_shared_library_marker.c is compiled twice to generate two object files that contain start/end functions and variables. MSVC `pragma` segment definitions are used to place the markers in specific sections (e.g. `.fipstx$a`). This particular name format uses Portable Executable Grouped Sections to control what section the code is placed in and the order within the section. With the start and end markers placed at `$a` and `$z` respectively, BCM puts everything in the `$b` section. When the final crypto.dll is built all the code is in the `.fipstx` section, all data is in `.fipsda`, all constants are in `.fipsco`, all unitialized items in `.fipsbs`, and everything is in the correct order. - +Microsoft Visual C compiler (MSVC) does not support linker scripts that add symbols to mark the start and end of the text and rodata sections, as is done on Linux. Instead, `fips_shared_library_marker.c` is compiled twice to generate two object files that contain start/end functions and variables. MSVC `pragma` segment definitions are used to place the markers in specific sections (e.g. `.fipstx$a`). This particular name format uses [Portable Executable Grouped Sections](https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#grouped-sections-object-only) to control what section the code is placed in and the order within the section. With the start and end markers placed at `$a` and `$z` respectively, BCM puts everything in the `$b` section. When the final crypto.dll is built, all the code is in the `.fipstx` section, all data is in `.fipsda`, all constants are in `.fipsco`, all uninitialized items in `.fipsbs`, and everything is in the correct order. The process to generate the expected integrity fingerprint is also different from Linux: -1. Build the required object files once: `bcm.obj` from `bcm.c` and the start/end object files - 1. `bcm.obj` places the power-on self tests in the `.CRT$XCU` section which is run automatically by the Windows Common Runtime library (CRT) startup code -2. Use MSVC's `lib.exe` to combine the start/end object files with `bcm.obj` to create the static library `bcm.lib`. - 1. MSVC does not support combining multiple object files into another object file like the Apple build. + +1. Build the required object files once: `bcm.obj` from `bcm.c` and the start/end object files + 1. `bcm.obj` places the power-on self tests in the `.CRT$XCU` section which is run automatically by the Windows Common Runtime library (CRT) startup code +2. Use MSVC's `lib.exe` to combine the start/end object files with `bcm.obj` to create the static library `bcm.lib`. + 1. MSVC does not support combining multiple object files into another object file like the Apple build. 3. Build `fipsmodule` which contains the placeholder integrity hash 4. Build `precrypto.dll` with `bcm.obj` and `fipsmodule` 5. Build the small application `fips_empty_main.exe` and link it with `precrypto.dll` 6. `capture-hash.go` runs `fips_empty_main.exe` - 1. The CRT runs all functions in the `.CRT$XC*` sections in order starting with `.CRT$XCA` - 2. The BCM power-on tests are in `.CRT$XCU` and are run after all other Windows initialization is complete - 3. BCM calculates the correct integrity value which will not match the placeholder value. Before aborting the process the correct value is printed - 4. `capture-hash.go` reads the correct integrity value and writes it to `generated_fips_shared_support.c` + 1. The CRT runs all functions in the `.CRT$XC*` sections in order starting with `.CRT$XCA` + 2. The BCM power-on tests are in `.CRT$XCU` and are run after all other Windows initialization is complete + 3. BCM calculates the correct integrity value which will not match the placeholder value. Before aborting the process the correct value is printed + 4. `capture-hash.go` reads the correct integrity value and writes it to `generated_fips_shared_support.c` 7. `generated_fipsmodule` is built with `generated_fips_shared_support.c` 8. `crypto.dll` is built with the same original `bcm.lib` and `generated_fipsmodule` -### Static build - -The static build cannot depend on the shared-object link to resolve relocations and thus must take another path. +### Linux Static build -As with the shared build, all the C sources are build in a single compilation unit. The `-fPIC` flag is used to cause the compiler to use IP-relative addressing in many (but not all) cases. Also the `-S` flag is used to instruct the compiler to produce a textual assembly file rather than a binary object file. +The static build cannot depend on the shared-object linkage to resolve relocations and thus must take another path. +As with the shared build, all the C sources are built into a single compilation unit. The `-fPIC` flag is used to cause the compiler to use [IP-relative addressing](https://en.wikipedia.org/wiki/Position-independent_code) in many (but not all) cases. -The textual assembly file is then processed by a script to merge in assembly implementations of some primitives and to eliminate the remaining sources of relocations. +The unit is built with the `-S` flag to instruct the compiler to produce a textual assembly file rather than a binary object file. +The textual assembly file is then processed by a “delocator” script to merge in assembly implementations of some primitives and to eliminate the remaining sources of relocations. -##### Redirector functions +#### Redirector functions -The most obvious cause of relocations are out-calls from the module to non-cryptographic functions outside of the module. Most obviously these include `malloc`, `memcpy` and other libc functions, but also include calls to support code in BoringSSL such as functions for managing the error queue. +The most obvious cause of relocations are out-calls from the module to non-cryptographic functions outside of the module. Most obviously these include `malloc`, `memcpy` and other libc functions, but also include calls to support code in AWS-LC such as functions for managing the error queue. -Offsets to these functions cannot be known until the final link because only the linker sees the object files containing them. Thus calls to these functions are rewritten into an IP-relative jump to a redirector function. The redirector functions contain a single jump instruction to the real function and are placed outside of the module and are thus not hashed (see diagram). +Offsets to these functions cannot be known until the final link because only the linker sees the object files containing them. Thus calls to these functions are rewritten into an IP-relative jump to a *redirector* function. The redirector functions contain a single jump instruction to the real function and are placed outside of the module and are thus not hashed (see diagram). +In this diagram, the integrity check hashes from `module_start` to `module_end`. Since this does not cover the jump to `memcpy`, it's fine that the linker will poke the final offset into that instruction. ![module structure](./intcheck1.png) -In this diagram, the integrity check hashes from `module_start` to `module_end`. Since this does not cover the jump to `memcpy`, it's fine that the linker will poke the final offset into that instruction. - -##### Read-only data +#### Read-only data Normally read-only data is placed in an `.rodata` segment that doesn't get mapped into memory with execute permissions. However, the offset of the data segment from the text segment is another thing that isn't determined until the final link. In order to fix data offsets before the link, read-only data is simply placed in the module's `.text` segment. This might make building ROP chains easier for an attacker, but so it goes. Data containing function pointers remains an issue. The source-code changes described above for the shared build apply here too, but no direct references to a BSS section are possible because the offset to that section is not known at compile time. Instead, the script generates functions outside of the module that return pointers to these areas of memory—they effectively act like special-purpose malloc calls that cannot fail. -##### Read-write data +#### Read-write data Mutable data is a problem. It cannot be in the text segment because the text segment is mapped read-only. If it's in a different segment then the code cannot reference it with a known, IP-relative offset because the segment layout is only fixed during the final link. - In order to allow this we use a similar design to the redirector functions: the code references a symbol that's in the text segment, but out of the module and thus not hashed. A relocation record is emitted to instruct the linker to poke the final offset to the variable in that location. Thus the only change needed is an extra indirection when loading the value. -##### Other transforms +#### Other transforms -The script performs a number of other transformations which are worth noting but do not warrant their own discussions: +The delocator script performs a number of other transformations which are worth noting but do not warrant their own discussions: -1. It duplicates each global symbol with a local symbol that has `_local_target` appended to the name. References to the global symbols are rewritten to use these duplicates instead. Otherwise, although the generated code uses IP-relative references, relocations are emitted for global symbols in case they are overridden by a different object file during the link. -1. Various sections, notably `.rodata`, are moved to the `.text` section, inside the module, so module code may reference it without relocations. -1. For each BSS symbol, it generates a function named after that symbol but with `_bss_get` appended, which returns its address. -1. It inserts the labels that delimit the module's code and data (called `module_start` and `module_end` in the diagram above). -1. It adds a 64-byte, read-only array outside of the module to contain the known-good HMAC value. +1. It duplicates each global symbol with a local symbol that has `_local_target` appended to the name. References to the global symbols are rewritten to use these duplicates instead. Otherwise, although the generated code uses IP-relative references, relocations are emitted for global symbols in case they are overridden by a different object file during the link. +2. Various sections, notably `.rodata`, are moved to the `.text` section, inside the module, so module code may reference it without relocations. +3. For each BSS symbol, it generates a function named after that symbol but with `_bss_get` appended, which returns its address. +4. It inserts the labels that delimit the module's code and data (called `module_start` and `module_end` in the diagram above). +5. It adds a 64-byte, read-only array outside of the module to contain the known-good HMAC value. -##### Integrity testing +#### Integrity testing In order to actually implement the integrity test, a constructor function within the module calculates an HMAC from `module_start` to `module_end` using a fixed, all-zero key. It compares the result with the known-good value added (by the script) to the unhashed portion of the text segment. If they don't match, it calls `exit` in an infinite loop. - Initially the known-good value will be incorrect. Another script (`inject_hash.go`) calculates the correct value from the assembled object and injects it back into the object. -![build process](./intcheck2.png) - -### Comparison with OpenSSL's method - -(This is based on reading OpenSSL's [user guide](https://www.openssl.org/docs/fips/UserGuide-2.0.pdf) and inspecting the code of OpenSSL FIPS 2.0.12.) +### Breaking the integrity test -OpenSSL's solution to this problem is very similar to our shared build, with just a few differences: - -1. OpenSSL deals with run-time relocations by not hashing parts of the module's data. -1. OpenSSL uses `ld -r` (the partial linking mode) to merge a number of object files into their `fipscanister.o`. For BoringCrypto's static build, we merge all the C source files by building a single C file that #includes all the others, and we merge the assembly sources by appending them to the assembly output from the C compiler. -1. OpenSSL depends on the link order and inserts two object files, `fips_start.o` and `fips_end.o`, in order to establish the `module_start` and `module_end` values. BoringCrypto adds labels at the correct places in the assembly for the static build, or uses a linker script for the shared build. -1. OpenSSL calculates the hash after the final link and either injects it into the binary or recompiles with the value of the hash passed in as a #define. BoringCrypto calculates it prior to the final link and injects it into the object file. -1. OpenSSL references read-write data directly, since it can know the offsets to it. BoringCrypto indirects these loads and stores. -1. OpenSSL doesn't run the power-on test until `FIPS_module_mode_set` is called. BoringCrypto does it in a constructor function. Failure of the test is non-fatal in OpenSSL, BoringCrypto will crash. -1. Since the contents of OpenSSL's module change between compilation and use, OpenSSL generates `fipscanister.o.sha1` to check that the compiled object doesn't change before linking. Since BoringCrypto's module is fixed after compilation (in the static case), the final integrity check is unbroken through the linking process. - -Some of the similarities are worth noting: +The utility in `util/fipstools/break-hash.go` can be used to corrupt the FIPS module inside a binary and thus trigger a failure of the integrity test. Note that the binary must not be stripped, otherwise the utility will not be able to find the FIPS module. -1. OpenSSL has all out-calls from the module indirecting via the PLT, which is equivalent to the redirector functions described above. +![build process](./intcheck2.png) -![OpenSSL build process](./intcheck3.png) diff --git a/crypto/fipsmodule/hmac/hmac.c b/crypto/fipsmodule/hmac/hmac.c index 0e576c026a..0393888e58 100644 --- a/crypto/fipsmodule/hmac/hmac.c +++ b/crypto/fipsmodule/hmac/hmac.c @@ -108,14 +108,14 @@ struct hmac_methods_st { // The maximum number of HMAC implementations #define HMAC_METHOD_MAX 8 -MD_TRAMPOLINES_EXPLICIT(MD5, MD5_CTX, MD5_CBLOCK); -MD_TRAMPOLINES_EXPLICIT(SHA1, SHA_CTX, SHA_CBLOCK); -MD_TRAMPOLINES_EXPLICIT(SHA224, SHA256_CTX, SHA256_CBLOCK); -MD_TRAMPOLINES_EXPLICIT(SHA256, SHA256_CTX, SHA256_CBLOCK); -MD_TRAMPOLINES_EXPLICIT(SHA384, SHA512_CTX, SHA512_CBLOCK); -MD_TRAMPOLINES_EXPLICIT(SHA512, SHA512_CTX, SHA512_CBLOCK); -MD_TRAMPOLINES_EXPLICIT(SHA512_224, SHA512_CTX, SHA512_CBLOCK); -MD_TRAMPOLINES_EXPLICIT(SHA512_256, SHA512_CTX, SHA512_CBLOCK); +MD_TRAMPOLINES_EXPLICIT(MD5, MD5_CTX, MD5_CBLOCK) +MD_TRAMPOLINES_EXPLICIT(SHA1, SHA_CTX, SHA_CBLOCK) +MD_TRAMPOLINES_EXPLICIT(SHA224, SHA256_CTX, SHA256_CBLOCK) +MD_TRAMPOLINES_EXPLICIT(SHA256, SHA256_CTX, SHA256_CBLOCK) +MD_TRAMPOLINES_EXPLICIT(SHA384, SHA512_CTX, SHA512_CBLOCK) +MD_TRAMPOLINES_EXPLICIT(SHA512, SHA512_CTX, SHA512_CBLOCK) +MD_TRAMPOLINES_EXPLICIT(SHA512_224, SHA512_CTX, SHA512_CBLOCK) +MD_TRAMPOLINES_EXPLICIT(SHA512_256, SHA512_CTX, SHA512_CBLOCK) struct hmac_method_array_st { HmacMethods methods[HMAC_METHOD_MAX]; diff --git a/crypto/mem.c b/crypto/mem.c index 02799f8fbc..efe42dbee3 100644 --- a/crypto/mem.c +++ b/crypto/mem.c @@ -107,7 +107,7 @@ static void __asan_unpoison_memory_region(const void *addr, size_t size) {} // implementation is statically linked with BoringSSL. So, if |sdallocx| is // provided in, say, libc.so, we still won't use it because that's dynamically // linked. This isn't an ideal result, but its helps in some cases. -WEAK_SYMBOL_FUNC(void, sdallocx, (void *ptr, size_t size, int flags)); +WEAK_SYMBOL_FUNC(void, sdallocx, (void *ptr, size_t size, int flags)) // The following four functions can be defined to override default heap // allocation and freeing. If defined, it is the responsibility of diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc index 8d512bf580..8487778412 100644 --- a/ssl/ssl_test.cc +++ b/ssl/ssl_test.cc @@ -5217,12 +5217,14 @@ TEST(SSLTest, BuildCertChain) { // Verification will fail because there is no valid root cert available. EXPECT_FALSE(SSL_CTX_build_cert_chain(ctx.get(), 0)); + ERR_clear_error(); // Should return 2 when |SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR| is set. EXPECT_EQ( SSL_CTX_build_cert_chain(ctx.get(), SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR), 2); EXPECT_TRUE(ExpectSingleError(ERR_LIB_SSL, SSL_R_CERTIFICATE_VERIFY_FAILED)); + ERR_clear_error(); // Should return 2, but with no error on the stack when // |SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR| and |SSL_BUILD_CHAIN_FLAG_CLEAR_ERROR| diff --git a/ssl/ssl_x509.cc b/ssl/ssl_x509.cc index 5388a475ee..112d0747ed 100644 --- a/ssl/ssl_x509.cc +++ b/ssl/ssl_x509.cc @@ -1061,12 +1061,13 @@ static int ssl_build_cert_chain(CERT *cert, X509_STORE *cert_store, int flags) { bool ignore_error = false; if (X509_verify_cert(store_ctx.get()) <= 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_CERTIFICATE_VERIFY_FAILED); + ERR_add_error_data(2, "Verify error:", + X509_verify_cert_error_string( + X509_STORE_CTX_get_error(store_ctx.get()))); + // Fail if |SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR| is not set. - if(!is_flag_set(flags, SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR)) { - OPENSSL_PUT_ERROR(SSL, SSL_R_CERTIFICATE_VERIFY_FAILED); - ERR_add_error_data(2, "Verify error:", - X509_verify_cert_error_string( - X509_STORE_CTX_get_error(store_ctx.get()))); + if (!is_flag_set(flags, SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR)) { return 0; } @@ -1098,7 +1099,7 @@ static int ssl_build_cert_chain(CERT *cert, X509_STORE *cert_store, int flags) { // Anything that has passed successfully up to here is valid. // 2 is used to indicate a verification error has happened, but was ignored // because |SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR| was set. - if(ignore_error) { + if (ignore_error) { return 2; } return 1; diff --git a/util/build_compilation_database.sh b/util/build_compilation_database.sh new file mode 100755 index 0000000000..8cd675cf5c --- /dev/null +++ b/util/build_compilation_database.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +set -ex + +BASE_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}/" )/.." &> /dev/null && pwd ) + +TMP_DIR=`mktemp -d` +echo ${TMP_DIR} +AWS_LC_BUILD="${TMP_DIR}/AWS-LC-BUILD" + +MY_CMAKE_FLAGS=("-GNinja" "-DCMAKE_BUILD_TYPE=Debug" "-DCMAKE_EXPORT_COMPILE_COMMANDS=ON") + +mkdir -p "${AWS_LC_BUILD}" + +cmake "${BASE_DIR}" -B "${AWS_LC_BUILD}" ${MY_CMAKE_FLAGS[@]} + +cmake --build "${AWS_LC_BUILD}" --target all + +cp "${AWS_LC_BUILD}"/compile_commands.json "${BASE_DIR}"/