diff --git a/ecdh.go b/ecdh.go index 62e23333..58d8f819 100644 --- a/ecdh.go +++ b/ecdh.go @@ -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 { + outLen := C.go_openssl_EVP_PKEY_derive_wrapper_get_len(ctx) + if outLen == 0 { return nil, newOpenSSLError("EVP_PKEY_derive_init") } out := make([]byte, outLen) - if C.go_openssl_EVP_PKEY_derive(ctx, base(out), &outLen) != 1 { + if C.go_openssl_EVP_PKEY_derive_wrapper_with_len(ctx, base(out), outLen) != 1 { return nil, newOpenSSLError("EVP_PKEY_derive_init") } return out, nil diff --git a/ed25519.go b/ed25519.go index f66a2a1d..b52e5ca4 100644 --- a/ed25519.go +++ b/ed25519.go @@ -146,7 +146,7 @@ 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 { + if C.go_openssl_EVP_PKEY_get_raw_public_key_wrapper_with_len(pkey, base(pub), pubSize) != 1 { return newOpenSSLError("EVP_PKEY_get_raw_public_key") } if pubSize != publicKeySizeEd25519 { @@ -160,7 +160,7 @@ func extractPKEYPrivEd25519(pkey C.GO_EVP_PKEY_PTR, priv []byte) error { return err } privSize := C.size_t(seedSizeEd25519) - if C.go_openssl_EVP_PKEY_get_raw_private_key(pkey, base(priv), &privSize) != 1 { + if C.go_openssl_EVP_PKEY_get_raw_private_key_wrapper_with_len(pkey, base(priv), privSize) != 1 { return newOpenSSLError("EVP_PKEY_get_raw_private_key") } if privSize != seedSizeEd25519 { @@ -191,7 +191,8 @@ func signEd25519(priv *PrivateKeyEd25519, sig, message []byte) error { 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 { + siglen = C.go_openssl_EVP_DigestSign_wrapper_modify_len(ctx, base(sig), siglen, base(message), C.size_t(len(message))) + if siglen == 0 { return newOpenSSLError("EVP_DigestSign") } if siglen != signatureSizeEd25519 { diff --git a/goopenssl.h b/goopenssl.h index a0e2b623..3ed4e895 100644 --- a/goopenssl.h +++ b/goopenssl.h @@ -105,6 +105,47 @@ 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 allocate a length variable on the C stack to avoid having to pass a pointer from Go, which would escape to the heap. +// Some OpenGL APIs use length as either "in" and "out" depending on the value of another parameter, so they accept a pointer. +// By splitting the "in" and "out" situations into distinct functions, a pointer is not necessary. +// +// Note: for simplicity, the "out" wrappers treat a 0 length the same as an error. +// A 0 is not an expected return value in this context, so this is expected to be ok. + +static inline size_t +go_openssl_EVP_PKEY_derive_wrapper_get_len(GO_EVP_PKEY_CTX_PTR ctx) +{ + size_t keylen; + if (go_openssl_EVP_PKEY_derive(ctx, NULL, &keylen) != 1) + return 0; + return keylen; +} + +static inline int +go_openssl_EVP_PKEY_derive_wrapper_with_len(GO_EVP_PKEY_CTX_PTR ctx, unsigned char *key, size_t keylen) +{ + return go_openssl_EVP_PKEY_derive(ctx, key, &keylen); +} + +static inline int +go_openssl_EVP_PKEY_get_raw_public_key_wrapper_with_len(const GO_EVP_PKEY_PTR pkey, unsigned char *pub, size_t len) +{ + return go_openssl_EVP_PKEY_get_raw_public_key(pkey, pub, &len); +} + +static inline int +go_openssl_EVP_PKEY_get_raw_private_key_wrapper_with_len(const GO_EVP_PKEY_PTR pkey, unsigned char *priv, size_t len) +{ + return go_openssl_EVP_PKEY_get_raw_private_key(pkey, priv, &len); +} + +static inline size_t +go_openssl_EVP_DigestSign_wrapper_modify_len(GO_EVP_MD_CTX_PTR ctx, unsigned char *sigret, size_t siglen, const unsigned char *tbs, size_t tbslen) +{ + if (go_openssl_EVP_DigestSign(ctx, sigret, &siglen, tbs, tbslen) != 1) + return 0; + return siglen; +} // 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. diff --git a/hkdf.go b/hkdf.go index ac3fbba0..e3d500ad 100644 --- a/hkdf.go +++ b/hkdf.go @@ -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_with_len(c.ctx, base(c.buf), outLen) != 1 { return 0, newOpenSSLError("EVP_PKEY_derive") } n := copy(p, c.buf[prevLen:outLen]) @@ -132,12 +132,12 @@ 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 { + outLen := C.go_openssl_EVP_PKEY_derive_wrapper_get_len(c.ctx) + if outLen == 0 { return nil, newOpenSSLError("EVP_PKEY_derive_init") } out := make([]byte, outLen) - if C.go_openssl_EVP_PKEY_derive(c.ctx, base(out), &outLen) != 1 { + if C.go_openssl_EVP_PKEY_derive_wrapper_with_len(c.ctx, base(out), outLen) != 1 { return nil, newOpenSSLError("EVP_PKEY_derive") } return out[:outLen], nil diff --git a/tls1prf.go b/tls1prf.go index 3153fc81..7f8bcfda 100644 --- a/tls1prf.go +++ b/tls1prf.go @@ -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_with_len(ctx, base(result), outLen) != 1 { return newOpenSSLError("EVP_PKEY_derive") } // The Go standard library expects TLS1PRF to return the requested number of bytes,