Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

boringcrypto: Not using FIPS compliant calls for SHA functions (also should we add md5? for easy proof of FIPS mode on) #66513

Closed
evanskinner opened this issue Mar 25, 2024 · 3 comments

Comments

@evanskinner
Copy link

evanskinner commented Mar 25, 2024

Go version

go1.20.12

Output of go env in your module/workspace:

GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/root/.cache/go-build"
GOENV="/root/.config/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/tmp/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/tmp/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/lib/golang"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/lib/golang/pkg/tool/linux_amd64"
GOVCS=""
GOVERSION="go1.20.12"
GCCGO="gccgo"
GOAMD64="v1"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/dev/null"
GOWORK=""
CGO_CFLAGS="-O2 -g"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-O2 -g"
CGO_FFLAGS="-O2 -g"
CGO_LDFLAGS="-O2 -g"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build2938776592=/tmp/go-build -gno-record-gcc-switches"

What did you do?

After building a golang binary I wanted to prove that boringcrypto was using the openssl library in FIPS mode (my kernel was already in FIPS mode) and the way I had done that for other languages was to attempt an md5 hash and see that openssl threw an error. AFter ralising the the md5() function of golang is not implemented in boringcrypto I set about patching my copy by copying the way the sha1 functions work but for md5. After getting this to work I was surprised when the md5() function returned the hash, instead of returning an erorr. Using LD_DEBUG=symbols I could see that my binary was correctly calling into openssl for both an md5() call and a sha1() call:

		result := md5.Sum([]byte("hash me"))
		fmt.Printf("%x\n", result)
		result2 := sha1.Sum([]byte("hash me"))
		fmt.Printf("%x\n", result2)

when executrd with LD_DEBUG=symbols produced:

...
	  5752:	symbol=MD5_Final;  lookup in file=/lib64/libresolv.so.2 [0]
	  5752:	symbol=MD5_Final;  lookup in file=/lib64/libdl.so.2 [0]
	  5752:	symbol=MD5_Final;  lookup in file=/lib64/libpthread.so.0 [0]
	  5752:	symbol=MD5_Final;  lookup in file=/lib64/libc.so.6 [0]
	  5752:	symbol=MD5_Final;  lookup in file=/lib64/ld-linux-x86-64.so.2 [0]
	  5752:	symbol=MD5_Final;  lookup in file=/lib64/libcrypto.so.1.1 [0]
17b31dce96b9d6c6d0a6ba95f47796fb
...
	  5752:	symbol=SHA1_Final;  lookup in file=/lib64/libresolv.so.2 [0]
	  5752:	symbol=SHA1_Final;  lookup in file=/lib64/libdl.so.2 [0]
	  5752:	symbol=SHA1_Final;  lookup in file=/lib64/libpthread.so.0 [0]
	  5752:	symbol=SHA1_Final;  lookup in file=/lib64/libc.so.6 [0]
	  5752:	symbol=SHA1_Final;  lookup in file=/lib64/ld-linux-x86-64.so.2 [0]
	  5752:	symbol=SHA1_Final;  lookup in file=/lib64/libcrypto.so.1.1 [0]
43f932e4f7c6ecd136a695b7008694bb69d517bd

A bit of digging around and I found this section in the openssl fips page (https://www.openssl.org/docs/manmaster/man7/fips_module.html):

Applications written to use the OpenSSL 3.0 FIPS module should not use any legacy APIs or features that avoid the FIPS module. Specifically this includes:

Low level cryptographic APIs (use the high level APIs, such as EVP, instead)

It appears that the MD5_* and SHA1_* functions are considered low level APIs by openssl and so avoid the FIPS module. I tested this theory by creating a small C program that first uses the low level MD5 api and then the haigh level EVP_ MD5 api:

#include <stdio.h>
#include <string.h>
#include <openssl/md5.h>
#include <openssl/des.h>
#include <openssl/fips.h>
#include <openssl/ssl.h>
#include <openssl/evp.h>
#include <openssl/err.h>

void compute_md5(char *str, unsigned char digest[16]);

int main(int argc, char *argv[])
{
    if (argc == 2) {
	printf("Setting FIPS mode to %s\n",argv[1]);
#if OPENSSL_VERSION_MAJOR < 3
        FIPS_mode_set(atoi(argv[1]));
#else
        EVP_default_properties_enable_fips(NULL, atoi(argv[1]));
#endif
    }
    printf("FIPS Mode: %d\n",FIPS_mode());
    char data[] = "hash me";
    unsigned char md_value[EVP_MAX_MD_SIZE];
    unsigned int md_len, i;

    printf("Attempting md5 hash via MD5_ methods.\n");
    unsigned char digest[16];
    compute_md5("hash me", digest);
    for (int i = 0; i < 16; i++)
        printf("%02x", digest[i]);
    putchar ('\n');

    printf("Attempting md5 hash via EVP_ methods.\n");
    const EVP_MD *md = EVP_get_digestbyname("md5");
    if (md == NULL) {
        printf("Unknown message digest %s\n", "md5");
        exit(1);
    }
    EVP_MD_CTX *ctx = EVP_MD_CTX_new();
        if (!EVP_DigestInit_ex(ctx, md, NULL)) {
        printf("Message digest initialization failed.\n");
	ERR_print_errors_fp (stderr);
        EVP_MD_CTX_free(ctx);
        exit(1);
    }
    EVP_DigestUpdate(ctx, data, strlen(data));
    EVP_DigestFinal_ex(ctx, md_value, &md_len);
    for (int i = 0; i < md_len; i++)
        printf("%02x", md_value[i]);
    putchar ('\n');

    return 0;
}

void compute_md5(char *str, unsigned char digest[16]) {
    MD5_CTX ctx;
    MD5_Init(&ctx);
    MD5_Update(&ctx, str, strlen(str));
    MD5_Final(digest, &ctx);
}

Which when ran with FIPS enabled (either via the cmd line parameter, or on a kernel with fips=1) gives the output:

gcc md5_fips_test.c -o md5_fips_test -lssl -lcrypto
./md5_fips_test 1
Setting FIPS mode to 1
FIPS Mode: 1
Attempting md5 hash via MD5_ methods.
17b31dce96b9d6c6d0a6ba95f47796fb
Attempting md5 hash via EVP_ methods.
Message digest initialization failed.
140030451414848:error:060800C8:digital envelope routines:EVP_DigestInit_ex:disabled for FIPS:crypto/evp/digest.c:135:

Which seems to prove that the MD5_ methods, and so presumably all the SHA*_ methods do not go via the fips module. I wasn't able to find a way to configure the system to disallow a one of the SHA* hashes to test my theory with the SHA*_ functions though.

Note I was actually using go-toolset on Redhat, but I think the changes required are in the boringcrypto code in https://github.com/golang/go/blob/master/src/crypto/internal/boring/sha.go and other places.

I also think it would be handy to add an implementation of md5 (would be very simlar to the fixed sha implementation) in boringcrypto so that you can try an md5 hash and see it refused. It would also need to use the EVP_ methods.

What did you see happen?

Covered above, sorry.

What did you expect to see?

I expected to be able to prove that the binary was running openssl in fips mode, by seeing it deny a bad algorithmn, such as md5.

@seankhliao
Copy link
Member

Duplicate of #45565

@seankhliao seankhliao marked this as a duplicate of #45565 Mar 25, 2024
@seankhliao seankhliao closed this as not planned Won't fix, can't repro, duplicate, stale Mar 25, 2024
@evanskinner
Copy link
Author

@seankhliao Thanks for the pointer to #45565 but I don't think this is a direct duplicate of that bug. I can see the crossover where I am asking why md5() is not implemented in boringcrypto and I see #45565 (comment) indicates that is working as designed and it is up to the application to not cal md5(), but my bigger concern here is that all the functions in sha.go are using the low level openssl calls, instead of the high level ones that go via the fips provider.

@evanskinner
Copy link
Author

evanskinner commented Mar 25, 2024

I figured closed bugs won't get any attention and to remove the confusion over md5 and concentrate on the sha issue I raised a new bug: #66520

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants