Skip to content

Commit

Permalink
add extra ossl wrappers
Browse files Browse the repository at this point in the history
  • Loading branch information
qmuntal committed Dec 5, 2024
1 parent 5d8a3b1 commit d00b580
Show file tree
Hide file tree
Showing 4 changed files with 596 additions and 0 deletions.
137 changes: 137 additions & 0 deletions internal/ossl/ossl.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
//go:build unix || windows

#ifdef _WIN32
# include <windows.h>
# define dlsym (void*)GetProcAddress
#else
# include <dlfcn.h> // dlsym
#endif
#include <stdio.h> // fprintf

// go_openssl_fips_enabled returns 1 if FIPS mode is enabled, 0 otherwise.
// As a special case, it returns -1 if it cannot determine if FIPS mode is enabled.
// See openssl.FIPS for details about its implementation.
//
// This function is reimplemented here because openssl.FIPS assumes that
// all the OpenSSL bindings are loaded, that is, go_openssl_load_functions has
// already been called. On the other hand, go_openssl_fips_enabled is called from
// openssl.CheckVersion, which is used to check if a given OpenSSL shared library
// exists and is FIPS compliant. That shared library might not be the one that
// was passed to go_openssl_load_functions, or it might not even have been called at all.
//
// It is written in C because it is not possible to directly call C function pointers
// retrieved using dlsym from Go.
int
go_openssl_fips_enabled(void* handle)
{
// For OpenSSL 1.x.
int (*FIPS_mode)(void);
FIPS_mode = (int (*)(void))dlsym(handle, "FIPS_mode");
if (FIPS_mode != NULL)
return FIPS_mode();

// For OpenSSL 3.x.
int (*EVP_default_properties_is_fips_enabled)(void*) = (int (*)(void*))dlsym(handle, "EVP_default_properties_is_fips_enabled");
void *(*EVP_MD_fetch)(void*, const char*, const char*) = (void* (*)(void*, const char*, const char*))dlsym(handle, "EVP_MD_fetch");
void (*EVP_MD_free)(void*) = (void (*)(void*))dlsym(handle, "EVP_MD_free");

if (EVP_default_properties_is_fips_enabled == NULL || EVP_MD_fetch == NULL || EVP_MD_free == NULL) {
// Shouldn't happen, but if it does, we can't determine if FIPS mode is enabled.
return -1;
}

if (EVP_default_properties_is_fips_enabled(NULL) != 1)
return 0;

void *md = EVP_MD_fetch(NULL, "SHA2-256", NULL);
if (md == NULL)
return 0;

EVP_MD_free(md);
return 1;
}

static unsigned long
version_num(void* handle)
{
unsigned long (*fn)(void);
// OPENSSL_version_num is defined in OpenSSL 1.1.0 and 1.1.1.
fn = (unsigned long (*)(void))dlsym(handle, "OpenSSL_version_num");
if (fn != NULL)
return fn();

// SSLeay is defined in OpenSSL 1.0.2.
fn = (unsigned long (*)(void))dlsym(handle, "SSLeay");
if (fn != NULL)
return fn();

return 0;
}

int
go_openssl_version_major(void* handle)
{
unsigned int (*fn)(void);
// OPENSSL_version_major is supported since OpenSSL 3.
fn = (unsigned int (*)(void))dlsym(handle, "OPENSSL_version_major");
if (fn != NULL)
return (int)fn();

// If OPENSSL_version_major is not defined, try with OpenSSL 1 functions.
unsigned long num = version_num(handle);
if (num < 0x10000000L || num >= 0x20000000L)
return -1;

return 1;
}

int
go_openssl_version_minor(void* handle)
{
unsigned int (*fn)(void);
// OPENSSL_version_minor is supported since OpenSSL 3.
fn = (unsigned int (*)(void))dlsym(handle, "OPENSSL_version_minor");
if (fn != NULL)
return (int)fn();

// If OPENSSL_version_minor is not defined, try with OpenSSL 1 functions.
unsigned long num = version_num(handle);
// OpenSSL version number follows this schema:
// MNNFFPPS: major minor fix patch status.
if (num < 0x10000000L || num >= 0x10200000L)
{
// We only support minor version 0 and 1,
// so there is no need to implement an algorithm
// that decodes the version number into individual components.
return -1;
}

if (num >= 0x10100000L)
return 1;

return 0;
}

int
go_openssl_version_patch(void* handle)
{
unsigned int (*fn)(void);
// OPENSSL_version_patch is supported since OpenSSL 3.
fn = (unsigned int (*)(void))dlsym(handle, "OPENSSL_version_patch");
if (fn != NULL)
return (int)fn();

// If OPENSSL_version_patch is not defined, try with OpenSSL 1 functions.
unsigned long num = version_num(handle);
// OpenSSL version number follows this schema:
// MNNFFPPS: major minor fix patch status.
if (num < 0x10000000L || num >= 0x10200000L)
{
// We only support minor version 0 and 1,
// so there is no need to implement an algorithm
// that decodes the version number into individual components.
return -1;
}

return (num >> 12) & 0xff;
}
113 changes: 113 additions & 0 deletions internal/ossl/ossl.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,117 @@

package ossl

import (
"errors"
"strconv"
"strings"
"unsafe"
)

//go:generate go run github.com/golang-fips/openssl/v2/internal/mkcgo -out zossl.go --package ossl --lib crypto --include ossl.h api.h

var vMajor, vMinor, vPatch int

func LoadLcrypto(handle unsafe.Pointer) {
mkcgoLoad_crypto(handle)
vMajor = Go_openssl_version_major(handle)
vMinor = Go_openssl_version_minor(handle)
vPatch = Go_openssl_version_minor(handle)
}

func newError(msg string) error {
var b strings.Builder
b.WriteString(msg)
b.WriteString("\nopenssl error(s):")
for {
var (
e uint64
file *byte
line int32
)
if vMajor == 1 {
e = ERR_get_error_line(&file, &line)
} else {
e = ERR_get_error_all(&file, &line, nil, nil, nil)
}
if e == 0 {
break
}
b.WriteByte('\n')
var buf [256]byte
ERR_error_string_n(e, &buf[0], len(buf))
b.WriteString(string(buf[:]) + "\n\t" + goString(file) + ":" + strconv.Itoa(int(line)))
}
return errors.New(b.String())
}

// goString converts a C null-terminated string to a Go string.
func goString(p *byte) string {
if p == nil {
return ""
}
end := unsafe.Pointer(p)
n := 0
for *(*byte)(end) != 0 {
end = unsafe.Pointer(uintptr(end) + unsafe.Sizeof(*p))
n++
}
return string(unsafe.Slice(p, n))
}

func OpenSSL_version(typ int32) *byte {
if vMajor == 1 && vMinor == 0 {
return _SSLeay_version(typ)
}
return _OpenSSL_version(typ)
}

func EVP_MD_CTX_free(ctx EVP_MD_CTX_PTR) {
if vMajor == 1 && vMinor == 0 {
_EVP_MD_CTX_destroy(ctx)
} else {
_EVP_MD_CTX_free(ctx)
}
}

func EVP_MD_CTX_new() (EVP_MD_CTX_PTR, error) {
if vMajor == 1 && vMinor == 0 {
return _EVP_MD_CTX_create()
}
return _EVP_MD_CTX_new()
}

func EVP_MD_get_size(md EVP_MD_PTR) int32 {
if vMajor == 1 {
return _EVP_MD_size(md)
}
return _EVP_MD_get_size(md)
}

func EVP_MD_get_block_size(md EVP_MD_PTR) int32 {
if vMajor == 1 {
return _EVP_MD_block_size(md)
}
return _EVP_MD_get_block_size(md)
}

func EVP_PKEY_get_size(pkey EVP_PKEY_PTR) int32 {
if vMajor == 1 {
return _EVP_PKEY_size(pkey)
}
return _EVP_PKEY_get_size(pkey)
}

func EVP_PKEY_get_bits(pkey EVP_PKEY_PTR) int32 {
if vMajor == 1 {
return _EVP_PKEY_bits(pkey)
}
return _EVP_PKEY_get_bits(pkey)
}

func EVP_CIPHER_get_block_size(cipher EVP_CIPHER_PTR) int32 {
if vMajor == 1 {
return _EVP_CIPHER_block_size(cipher)
}
return _EVP_CIPHER_get_block_size(cipher)
}
Loading

0 comments on commit d00b580

Please sign in to comment.