Skip to content

Use Go strings instead of C.CString #252

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions const.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package openssl

import "C"
import "unsafe"

// cString is a null-terminated string,
// akin to C's char*.
type cString string

// str returns the string value.
func (s cString) str() string {
return string(s)
}

// ptr returns a pointer to the string data.
// It panics if the string is not null-terminated.
//
// The memory pointed to by the returned pointer should
// not be modified and it must only be passed to
// "const char*" parameters. Any attempt to modify it
// will result in a runtime panic, as Go strings are
// allocated in read-only memory.
func (s cString) ptr() *C.char {
if len(s) == 0 {
return nil
}
if s[len(s)-1] != 0 {
panic("must be null-terminated")
}
return (*C.char)(unsafe.Pointer(unsafe.StringData(string(s))))
}

const (
// Provider names
_ProviderNameFips cString = "fips\x00"
_ProviderNameDefault cString = "default\x00"

// Property strings
_PropFIPSYes cString = "fips=yes\x00"
_PropFIPSNo cString = "-fips\x00"

// Key types
_KeyTypeRSA cString = "RSA\x00"
_KeyTypeEC cString = "EC\x00"
_KeyTypeED25519 cString = "ED25519\x00"

// Digest Names
_DigestNameSHA2_256 cString = "SHA2-256\x00"

// KDF names
_OSSL_KDF_NAME_HKDF cString = "HKDF\x00"
_OSSL_KDF_NAME_PBKDF2 cString = "PBKDF2\x00"
_OSSL_KDF_NAME_TLS1_PRF cString = "TLS1-PRF\x00"
_OSSL_MAC_NAME_HMAC cString = "HMAC\x00"

// KDF parameters
_OSSL_KDF_PARAM_DIGEST cString = "digest\x00"
_OSSL_KDF_PARAM_SECRET cString = "secret\x00"
_OSSL_KDF_PARAM_SEED cString = "seed\x00"
_OSSL_KDF_PARAM_KEY cString = "key\x00"
_OSSL_KDF_PARAM_INFO cString = "info\x00"
_OSSL_KDF_PARAM_SALT cString = "salt\x00"
_OSSL_KDF_PARAM_MODE cString = "mode\x00"

// PKEY parameters
_OSSL_PKEY_PARAM_PUB_KEY cString = "pub\x00"
_OSSL_PKEY_PARAM_PRIV_KEY cString = "priv\x00"
_OSSL_PKEY_PARAM_GROUP_NAME cString = "group\x00"
_OSSL_PKEY_PARAM_EC_PUB_X cString = "qx\x00"
_OSSL_PKEY_PARAM_EC_PUB_Y cString = "qy\x00"
_OSSL_PKEY_PARAM_FFC_PBITS cString = "pbits\x00"
_OSSL_PKEY_PARAM_FFC_QBITS cString = "qbits\x00"
_OSSL_PKEY_PARAM_RSA_N cString = "n\x00"
_OSSL_PKEY_PARAM_RSA_E cString = "e\x00"
_OSSL_PKEY_PARAM_RSA_D cString = "d\x00"
_OSSL_PKEY_PARAM_FFC_P cString = "p\x00"
_OSSL_PKEY_PARAM_FFC_Q cString = "q\x00"
_OSSL_PKEY_PARAM_FFC_G cString = "g\x00"
_OSSL_PKEY_PARAM_RSA_FACTOR1 cString = "rsa-factor1\x00"
_OSSL_PKEY_PARAM_RSA_FACTOR2 cString = "rsa-factor2\x00"
_OSSL_PKEY_PARAM_RSA_EXPONENT1 cString = "rsa-exponent1\x00"
_OSSL_PKEY_PARAM_RSA_EXPONENT2 cString = "rsa-exponent2\x00"
_OSSL_PKEY_PARAM_RSA_COEFFICIENT1 cString = "rsa-coefficient1\x00"

// MAC parameters
_OSSL_MAC_PARAM_DIGEST cString = "digest\x00"
)
10 changes: 5 additions & 5 deletions dsa.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,9 @@ func GenerateParametersDSA(l, n int) (DSAParameters, error) {
C.go_openssl_BN_free(q)
C.go_openssl_BN_free(g)
}()
if C.go_openssl_EVP_PKEY_get_bn_param(pkey, _OSSL_PKEY_PARAM_FFC_P, &p) != 1 ||
C.go_openssl_EVP_PKEY_get_bn_param(pkey, _OSSL_PKEY_PARAM_FFC_Q, &q) != 1 ||
C.go_openssl_EVP_PKEY_get_bn_param(pkey, _OSSL_PKEY_PARAM_FFC_G, &g) != 1 {
if C.go_openssl_EVP_PKEY_get_bn_param(pkey, _OSSL_PKEY_PARAM_FFC_P.ptr(), &p) != 1 ||
C.go_openssl_EVP_PKEY_get_bn_param(pkey, _OSSL_PKEY_PARAM_FFC_Q.ptr(), &q) != 1 ||
C.go_openssl_EVP_PKEY_get_bn_param(pkey, _OSSL_PKEY_PARAM_FFC_G.ptr(), &g) != 1 {
return DSAParameters{}, newOpenSSLError("EVP_PKEY_get_bn_param")
}
default:
Expand Down Expand Up @@ -159,8 +159,8 @@ func GenerateKeyDSA(params DSAParameters) (x, y BigInt, err error) {
C.go_openssl_BN_clear_free(bx)
C.go_openssl_BN_free(by)
}()
if C.go_openssl_EVP_PKEY_get_bn_param(pkey, _OSSL_PKEY_PARAM_PUB_KEY, &by) != 1 ||
C.go_openssl_EVP_PKEY_get_bn_param(pkey, _OSSL_PKEY_PARAM_PRIV_KEY, &bx) != 1 {
if C.go_openssl_EVP_PKEY_get_bn_param(pkey, _OSSL_PKEY_PARAM_PUB_KEY.ptr(), &by) != 1 ||
C.go_openssl_EVP_PKEY_get_bn_param(pkey, _OSSL_PKEY_PARAM_PRIV_KEY.ptr(), &bx) != 1 {
return nil, nil, newOpenSSLError("EVP_PKEY_get_bn_param")
}
default:
Expand Down
2 changes: 1 addition & 1 deletion ecdh.go
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ func GenerateKeyECDH(curve string) (*PrivateKeyECDH, []byte, error) {
return nil, nil, newOpenSSLError("EC_KEY_get0_private_key")
}
case 3:
if C.go_openssl_EVP_PKEY_get_bn_param(pkey, _OSSL_PKEY_PARAM_PRIV_KEY, &priv) != 1 {
if C.go_openssl_EVP_PKEY_get_bn_param(pkey, _OSSL_PKEY_PARAM_PRIV_KEY.ptr(), &priv) != 1 {
return nil, nil, newOpenSSLError("EVP_PKEY_get_bn_param")
}
defer C.go_openssl_BN_clear_free(priv)
Expand Down
6 changes: 3 additions & 3 deletions ecdsa.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,9 @@ func GenerateKeyECDSA(curve string) (x, y, d BigInt, err error) {
// Get Z. We don't need to free it, get0 does not increase the reference count.
bd = C.go_openssl_EC_KEY_get0_private_key(key)
case 3:
if C.go_openssl_EVP_PKEY_get_bn_param(pkey, _OSSL_PKEY_PARAM_EC_PUB_X, &bx) != 1 ||
C.go_openssl_EVP_PKEY_get_bn_param(pkey, _OSSL_PKEY_PARAM_EC_PUB_Y, &by) != 1 ||
C.go_openssl_EVP_PKEY_get_bn_param(pkey, _OSSL_PKEY_PARAM_PRIV_KEY, &bd) != 1 {
if C.go_openssl_EVP_PKEY_get_bn_param(pkey, _OSSL_PKEY_PARAM_EC_PUB_X.ptr(), &bx) != 1 ||
C.go_openssl_EVP_PKEY_get_bn_param(pkey, _OSSL_PKEY_PARAM_EC_PUB_Y.ptr(), &by) != 1 ||
C.go_openssl_EVP_PKEY_get_bn_param(pkey, _OSSL_PKEY_PARAM_PRIV_KEY.ptr(), &bd) != 1 {
return nil, nil, nil, newOpenSSLError("EVP_PKEY_get_bn_param")
}
defer C.go_openssl_BN_clear_free(bd)
Expand Down
2 changes: 1 addition & 1 deletion ed25519.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ var supportsEd25519 = sync.OnceValue(func() bool {
}
}
case 3:
sig := C.go_openssl_EVP_SIGNATURE_fetch(nil, keyTypeED25519, nil)
sig := C.go_openssl_EVP_SIGNATURE_fetch(nil, _KeyTypeED25519.ptr(), nil)
if sig != nil {
C.go_openssl_EVP_SIGNATURE_free(sig)
return true
Expand Down
12 changes: 3 additions & 9 deletions evp.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,6 @@ import (
"unsafe"
)

var (
keyTypeRSA = C.CString("RSA")
keyTypeEC = C.CString("EC")
keyTypeED25519 = C.CString("ED25519")
)

// cacheMD is a cache of crypto.Hash to GO_EVP_MD_PTR.
var cacheMD sync.Map

Expand Down Expand Up @@ -201,11 +195,11 @@ func generateEVPPKey(id C.int, bits int, curve string) (C.GO_EVP_PKEY_PTR, error
case 3:
switch id {
case C.GO_EVP_PKEY_RSA:
pkey = C.go_openssl_EVP_PKEY_Q_keygen_RSA(nil, nil, keyTypeRSA, C.size_t(bits))
pkey = C.go_openssl_EVP_PKEY_Q_keygen_RSA(nil, nil, _KeyTypeRSA.ptr(), C.size_t(bits))
case C.GO_EVP_PKEY_EC:
pkey = C.go_openssl_EVP_PKEY_Q_keygen_EC(nil, nil, keyTypeEC, C.go_openssl_OBJ_nid2sn(curveNID(curve)))
pkey = C.go_openssl_EVP_PKEY_Q_keygen_EC(nil, nil, _KeyTypeEC.ptr(), C.go_openssl_OBJ_nid2sn(curveNID(curve)))
case C.GO_EVP_PKEY_ED25519:
pkey = C.go_openssl_EVP_PKEY_Q_keygen(nil, nil, keyTypeED25519)
pkey = C.go_openssl_EVP_PKEY_Q_keygen(nil, nil, _KeyTypeED25519.ptr())
default:
panic("unsupported key type '" + strconv.Itoa(int(id)) + "'")
}
Expand Down
4 changes: 1 addition & 3 deletions hkdf.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,9 +239,7 @@ func (c *hkdf3) finalize() {
var fetchHKDF3 = sync.OnceValues(func() (C.GO_EVP_KDF_PTR, error) {
checkMajorVersion(3)

name := C.CString("HKDF")
kdf := C.go_openssl_EVP_KDF_fetch(nil, name, nil)
C.free(unsafe.Pointer(name))
kdf := C.go_openssl_EVP_KDF_fetch(nil, _OSSL_KDF_NAME_HKDF.ptr(), nil)
if kdf == nil {
return nil, newOpenSSLError("EVP_KDF_fetch")
}
Expand Down
24 changes: 10 additions & 14 deletions hmac.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,27 +88,26 @@ func newHMAC1(key []byte, md C.GO_EVP_MD_PTR) hmacCtx1 {

var hmacDigestsSupported sync.Map
var fetchHMAC3 = sync.OnceValue(func() C.GO_EVP_MAC_PTR {
name := C.CString("HMAC")
mac := C.go_openssl_EVP_MAC_fetch(nil, name, nil)
C.free(unsafe.Pointer(name))
mac := C.go_openssl_EVP_MAC_fetch(nil, _OSSL_MAC_NAME_HMAC.ptr(), nil)
if mac == nil {
panic("openssl: HMAC not supported")
}
return mac
})

func buildHMAC3Params(digest *C.char) (C.GO_OSSL_PARAM_PTR, error) {
func buildHMAC3Params(md C.GO_EVP_MD_PTR) (C.GO_OSSL_PARAM_PTR, error) {
bld, err := newParamBuilder()
if err != nil {
return nil, err
}
defer bld.finalize()
bld.addUTF8String(_OSSL_MAC_PARAM_DIGEST, digest, 0)
bld.addUTF8String(_OSSL_MAC_PARAM_DIGEST, C.go_openssl_EVP_MD_get0_name(md), 0)
return bld.build()
}

func isHMAC3DigestSupported(digest string) bool {
if v, ok := hmacDigestsSupported.Load(digest); ok {
func isHMAC3DigestSupported(md C.GO_EVP_MD_PTR) bool {
nid := C.go_openssl_EVP_MD_get_type(md)
if v, ok := hmacDigestsSupported.Load(nid); ok {
return v.(bool)
}
ctx := C.go_openssl_EVP_MAC_CTX_new(fetchHMAC3())
Expand All @@ -117,29 +116,26 @@ func isHMAC3DigestSupported(digest string) bool {
}
defer C.go_openssl_EVP_MAC_CTX_free(ctx)

cdigest := C.CString(digest)
defer C.free(unsafe.Pointer(cdigest))
params, err := buildHMAC3Params(cdigest)
params, err := buildHMAC3Params(md)
if err != nil {
panic(err)
}
defer C.go_openssl_OSSL_PARAM_free(params)

supported := C.go_openssl_EVP_MAC_CTX_set_params(ctx, params) != 0
hmacDigestsSupported.Store(digest, supported)
hmacDigestsSupported.Store(nid, supported)
return supported
}

func newHMAC3(key []byte, md C.GO_EVP_MD_PTR) hmacCtx3 {
digest := C.go_openssl_EVP_MD_get0_name(md)
if !isHMAC3DigestSupported(C.GoString(digest)) {
if !isHMAC3DigestSupported(md) {
// The digest is not supported by the HMAC provider.
// Don't panic here so the Go standard library to
// fall back to the Go implementation.
// See https://github.com/golang-fips/openssl/issues/153.
return hmacCtx3{}
}
params, err := buildHMAC3Params(digest)
params, err := buildHMAC3Params(md)
if err != nil {
panic(err)
}
Expand Down
33 changes: 12 additions & 21 deletions openssl.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,15 +99,6 @@ func VersionText() string {
return C.GoString(C.go_openssl_OpenSSL_version(0))
}

var (
providerNameFips = C.CString("fips")
providerNameDefault = C.CString("default")
propFIPS = C.CString("fips=yes")
propNoFIPS = C.CString("-fips")

algorithmSHA256 = C.CString("SHA2-256")
)

// FIPS returns true if OpenSSL is running in FIPS mode and there is
// a provider available that supports FIPS. It returns false otherwise.
func FIPS() bool {
Expand All @@ -128,7 +119,7 @@ func FIPS() bool {
// but that is highly unlikely because SHA-256 is one of the most common algorithms and fundamental to many cryptographic operations.
// It also has a small chance of false positive if the FIPS provider implements the SHA-256 algorithm but not the other algorithms
// used by the caller application, but that is also unlikely because the FIPS provider should provide all common algorithms.
return proveSHA256(nil)
return proveSHA256("")
default:
panic(errUnsupportedVersion())
}
Expand All @@ -155,11 +146,11 @@ func FIPSCapable() bool {
if vMajor == 3 {
// Load the provider with and without the `fips=yes` query.
// If the providers are the same, then the default provider is FIPS-capable.
provFIPS := sha256Provider(propFIPS)
provFIPS := sha256Provider(_ProviderNameFips)
if provFIPS == nil {
return false
}
provDefault := sha256Provider(nil)
provDefault := sha256Provider("")
return provFIPS == provDefault
}
return false
Expand Down Expand Up @@ -198,18 +189,18 @@ func SetFIPS(enable bool) error {
}
return nil
case 3:
var shaProps, provName *C.char
var shaProps, provName cString
if enable {
shaProps = propFIPS
provName = providerNameFips
shaProps = _PropFIPSYes
provName = _ProviderNameFips
} else {
shaProps = propNoFIPS
provName = providerNameDefault
shaProps = _PropFIPSNo
provName = _ProviderNameDefault
}
if !proveSHA256(shaProps) {
// There is no provider available that supports the desired FIPS mode.
// Try to load the built-in provider associated with the given mode.
if C.go_openssl_OSSL_PROVIDER_try_load(nil, provName, 1) == nil {
if C.go_openssl_OSSL_PROVIDER_try_load(nil, provName.ptr(), 1) == nil {
// The built-in provider was not loaded successfully, we can't enable FIPS mode.
C.go_openssl_ERR_clear_error()
return errors.New("openssl: FIPS mode not supported by any provider")
Expand All @@ -226,8 +217,8 @@ func SetFIPS(enable bool) error {

// sha256Provider returns the provider for the SHA-256 algorithm
// using the given properties.
func sha256Provider(props *C.char) C.GO_OSSL_PROVIDER_PTR {
md := C.go_openssl_EVP_MD_fetch(nil, algorithmSHA256, props)
func sha256Provider(props cString) C.GO_OSSL_PROVIDER_PTR {
md := C.go_openssl_EVP_MD_fetch(nil, _DigestNameSHA2_256.ptr(), props.ptr())
if md == nil {
C.go_openssl_ERR_clear_error()
return nil
Expand All @@ -238,7 +229,7 @@ func sha256Provider(props *C.char) C.GO_OSSL_PROVIDER_PTR {

// proveSHA256 checks if the SHA-256 algorithm is available
// using the given properties.
func proveSHA256(props *C.char) bool {
func proveSHA256(props cString) bool {
return sha256Provider(props) != nil
}

Expand Down
Loading
Loading