Skip to content

Support Unix and Windows #98

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 5 commits into from
Aug 25, 2023
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
36 changes: 36 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,39 @@ jobs:
$ok
env:
GO_OPENSSL_VERSION_OVERRIDE: ${{ matrix.openssl-version }}
wintest:
runs-on: windows-2022
strategy:
fail-fast: false
matrix:
go-version: [1.20.x]
openssl-version: [libcrypto-1_1-x64.dll, libcrypto-3-x64.dll]
steps:
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: ${{ matrix.go-version }}
- name: Checkout code
uses: actions/checkout@v3
- name: Run Test
run: go test -gcflags=all=-d=checkptr -count 10 -v ./...
env:
GO_OPENSSL_VERSION_OVERRIDE: ${{ matrix.openssl-version }}
mactest:
strategy:
fail-fast: false
matrix:
go-version: [1.20.x]
openssl-version: [libcrypto.3.dylib]
runs-on: macos-12
steps:
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: ${{ matrix.go-version }}
- name: Checkout code
uses: actions/checkout@v3
- name: Run Test
run: go test -gcflags=all=-d=checkptr -count 10 -v ./...
env:
GO_OPENSSL_VERSION_OVERRIDE: ${{ matrix.openssl-version }}
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,7 @@ This feature does not require any additional configuration, but it only works wi

## Limitations

OpenSSL is used for a given build only in limited circumstances:

- The platform must be `GOOS=linux`.
- Only Unix, Unix-like and Windows platforms are supported.
- The build must set `CGO_ENABLED=1`.

## Acknowledgements
Expand Down
2 changes: 1 addition & 1 deletion aes.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build linux && !cmd_go_bootstrap
//go:build !cmd_go_bootstrap

package openssl

Expand Down
2 changes: 0 additions & 2 deletions aes_test.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
//go:build linux

package openssl

import (
Expand Down
2 changes: 1 addition & 1 deletion ec.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build linux && !cmd_go_bootstrap
//go:build !cmd_go_bootstrap

package openssl

Expand Down
8 changes: 4 additions & 4 deletions ecdh.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build linux && !cmd_go_bootstrap
//go:build !cmd_go_bootstrap

package openssl

Expand Down Expand Up @@ -101,7 +101,7 @@ func (k *PrivateKeyECDH) PublicKey() (*PublicKeyECDH, error) {
return nil, newOpenSSLError("EVP_PKEY_get_octet_string_param")
}
bytes = C.GoBytes(unsafe.Pointer(cbytes), C.int(n))
C.free(unsafe.Pointer(cbytes))
cryptoFree(unsafe.Pointer(cbytes))
default:
panic(errUnsupportedVersion())
}
Expand Down Expand Up @@ -314,8 +314,8 @@ func GenerateKeyECDH(curve string) (*PrivateKeyECDH, []byte, error) {
// generating a private ECDH key.
bits := C.go_openssl_EVP_PKEY_get_bits(pkey)
bytes := make([]byte, (bits+7)/8)
if C.go_openssl_BN_bn2binpad(priv, base(bytes), C.int(len(bytes))) == 0 {
return nil, nil, newOpenSSLError("BN_bn2binpad")
if err := bnToBinPad(priv, bytes); err != nil {
return nil, nil, err
}
k = &PrivateKeyECDH{pkey, curve, true}
runtime.SetFinalizer(k, (*PrivateKeyECDH).finalize)
Expand Down
2 changes: 0 additions & 2 deletions ecdh_test.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
//go:build linux

package openssl_test

import (
Expand Down
2 changes: 1 addition & 1 deletion ecdsa.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build linux && !cmd_go_bootstrap
//go:build !cmd_go_bootstrap

package openssl

Expand Down
2 changes: 0 additions & 2 deletions ecdsa_test.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
//go:build linux

package openssl_test

import (
Expand Down
9 changes: 4 additions & 5 deletions evp.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build linux && !cmd_go_bootstrap
//go:build !cmd_go_bootstrap

package openssl

Expand Down Expand Up @@ -197,12 +197,11 @@ func setupEVP(withKey withKeyFunc, padding C.int,
}
}
// ctx takes ownership of label, so malloc a copy for OpenSSL to free.
// OpenSSL 1.1.1 and higher does not take ownership of the label if the length is zero,
// OpenSSL does not take ownership of the label if the length is zero,
// so better avoid the allocation.
var clabel *C.uchar
if len(label) > 0 {
// Go guarantees C.malloc never returns nil.
clabel = (*C.uchar)(C.malloc(C.size_t(len(label))))
clabel = (*C.uchar)(cryptoMalloc(len(label)))
copy((*[1 << 30]byte)(unsafe.Pointer(clabel))[:len(label)], label)
}
var ret C.int
Expand All @@ -212,7 +211,7 @@ func setupEVP(withKey withKeyFunc, padding C.int,
ret = C.go_openssl_EVP_PKEY_CTX_ctrl(ctx, C.GO_EVP_PKEY_RSA, -1, C.GO_EVP_PKEY_CTRL_RSA_OAEP_LABEL, C.int(len(label)), unsafe.Pointer(clabel))
}
if ret != 1 {
C.free(unsafe.Pointer(clabel))
cryptoFree(unsafe.Pointer(clabel))
return nil, newOpenSSLError("EVP_PKEY_CTX_ctrl failed")
}
case C.GO_RSA_PKCS1_PSS_PADDING:
Expand Down
9 changes: 7 additions & 2 deletions goopenssl.c
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
//go:build linux
//go:build unix || windows

#include "goopenssl.h"

#include <dlfcn.h> // dlsym
#ifdef _WIN32
# include <windows.h>
# define dlsym (void*)GetProcAddress
#else
# include <dlfcn.h> // dlsym
#endif
#include <stdio.h> // fprintf

// Approach taken from .Net System.Security.Cryptography.Native
Expand Down
21 changes: 17 additions & 4 deletions goopenssl.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,8 @@ go_openssl_EVP_CIPHER_CTX_seal_wrapper(const GO_EVP_CIPHER_CTX_PTR ctx,
const unsigned char *in, int in_len,
const unsigned char *aad, int aad_len)
{
if (in_len == 0) in = "";
if (aad_len == 0) aad = "";
if (in_len == 0) in = (const unsigned char *)"";
if (aad_len == 0) aad = (const unsigned char *)"";

if (go_openssl_EVP_CipherInit_ex(ctx, NULL, NULL, NULL, nonce, GO_AES_ENCRYPT) != 1)
return 0;
Expand All @@ -144,8 +144,8 @@ go_openssl_EVP_CIPHER_CTX_open_wrapper(const GO_EVP_CIPHER_CTX_PTR ctx,
const unsigned char *aad, int aad_len,
const unsigned char *tag)
{
if (in_len == 0) in = "";
if (aad_len == 0) aad = "";
if (in_len == 0) in = (const unsigned char *)"";
if (aad_len == 0) aad = (const unsigned char *)"";

if (go_openssl_EVP_CipherInit_ex(ctx, NULL, NULL, NULL, nonce, GO_AES_DECRYPT) != 1)
return 0;
Expand All @@ -168,3 +168,16 @@ go_openssl_EVP_CIPHER_CTX_open_wrapper(const GO_EVP_CIPHER_CTX_PTR ctx,

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);
}
2 changes: 1 addition & 1 deletion hkdf.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build linux && !cmd_go_bootstrap
//go:build !cmd_go_bootstrap

package openssl

Expand Down
2 changes: 0 additions & 2 deletions hkdf_test.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
//go:build linux

package openssl_test

import (
Expand Down
2 changes: 1 addition & 1 deletion hmac.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build linux && !cmd_go_bootstrap
//go:build !cmd_go_bootstrap

package openssl

Expand Down
2 changes: 0 additions & 2 deletions hmac_test.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
//go:build linux

package openssl

import (
Expand Down
21 changes: 6 additions & 15 deletions init.go
Original file line number Diff line number Diff line change
@@ -1,26 +1,23 @@
//go:build linux && !cmd_go_bootstrap
//go:build !cmd_go_bootstrap

package openssl

// #include "goopenssl.h"
// #include <dlfcn.h>
import "C"
import (
"errors"
"unsafe"
)

// opensslInit loads and initialize OpenSSL.
// If successful, it returns the major and minor OpenSSL version
// as reported by the OpenSSL API.
//
// See Init() for details about version.
func opensslInit(version string) (major, minor, patch int, err error) {
// See Init() for details about file.
func opensslInit(file string) (major, minor, patch int, err error) {
// Load the OpenSSL shared library using dlopen.
handle := dlopen(version)
if handle == nil {
errstr := C.GoString(C.dlerror())
return 0, 0, 0, errors.New("openssl: can't load libcrypto.so." + version + ": " + errstr)
handle, err := dlopen(file)
if err != nil {
return 0, 0, 0, err
}

// Retrieve the loaded OpenSSL version and check if it is supported.
Expand Down Expand Up @@ -64,9 +61,3 @@ func opensslInit(version string) (major, minor, patch int, err error) {
}
return major, minor, patch, nil
}

func dlopen(version string) unsafe.Pointer {
cv := C.CString("libcrypto.so." + version)
defer C.free(unsafe.Pointer(cv))
return C.dlopen(cv, C.RTLD_LAZY|C.RTLD_LOCAL)
}
31 changes: 31 additions & 0 deletions init_unix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//go:build unix && !cmd_go_bootstrap

package openssl

// #cgo LDFLAGS: -ldl
// #include <stdlib.h>
// #include <dlfcn.h>
import "C"
import (
"errors"
"unsafe"
)

func dlopen(file string) (handle unsafe.Pointer, err error) {
cv := C.CString(file)
defer C.free(unsafe.Pointer(cv))
handle = C.dlopen(cv, C.RTLD_LAZY|C.RTLD_LOCAL)
if handle == nil {
errstr := C.GoString(C.dlerror())
return nil, errors.New("openssl: can't load " + file + ": " + errstr)
}
return handle, nil
}

func dlclose(handle unsafe.Pointer) error {
if C.dlclose(handle) != 0 {
errstr := C.GoString(C.dlerror())
return errors.New("openssl: can't close libcrypto: " + errstr)
}
return nil
}
36 changes: 36 additions & 0 deletions init_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//go:build !cmd_go_bootstrap

package openssl

import (
"syscall"
"unsafe"
)

type dlopenError struct {
file string
err error
}

func (e *dlopenError) Error() string {
return "openssl: can't load " + e.file + ": " + e.err.Error()
}

func (e *dlopenError) Unwrap() error {
return e.err
}

func dlopen(file string) (handle unsafe.Pointer, err error) {
// As Windows generally does not ship with a system OpenSSL library, let
// alone a FIPS 140 certified one, use the default library search order so
// that we preferentially load the DLL bundled with the application.
h, err := syscall.LoadLibrary(file)
if err != nil {
return nil, &dlopenError{file: file, err: err}
}
return unsafe.Pointer(h), nil
}

func dlclose(handle unsafe.Pointer) error {
return syscall.FreeLibrary(syscall.Handle(handle))
}
Loading