Skip to content

Commit c464fce

Browse files
committed
add extra ossl wrappers
1 parent 9c1f968 commit c464fce

File tree

4 files changed

+596
-0
lines changed

4 files changed

+596
-0
lines changed

Diff for: 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+
}

Diff for: internal/ossl/ossl.go

+113
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,116 @@
11
package ossl
22

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

0 commit comments

Comments
 (0)