Skip to content

Commit 98604fd

Browse files
authored
Merge pull request #131 from golang-fips/dev/dagood/heap-avoidance-direct-wrappers
Wrap multi-return APIs using structs: avoid heap escape
2 parents 7eb5b52 + 31c3fb2 commit 98604fd

File tree

5 files changed

+75
-23
lines changed

5 files changed

+75
-23
lines changed

ecdh.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -269,12 +269,12 @@ func ECDH(priv *PrivateKeyECDH, pub *PublicKeyECDH) ([]byte, error) {
269269
if C.go_openssl_EVP_PKEY_derive_set_peer(ctx, pub._pkey) != 1 {
270270
return nil, newOpenSSLError("EVP_PKEY_derive_set_peer")
271271
}
272-
var outLen C.size_t
273-
if C.go_openssl_EVP_PKEY_derive(ctx, nil, &outLen) != 1 {
272+
r := C.go_openssl_EVP_PKEY_derive_wrapper(ctx, nil, 0)
273+
if r.result != 1 {
274274
return nil, newOpenSSLError("EVP_PKEY_derive_init")
275275
}
276-
out := make([]byte, outLen)
277-
if C.go_openssl_EVP_PKEY_derive(ctx, base(out), &outLen) != 1 {
276+
out := make([]byte, r.keylen)
277+
if C.go_openssl_EVP_PKEY_derive_wrapper(ctx, base(out), r.keylen).result != 1 {
278278
return nil, newOpenSSLError("EVP_PKEY_derive_init")
279279
}
280280
return out, nil

ed25519.go

+12-12
Original file line numberDiff line numberDiff line change
@@ -145,12 +145,12 @@ func NewPrivateKeyEd25519FromSeed(seed []byte) (*PrivateKeyEd25519, error) {
145145
}
146146

147147
func extractPKEYPubEd25519(pkey C.GO_EVP_PKEY_PTR, pub []byte) error {
148-
pubSize := C.size_t(publicKeySizeEd25519)
149-
if C.go_openssl_EVP_PKEY_get_raw_public_key(pkey, base(pub), &pubSize) != 1 {
148+
r := C.go_openssl_EVP_PKEY_get_raw_public_key_wrapper(pkey, base(pub), C.size_t(publicKeySizeEd25519))
149+
if r.result != 1 {
150150
return newOpenSSLError("EVP_PKEY_get_raw_public_key")
151151
}
152-
if pubSize != publicKeySizeEd25519 {
153-
return errors.New("ed25519: bad public key length: " + strconv.Itoa(int(pubSize)))
152+
if r.len != publicKeySizeEd25519 {
153+
return errors.New("ed25519: bad public key length: " + strconv.Itoa(int(r.len)))
154154
}
155155
return nil
156156
}
@@ -159,12 +159,12 @@ func extractPKEYPrivEd25519(pkey C.GO_EVP_PKEY_PTR, priv []byte) error {
159159
if err := extractPKEYPubEd25519(pkey, priv[seedSizeEd25519:]); err != nil {
160160
return err
161161
}
162-
privSize := C.size_t(seedSizeEd25519)
163-
if C.go_openssl_EVP_PKEY_get_raw_private_key(pkey, base(priv), &privSize) != 1 {
162+
r := C.go_openssl_EVP_PKEY_get_raw_private_key_wrapper(pkey, base(priv), C.size_t(seedSizeEd25519))
163+
if r.result != 1 {
164164
return newOpenSSLError("EVP_PKEY_get_raw_private_key")
165165
}
166-
if privSize != seedSizeEd25519 {
167-
return errors.New("ed25519: bad private key length: " + strconv.Itoa(int(privSize)))
166+
if r.len != seedSizeEd25519 {
167+
return errors.New("ed25519: bad private key length: " + strconv.Itoa(int(r.len)))
168168
}
169169
return nil
170170
}
@@ -190,12 +190,12 @@ func signEd25519(priv *PrivateKeyEd25519, sig, message []byte) error {
190190
if C.go_openssl_EVP_DigestSignInit(ctx, nil, nil, nil, priv._pkey) != 1 {
191191
return newOpenSSLError("EVP_DigestSignInit")
192192
}
193-
siglen := C.size_t(signatureSizeEd25519)
194-
if C.go_openssl_EVP_DigestSign(ctx, base(sig), &siglen, base(message), C.size_t(len(message))) != 1 {
193+
r := C.go_openssl_EVP_DigestSign_wrapper(ctx, base(sig), C.size_t(signatureSizeEd25519), base(message), C.size_t(len(message)))
194+
if r.result != 1 {
195195
return newOpenSSLError("EVP_DigestSign")
196196
}
197-
if siglen != signatureSizeEd25519 {
198-
return errors.New("ed25519: bad signature length: " + strconv.Itoa(int(siglen)))
197+
if r.siglen != signatureSizeEd25519 {
198+
return errors.New("ed25519: bad signature length: " + strconv.Itoa(int(r.siglen)))
199199
}
200200
return nil
201201
}

goopenssl.h

+52
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,58 @@ go_openssl_EVP_CipherUpdate_wrapper(GO_EVP_CIPHER_CTX_PTR ctx, unsigned char *ou
107107
return go_openssl_EVP_CipherUpdate(ctx, out, &len, in, in_len);
108108
}
109109

110+
// These wrappers also allocate length variables on the C stack to avoid escape to the heap, but do return the result.
111+
// A struct is returned that contains multiple return values instead of OpenSSL's approach of using pointers.
112+
113+
typedef struct
114+
{
115+
int result;
116+
size_t keylen;
117+
} go_openssl_EVP_PKEY_derive_wrapper_out;
118+
119+
static inline go_openssl_EVP_PKEY_derive_wrapper_out
120+
go_openssl_EVP_PKEY_derive_wrapper(GO_EVP_PKEY_CTX_PTR ctx, unsigned char *key, size_t keylen)
121+
{
122+
go_openssl_EVP_PKEY_derive_wrapper_out r = {0, keylen};
123+
r.result = go_openssl_EVP_PKEY_derive(ctx, key, &r.keylen);
124+
return r;
125+
}
126+
127+
typedef struct
128+
{
129+
int result;
130+
size_t len;
131+
} go_openssl_EVP_PKEY_get_raw_key_out;
132+
133+
static inline go_openssl_EVP_PKEY_get_raw_key_out
134+
go_openssl_EVP_PKEY_get_raw_public_key_wrapper(const GO_EVP_PKEY_PTR pkey, unsigned char *pub, size_t len)
135+
{
136+
go_openssl_EVP_PKEY_get_raw_key_out r = {0, len};
137+
r.result = go_openssl_EVP_PKEY_get_raw_public_key(pkey, pub, &r.len);
138+
return r;
139+
}
140+
141+
static inline go_openssl_EVP_PKEY_get_raw_key_out
142+
go_openssl_EVP_PKEY_get_raw_private_key_wrapper(const GO_EVP_PKEY_PTR pkey, unsigned char *priv, size_t len)
143+
{
144+
go_openssl_EVP_PKEY_get_raw_key_out r = {0, len};
145+
r.result = go_openssl_EVP_PKEY_get_raw_private_key(pkey, priv, &r.len);
146+
return r;
147+
}
148+
149+
typedef struct
150+
{
151+
int result;
152+
size_t siglen;
153+
} go_openssl_EVP_DigestSign_wrapper_out;
154+
155+
static inline go_openssl_EVP_DigestSign_wrapper_out
156+
go_openssl_EVP_DigestSign_wrapper(GO_EVP_MD_CTX_PTR ctx, unsigned char *sigret, size_t siglen, const unsigned char *tbs, size_t tbslen)
157+
{
158+
go_openssl_EVP_DigestSign_wrapper_out r = {0, siglen};
159+
r.result = go_openssl_EVP_DigestSign(ctx, sigret, &r.siglen, tbs, tbslen);
160+
return r;
161+
}
110162

111163
// These wrappers allocate out_len on the C stack, and check that it matches the expected
112164
// value, to avoid having to pass a pointer from Go, which would escape to the heap.

hkdf.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ func (c *hkdf) Read(p []byte) (int, error) {
9898
}
9999
c.buf = append(c.buf, make([]byte, needLen)...)
100100
outLen := C.size_t(prevLen + needLen)
101-
if C.go_openssl_EVP_PKEY_derive(c.ctx, base(c.buf), &outLen) != 1 {
101+
if C.go_openssl_EVP_PKEY_derive_wrapper(c.ctx, base(c.buf), outLen).result != 1 {
102102
return 0, newOpenSSLError("EVP_PKEY_derive")
103103
}
104104
n := copy(p, c.buf[prevLen:outLen])
@@ -132,15 +132,15 @@ func ExtractHKDF(h func() hash.Hash, secret, salt []byte) ([]byte, error) {
132132
return nil, newOpenSSLError("EVP_PKEY_CTX_set1_hkdf_salt")
133133
}
134134
}
135-
var outLen C.size_t
136-
if C.go_openssl_EVP_PKEY_derive(c.ctx, nil, &outLen) != 1 {
135+
r := C.go_openssl_EVP_PKEY_derive_wrapper(c.ctx, nil, 0)
136+
if r.result != 1 {
137137
return nil, newOpenSSLError("EVP_PKEY_derive_init")
138138
}
139-
out := make([]byte, outLen)
140-
if C.go_openssl_EVP_PKEY_derive(c.ctx, base(out), &outLen) != 1 {
139+
out := make([]byte, r.keylen)
140+
if C.go_openssl_EVP_PKEY_derive_wrapper(c.ctx, base(out), r.keylen).result != 1 {
141141
return nil, newOpenSSLError("EVP_PKEY_derive")
142142
}
143-
return out[:outLen], nil
143+
return out[:r.keylen], nil
144144
}
145145

146146
func ExpandHKDF(h func() hash.Hash, pseudorandomKey, info []byte) (io.Reader, error) {

tls1prf.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ func TLS1PRF(result, secret, label, seed []byte, h func() hash.Hash) error {
9090
}
9191
}
9292
outLen := C.size_t(len(result))
93-
if C.go_openssl_EVP_PKEY_derive(ctx, base(result), &outLen) != 1 {
93+
if C.go_openssl_EVP_PKEY_derive_wrapper(ctx, base(result), outLen).result != 1 {
9494
return newOpenSSLError("EVP_PKEY_derive")
9595
}
9696
// The Go standard library expects TLS1PRF to return the requested number of bytes,

0 commit comments

Comments
 (0)