Skip to content

Commit d00b580

Browse files
committed
add extra ossl wrappers
1 parent 5d8a3b1 commit d00b580

File tree

4 files changed

+596
-0
lines changed

4 files changed

+596
-0
lines changed

internal/ossl/ossl.c

+137
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
//go:build unix || windows
2+
3+
#ifdef _WIN32
4+
# include <windows.h>
5+
# define dlsym (void*)GetProcAddress
6+
#else
7+
# include <dlfcn.h> // dlsym
8+
#endif
9+
#include <stdio.h> // fprintf
10+
11+
// go_openssl_fips_enabled returns 1 if FIPS mode is enabled, 0 otherwise.
12+
// As a special case, it returns -1 if it cannot determine if FIPS mode is enabled.
13+
// See openssl.FIPS for details about its implementation.
14+
//
15+
// This function is reimplemented here because openssl.FIPS assumes that
16+
// all the OpenSSL bindings are loaded, that is, go_openssl_load_functions has
17+
// already been called. On the other hand, go_openssl_fips_enabled is called from
18+
// openssl.CheckVersion, which is used to check if a given OpenSSL shared library
19+
// exists and is FIPS compliant. That shared library might not be the one that
20+
// was passed to go_openssl_load_functions, or it might not even have been called at all.
21+
//
22+
// It is written in C because it is not possible to directly call C function pointers
23+
// retrieved using dlsym from Go.
24+
int
25+
go_openssl_fips_enabled(void* handle)
26+
{
27+
// For OpenSSL 1.x.
28+
int (*FIPS_mode)(void);
29+
FIPS_mode = (int (*)(void))dlsym(handle, "FIPS_mode");
30+
if (FIPS_mode != NULL)
31+
return FIPS_mode();
32+
33+
// For OpenSSL 3.x.
34+
int (*EVP_default_properties_is_fips_enabled)(void*) = (int (*)(void*))dlsym(handle, "EVP_default_properties_is_fips_enabled");
35+
void *(*EVP_MD_fetch)(void*, const char*, const char*) = (void* (*)(void*, const char*, const char*))dlsym(handle, "EVP_MD_fetch");
36+
void (*EVP_MD_free)(void*) = (void (*)(void*))dlsym(handle, "EVP_MD_free");
37+
38+
if (EVP_default_properties_is_fips_enabled == NULL || EVP_MD_fetch == NULL || EVP_MD_free == NULL) {
39+
// Shouldn't happen, but if it does, we can't determine if FIPS mode is enabled.
40+
return -1;
41+
}
42+
43+
if (EVP_default_properties_is_fips_enabled(NULL) != 1)
44+
return 0;
45+
46+
void *md = EVP_MD_fetch(NULL, "SHA2-256", NULL);
47+
if (md == NULL)
48+
return 0;
49+
50+
EVP_MD_free(md);
51+
return 1;
52+
}
53+
54+
static unsigned long
55+
version_num(void* handle)
56+
{
57+
unsigned long (*fn)(void);
58+
// OPENSSL_version_num is defined in OpenSSL 1.1.0 and 1.1.1.
59+
fn = (unsigned long (*)(void))dlsym(handle, "OpenSSL_version_num");
60+
if (fn != NULL)
61+
return fn();
62+
63+
// SSLeay is defined in OpenSSL 1.0.2.
64+
fn = (unsigned long (*)(void))dlsym(handle, "SSLeay");
65+
if (fn != NULL)
66+
return fn();
67+
68+
return 0;
69+
}
70+
71+
int
72+
go_openssl_version_major(void* handle)
73+
{
74+
unsigned int (*fn)(void);
75+
// OPENSSL_version_major is supported since OpenSSL 3.
76+
fn = (unsigned int (*)(void))dlsym(handle, "OPENSSL_version_major");
77+
if (fn != NULL)
78+
return (int)fn();
79+
80+
// If OPENSSL_version_major is not defined, try with OpenSSL 1 functions.
81+
unsigned long num = version_num(handle);
82+
if (num < 0x10000000L || num >= 0x20000000L)
83+
return -1;
84+
85+
return 1;
86+
}
87+
88+
int
89+
go_openssl_version_minor(void* handle)
90+
{
91+
unsigned int (*fn)(void);
92+
// OPENSSL_version_minor is supported since OpenSSL 3.
93+
fn = (unsigned int (*)(void))dlsym(handle, "OPENSSL_version_minor");
94+
if (fn != NULL)
95+
return (int)fn();
96+
97+
// If OPENSSL_version_minor is not defined, try with OpenSSL 1 functions.
98+
unsigned long num = version_num(handle);
99+
// OpenSSL version number follows this schema:
100+
// MNNFFPPS: major minor fix patch status.
101+
if (num < 0x10000000L || num >= 0x10200000L)
102+
{
103+
// We only support minor version 0 and 1,
104+
// so there is no need to implement an algorithm
105+
// that decodes the version number into individual components.
106+
return -1;
107+
}
108+
109+
if (num >= 0x10100000L)
110+
return 1;
111+
112+
return 0;
113+
}
114+
115+
int
116+
go_openssl_version_patch(void* handle)
117+
{
118+
unsigned int (*fn)(void);
119+
// OPENSSL_version_patch is supported since OpenSSL 3.
120+
fn = (unsigned int (*)(void))dlsym(handle, "OPENSSL_version_patch");
121+
if (fn != NULL)
122+
return (int)fn();
123+
124+
// If OPENSSL_version_patch is not defined, try with OpenSSL 1 functions.
125+
unsigned long num = version_num(handle);
126+
// OpenSSL version number follows this schema:
127+
// MNNFFPPS: major minor fix patch status.
128+
if (num < 0x10000000L || num >= 0x10200000L)
129+
{
130+
// We only support minor version 0 and 1,
131+
// so there is no need to implement an algorithm
132+
// that decodes the version number into individual components.
133+
return -1;
134+
}
135+
136+
return (num >> 12) & 0xff;
137+
}

internal/ossl/ossl.go

+113
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,117 @@
22

33
package ossl
44

5+
import (
6+
"errors"
7+
"strconv"
8+
"strings"
9+
"unsafe"
10+
)
11+
512
//go:generate go run github.com/golang-fips/openssl/v2/internal/mkcgo -out zossl.go --package ossl --lib crypto --include ossl.h api.h
13+
14+
var vMajor, vMinor, vPatch int
15+
16+
func LoadLcrypto(handle unsafe.Pointer) {
17+
mkcgoLoad_crypto(handle)
18+
vMajor = Go_openssl_version_major(handle)
19+
vMinor = Go_openssl_version_minor(handle)
20+
vPatch = Go_openssl_version_minor(handle)
21+
}
22+
23+
func newError(msg string) error {
24+
var b strings.Builder
25+
b.WriteString(msg)
26+
b.WriteString("\nopenssl error(s):")
27+
for {
28+
var (
29+
e uint64
30+
file *byte
31+
line int32
32+
)
33+
if vMajor == 1 {
34+
e = ERR_get_error_line(&file, &line)
35+
} else {
36+
e = ERR_get_error_all(&file, &line, nil, nil, nil)
37+
}
38+
if e == 0 {
39+
break
40+
}
41+
b.WriteByte('\n')
42+
var buf [256]byte
43+
ERR_error_string_n(e, &buf[0], len(buf))
44+
b.WriteString(string(buf[:]) + "\n\t" + goString(file) + ":" + strconv.Itoa(int(line)))
45+
}
46+
return errors.New(b.String())
47+
}
48+
49+
// goString converts a C null-terminated string to a Go string.
50+
func goString(p *byte) string {
51+
if p == nil {
52+
return ""
53+
}
54+
end := unsafe.Pointer(p)
55+
n := 0
56+
for *(*byte)(end) != 0 {
57+
end = unsafe.Pointer(uintptr(end) + unsafe.Sizeof(*p))
58+
n++
59+
}
60+
return string(unsafe.Slice(p, n))
61+
}
62+
63+
func OpenSSL_version(typ int32) *byte {
64+
if vMajor == 1 && vMinor == 0 {
65+
return _SSLeay_version(typ)
66+
}
67+
return _OpenSSL_version(typ)
68+
}
69+
70+
func EVP_MD_CTX_free(ctx EVP_MD_CTX_PTR) {
71+
if vMajor == 1 && vMinor == 0 {
72+
_EVP_MD_CTX_destroy(ctx)
73+
} else {
74+
_EVP_MD_CTX_free(ctx)
75+
}
76+
}
77+
78+
func EVP_MD_CTX_new() (EVP_MD_CTX_PTR, error) {
79+
if vMajor == 1 && vMinor == 0 {
80+
return _EVP_MD_CTX_create()
81+
}
82+
return _EVP_MD_CTX_new()
83+
}
84+
85+
func EVP_MD_get_size(md EVP_MD_PTR) int32 {
86+
if vMajor == 1 {
87+
return _EVP_MD_size(md)
88+
}
89+
return _EVP_MD_get_size(md)
90+
}
91+
92+
func EVP_MD_get_block_size(md EVP_MD_PTR) int32 {
93+
if vMajor == 1 {
94+
return _EVP_MD_block_size(md)
95+
}
96+
return _EVP_MD_get_block_size(md)
97+
}
98+
99+
func EVP_PKEY_get_size(pkey EVP_PKEY_PTR) int32 {
100+
if vMajor == 1 {
101+
return _EVP_PKEY_size(pkey)
102+
}
103+
return _EVP_PKEY_get_size(pkey)
104+
}
105+
106+
func EVP_PKEY_get_bits(pkey EVP_PKEY_PTR) int32 {
107+
if vMajor == 1 {
108+
return _EVP_PKEY_bits(pkey)
109+
}
110+
return _EVP_PKEY_get_bits(pkey)
111+
}
112+
113+
func EVP_CIPHER_get_block_size(cipher EVP_CIPHER_PTR) int32 {
114+
if vMajor == 1 {
115+
return _EVP_CIPHER_block_size(cipher)
116+
}
117+
return _EVP_CIPHER_get_block_size(cipher)
118+
}

0 commit comments

Comments
 (0)