Skip to content

Commit

Permalink
Merge pull request #131 from golang-fips/dev/dagood/heap-avoidance-di…
Browse files Browse the repository at this point in the history
…rect-wrappers

Wrap multi-return APIs using structs: avoid heap escape
  • Loading branch information
qmuntal authored Jan 11, 2024
2 parents 7eb5b52 + 31c3fb2 commit 98604fd
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 23 deletions.
8 changes: 4 additions & 4 deletions ecdh.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,12 +269,12 @@ func ECDH(priv *PrivateKeyECDH, pub *PublicKeyECDH) ([]byte, error) {
if C.go_openssl_EVP_PKEY_derive_set_peer(ctx, pub._pkey) != 1 {
return nil, newOpenSSLError("EVP_PKEY_derive_set_peer")
}
var outLen C.size_t
if C.go_openssl_EVP_PKEY_derive(ctx, nil, &outLen) != 1 {
r := C.go_openssl_EVP_PKEY_derive_wrapper(ctx, nil, 0)
if r.result != 1 {
return nil, newOpenSSLError("EVP_PKEY_derive_init")
}
out := make([]byte, outLen)
if C.go_openssl_EVP_PKEY_derive(ctx, base(out), &outLen) != 1 {
out := make([]byte, r.keylen)
if C.go_openssl_EVP_PKEY_derive_wrapper(ctx, base(out), r.keylen).result != 1 {
return nil, newOpenSSLError("EVP_PKEY_derive_init")
}
return out, nil
Expand Down
24 changes: 12 additions & 12 deletions ed25519.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,12 +145,12 @@ func NewPrivateKeyEd25519FromSeed(seed []byte) (*PrivateKeyEd25519, error) {
}

func extractPKEYPubEd25519(pkey C.GO_EVP_PKEY_PTR, pub []byte) error {
pubSize := C.size_t(publicKeySizeEd25519)
if C.go_openssl_EVP_PKEY_get_raw_public_key(pkey, base(pub), &pubSize) != 1 {
r := C.go_openssl_EVP_PKEY_get_raw_public_key_wrapper(pkey, base(pub), C.size_t(publicKeySizeEd25519))
if r.result != 1 {
return newOpenSSLError("EVP_PKEY_get_raw_public_key")
}
if pubSize != publicKeySizeEd25519 {
return errors.New("ed25519: bad public key length: " + strconv.Itoa(int(pubSize)))
if r.len != publicKeySizeEd25519 {
return errors.New("ed25519: bad public key length: " + strconv.Itoa(int(r.len)))
}
return nil
}
Expand All @@ -159,12 +159,12 @@ func extractPKEYPrivEd25519(pkey C.GO_EVP_PKEY_PTR, priv []byte) error {
if err := extractPKEYPubEd25519(pkey, priv[seedSizeEd25519:]); err != nil {
return err
}
privSize := C.size_t(seedSizeEd25519)
if C.go_openssl_EVP_PKEY_get_raw_private_key(pkey, base(priv), &privSize) != 1 {
r := C.go_openssl_EVP_PKEY_get_raw_private_key_wrapper(pkey, base(priv), C.size_t(seedSizeEd25519))
if r.result != 1 {
return newOpenSSLError("EVP_PKEY_get_raw_private_key")
}
if privSize != seedSizeEd25519 {
return errors.New("ed25519: bad private key length: " + strconv.Itoa(int(privSize)))
if r.len != seedSizeEd25519 {
return errors.New("ed25519: bad private key length: " + strconv.Itoa(int(r.len)))
}
return nil
}
Expand All @@ -190,12 +190,12 @@ func signEd25519(priv *PrivateKeyEd25519, sig, message []byte) error {
if C.go_openssl_EVP_DigestSignInit(ctx, nil, nil, nil, priv._pkey) != 1 {
return newOpenSSLError("EVP_DigestSignInit")
}
siglen := C.size_t(signatureSizeEd25519)
if C.go_openssl_EVP_DigestSign(ctx, base(sig), &siglen, base(message), C.size_t(len(message))) != 1 {
r := C.go_openssl_EVP_DigestSign_wrapper(ctx, base(sig), C.size_t(signatureSizeEd25519), base(message), C.size_t(len(message)))
if r.result != 1 {
return newOpenSSLError("EVP_DigestSign")
}
if siglen != signatureSizeEd25519 {
return errors.New("ed25519: bad signature length: " + strconv.Itoa(int(siglen)))
if r.siglen != signatureSizeEd25519 {
return errors.New("ed25519: bad signature length: " + strconv.Itoa(int(r.siglen)))
}
return nil
}
Expand Down
52 changes: 52 additions & 0 deletions goopenssl.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,58 @@ go_openssl_EVP_CipherUpdate_wrapper(GO_EVP_CIPHER_CTX_PTR ctx, unsigned char *ou
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.
Expand Down
12 changes: 6 additions & 6 deletions hkdf.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ func (c *hkdf) Read(p []byte) (int, error) {
}
c.buf = append(c.buf, make([]byte, needLen)...)
outLen := C.size_t(prevLen + needLen)
if C.go_openssl_EVP_PKEY_derive(c.ctx, base(c.buf), &outLen) != 1 {
if C.go_openssl_EVP_PKEY_derive_wrapper(c.ctx, base(c.buf), outLen).result != 1 {
return 0, newOpenSSLError("EVP_PKEY_derive")
}
n := copy(p, c.buf[prevLen:outLen])
Expand Down Expand Up @@ -132,15 +132,15 @@ func ExtractHKDF(h func() hash.Hash, secret, salt []byte) ([]byte, error) {
return nil, newOpenSSLError("EVP_PKEY_CTX_set1_hkdf_salt")
}
}
var outLen C.size_t
if C.go_openssl_EVP_PKEY_derive(c.ctx, nil, &outLen) != 1 {
r := C.go_openssl_EVP_PKEY_derive_wrapper(c.ctx, nil, 0)
if r.result != 1 {
return nil, newOpenSSLError("EVP_PKEY_derive_init")
}
out := make([]byte, outLen)
if C.go_openssl_EVP_PKEY_derive(c.ctx, base(out), &outLen) != 1 {
out := make([]byte, r.keylen)
if C.go_openssl_EVP_PKEY_derive_wrapper(c.ctx, base(out), r.keylen).result != 1 {
return nil, newOpenSSLError("EVP_PKEY_derive")
}
return out[:outLen], nil
return out[:r.keylen], nil
}

func ExpandHKDF(h func() hash.Hash, pseudorandomKey, info []byte) (io.Reader, error) {
Expand Down
2 changes: 1 addition & 1 deletion tls1prf.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ func TLS1PRF(result, secret, label, seed []byte, h func() hash.Hash) error {
}
}
outLen := C.size_t(len(result))
if C.go_openssl_EVP_PKEY_derive(ctx, base(result), &outLen) != 1 {
if C.go_openssl_EVP_PKEY_derive_wrapper(ctx, base(result), outLen).result != 1 {
return newOpenSSLError("EVP_PKEY_derive")
}
// The Go standard library expects TLS1PRF to return the requested number of bytes,
Expand Down

0 comments on commit 98604fd

Please sign in to comment.