-
Notifications
You must be signed in to change notification settings - Fork 12
/
goopenssl.h
259 lines (222 loc) · 10 KB
/
goopenssl.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
// This header file describes the OpenSSL ABI as built for use in Go.
#include <stdlib.h> // size_t
#include "shims.h"
// Suppress warnings about unused parameters.
#define UNUSED(x) (void)(x)
static inline void
go_openssl_do_leak_check(void)
{
#ifndef __has_feature
#define __has_feature(x) 0
#endif
#if (defined(__SANITIZE_ADDRESS__) && __SANITIZE_ADDRESS__) || \
__has_feature(address_sanitizer)
extern void __lsan_do_leak_check(void);
__lsan_do_leak_check();
#endif
}
int go_openssl_fips_enabled(void* handle);
int go_openssl_version_major(void* handle);
int go_openssl_version_minor(void* handle);
int go_openssl_version_patch(void* handle);
int go_openssl_thread_setup(void);
void go_openssl_load_functions(void* handle, unsigned int major, unsigned int minor, unsigned int patch);
const GO_EVP_MD_PTR go_openssl_EVP_md5_sha1_backport(void);
void go_openssl_DSA_get0_pqg_backport(const GO_DSA_PTR d, GO_BIGNUM_PTR *p, GO_BIGNUM_PTR *q, GO_BIGNUM_PTR *g);
int go_openssl_DSA_set0_pqg_backport(GO_DSA_PTR d, GO_BIGNUM_PTR p, GO_BIGNUM_PTR q, GO_BIGNUM_PTR g);
void go_openssl_DSA_get0_key_backport(const GO_DSA_PTR d, GO_BIGNUM_PTR *pub_key, GO_BIGNUM_PTR *priv_key);
int go_openssl_DSA_set0_key_backport(GO_DSA_PTR d, GO_BIGNUM_PTR pub_key, GO_BIGNUM_PTR priv_key);
// Define pointers to all the used OpenSSL functions.
// Calling C function pointers from Go is currently not supported.
// It is possible to circumvent this by using a C function wrapper.
// https://pkg.go.dev/cmd/cgo
#define DEFINEFUNC(ret, func, args, argscall) \
extern ret (*_g_##func)args; \
static inline ret go_openssl_##func args \
{ \
return _g_##func argscall; \
}
#define DEFINEFUNC_LEGACY_1_1(ret, func, args, argscall) \
DEFINEFUNC(ret, func, args, argscall)
#define DEFINEFUNC_LEGACY_1_0(ret, func, args, argscall) \
DEFINEFUNC(ret, func, args, argscall)
#define DEFINEFUNC_LEGACY_1(ret, func, args, argscall) \
DEFINEFUNC(ret, func, args, argscall)
#define DEFINEFUNC_1_1(ret, func, args, argscall) \
DEFINEFUNC(ret, func, args, argscall)
#define DEFINEFUNC_1_1_1(ret, func, args, argscall) \
DEFINEFUNC(ret, func, args, argscall)
#define DEFINEFUNC_3_0(ret, func, args, argscall) \
DEFINEFUNC(ret, func, args, argscall)
#define DEFINEFUNC_RENAMED_1_1(ret, func, oldfunc, args, argscall) \
DEFINEFUNC(ret, func, args, argscall)
#define DEFINEFUNC_RENAMED_3_0(ret, func, oldfunc, args, argscall) \
DEFINEFUNC(ret, func, args, argscall)
FOR_ALL_OPENSSL_FUNCTIONS
#undef DEFINEFUNC
#undef DEFINEFUNC_LEGACY_1_1
#undef DEFINEFUNC_LEGACY_1_0
#undef DEFINEFUNC_LEGACY_1
#undef DEFINEFUNC_1_1
#undef DEFINEFUNC_1_1_1
#undef DEFINEFUNC_3_0
#undef DEFINEFUNC_RENAMED_1_1
#undef DEFINEFUNC_RENAMED_3_0
// go_hash_sum copies ctx into ctx2 and calls EVP_DigestFinal using ctx2.
// This is necessary because Go hash.Hash mandates that Sum has no effect
// on the underlying stream. In particular it is OK to Sum, then Write more,
// then Sum again, and the second Sum acts as if the first didn't happen.
// It is written in C because Sum() tend to be in the hot path,
// and doing one cgo call instead of two is a significant performance win.
static inline int
go_hash_sum(GO_EVP_MD_CTX_PTR ctx, GO_EVP_MD_CTX_PTR ctx2, unsigned char *out)
{
if (go_openssl_EVP_MD_CTX_copy(ctx2, ctx) != 1)
return 0;
// TODO: use EVP_DigestFinal_ex once we know why it leaks
// memory on OpenSSL 1.0.2.
return go_openssl_EVP_DigestFinal(ctx2, out, NULL);
}
// These wrappers allocate out_len on the C stack to avoid having to pass a pointer from Go, which would escape to the heap.
// Use them only in situations where the output length can be safely discarded.
static inline int
go_openssl_EVP_EncryptUpdate_wrapper(GO_EVP_CIPHER_CTX_PTR ctx, unsigned char *out, const unsigned char *in, int in_len)
{
int len;
return go_openssl_EVP_EncryptUpdate(ctx, out, &len, in, in_len);
}
static inline int
go_openssl_EVP_DecryptUpdate_wrapper(GO_EVP_CIPHER_CTX_PTR ctx, unsigned char *out, const unsigned char *in, int in_len)
{
int len;
return go_openssl_EVP_DecryptUpdate(ctx, out, &len, in, in_len);
}
static inline int
go_openssl_EVP_CipherUpdate_wrapper(GO_EVP_CIPHER_CTX_PTR ctx, unsigned char *out, const unsigned char *in, int in_len)
{
int len;
return go_openssl_EVP_CipherUpdate(ctx, out, &len, in, in_len);
}
// These wrappers also allocate length variables on the C stack to avoid escape to the heap, but do return the result.
// A struct is returned that contains multiple return values instead of OpenSSL's approach of using pointers.
typedef struct
{
int result;
size_t keylen;
} go_openssl_EVP_PKEY_derive_wrapper_out;
static inline go_openssl_EVP_PKEY_derive_wrapper_out
go_openssl_EVP_PKEY_derive_wrapper(GO_EVP_PKEY_CTX_PTR ctx, unsigned char *key, size_t keylen)
{
go_openssl_EVP_PKEY_derive_wrapper_out r = {0, keylen};
r.result = go_openssl_EVP_PKEY_derive(ctx, key, &r.keylen);
return r;
}
typedef struct
{
int result;
size_t len;
} go_openssl_EVP_PKEY_get_raw_key_out;
static inline go_openssl_EVP_PKEY_get_raw_key_out
go_openssl_EVP_PKEY_get_raw_public_key_wrapper(const GO_EVP_PKEY_PTR pkey, unsigned char *pub, size_t len)
{
go_openssl_EVP_PKEY_get_raw_key_out r = {0, len};
r.result = go_openssl_EVP_PKEY_get_raw_public_key(pkey, pub, &r.len);
return r;
}
static inline go_openssl_EVP_PKEY_get_raw_key_out
go_openssl_EVP_PKEY_get_raw_private_key_wrapper(const GO_EVP_PKEY_PTR pkey, unsigned char *priv, size_t len)
{
go_openssl_EVP_PKEY_get_raw_key_out r = {0, len};
r.result = go_openssl_EVP_PKEY_get_raw_private_key(pkey, priv, &r.len);
return r;
}
typedef struct
{
int result;
size_t siglen;
} go_openssl_EVP_DigestSign_wrapper_out;
static inline go_openssl_EVP_DigestSign_wrapper_out
go_openssl_EVP_DigestSign_wrapper(GO_EVP_MD_CTX_PTR ctx, unsigned char *sigret, size_t siglen, const unsigned char *tbs, size_t tbslen)
{
go_openssl_EVP_DigestSign_wrapper_out r = {0, siglen};
r.result = go_openssl_EVP_DigestSign(ctx, sigret, &r.siglen, tbs, tbslen);
return r;
}
// These wrappers allocate out_len on the C stack, and check that it matches the expected
// value, to avoid having to pass a pointer from Go, which would escape to the heap.
static inline int
go_openssl_EVP_CIPHER_CTX_seal_wrapper(const GO_EVP_CIPHER_CTX_PTR ctx,
unsigned char *out,
const unsigned char *nonce,
const unsigned char *in, int in_len,
const unsigned char *aad, int aad_len)
{
if (in_len == 0) in = (const unsigned char *)"";
if (aad_len == 0) aad = (const unsigned char *)"";
if (go_openssl_EVP_EncryptInit_ex(ctx, NULL, NULL, NULL, nonce) != 1)
return 0;
int discard_len, out_len;
if (go_openssl_EVP_EncryptUpdate(ctx, NULL, &discard_len, aad, aad_len) != 1
|| go_openssl_EVP_EncryptUpdate(ctx, out, &out_len, in, in_len) != 1
|| go_openssl_EVP_EncryptFinal_ex(ctx, out + out_len, &discard_len) != 1)
{
return 0;
}
if (in_len != out_len)
return 0;
return go_openssl_EVP_CIPHER_CTX_ctrl(ctx, GO_EVP_CTRL_GCM_GET_TAG, 16, out + out_len);
}
static inline int
go_openssl_EVP_CIPHER_CTX_open_wrapper(const GO_EVP_CIPHER_CTX_PTR ctx,
unsigned char *out,
const unsigned char *nonce,
const unsigned char *in, int in_len,
const unsigned char *aad, int aad_len,
const unsigned char *tag)
{
if (in_len == 0) {
in = (const unsigned char *)"";
// OpenSSL 1.0.2 in FIPS mode contains a bug: it will fail to verify
// unless EVP_DecryptUpdate is called at least once with a non-NULL
// output buffer. OpenSSL will not dereference the output buffer when
// the input length is zero, so set it to an arbitrary non-NULL pointer
// to satisfy OpenSSL when the caller only has authenticated additional
// data (AAD) to verify. While a stack-allocated buffer could be used,
// that would risk a stack-corrupting buffer overflow if OpenSSL
// unexpectedly dereferenced it. Instead pass a value which would
// segfault if dereferenced on any modern platform where a NULL-pointer
// dereference would also segfault.
if (out == NULL) out = (unsigned char *)1;
}
if (aad_len == 0) aad = (const unsigned char *)"";
if (go_openssl_EVP_DecryptInit_ex(ctx, NULL, NULL, NULL, nonce) != 1)
return 0;
// OpenSSL 1.0.x FIPS Object Module 2.0 versions below 2.0.5 require that
// the tag be set before the ciphertext, otherwise EVP_DecryptUpdate returns
// an error. At least one extant commercially-supported, FIPS validated
// build of OpenSSL 1.0.2 uses FIPS module version 2.0.1. Set the tag first
// to maximize compatibility with all OpenSSL version combinations.
if (go_openssl_EVP_CIPHER_CTX_ctrl(ctx, GO_EVP_CTRL_GCM_SET_TAG, 16, (unsigned char *)(tag)) != 1)
return 0;
int discard_len, out_len;
if (go_openssl_EVP_DecryptUpdate(ctx, NULL, &discard_len, aad, aad_len) != 1
|| go_openssl_EVP_DecryptUpdate(ctx, out, &out_len, in, in_len) != 1)
{
return 0;
}
if (go_openssl_EVP_DecryptFinal_ex(ctx, out + out_len, &discard_len) != 1)
return 0;
if (out_len != in_len)
return 0;
return 1;
}
// Hand-roll custom wrappers for CRYPTO_malloc and CRYPTO_free which cast the
// function pointers to the correct signatures for OpenSSL 1.0.2.
static inline void *
go_openssl_CRYPTO_malloc_legacy102(int num, const char *file, int line) {
return ((void *(*)(int, const char *, int))_g_CRYPTO_malloc)(num, file, line);
}
static inline void
go_openssl_CRYPTO_free_legacy102(void *str) {
((void (*)(void *))_g_CRYPTO_free)(str);
}