First part of the FIPS module.

Change-Id: Ic3a91ccd2c8cdc364740f256fdb8a7ff66177947
Reviewed-on: https://boringssl-review.googlesource.com/14506
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: Adam Langley <agl@google.com>
This commit is contained in:
Adam Langley 2017-04-04 14:21:43 -07:00
parent 0ef8c7bd8f
commit fd49993c3b
56 changed files with 1708 additions and 379 deletions

View File

@ -236,6 +236,10 @@ if (OPENSSL_NO_ASM)
set(ARCH "generic")
endif()
if(FIPS)
add_definitions(-DBORINGSSL_FIPS)
endif()
# Add minimal googletest targets. The provided one has many side-effects, and
# googletest has a very straightforward build.
add_library(gtest third_party/googletest/src/gtest-all.cc)

View File

@ -67,9 +67,6 @@ add_subdirectory(bytestring)
add_subdirectory(pool)
# Level 0.2 - depends on nothing but itself
add_subdirectory(sha)
add_subdirectory(md4)
add_subdirectory(md5)
add_subdirectory(modes)
add_subdirectory(aes)
add_subdirectory(des)
@ -80,7 +77,7 @@ add_subdirectory(poly1305)
add_subdirectory(curve25519)
# Level 1, depends only on 0.*
add_subdirectory(digest)
add_subdirectory(digest_extra)
add_subdirectory(cipher)
add_subdirectory(rand)
add_subdirectory(bio)
@ -96,7 +93,7 @@ add_subdirectory(rsa)
add_subdirectory(ec)
add_subdirectory(ecdh)
add_subdirectory(ecdsa)
add_subdirectory(hmac)
add_subdirectory(hmac_extra)
# Level 3
add_subdirectory(cmac)
@ -112,8 +109,12 @@ add_subdirectory(pkcs8)
# Test support code
add_subdirectory(test)
add_subdirectory(fipsmodule)
add_library(
crypto
crypto_base
OBJECT
cpu-aarch64-linux.c
cpu-arm.c
@ -129,17 +130,31 @@ add_library(
thread_none.c
thread_pthread.c
thread_win.c
)
if(FIPS)
SET_SOURCE_FILES_PROPERTIES(fipsmodule/bcm.o PROPERTIES EXTERNAL_OBJECT true)
SET_SOURCE_FILES_PROPERTIES(fipsmodule/bcm.o PROPERTIES GENERATED true)
set(
CRYPTO_FIPS_OBJECTS
fipsmodule/bcm.o
)
endif()
add_library(
crypto
$<TARGET_OBJECTS:crypto_base>
$<TARGET_OBJECTS:stack>
$<TARGET_OBJECTS:lhash>
$<TARGET_OBJECTS:err>
$<TARGET_OBJECTS:base64>
$<TARGET_OBJECTS:bytestring>
$<TARGET_OBJECTS:pool>
$<TARGET_OBJECTS:sha>
$<TARGET_OBJECTS:md4>
$<TARGET_OBJECTS:md5>
$<TARGET_OBJECTS:digest>
$<TARGET_OBJECTS:fipsmodule>
$<TARGET_OBJECTS:digest_extra>
$<TARGET_OBJECTS:cipher>
$<TARGET_OBJECTS:modes>
$<TARGET_OBJECTS:aes>
@ -162,7 +177,6 @@ add_library(
$<TARGET_OBJECTS:ec>
$<TARGET_OBJECTS:ecdh>
$<TARGET_OBJECTS:ecdsa>
$<TARGET_OBJECTS:hmac>
$<TARGET_OBJECTS:cmac>
$<TARGET_OBJECTS:evp>
$<TARGET_OBJECTS:hkdf>
@ -170,8 +184,16 @@ add_library(
$<TARGET_OBJECTS:x509>
$<TARGET_OBJECTS:x509v3>
$<TARGET_OBJECTS:pkcs8_lib>
${CRYPTO_FIPS_OBJECTS}
)
if(FIPS)
add_dependencies(crypto bcm_o_target)
endif()
SET_TARGET_PROPERTIES(crypto PROPERTIES LINKER_LANGUAGE C)
if(NOT MSVC AND NOT ANDROID)
target_link_libraries(crypto pthread)
endif()

View File

@ -93,6 +93,11 @@ uint32_t OPENSSL_armcap_P = 0;
#endif
#if defined(BORINGSSL_FIPS)
/* In FIPS mode, the power-on self-test function calls |CRYPTO_library_init|
* because we have to ensure that CPUID detection occurs first. */
#define BORINGSSL_NO_STATIC_INITIALIZER
#endif
#if defined(OPENSSL_WINDOWS) && !defined(BORINGSSL_NO_STATIC_INITIALIZER)
#define OPENSSL_CDECL __cdecl
@ -166,5 +171,3 @@ int ENGINE_register_all_complete(void) {
}
void OPENSSL_load_builtin_modules(void) {}
int FIPS_mode(void) { return 0; }

View File

@ -1,12 +1,11 @@
include_directories(../../include)
add_library(
digest
digest_extra
OBJECT
digest.c
digests.c
digest_extra.c
)
add_executable(

View File

@ -56,206 +56,15 @@
#include <openssl/digest.h>
#include <assert.h>
#include <string.h>
#include <openssl/asn1.h>
#include <openssl/bytestring.h>
#include <openssl/md4.h>
#include <openssl/md5.h>
#include <openssl/nid.h>
#include <openssl/sha.h>
#include "internal.h"
#include "../internal.h"
#if defined(NDEBUG)
#define CHECK(x) (void) (x)
#else
#define CHECK(x) assert(x)
#endif
static void md4_init(EVP_MD_CTX *ctx) {
CHECK(MD4_Init(ctx->md_data));
}
static void md4_update(EVP_MD_CTX *ctx, const void *data, size_t count) {
CHECK(MD4_Update(ctx->md_data, data, count));
}
static void md4_final(EVP_MD_CTX *ctx, uint8_t *out) {
CHECK(MD4_Final(out, ctx->md_data));
}
static const EVP_MD md4_md = {
NID_md4, MD4_DIGEST_LENGTH, 0 /* flags */, md4_init,
md4_update, md4_final, 64 /* block size */, sizeof(MD4_CTX),
};
const EVP_MD *EVP_md4(void) { return &md4_md; }
static void md5_init(EVP_MD_CTX *ctx) {
CHECK(MD5_Init(ctx->md_data));
}
static void md5_update(EVP_MD_CTX *ctx, const void *data, size_t count) {
CHECK(MD5_Update(ctx->md_data, data, count));
}
static void md5_final(EVP_MD_CTX *ctx, uint8_t *out) {
CHECK(MD5_Final(out, ctx->md_data));
}
static const EVP_MD md5_md = {
NID_md5, MD5_DIGEST_LENGTH, 0 /* flags */, md5_init,
md5_update, md5_final, 64 /* block size */, sizeof(MD5_CTX),
};
const EVP_MD *EVP_md5(void) { return &md5_md; }
static void sha1_init(EVP_MD_CTX *ctx) {
CHECK(SHA1_Init(ctx->md_data));
}
static void sha1_update(EVP_MD_CTX *ctx, const void *data, size_t count) {
CHECK(SHA1_Update(ctx->md_data, data, count));
}
static void sha1_final(EVP_MD_CTX *ctx, uint8_t *md) {
CHECK(SHA1_Final(md, ctx->md_data));
}
static const EVP_MD sha1_md = {
NID_sha1, SHA_DIGEST_LENGTH, 0 /* flags */, sha1_init,
sha1_update, sha1_final, 64 /* block size */, sizeof(SHA_CTX),
};
const EVP_MD *EVP_sha1(void) { return &sha1_md; }
static void sha224_init(EVP_MD_CTX *ctx) {
CHECK(SHA224_Init(ctx->md_data));
}
static void sha224_update(EVP_MD_CTX *ctx, const void *data, size_t count) {
CHECK(SHA224_Update(ctx->md_data, data, count));
}
static void sha224_final(EVP_MD_CTX *ctx, uint8_t *md) {
CHECK(SHA224_Final(md, ctx->md_data));
}
static const EVP_MD sha224_md = {
NID_sha224, SHA224_DIGEST_LENGTH, 0 /* flags */,
sha224_init, sha224_update, sha224_final,
64 /* block size */, sizeof(SHA256_CTX),
};
const EVP_MD *EVP_sha224(void) { return &sha224_md; }
static void sha256_init(EVP_MD_CTX *ctx) {
CHECK(SHA256_Init(ctx->md_data));
}
static void sha256_update(EVP_MD_CTX *ctx, const void *data, size_t count) {
CHECK(SHA256_Update(ctx->md_data, data, count));
}
static void sha256_final(EVP_MD_CTX *ctx, uint8_t *md) {
CHECK(SHA256_Final(md, ctx->md_data));
}
static const EVP_MD sha256_md = {
NID_sha256, SHA256_DIGEST_LENGTH, 0 /* flags */,
sha256_init, sha256_update, sha256_final,
64 /* block size */, sizeof(SHA256_CTX),
};
const EVP_MD *EVP_sha256(void) { return &sha256_md; }
static void sha384_init(EVP_MD_CTX *ctx) {
CHECK(SHA384_Init(ctx->md_data));
}
static void sha384_update(EVP_MD_CTX *ctx, const void *data, size_t count) {
CHECK(SHA384_Update(ctx->md_data, data, count));
}
static void sha384_final(EVP_MD_CTX *ctx, uint8_t *md) {
CHECK(SHA384_Final(md, ctx->md_data));
}
static const EVP_MD sha384_md = {
NID_sha384, SHA384_DIGEST_LENGTH, 0 /* flags */,
sha384_init, sha384_update, sha384_final,
128 /* block size */, sizeof(SHA512_CTX),
};
const EVP_MD *EVP_sha384(void) { return &sha384_md; }
static void sha512_init(EVP_MD_CTX *ctx) {
CHECK(SHA512_Init(ctx->md_data));
}
static void sha512_update(EVP_MD_CTX *ctx, const void *data, size_t count) {
CHECK(SHA512_Update(ctx->md_data, data, count));
}
static void sha512_final(EVP_MD_CTX *ctx, uint8_t *md) {
CHECK(SHA512_Final(md, ctx->md_data));
}
static const EVP_MD sha512_md = {
NID_sha512, SHA512_DIGEST_LENGTH, 0 /* flags */,
sha512_init, sha512_update, sha512_final,
128 /* block size */, sizeof(SHA512_CTX),
};
const EVP_MD *EVP_sha512(void) { return &sha512_md; }
typedef struct {
MD5_CTX md5;
SHA_CTX sha1;
} MD5_SHA1_CTX;
static void md5_sha1_init(EVP_MD_CTX *md_ctx) {
MD5_SHA1_CTX *ctx = md_ctx->md_data;
CHECK(MD5_Init(&ctx->md5) && SHA1_Init(&ctx->sha1));
}
static void md5_sha1_update(EVP_MD_CTX *md_ctx, const void *data,
size_t count) {
MD5_SHA1_CTX *ctx = md_ctx->md_data;
CHECK(MD5_Update(&ctx->md5, data, count) &&
SHA1_Update(&ctx->sha1, data, count));
}
static void md5_sha1_final(EVP_MD_CTX *md_ctx, uint8_t *out) {
MD5_SHA1_CTX *ctx = md_ctx->md_data;
CHECK(MD5_Final(out, &ctx->md5) &&
SHA1_Final(out + MD5_DIGEST_LENGTH, &ctx->sha1));
}
static const EVP_MD md5_sha1_md = {
NID_md5_sha1,
MD5_DIGEST_LENGTH + SHA_DIGEST_LENGTH,
0 /* flags */,
md5_sha1_init,
md5_sha1_update,
md5_sha1_final,
64 /* block size */,
sizeof(MD5_SHA1_CTX),
};
const EVP_MD *EVP_md5_sha1(void) { return &md5_sha1_md; }
struct nid_to_digest {
int nid;

View File

@ -0,0 +1,32 @@
/* Copyright (c) 2017, Google Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
#ifndef OPENSSL_HEADER_DIGEST_EXTRA_INTERNAL_H
#define OPENSSL_HEADER_DIGEST_EXTRA_INTERNAL_H
#include <openssl/base.h>
#if defined(__cplusplus)
extern "C" {
#endif
const EVP_MD *EVP_parse_digest_algorithm(CBS *cbs);
#if defined(__cplusplus)
} /* extern C */
#endif
#endif /* OPENSSL_HEADER_DIGEST_EXTRA_INTERNAL */

View File

@ -58,7 +58,7 @@
#include <openssl/err.h>
#include "internal.h"
#include "../digest/internal.h"
#include "../fipsmodule/digest/internal.h"
static const struct evp_md_pctx_ops md_pctx_ops = {

View File

@ -0,0 +1,179 @@
include_directories(../../include)
if (${ARCH} STREQUAL "x86_64")
set(
BCM_ASM_SOURCES
md5-x86_64.${ASM_EXT}
sha1-x86_64.${ASM_EXT}
sha256-x86_64.${ASM_EXT}
sha512-x86_64.${ASM_EXT}
)
endif()
if (${ARCH} STREQUAL "x86")
set(
BCM_ASM_SOURCES
md5-586.${ASM_EXT}
sha1-586.${ASM_EXT}
sha256-586.${ASM_EXT}
sha512-586.${ASM_EXT}
)
endif()
if (${ARCH} STREQUAL "arm")
set(
BCM_ASM_SOURCES
sha1-armv4-large.${ASM_EXT}
sha256-armv4.${ASM_EXT}
sha512-armv4.${ASM_EXT}
)
endif()
if (${ARCH} STREQUAL "aarch64")
set(
BCM_ASM_SOURCES
sha1-armv8.${ASM_EXT}
sha256-armv8.${ASM_EXT}
sha512-armv8.${ASM_EXT}
)
endif()
perlasm(md5-586.${ASM_EXT} md5/asm/md5-586.pl)
perlasm(md5-x86_64.${ASM_EXT} md5/asm/md5-x86_64.pl)
perlasm(sha1-586.${ASM_EXT} sha/asm/sha1-586.pl)
perlasm(sha1-armv4-large.${ASM_EXT} sha/asm/sha1-armv4-large.pl)
perlasm(sha1-armv8.${ASM_EXT} sha/asm/sha1-armv8.pl)
perlasm(sha1-x86_64.${ASM_EXT} sha/asm/sha1-x86_64.pl)
perlasm(sha256-586.${ASM_EXT} sha/asm/sha256-586.pl)
perlasm(sha256-armv4.${ASM_EXT} sha/asm/sha256-armv4.pl)
perlasm(sha256-armv8.${ASM_EXT} sha/asm/sha512-armv8.pl)
perlasm(sha256-x86_64.${ASM_EXT} sha/asm/sha512-x86_64.pl)
perlasm(sha512-586.${ASM_EXT} sha/asm/sha512-586.pl)
perlasm(sha512-armv4.${ASM_EXT} sha/asm/sha512-armv4.pl)
perlasm(sha512-armv8.${ASM_EXT} sha/asm/sha512-armv8.pl)
perlasm(sha512-x86_64.${ASM_EXT} sha/asm/sha512-x86_64.pl)
if(FIPS)
add_library(
bcm_c_generated_asm
STATIC
bcm.c
)
SET_TARGET_PROPERTIES(bcm_c_generated_asm PROPERTIES COMPILE_OPTIONS "-S")
SET_TARGET_PROPERTIES(bcm_c_generated_asm PROPERTIES POSITION_INDEPENDENT_CODE ON)
function(JOIN VALUES GLUE OUTPUT)
string (REPLACE ";" "${GLUE}" _TMP_STR "${VALUES}")
set (${OUTPUT} "${_TMP_STR}" PARENT_SCOPE)
endfunction()
JOIN("${BCM_ASM_SOURCES}" ",${CMAKE_CURRENT_BINARY_DIR}/" BCM_ASM_SOURCES_COMMA_SEP)
add_custom_command(
OUTPUT bcm-delocated.S
COMMAND ${GO_EXECUTABLE} run crypto/fipsmodule/delocate.go crypto/fipsmodule/ar.go -a $<TARGET_FILE:bcm_c_generated_asm> -as ${CMAKE_CURRENT_BINARY_DIR}/${BCM_ASM_SOURCES_COMMA_SEP} -o ${CMAKE_CURRENT_BINARY_DIR}/bcm-delocated.S
DEPENDS bcm_c_generated_asm ${BCM_ASM_SOURCES} delocate.go ar.go
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
)
add_library(
bcm_hashunset
STATIC
bcm-delocated.S
)
set_target_properties(bcm_hashunset PROPERTIES POSITION_INDEPENDENT_CODE ON)
set_target_properties(bcm_hashunset PROPERTIES LINKER_LANGUAGE C)
add_executable(
bcm_hashunset_test
bcm_hashunset_test.c
$<TARGET_OBJECTS:crypto_base>
$<TARGET_OBJECTS:stack>
$<TARGET_OBJECTS:lhash>
$<TARGET_OBJECTS:err>
$<TARGET_OBJECTS:base64>
$<TARGET_OBJECTS:bytestring>
$<TARGET_OBJECTS:pool>
$<TARGET_OBJECTS:digest_extra>
$<TARGET_OBJECTS:cipher>
$<TARGET_OBJECTS:modes>
$<TARGET_OBJECTS:aes>
$<TARGET_OBJECTS:des>
$<TARGET_OBJECTS:rc4>
$<TARGET_OBJECTS:conf>
$<TARGET_OBJECTS:chacha>
$<TARGET_OBJECTS:poly1305>
$<TARGET_OBJECTS:curve25519>
$<TARGET_OBJECTS:buf>
$<TARGET_OBJECTS:bn>
$<TARGET_OBJECTS:bio>
$<TARGET_OBJECTS:rand>
$<TARGET_OBJECTS:obj>
$<TARGET_OBJECTS:asn1>
$<TARGET_OBJECTS:engine>
$<TARGET_OBJECTS:dh>
$<TARGET_OBJECTS:dsa>
$<TARGET_OBJECTS:rsa>
$<TARGET_OBJECTS:ec>
$<TARGET_OBJECTS:ecdh>
$<TARGET_OBJECTS:ecdsa>
$<TARGET_OBJECTS:cmac>
$<TARGET_OBJECTS:evp>
$<TARGET_OBJECTS:hkdf>
$<TARGET_OBJECTS:pem>
$<TARGET_OBJECTS:x509>
$<TARGET_OBJECTS:x509v3>
$<TARGET_OBJECTS:pkcs8_lib>
)
target_link_libraries(bcm_hashunset_test bcm_hashunset)
if(NOT MSVC AND NOT ANDROID)
target_link_libraries(bcm_hashunset_test pthread)
endif()
add_custom_command(
OUTPUT bcm.o
COMMAND ${GO_EXECUTABLE} run crypto/fipsmodule/inject-hash.go crypto/fipsmodule/ar.go -o ${CMAKE_CURRENT_BINARY_DIR}/bcm.o -in $<TARGET_FILE:bcm_hashunset> -bin $<TARGET_FILE:bcm_hashunset_test>
DEPENDS bcm_hashunset_test bcm_hashunset inject-hash.go ar.go
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
)
# The outputs of add_custom_command cannot be referenced outside of the
# CMakeLists.txt that defines it. Thus we have to wrap bcm.o in a custom target
# so that crypto can depend on it.
add_custom_target(bcm_o_target DEPENDS bcm.o)
add_library(
fipsmodule
OBJECT
is_fips.c
)
set_target_properties(fipsmodule PROPERTIES LINKER_LANGUAGE C)
else()
add_library(
fipsmodule
OBJECT
bcm.c
is_fips.c
${BCM_ASM_SOURCES}
)
endif()

74
crypto/fipsmodule/FIPS.md Normal file
View File

@ -0,0 +1,74 @@
## 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.
BoringSSL 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 BoringSSL 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. This document describes how we build C and assembly code in order to produce an object file containing all the code and data for the FIPS module without that code having any relocations.
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`). 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 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.
##### 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.
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).
![module structure](/crypto/fipsmodule/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
Normally read-only data is placed in a `.data` 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.
One special case is `rel.ro` data, which is data that contains 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, we'll change this pattern to instead to reserve space in the BSS for the object, and add a `CRYPTO_once_t` to protect its initialisation. Functions outside of the module are used to access the reserved space—they effectively act like a special-purpose `malloc` calls that cannot fail. This is implemented by the `DEFINE_METHOD_FUNCTION` macro.
##### 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.
Thankfully, mutable data is very rare in our cryptographic code and I hope that we can get it down to just a few variables. 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
The script performs a number of other transformations which are worth noting but do not warrant their own sections:
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. Everything in `nonmodule_rodata` and `nonmodule_text` segments is moved to the bottom of the text segment, outside the module.
1. Various sections, notably `.rodata`, are moved to the `.text` section, inside the module, so module code may reference it without relocations.
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 rewrites some "dummy" references to point to those labels and that array. In order to get the C compiler to emit the correct code it's necessary to make it think that it's referencing static functions. This compensates for that trick.
##### 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 runs the module after an object file has been produced to get the calculated value which it then injects back into the object file.
![build process](/crypto/fipsmodule/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.)
OpenSSL's solution to this problem is broadly similar but has a number of differences:
1. OpenSSL deals with run-time relocations by not hashing parts of the module's data. BoringSSL will eliminate run-time relocations instead and hash everything.
1. OpenSSL uses `ld -r` (the partial linking mode) to merge a number of object files into their `fipscanister.o`. For BoringSSL, we propose to merge all the C source files by building a single C file that #includes all the others, then we propose to merge the assembly sources by concatenating 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. BoringSSL proposes to simply add labels at the correct places in the assembly.
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. BoringSSL 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. BoringSSL indirects these loads and stores.
1. OpenSSL doesn't run the power-on test until `FIPS_module_mode_set` is called, BoringSSL plans to do it in a constructor function. Failure of the test is non-fatal in OpenSSL, BoringSSL plans to 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 BoringSSL's module is fixed after compilation, the final integrity check is unbroken through the linking process.
Some of the similarities are worth noting:
1. OpenSSL has all out-calls from the module indirecting via the PLT, which is equivalent to the redirector functions described above.
![OpenSSL build process](/crypto/fipsmodule/intcheck3.png)

181
crypto/fipsmodule/ar.go Normal file
View File

@ -0,0 +1,181 @@
// Copyright (c) 2017, Google Inc.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
// ar.go contains functions for parsing .a archive files.
package main
import (
"fmt"
"io"
"io/ioutil"
"os"
"strconv"
"strings"
"time"
)
const (
// the string which begins a proper archive
arMagic = "!<arch>\n"
// the magic numbers for individual file headers
fileMagic = "`\n"
headerSize = 60
)
// An ARHeader represents a single header in an ar archive.
type ARHeader struct {
Name string
ModTime time.Time
UID int
GID int
Mode os.FileMode
Size int64
}
type slicer []byte
func (sp *slicer) next(n int) (b []byte) {
s := *sp
b, *sp = s[0:n], s[n:]
return
}
// A Reader provides sequential access to the contents of an ar archive.
// The Next method advances to the next file in the archive (including
// the first), and then it can be treated as an io.Reader to access the
// file's data.
type AR struct {
r io.Reader
err error
nb int64 // number of unread bytes for current file entry
pad int // amount of padding after current file entry
}
// NewReader returns a reader for the members of the provided ar archive.
func NewAR(r io.Reader) *AR {
magiclen := len(arMagic)
buf := make([]byte, magiclen)
_, err := io.ReadFull(r, buf)
if err != nil || arMagic != string(buf) {
err = fmt.Errorf("ar: bad magic number %v in ar file header", buf)
}
return &AR{r: r, err: err}
}
// Next advances the reader to the next file in the archive.
func (ar *AR) Next() (*ARHeader, error) {
var hdr *ARHeader
if ar.err == nil {
ar.skipUnread()
}
if ar.err == nil {
hdr = ar.readHeader()
}
return hdr, ar.err
}
func (ar *AR) cvt(b []byte, base int) int64 {
// Removing leading spaces
for len(b) > 0 && b[0] == ' ' {
b = b[1:]
}
// Removing trailing NULs and spaces.
for len(b) > 0 && (b[len(b)-1] == ' ' || b[len(b)-1] == '\x00') {
b = b[:len(b)-1]
}
if len(b) == 0 {
return 0
}
x, err := strconv.ParseUint(string(b), base, 64)
if err != nil {
ar.err = err
}
return int64(x)
}
// Skip any unused bytes in the existing file entry, as well as any alignment padding.
func (ar *AR) skipUnread() {
nr := ar.nb + int64(ar.pad)
ar.nb, ar.pad = 0, 0
if sr, ok := ar.r.(io.Seeker); ok {
if _, err := sr.Seek(nr, io.SeekCurrent); err == nil {
return
}
}
_, ar.err = io.CopyN(ioutil.Discard, ar.r, nr)
}
func (ar *AR) readHeader() *ARHeader {
var n int
header := make([]byte, headerSize)
n, ar.err = io.ReadFull(ar.r, header)
if ar.err == io.ErrUnexpectedEOF {
ar.err = fmt.Errorf("ar: short header in ar archive; got %d bytes, want %d", n, headerSize)
}
if ar.err != nil {
// io.EOF will get passed through
return nil
}
hdr := new(ARHeader)
s := slicer(header)
hdr.Name = strings.TrimRight(string(s.next(16)), " ")
hdr.Name = strings.TrimRight(hdr.Name, "/")
hdr.ModTime = time.Unix(ar.cvt(s.next(12), 10), 0)
hdr.UID = int(ar.cvt(s.next(6), 10))
hdr.GID = int(ar.cvt(s.next(6), 10))
hdr.Mode = os.FileMode(ar.cvt(s.next(8), 8))
hdr.Size = ar.cvt(s.next(10), 10)
magic := string(s.next(2))
if magic != fileMagic {
ar.err = fmt.Errorf("ar: bad magic number %v in ar member header", magic)
return nil
}
ar.nb = int64(hdr.Size)
// at most one pad byte just to be even
ar.pad = int(ar.nb & 1)
return hdr
}
// Read reads from the current entry in the ar archive.
// It returns 0, io.EOF when it reaches the end of that entry,
// until Next is called to advance to the next entry.
func (ar *AR) Read(b []byte) (n int, err error) {
if ar.nb == 0 {
// file consumed
return 0, io.EOF
}
// trim read to the amount available
if int64(len(b)) > ar.nb {
b = b[0:ar.nb]
}
n, err = ar.r.Read(b)
ar.nb -= int64(n)
if err == io.EOF && ar.nb > 0 {
// archive ended while more file contents expected
err = io.ErrUnexpectedEOF
}
ar.err = err
return
}

100
crypto/fipsmodule/bcm.c Normal file
View File

@ -0,0 +1,100 @@
/* Copyright (c) 2017, Google Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
#include <openssl/base.h>
#include <openssl/cpu.h>
#include <openssl/crypto.h>
#include <openssl/hmac.h>
#include "../internal.h"
#include "./delocate.h"
#include "digest/digest.c"
#include "digest/digests.c"
#include "hmac/hmac.c"
#include "md4/md4.c"
#include "md5/md5.c"
#include "sha/sha1-altivec.c"
#include "sha/sha1.c"
#include "sha/sha256.c"
#include "sha/sha512.c"
#if defined(BORINGSSL_FIPS)
static void hexdump(const uint8_t *in, size_t len) {
for (size_t i = 0; i < len; i++) {
printf("%02x", in[i]);
}
}
/* These functions are removed by delocate.go and references to them are
* rewritten to point to the start and end of the module, and the location of
* the integrity hash. */
static void BORINGSSL_bcm_text_dummy_start(void) {}
static void BORINGSSL_bcm_text_dummy_end(void) {}
/* BORINGSSL_bcm_text_hash is outside the module so it may be filled in with the
* correct hash without a circular dependency. This must match the value used
* in inject-hash.go. */
NONMODULE_RODATA static const uint8_t BORINGSSL_bcm_text_hash[32] = {
0x5f, 0x30, 0xd1, 0x80, 0xe7, 0x9e, 0x8f, 0x8f, 0xdf, 0x8b, 0x93,
0xd4, 0x96, 0x36, 0x30, 0xcc, 0x30, 0xea, 0x38, 0x0f, 0x75, 0x56,
0x9a, 0x1b, 0x23, 0x2f, 0x7c, 0x79, 0xff, 0x1b, 0x2b, 0xca,
};
static void BORINGSSL_bcm_power_on_self_test(void) __attribute__((constructor));
static void BORINGSSL_bcm_power_on_self_test(void) {
CRYPTO_library_init();
const uint8_t *const start = (const uint8_t *)BORINGSSL_bcm_text_dummy_start;
const uint8_t *const end = (const uint8_t *)BORINGSSL_bcm_text_dummy_end;
static const uint8_t kHMACKey[32] = {0};
uint8_t result[SHA256_DIGEST_LENGTH];
unsigned result_len;
if (!HMAC(EVP_sha256(), kHMACKey, sizeof(kHMACKey), start, end - start,
result, &result_len) ||
result_len != sizeof(result)) {
goto err;
}
const uint8_t *const expected = BORINGSSL_bcm_text_hash;
if (OPENSSL_memcmp(expected, result, sizeof(result)) != 0) {
printf("FIPS integrity test failed.\nExpected: ");
hexdump(expected, sizeof(result));
printf("\nCalculated: ");
hexdump(result, sizeof(result));
printf("\n");
goto err;
}
// TODO(fips): KAT tests go here.
return;
err:
for (;;) {
exit(1);
abort();
}
}
#endif /* BORINGSSL_FIPS */
#if defined(OPENSSL_X86) || defined(OPENSSL_X86_64)
/* OPENSSL_ia32cap_addr is outside the FIPS module so the FIPS module may locate
* the address of |OPENSSL_ia32cap_P| without a relocation. */
NONMODULE_RODATA uint32_t *const OPENSSL_ia32cap_addr = OPENSSL_ia32cap_P;
#endif

View File

@ -0,0 +1,11 @@
#include <openssl/digest.h>
#if !defined(BORINGSSL_FIPS)
#error "This file should not be built outside of the FIPS build."
#endif
int main(void) {
/* This program only needs to trigger the FIPS power-on self-test. */
EVP_sha256();
return 0;
}

View File

@ -0,0 +1,421 @@
// Copyright (c) 2017, Google Inc.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
// delocate performs several transformations of textual assembly code. See
// FIPS.md in this directory for an overview.
package main
import (
"bufio"
"flag"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"unicode/utf8"
)
func main() {
// The .a file, if given, is expected to be an archive of textual
// assembly sources. That's odd, but CMake really wants to create
// archive files so it's the only way that we can make it work.
arInput := flag.String("a", "", "Path to a .a file containing assembly sources")
outFile := flag.String("o", "", "Path to output assembly")
asmFiles := flag.String("as", "", "Comma separated list of assembly inputs")
flag.Parse()
var lines []string
var err error
if len(*arInput) > 0 {
if lines, err = arLines(lines, *arInput); err != nil {
panic(err)
}
}
asPaths := strings.Split(*asmFiles, ",")
for i, path := range asPaths {
if lines, err = asLines(lines, path, i); err != nil {
panic(err)
}
}
symbols := definedSymbols(lines)
lines = transform(lines, symbols)
out, err := os.OpenFile(*outFile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
if err != nil {
panic(err)
}
defer out.Close()
for _, line := range lines {
out.WriteString(line)
out.WriteString("\n")
}
}
// isSymbolDef returns detects whether line contains a (non-local) symbol
// definition. If so, it returns the symbol and true. Otherwise it returns ""
// and false.
func isSymbolDef(line string) (string, bool) {
line = strings.TrimSpace(line)
if len(line) > 0 && line[len(line)-1] == ':' && line[0] != '.' {
symbol := line[:len(line)-1]
if validSymbolName(symbol) {
return symbol, true
}
}
return "", false
}
// definedSymbols finds all (non-local) symbols from lines and returns a map
// from symbol name to whether or not that symbol is global.
func definedSymbols(lines []string) map[string]bool {
globalSymbols := make(map[string]struct{})
symbols := make(map[string]bool)
for _, line := range lines {
if len(line) == 0 {
continue
}
if symbol, ok := isSymbolDef(line); ok {
_, isGlobal := globalSymbols[symbol]
symbols[symbol] = isGlobal
}
parts := strings.Fields(strings.TrimSpace(line))
if parts[0] == ".globl" {
globalSymbols[parts[1]] = struct{}{}
}
}
return symbols
}
// transform performs a number of transformations on the given assembly code.
// See FIPS.md in the current directory for an overview.
func transform(lines []string, symbols map[string]bool) (ret []string) {
ret = append(ret, ".text", "BORINGSSL_bcm_text_start:")
// redirectors maps from out-call symbol name to the name of a
// redirector function for that symbol.
redirectors := make(map[string]string)
// extractedText contains lines that have been extracted from the
// assembly and need to be moved outside of the module. (This is used
// for the .init_array section that specifies constructors.)
var extractedText []string
extractingText := false
// dropping is true iff the current part of the assembly file is being
// dropped from the final output.
dropping := false
for lineNo, line := range lines {
if strings.Contains(line, "OPENSSL_ia32cap_P(%rip)") {
panic("reference to OPENSSL_ia32cap_P needs to be changed to indirect via OPENSSL_ia32cap_addr")
}
parts := strings.Fields(strings.TrimSpace(line))
if len(parts) == 0 {
ret = append(ret, line)
continue
}
switch parts[0] {
case ".type":
if strings.HasPrefix(parts[1], "BORINGSSL_bcm_text_dummy_") {
// This is a dummy function, drop it.
dropping = true
}
case ".size":
if strings.HasPrefix(parts[1], "BORINGSSL_bcm_text_dummy_") {
// End of dummy function that we dropped.
dropping = false
continue
}
case ".file":
// These directives must be included even when in a
// dummy function.
ret = append(ret, line)
continue
}
// Symbol definitions inside dropped functions cannot be
// eliminated because the debug information may reference them.
if dropping && !strings.HasSuffix(line, ":") {
continue
}
switch parts[0] {
case "call", "jmp":
target := parts[1]
// indirect via register or local label
if strings.HasPrefix(target, "*") || strings.HasPrefix(target, ".L") {
ret = append(ret, line)
continue
}
if isGlobal, ok := symbols[target]; ok {
newTarget := target
if isGlobal {
newTarget = localTargetName(target)
}
ret = append(ret, fmt.Sprintf("\t%s %s", parts[0], newTarget))
continue
}
redirectorName := "bcm_redirector_" + target
if strings.HasSuffix(target, "@PLT") {
withoutPLT := target[:len(target)-4]
if isGlobal, ok := symbols[withoutPLT]; ok {
newTarget := withoutPLT
if isGlobal {
newTarget = localTargetName(withoutPLT)
}
ret = append(ret, fmt.Sprintf("\t%s %s", parts[0], newTarget))
continue
}
redirectorName = redirectorName[:len(redirectorName)-4]
}
ret = append(ret, fmt.Sprintf("\t%s %s", parts[0], redirectorName))
redirectors[redirectorName] = target
continue
case ".section":
extractingText = false
p := strings.Split(parts[1], ",")
section := p[0]
if section == ".rodata" || section == ".text.startup" || strings.HasPrefix(section, ".rodata.") {
// Move .rodata to .text so it may be accessed
// without a relocation. GCC with
// -fmerge-constants will place strings into
// separate sections, so we move all sections
// named like .rodata. Also move .text.startup
// so the self-test function is also in the
// module.
ret = append(ret, ".text # "+section)
break
}
switch section {
case ".data", ".data.rel.ro.local":
panic(fmt.Sprintf("bad section %q on line %d", parts[1], lineNo+1))
case ".init_array":
// init_array contains function pointers to
// constructor functions. Since these must be
// relocated, this section is moved to the end
// of the file.
extractedText = append(extractedText, line)
extractingText = true
case "nonmodule_rodata", "nonmodule_text":
// Symbols in either nonmodule_rodata or
// nonmodule_text get moved to the text section
// outside the module. These are used to avoid
// relocations in referencing data.
extractedText = append(extractedText, ".text # "+section)
extractingText = true
default:
ret = append(ret, line)
}
case ".text":
extractingText = false
fallthrough
default:
if extractingText {
extractedText = append(extractedText, line)
continue
}
if symbol, ok := isSymbolDef(line); ok {
if isGlobal := symbols[symbol]; isGlobal {
ret = append(ret, localTargetName(symbol)+":")
}
}
if parts[0] == "leaq" {
line = strings.Replace(line, "BORINGSSL_bcm_text_dummy_", "BORINGSSL_bcm_text_", -1)
}
ret = append(ret, line)
}
}
ret = append(ret, "BORINGSSL_bcm_text_end:")
// Emit redirector functions. Each is a single JMP instruction.
for redirectorName, target := range redirectors {
ret = append(ret, ".type "+redirectorName+", @function")
ret = append(ret, redirectorName+":")
ret = append(ret, "\tjmp "+target)
}
ret = append(ret, extractedText...)
return ret
}
// localTargetName returns the name of the local target label for a global
// symbol named name.
func localTargetName(name string) string {
return ".L" + name + "_local_target"
}
// asLines appends the contents of path to lines. Local symbols are renamed
// using uniqueId to avoid collisions.
func asLines(lines []string, path string, uniqueId int) ([]string, error) {
basename := symbolRuneOrUnderscore(filepath.Base(path))
asFile, err := os.Open(path)
if err != nil {
return nil, err
}
defer asFile.Close()
var contents []string
// localSymbols maps from the symbol name used in the input, to a
// unique symbol name.
localSymbols := make(map[string]string)
scanner := bufio.NewScanner(asFile)
for scanner.Scan() {
line := scanner.Text()
trimmed := strings.TrimSpace(line)
if strings.HasPrefix(trimmed, ".L") && strings.HasSuffix(trimmed, ":") {
symbol := trimmed[:len(trimmed)-1]
mappedSymbol := fmt.Sprintf(".L%s_%d_%s", basename, uniqueId, symbol[2:])
localSymbols[symbol] = mappedSymbol
contents = append(contents, mappedSymbol+":")
continue
}
contents = append(contents, scanner.Text())
}
if err := scanner.Err(); err != nil {
return nil, err
}
for _, line := range contents {
for symbol, mappedSymbol := range localSymbols {
for i := strings.Index(line, symbol); i >= 0; i = strings.Index(line[i:], symbol) {
before := ' '
if i > 0 {
before, _ = utf8.DecodeLastRuneInString(line[:i])
}
after, _ := utf8.DecodeRuneInString(line[i+len(symbol):])
if !symbolRune(before) && !symbolRune(after) {
line = strings.Replace(line, symbol, mappedSymbol, 1)
i += len(mappedSymbol)
} else {
i += len(symbol)
}
}
}
lines = append(lines, line)
}
return lines, nil
}
func arLines(lines []string, arPath string) ([]string, error) {
arFile, err := os.Open(arPath)
if err != nil {
return nil, err
}
defer arFile.Close()
ar := NewAR(arFile)
for {
header, err := ar.Next()
if err == io.EOF {
return lines, nil
}
if err != nil {
return nil, err
}
if len(header.Name) == 0 {
continue
}
scanner := bufio.NewScanner(ar)
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
if err := scanner.Err(); err != nil {
return nil, err
}
}
}
// validSymbolName returns true if s is a valid (non-local) name for a symbol.
func validSymbolName(s string) bool {
if len(s) == 0 {
return false
}
r, n := utf8.DecodeRuneInString(s)
// symbols don't start with a digit.
if r == utf8.RuneError || !symbolRune(r) || ('0' <= s[0] && s[0] <= '9') {
return false
}
return strings.IndexFunc(s[n:], func(r rune) bool {
return !symbolRune(r)
}) == -1
}
// symbolRune returns true if r is valid in a symbol name.
func symbolRune(r rune) bool {
return (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') || r == '$' || r == '_'
}
// symbolRuneOrUnderscore maps s where runes valid in a symbol name map to
// themselves and all other runs map to underscore.
func symbolRuneOrUnderscore(s string) string {
runes := make([]rune, 0, len(s))
for _, r := range s {
if symbolRune(r) {
runes = append(runes, r)
} else {
runes = append(runes, '_')
}
}
return string(runes)
}

View File

@ -0,0 +1,78 @@
/* Copyright (c) 2017, Google Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
#ifndef OPENSSL_HEADER_FIPSMODULE_DELOCATE_H
#define OPENSSL_HEADER_FIPSMODULE_DELOCATE_H
#include <openssl/base.h>
#include "../internal.h"
#if defined(BORINGSSL_FIPS)
/* NONMODULE_RODATA, in FIPS mode, causes delocate.go to move the specified
* const global to the unhashed non-module area of code located after the
* module. */
#define NONMODULE_RODATA __attribute__((section("nonmodule_rodata")))
/* NONMODULE_TEXT, in FIPS mode, causes delocate.go to move the specified
* function to the unhashed non-module area of code located after the module. We
* mark such functions noinline to prevent module callers from inlining the
* relocations into themselves. */
#define NONMODULE_TEXT __attribute__((section("nonmodule_text"), noinline))
/* DEFINE_METHOD_FUNCTION defines a function named |name| which returns a method
* table of type const |type|*, initialized by |initializer|. In FIPS mode, to
* avoid rel.ro data, it is split into a CRYPTO_once_t-guarded initializer in
* the module and unhashed, non-module accessor functions to space reserved in
* the BSS. This does not use a static initializer because their execution order
* is undefined. See FIPS.md for more details.
*
* __VA_ARGS__ is used to get around macros not allowing arguments with
* commas. */
#define DEFINE_METHOD_FUNCTION(type, name, ... /* initializer */) \
NONMODULE_TEXT static type *name##_bss_get(void) { \
static type ret; \
return &ret; \
} \
\
NONMODULE_TEXT static CRYPTO_once_t *name##_once_get(void) { \
static CRYPTO_once_t ret = CRYPTO_ONCE_INIT; \
return &ret; \
} \
\
static void name##_init(void) { \
type ret = __VA_ARGS__; \
OPENSSL_memcpy(name##_bss_get(), &ret, sizeof(ret)); \
} \
\
const type *name(void) { \
CRYPTO_once(name##_once_get(), name##_init); \
return name##_bss_get(); \
}
#else /* !BORINGSSL_FIPS */
#define NONMODULE_RODATA
#define NONMODULE_TEXT
#define DEFINE_METHOD_FUNCTION(type, name, ...) \
const type *name(void) { \
static const type ret = __VA_ARGS__; \
return &ret; \
}
#endif /* BORINGSSL_FIPS */
#endif /* OPENSSL_HEADER_FIPSMODULE_DELOCATE_H */

View File

@ -63,7 +63,7 @@
#include <openssl/mem.h>
#include "internal.h"
#include "../internal.h"
#include "../../internal.h"
int EVP_MD_type(const EVP_MD *md) { return md->type; }

View File

@ -0,0 +1,250 @@
/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
* All rights reserved.
*
* This package is an SSL implementation written
* by Eric Young (eay@cryptsoft.com).
* The implementation was written so as to conform with Netscapes SSL.
*
* This library is free for commercial and non-commercial use as long as
* the following conditions are aheared to. The following conditions
* apply to all code found in this distribution, be it the RC4, RSA,
* lhash, DES, etc., code; not just the SSL code. The SSL documentation
* included with this distribution is covered by the same copyright terms
* except that the holder is Tim Hudson (tjh@cryptsoft.com).
*
* Copyright remains Eric Young's, and as such any Copyright notices in
* the code are not to be removed.
* If this package is used in a product, Eric Young should be given attribution
* as the author of the parts of the library used.
* This can be in the form of a textual message at program startup or
* in documentation (online or textual) provided with the package.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* "This product includes cryptographic software written by
* Eric Young (eay@cryptsoft.com)"
* The word 'cryptographic' can be left out if the rouines from the library
* being used are not cryptographic related :-).
* 4. If you include any Windows specific code (or a derivative thereof) from
* the apps directory (application code) you must include an acknowledgement:
* "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
*
* THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* The licence and distribution terms for any publically available version or
* derivative of this code cannot be changed. i.e. this code cannot simply be
* copied and put under another distribution licence
* [including the GNU Public Licence.] */
#include <openssl/digest.h>
#include <assert.h>
#include <string.h>
#include <openssl/md4.h>
#include <openssl/md5.h>
#include <openssl/nid.h>
#include <openssl/sha.h>
#include "internal.h"
#include "../delocate.h"
#include "../../internal.h"
#if defined(NDEBUG)
#define CHECK(x) (void) (x)
#else
#define CHECK(x) assert(x)
#endif
static void md4_init(EVP_MD_CTX *ctx) {
CHECK(MD4_Init(ctx->md_data));
}
static void md4_update(EVP_MD_CTX *ctx, const void *data, size_t count) {
CHECK(MD4_Update(ctx->md_data, data, count));
}
static void md4_final(EVP_MD_CTX *ctx, uint8_t *out) {
CHECK(MD4_Final(out, ctx->md_data));
}
DEFINE_METHOD_FUNCTION(EVP_MD, EVP_md4,
{
NID_md4, MD4_DIGEST_LENGTH, 0 /* flags */, md4_init,
md4_update, md4_final, 64 /* block size */,
sizeof(MD4_CTX),
})
static void md5_init(EVP_MD_CTX *ctx) {
CHECK(MD5_Init(ctx->md_data));
}
static void md5_update(EVP_MD_CTX *ctx, const void *data, size_t count) {
CHECK(MD5_Update(ctx->md_data, data, count));
}
static void md5_final(EVP_MD_CTX *ctx, uint8_t *out) {
CHECK(MD5_Final(out, ctx->md_data));
}
DEFINE_METHOD_FUNCTION(EVP_MD, EVP_md5,
{
NID_md5, MD5_DIGEST_LENGTH, 0 /* flags */, md5_init,
md5_update, md5_final, 64 /* block size */,
sizeof(MD5_CTX),
})
static void sha1_init(EVP_MD_CTX *ctx) {
CHECK(SHA1_Init(ctx->md_data));
}
static void sha1_update(EVP_MD_CTX *ctx, const void *data, size_t count) {
CHECK(SHA1_Update(ctx->md_data, data, count));
}
static void sha1_final(EVP_MD_CTX *ctx, uint8_t *md) {
CHECK(SHA1_Final(md, ctx->md_data));
}
DEFINE_METHOD_FUNCTION(EVP_MD, EVP_sha1,
{
NID_sha1, SHA_DIGEST_LENGTH, 0 /* flags */,
sha1_init, sha1_update, sha1_final,
64 /* block size */, sizeof(SHA_CTX),
})
static void sha224_init(EVP_MD_CTX *ctx) {
CHECK(SHA224_Init(ctx->md_data));
}
static void sha224_update(EVP_MD_CTX *ctx, const void *data, size_t count) {
CHECK(SHA224_Update(ctx->md_data, data, count));
}
static void sha224_final(EVP_MD_CTX *ctx, uint8_t *md) {
CHECK(SHA224_Final(md, ctx->md_data));
}
DEFINE_METHOD_FUNCTION(EVP_MD, EVP_sha224,
{
NID_sha224, SHA224_DIGEST_LENGTH, 0 /* flags */,
sha224_init, sha224_update, sha224_final,
64 /* block size */, sizeof(SHA256_CTX),
})
static void sha256_init(EVP_MD_CTX *ctx) {
CHECK(SHA256_Init(ctx->md_data));
}
static void sha256_update(EVP_MD_CTX *ctx, const void *data, size_t count) {
CHECK(SHA256_Update(ctx->md_data, data, count));
}
static void sha256_final(EVP_MD_CTX *ctx, uint8_t *md) {
CHECK(SHA256_Final(md, ctx->md_data));
}
DEFINE_METHOD_FUNCTION(EVP_MD, EVP_sha256,
{
NID_sha256, SHA256_DIGEST_LENGTH, 0 /* flags */,
sha256_init, sha256_update, sha256_final,
64 /* block size */, sizeof(SHA256_CTX),
})
static void sha384_init(EVP_MD_CTX *ctx) {
CHECK(SHA384_Init(ctx->md_data));
}
static void sha384_update(EVP_MD_CTX *ctx, const void *data, size_t count) {
CHECK(SHA384_Update(ctx->md_data, data, count));
}
static void sha384_final(EVP_MD_CTX *ctx, uint8_t *md) {
CHECK(SHA384_Final(md, ctx->md_data));
}
DEFINE_METHOD_FUNCTION(EVP_MD, EVP_sha384,
{
NID_sha384, SHA384_DIGEST_LENGTH, 0 /* flags */,
sha384_init, sha384_update, sha384_final,
128 /* block size */, sizeof(SHA512_CTX),
})
static void sha512_init(EVP_MD_CTX *ctx) {
CHECK(SHA512_Init(ctx->md_data));
}
static void sha512_update(EVP_MD_CTX *ctx, const void *data, size_t count) {
CHECK(SHA512_Update(ctx->md_data, data, count));
}
static void sha512_final(EVP_MD_CTX *ctx, uint8_t *md) {
CHECK(SHA512_Final(md, ctx->md_data));
}
DEFINE_METHOD_FUNCTION(EVP_MD, EVP_sha512,
{
NID_sha512, SHA512_DIGEST_LENGTH, 0 /* flags */,
sha512_init, sha512_update, sha512_final,
128 /* block size */, sizeof(SHA512_CTX),
})
typedef struct {
MD5_CTX md5;
SHA_CTX sha1;
} MD5_SHA1_CTX;
static void md5_sha1_init(EVP_MD_CTX *md_ctx) {
MD5_SHA1_CTX *ctx = md_ctx->md_data;
CHECK(MD5_Init(&ctx->md5) && SHA1_Init(&ctx->sha1));
}
static void md5_sha1_update(EVP_MD_CTX *md_ctx, const void *data,
size_t count) {
MD5_SHA1_CTX *ctx = md_ctx->md_data;
CHECK(MD5_Update(&ctx->md5, data, count) &&
SHA1_Update(&ctx->sha1, data, count));
}
static void md5_sha1_final(EVP_MD_CTX *md_ctx, uint8_t *out) {
MD5_SHA1_CTX *ctx = md_ctx->md_data;
CHECK(MD5_Final(out, &ctx->md5) &&
SHA1_Final(out + MD5_DIGEST_LENGTH, &ctx->sha1));
}
DEFINE_METHOD_FUNCTION(EVP_MD, EVP_md5_sha1,
{
NID_md5_sha1, MD5_DIGEST_LENGTH + SHA_DIGEST_LENGTH,
0 /* flags */, md5_sha1_init, md5_sha1_update,
md5_sha1_final, 64 /* block size */,
sizeof(MD5_SHA1_CTX),
})
#undef CHECK

View File

@ -104,8 +104,6 @@ struct evp_md_pctx_ops {
EVP_PKEY_CTX* (*dup) (EVP_PKEY_CTX *pctx);
};
const EVP_MD *EVP_parse_digest_algorithm(CBS *cbs);
#if defined(__cplusplus)
} /* extern C */

View File

@ -46,14 +46,11 @@
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ==================================================================== */
#ifndef OPENSSL_HEADER_MD32_COMMON_H
#define OPENSSL_HEADER_MD32_COMMON_H
#include <openssl/base.h>
#include <assert.h>
#include "../internal.h"
#include "../../internal.h"
#if defined(__cplusplus)
extern "C" {
@ -270,5 +267,3 @@ int HASH_FINAL(uint8_t *md, HASH_CTX *c) {
#if defined(__cplusplus)
} /* extern C */
#endif
#endif /* OPENSSL_HEADER_MD32_COMMON_H */

View File

@ -62,7 +62,7 @@
#include <openssl/digest.h>
#include <openssl/mem.h>
#include "../internal.h"
#include "../../internal.h"
uint8_t *HMAC(const EVP_MD *evp_md, const void *key, size_t key_len,

View File

@ -0,0 +1,124 @@
// Copyright (c) 2017, Google Inc.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
// inject-hash runs a binary compiled against a FIPS module that hasn't had the
// correct hash injected. That binary will fail the power-on integrity check
// and write the calcualted hash value to stderr. This script parses that and
// injects the calcualted value into the given object file.
package main
import (
"bytes"
"encoding/hex"
"errors"
"flag"
"fmt"
"io/ioutil"
"os"
"os/exec"
"strings"
)
// uninitHashValue is the default hash value that we inject into the module.
// This value need only be distinct, i.e. so that we can safely
// search-and-replace it in an object file. This must match the value in bcm.c.
var uninitHashValue = [32]byte{
0x5f, 0x30, 0xd1, 0x80, 0xe7, 0x9e, 0x8f, 0x8f, 0xdf, 0x8b, 0x93, 0xd4, 0x96, 0x36, 0x30, 0xcc, 0x30, 0xea, 0x38, 0x0f, 0x75, 0x56, 0x9a, 0x1b, 0x23, 0x2f, 0x7c, 0x79, 0xff, 0x1b, 0x2b, 0xca,
}
func do(outPath, arInput, binPath string) error {
cmd := exec.Command(binPath)
out, err := cmd.CombinedOutput()
if err == nil {
return errors.New("binary did not fail self test")
}
lines := strings.Split(string(out), "\n")
if len(lines) < 3 {
return fmt.Errorf("too few lines in output: %q", out)
}
calculatedLine := lines[2]
if !strings.HasPrefix(calculatedLine, "Calculated: ") {
return errors.New("bad prefix of 3rd line: " + calculatedLine)
}
calculatedLine = calculatedLine[12:]
calculated, err := hex.DecodeString(calculatedLine)
if err != nil {
return err
}
if len(calculated) != len(uninitHashValue) {
return fmt.Errorf("unexpected length of calculated hash: got %d, want %d", len(calculated), len(uninitHashValue))
}
arFile, err := os.Open(arInput)
if err != nil {
return err
}
defer arFile.Close()
ar := NewAR(arFile)
for {
header, err := ar.Next()
if err != nil {
return errors.New("error reading from archive file: " + err.Error())
}
if len(header.Name) > 0 {
break
}
if _, err := ioutil.ReadAll(ar); err != nil {
return errors.New("error reading from archive file: " + err.Error())
}
}
object, err := ioutil.ReadAll(ar)
if err != nil {
return err
}
offset := bytes.Index(object, uninitHashValue[:])
if offset < 0 {
return errors.New("did not find uninitialised hash value in object file")
}
if bytes.Index(object[offset+1:], uninitHashValue[:]) >= 0 {
return errors.New("found two occurrences of uninitialised hash value in object file")
}
copy(object[offset:], calculated)
if err := ioutil.WriteFile(outPath, object, 0644); err != nil {
return err
}
return nil
}
func main() {
arInput := flag.String("in", "", "Path to a .a file")
outPath := flag.String("o", "", "Path to output object")
bin := flag.String("bin", "", "Binary compiled with the FIPS module")
flag.Parse()
if err := do(*outPath, *arInput, *bin); err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err)
os.Exit(1)
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

View File

@ -0,0 +1,27 @@
/* Copyright (c) 2017, Google Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
#include <openssl/crypto.h>
/* This file exists in order to give the fipsmodule target, in non-FIPS mode,
* something to compile. */
int FIPS_mode(void) {
#if defined(BORINGSSL_FIPS)
return 1;
#else
return 0;
#endif
}

View File

@ -59,7 +59,7 @@
#include <stdlib.h>
#include <string.h>
#include "../internal.h"
#include "../../internal.h"
uint8_t *MD4(const uint8_t *data, size_t len, uint8_t *out) {
@ -234,3 +234,21 @@ void md4_block_data_order(uint32_t *state, const uint8_t *data, size_t num) {
D = state[3] += D;
}
}
#undef DATA_ORDER_IS_LITTLE_ENDIAN
#undef HASH_CTX
#undef HASH_CBLOCK
#undef HASH_UPDATE
#undef HASH_TRANSFORM
#undef HASH_FINAL
#undef HASH_MAKE_STRING
#undef HASH_BLOCK_DATA_ORDER
#undef F
#undef G
#undef H
#undef ROTATE
#undef R0
#undef R1
#undef R2
#undef HOST_c2l
#undef HOST_l2c

View File

@ -8,7 +8,7 @@
$normal=0;
$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
push(@INC,"${dir}","${dir}../../perlasm");
push(@INC,"${dir}","${dir}../../../perlasm");
require "x86asm.pl";
$output=pop;

View File

@ -117,7 +117,7 @@ my $win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
$0 =~ m/(.*[\/\\])[^\/\\]+$/; my $dir=$1; my $xlate;
( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
( $xlate="${dir}../../../perlasm/x86_64-xlate.pl" and -f $xlate) or
die "can't locate x86_64-xlate.pl";
open OUT,"| \"$^X\" \"$xlate\" $flavour \"$output\"";

View File

@ -60,7 +60,7 @@
#include <openssl/mem.h>
#include "../internal.h"
#include "../../internal.h"
uint8_t *MD5(const uint8_t *data, size_t len, uint8_t *out) {
@ -275,4 +275,25 @@ void md5_block_data_order(uint32_t *state, const uint8_t *data, size_t num) {
D = state[3] += D;
}
}
#undef X
#endif
#undef DATA_ORDER_IS_LITTLE_ENDIAN
#undef HASH_CTX
#undef HASH_CBLOCK
#undef HASH_UPDATE
#undef HASH_TRANSFORM
#undef HASH_FINAL
#undef HASH_MAKE_STRING
#undef HASH_BLOCK_DATA_ORDER
#undef F
#undef G
#undef H
#undef I
#undef ROTATE
#undef R0
#undef R1
#undef R2
#undef R3
#undef HOST_c2l
#undef HOST_l2c

View File

@ -114,7 +114,7 @@
# (***) SHAEXT result
$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
push(@INC,"${dir}","${dir}../../perlasm");
push(@INC,"${dir}","${dir}../../../perlasm");
require "x86asm.pl";
$output=pop;

View File

@ -75,7 +75,7 @@ else { while (($output=shift) && ($output!~/\w[\w\-]*\.\w+$/)) {} }
if ($flavour && $flavour ne "void") {
$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
( $xlate="${dir}arm-xlate.pl" and -f $xlate ) or
( $xlate="${dir}../../perlasm/arm-xlate.pl" and -f $xlate) or
( $xlate="${dir}../../../perlasm/arm-xlate.pl" and -f $xlate) or
die "can't locate arm-xlate.pl";
open STDOUT,"| \"$^X\" $xlate $flavour $output";

View File

@ -28,7 +28,7 @@ $output = shift;
$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
( $xlate="${dir}arm-xlate.pl" and -f $xlate ) or
( $xlate="${dir}../../perlasm/arm-xlate.pl" and -f $xlate) or
( $xlate="${dir}../../../perlasm/arm-xlate.pl" and -f $xlate) or
die "can't locate arm-xlate.pl";
open OUT,"| \"$^X\" $xlate $flavour $output";

View File

@ -92,7 +92,7 @@ $win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
( $xlate="${dir}../../../perlasm/x86_64-xlate.pl" and -f $xlate) or
die "can't locate x86_64-xlate.pl";
# In upstream, this is controlled by shelling out to the compiler to check
@ -236,15 +236,17 @@ push(@xi,shift(@xi));
$code.=<<___;
.text
.extern OPENSSL_ia32cap_P
.extern OPENSSL_ia32cap_addr
.globl sha1_block_data_order
.type sha1_block_data_order,\@function,3
.align 16
sha1_block_data_order:
mov OPENSSL_ia32cap_P+0(%rip),%r9d
mov OPENSSL_ia32cap_P+4(%rip),%r8d
mov OPENSSL_ia32cap_P+8(%rip),%r10d
lea OPENSSL_ia32cap_addr(%rip),%r10
mov (%r10),%r10
mov 0(%r10),%r9d
mov 4(%r10),%r8d
mov 8(%r10),%r10d
test \$`1<<9`,%r8d # check SSSE3 bit
jz .Lialu
___

View File

@ -63,7 +63,7 @@
# (***) SHAEXT result is 4.1, strangely enough better than 64-bit one;
$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
push(@INC,"${dir}","${dir}../../perlasm");
push(@INC,"${dir}","${dir}../../../perlasm");
require "x86asm.pl";
$output=pop;

View File

@ -51,7 +51,7 @@ else { while (($output=shift) && ($output!~/\w[\w\-]*\.\w+$/)) {} }
if ($flavour && $flavour ne "void") {
$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
( $xlate="${dir}arm-xlate.pl" and -f $xlate ) or
( $xlate="${dir}../../perlasm/arm-xlate.pl" and -f $xlate) or
( $xlate="${dir}../../../perlasm/arm-xlate.pl" and -f $xlate) or
die "can't locate arm-xlate.pl";
open STDOUT,"| \"$^X\" $xlate $flavour $output";

View File

@ -49,7 +49,7 @@
# cost of increased data cache "footprint" by 1/2KB.
$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
push(@INC,"${dir}","${dir}../../perlasm");
push(@INC,"${dir}","${dir}../../../perlasm");
require "x86asm.pl";
$output=pop;

View File

@ -57,7 +57,7 @@ else { while (($output=shift) && ($output!~/\w[\w\-]*\.\w+$/)) {} }
if ($flavour && $flavour ne "void") {
$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
( $xlate="${dir}arm-xlate.pl" and -f $xlate ) or
( $xlate="${dir}../../perlasm/arm-xlate.pl" and -f $xlate) or
( $xlate="${dir}../../../perlasm/arm-xlate.pl" and -f $xlate) or
die "can't locate arm-xlate.pl";
open STDOUT,"| \"$^X\" $xlate $flavour $output";

View File

@ -34,7 +34,7 @@ $output=shift;
$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
( $xlate="${dir}arm-xlate.pl" and -f $xlate ) or
( $xlate="${dir}../../perlasm/arm-xlate.pl" and -f $xlate) or
( $xlate="${dir}../../../perlasm/arm-xlate.pl" and -f $xlate) or
die "can't locate arm-xlate.pl";
open OUT,"| \"$^X\" $xlate $flavour $output";

View File

@ -108,7 +108,7 @@ $win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
( $xlate="${dir}../../../perlasm/x86_64-xlate.pl" and -f $xlate) or
die "can't locate x86_64-xlate.pl";
# In upstream, this is controlled by shelling out to the compiler to check
@ -249,14 +249,15 @@ ___
$code=<<___;
.text
.extern OPENSSL_ia32cap_P
.extern OPENSSL_ia32cap_addr
.globl $func
.type $func,\@function,3
.align 16
$func:
___
$code.=<<___ if ($SZ==4 || $avx);
lea OPENSSL_ia32cap_P(%rip),%r11
lea OPENSSL_ia32cap_addr(%rip),%r11
mov (%r11),%r11
mov 0(%r11),%r9d
mov 4(%r11),%r10d
mov 8(%r11),%r11d
@ -311,6 +312,7 @@ $code.=<<___;
mov $SZ*5($ctx),$F
mov $SZ*6($ctx),$G
mov $SZ*7($ctx),$H
jmp .Lloop
.align 16

View File

@ -346,3 +346,16 @@ void sha1_block_data_order(uint32_t *state, const uint8_t *data, size_t num) {
}
#endif /* OPENSSL_PPC64LE */
#undef K_00_19
#undef K_20_39
#undef K_40_59
#undef K_60_79
#undef F_00_19
#undef F_20_39
#undef F_40_59
#undef F_60_79
#undef BODY_00_19
#undef BODY_20_39
#undef BODY_40_59
#undef BODY_60_79

View File

@ -60,7 +60,7 @@
#include <openssl/mem.h>
#include "../internal.h"
#include "../../internal.h"
#if !defined(OPENSSL_NO_ASM) && \
@ -345,3 +345,31 @@ static void sha1_block_data_order(uint32_t *state, const uint8_t *data,
}
}
#endif
#undef DATA_ORDER_IS_BIG_ENDIAN
#undef HASH_CTX
#undef HASH_CBLOCK
#undef HASH_MAKE_STRING
#undef HASH_UPDATE
#undef HASH_TRANSFORM
#undef HASH_FINAL
#undef HASH_BLOCK_DATA_ORDER
#undef ROTATE
#undef Xupdate
#undef K_00_19
#undef K_20_39
#undef K_40_59
#undef K_60_79
#undef F_00_19
#undef F_20_39
#undef F_40_59
#undef F_60_79
#undef BODY_00_15
#undef BODY_16_19
#undef BODY_20_31
#undef BODY_32_39
#undef BODY_40_59
#undef BODY_60_79
#undef X
#undef HOST_c2l
#undef HOST_l2c

View File

@ -60,7 +60,7 @@
#include <openssl/mem.h>
#include "../internal.h"
#include "../../internal.h"
#if !defined(OPENSSL_NO_ASM) && \
@ -314,4 +314,24 @@ static void sha256_block_data_order(uint32_t *state, const uint8_t *data,
}
}
#endif /* SHA256_ASM */
#endif /* !SHA256_ASM */
#undef DATA_ORDER_IS_BIG_ENDIAN
#undef HASH_CTX
#undef HASH_CBLOCK
#undef HASH_MAKE_STRING
#undef HASH_UPDATE
#undef HASH_TRANSFORM
#undef HASH_FINAL
#undef HASH_BLOCK_DATA_ORDER
#undef ROTATE
#undef Sigma0
#undef Sigma1
#undef sigma0
#undef sigma1
#undef Ch
#undef Maj
#undef ROUND_00_15
#undef ROUND_16_63
#undef HOST_c2l
#undef HOST_l2c

View File

@ -60,7 +60,7 @@
#include <openssl/mem.h>
#include "../internal.h"
#include "../../internal.h"
/* IMPLEMENTATION NOTES.
@ -593,4 +593,18 @@ static void sha512_block_data_order(uint64_t *state, const uint64_t *W,
#endif
#endif /* SHA512_ASM */
#endif /* !SHA512_ASM */
#undef ROTR
#undef PULL64
#undef B
#undef Sigma0
#undef Sigma1
#undef sigma0
#undef sigma1
#undef Ch
#undef Maj
#undef ROUND_00_15
#undef ROUND_16_80
#undef HOST_c2l
#undef HOST_l2c

View File

@ -1,14 +1,5 @@
include_directories(../../include)
add_library(
hmac
OBJECT
hmac.c
)
add_executable(
hmac_test

View File

@ -1,9 +0,0 @@
include_directories(../../include)
add_library(
md4
OBJECT
md4.c
)

View File

@ -1,30 +0,0 @@
include_directories(../../include)
if (${ARCH} STREQUAL "x86_64")
set(
MD5_ARCH_SOURCES
md5-x86_64.${ASM_EXT}
)
endif()
if (${ARCH} STREQUAL "x86")
set(
MD5_ARCH_SOURCES
md5-586.${ASM_EXT}
)
endif()
add_library(
md5
OBJECT
md5.c
${MD5_ARCH_SOURCES}
)
perlasm(md5-x86_64.${ASM_EXT} asm/md5-x86_64.pl)
perlasm(md5-586.${ASM_EXT} asm/md5-586.pl)

View File

@ -71,7 +71,7 @@
#include "internal.h"
#include "../bytestring/internal.h"
#include "../digest/internal.h"
#include "../digest_extra/internal.h"
#include "../internal.h"

View File

@ -1,67 +0,0 @@
include_directories(../../include)
if (${ARCH} STREQUAL "x86_64")
set(
SHA_ARCH_SOURCES
sha1-x86_64.${ASM_EXT}
sha256-x86_64.${ASM_EXT}
sha512-x86_64.${ASM_EXT}
)
endif()
if (${ARCH} STREQUAL "x86")
set(
SHA_ARCH_SOURCES
sha1-586.${ASM_EXT}
sha256-586.${ASM_EXT}
sha512-586.${ASM_EXT}
)
endif()
if (${ARCH} STREQUAL "arm")
set(
SHA_ARCH_SOURCES
sha1-armv4-large.${ASM_EXT}
sha256-armv4.${ASM_EXT}
sha512-armv4.${ASM_EXT}
)
endif()
if (${ARCH} STREQUAL "aarch64")
set(
SHA_ARCH_SOURCES
sha1-armv8.${ASM_EXT}
sha256-armv8.${ASM_EXT}
sha512-armv8.${ASM_EXT}
)
endif()
add_library(
sha
OBJECT
sha1-altivec.c
sha1.c
sha256.c
sha512.c
${SHA_ARCH_SOURCES}
)
perlasm(sha1-x86_64.${ASM_EXT} asm/sha1-x86_64.pl)
perlasm(sha256-x86_64.${ASM_EXT} asm/sha512-x86_64.pl)
perlasm(sha512-x86_64.${ASM_EXT} asm/sha512-x86_64.pl)
perlasm(sha1-586.${ASM_EXT} asm/sha1-586.pl)
perlasm(sha256-586.${ASM_EXT} asm/sha256-586.pl)
perlasm(sha512-586.${ASM_EXT} asm/sha512-586.pl)
perlasm(sha1-armv4-large.${ASM_EXT} asm/sha1-armv4-large.pl)
perlasm(sha256-armv4.${ASM_EXT} asm/sha256-armv4.pl)
perlasm(sha512-armv4.${ASM_EXT} asm/sha512-armv4.pl)
perlasm(sha1-armv8.${ASM_EXT} asm/sha1-armv8.pl)
perlasm(sha256-armv8.${ASM_EXT} asm/sha512-armv8.pl)
perlasm(sha512-armv8.${ASM_EXT} asm/sha512-armv8.pl)

View File

@ -91,7 +91,7 @@ static void ripemd160_block_data_order(uint32_t h[5], const uint8_t *data,
} while (0)
#define HASH_BLOCK_DATA_ORDER ripemd160_block_data_order
#include "../../crypto/digest/md32_common.h"
#include "../../crypto/fipsmodule/digest/md32_common.h"
/* Transformed F2 and F4 are courtesy of Wei Dai <weidai@eskimo.com> */
#define F1(x, y, z) ((x) ^ (y) ^ (z))

View File

@ -54,6 +54,10 @@ OPENSSL_EXPORT int CRYPTO_is_confidential_build(void);
* in which case it returns zero. */
OPENSSL_EXPORT int CRYPTO_has_asm(void);
/* FIPS_mode returns zero unless BoringSSL is built with BORINGSSL_FIPS, in
* which case it returns one. */
OPENSSL_EXPORT int FIPS_mode(void);
/* Deprecated functions. */
@ -83,9 +87,6 @@ OPENSSL_EXPORT int ENGINE_register_all_complete(void);
/* OPENSSL_load_builtin_modules does nothing. */
OPENSSL_EXPORT void OPENSSL_load_builtin_modules(void);
/* FIPS_mode returns zero. */
OPENSSL_EXPORT int FIPS_mode(void);
#if defined(__cplusplus)
} /* extern C */

View File

@ -29,6 +29,11 @@
#include "internal.h"
static bool IsFIPS(const std::vector<std::string> &args) {
printf("%d\n", FIPS_mode());
return true;
}
typedef bool (*tool_func_t)(const std::vector<std::string> &args);
struct Tool {
@ -39,6 +44,7 @@ struct Tool {
static const Tool kTools[] = {
{ "ciphers", Ciphers },
{ "client", Client },
{ "isfips", IsFIPS },
{ "generate-ed25519", GenerateEd25519Key },
{ "genrsa", GenerateRSAKey },
{ "md5sum", MD5Sum },

View File

@ -27,7 +27,7 @@
["crypto/crypto_test"],
["crypto/curve25519/ed25519_test", "crypto/curve25519/ed25519_tests.txt"],
["crypto/curve25519/spake25519_test"],
["crypto/digest/digest_test"],
["crypto/digest_extra/digest_test"],
["crypto/ec/example_mul"],
["crypto/ec/p256-x86_64_test", "crypto/ec/p256-x86_64_tests.txt"],
["crypto/ecdh/ecdh_test", "crypto/ecdh/ecdh_tests.txt"],
@ -37,7 +37,7 @@
["crypto/evp/evp_test", "crypto/evp/evp_tests.txt"],
["crypto/evp/pbkdf_test"],
["crypto/hkdf/hkdf_test"],
["crypto/hmac/hmac_test", "crypto/hmac/hmac_tests.txt"],
["crypto/hmac_extra/hmac_test", "crypto/hmac_extra/hmac_tests.txt"],
["crypto/lhash/lhash_test"],
["crypto/modes/gcm_test"],
["crypto/obj/obj_test"],

View File

@ -30,6 +30,7 @@ var (
)
func mapName(path string) string {
path = strings.Replace(path, filepath.FromSlash("/fipsmodule/"), string(filepath.Separator), 1)
switch filepath.ToSlash(path) {
case "crypto/cipher/asm/chacha20_poly1305_x86_64.pl", "crypto/rand/asm/rdrand-x86_64.pl":
return ""

View File

@ -208,6 +208,7 @@ class Bazel(object):
out.write(self.header)
self.PrintVariableSection(out, 'ssl_headers', files['ssl_headers'])
self.PrintVariableSection(out, 'fips_fragments', files['fips_fragments'])
self.PrintVariableSection(
out, 'ssl_internal_headers', files['ssl_internal_headers'])
self.PrintVariableSection(out, 'ssl_sources', files['ssl'])
@ -436,8 +437,13 @@ def FindCMakeFiles(directory):
return cmakefiles
def OnlyFIPSFragments(path, dent, is_dir):
return is_dir or path.startswith(os.path.join('src', 'crypto', 'fipsmodule', ''))
def NoTests(dent, is_dir):
def NoTestsNorFIPSFragments(path, dent, is_dir):
return NoTests(path, dent, is_dir) and (is_dir or not OnlyFIPSFragments(path, dent, is_dir))
def NoTests(path, dent, is_dir):
"""Filter function that can be passed to FindCFiles in order to remove test
sources."""
if is_dir:
@ -445,7 +451,7 @@ def NoTests(dent, is_dir):
return 'test.' not in dent and not dent.startswith('example_')
def OnlyTests(dent, is_dir):
def OnlyTests(path, dent, is_dir):
"""Filter function that can be passed to FindCFiles in order to remove
non-test sources."""
if is_dir:
@ -453,13 +459,13 @@ def OnlyTests(dent, is_dir):
return '_test.' in dent or dent.startswith('example_')
def AllFiles(dent, is_dir):
def AllFiles(path, dent, is_dir):
"""Filter function that can be passed to FindCFiles in order to include all
sources."""
return True
def NoTestRunnerFiles(dent, is_dir):
def NoTestRunnerFiles(path, dent, is_dir):
"""Filter function that can be passed to FindCFiles or FindHeaderFiles in
order to exclude test runner files."""
# NOTE(martinkr): This prevents .h/.cc files in src/ssl/test/runner, which
@ -467,11 +473,11 @@ def NoTestRunnerFiles(dent, is_dir):
return not is_dir or dent != 'runner'
def NotGTestMain(dent, is_dir):
def NotGTestMain(path, dent, is_dir):
return dent != 'gtest_main.cc'
def SSLHeaderFiles(dent, is_dir):
def SSLHeaderFiles(path, dent, is_dir):
return dent in ['ssl.h', 'tls1.h', 'ssl23.h', 'ssl3.h', 'dtls1.h']
@ -484,12 +490,12 @@ def FindCFiles(directory, filter_func):
for filename in filenames:
if not filename.endswith('.c') and not filename.endswith('.cc'):
continue
if not filter_func(filename, False):
if not filter_func(path, filename, False):
continue
cfiles.append(os.path.join(path, filename))
for (i, dirname) in enumerate(dirnames):
if not filter_func(dirname, True):
if not filter_func(path, dirname, True):
del dirnames[i]
return cfiles
@ -503,12 +509,12 @@ def FindHeaderFiles(directory, filter_func):
for filename in filenames:
if not filename.endswith('.h'):
continue
if not filter_func(filename, False):
if not filter_func(path, filename, False):
continue
hfiles.append(os.path.join(path, filename))
for (i, dirname) in enumerate(dirnames):
if not filter_func(dirname, True):
if not filter_func(path, dirname, True):
del dirnames[i]
return hfiles
@ -617,7 +623,8 @@ def IsGTest(path):
def main(platforms):
crypto_c_files = FindCFiles(os.path.join('src', 'crypto'), NoTests)
crypto_c_files = FindCFiles(os.path.join('src', 'crypto'), NoTestsNorFIPSFragments)
fips_fragments = FindCFiles(os.path.join('src', 'crypto', 'fipsmodule'), OnlyFIPSFragments)
ssl_source_files = FindCFiles(os.path.join('src', 'ssl'), NoTests)
tool_c_files = FindCFiles(os.path.join('src', 'tool'), NoTests)
tool_h_files = FindHeaderFiles(os.path.join('src', 'tool'), AllFiles)
@ -641,7 +648,9 @@ def main(platforms):
for path in FindCFiles(os.path.join('src', 'crypto'), OnlyTests):
if IsGTest(path):
crypto_test_files.append(path)
else:
# bcm_hashunset_test.c is only used in the FIPS build process.
elif path != os.path.join('src', 'crypto', 'fipsmodule',
'bcm_hashunset_test.c'):
test_c_files.append(path)
ssl_test_files = FindCFiles(os.path.join('src', 'ssl'), OnlyTests)
@ -654,8 +663,8 @@ def main(platforms):
os.path.join('src', 'include', 'openssl'),
SSLHeaderFiles))
def NotSSLHeaderFiles(filename, is_dir):
return not SSLHeaderFiles(filename, is_dir)
def NotSSLHeaderFiles(path, filename, is_dir):
return not SSLHeaderFiles(path, filename, is_dir)
crypto_h_files = (
FindHeaderFiles(
os.path.join('src', 'include', 'openssl'),
@ -677,6 +686,7 @@ def main(platforms):
'src/',
'')
for test in test_c_files])
if test_binaries != test_sources:
print 'Test sources and configured tests do not match'
a = test_binaries.difference(test_sources)
@ -691,6 +701,7 @@ def main(platforms):
'crypto_headers': crypto_h_files,
'crypto_internal_headers': crypto_internal_h_files,
'crypto_test': sorted(crypto_test_files),
'fips_fragments': fips_fragments,
'fuzz': fuzz_c_files,
'ssl': ssl_source_files,
'ssl_c': [s for s in ssl_source_files if s.endswith('.c')],