Skip to content
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

Add signer method for pre-hashed data to allow tls/crypto to use openssl engines #120

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
74 changes: 74 additions & 0 deletions key.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import "C"

import (
"errors"
"fmt"
"io/ioutil"
"runtime"
"unsafe"
Expand Down Expand Up @@ -94,6 +95,9 @@ type PrivateKey interface {
// Signs the data using PKCS1.15
SignPKCS1v15(Method, []byte) ([]byte, error)

// Signs pre-hashed data such as that which comes out of tls/crypto Signer, so OpenSSL can be used to sign instead (eg using an OpenSSL TPM engine)
SignPKCS1v15Hash(Method, []byte) ([]byte, error)

// MarshalPKCS1PrivateKeyPEM converts the private key to PEM-encoded PKCS1
// format
MarshalPKCS1PrivateKeyPEM() (pem_block []byte, err error)
Expand Down Expand Up @@ -165,6 +169,50 @@ func (key *pKey) SignPKCS1v15(method Method, data []byte) ([]byte, error) {
}
}

// SignPKCS1v15Hash signs pre-hashed data
func (key *pKey) SignPKCS1v15Hash(method Method, digest []byte) ([]byte, error) {

var ctx *C.EVP_PKEY_CTX
var ecode C.int

// Make a new key context
if ctx = C.EVP_PKEY_CTX_new(key.key, nil); ctx == nil {
ret := C.ERR_peek_last_error()
return nil, errors.New(fmt.Sprintf("signpkcs1v15hash: Failed to get new signing data. Error code: %d", ret))
}

// Free the context at the end of this function
defer C.EVP_PKEY_CTX_free(ctx)

// Initialise the context for signing
if ecode = C.EVP_PKEY_sign_init(ctx); ecode <= 0 {
return nil, errors.New(fmt.Sprintf("signpkcs1v15hash: Failed to initialise signing data. Error code: %d", ecode))
}

// Set the hashing method. Even though the data has already been hashed, the signing method needs to know the hash method
if ecode = C.X_EVP_PKEY_CTX_set_signature_md(ctx, method); ecode <= 0 {
return nil, errors.New(fmt.Sprintf("signpkcs1v15hash: Failed to set signature. Error code: %d", ecode))
}

// Initialise the signature
sig := make([]byte, C.X_EVP_PKEY_size(key.key))
var sigblen C.size_t

// Update the context with data to sign
if ecode = C.EVP_PKEY_sign(ctx,
nil, &sigblen, ((*C.uchar)(unsafe.Pointer(&digest[0]))), C.size_t(len(digest))); ecode != 1 {
return nil, errors.New(fmt.Sprintf("signpkcs1v15hash: Failed to update signature. Error code: %d", ecode))
}

// Sign the data
if ecode = C.EVP_PKEY_sign(ctx,
((*C.uchar)(unsafe.Pointer(&sig[0]))), &sigblen, ((*C.uchar)(unsafe.Pointer(&digest[0]))), C.size_t(len(digest))); ecode != 1 {
return nil, errors.New(fmt.Sprintf("signpkcs1v15hash: Failed to finalize signature. Error code: %d", ecode))
}

return sig[:sigblen], nil
}

func (key *pKey) VerifyPKCS1v15(method Method, data, sig []byte) error {
ctx := C.X_EVP_MD_CTX_new()
defer C.X_EVP_MD_CTX_free(ctx)
Expand Down Expand Up @@ -296,6 +344,31 @@ func LoadPrivateKeyFromPEM(pem_block []byte) (PrivateKey, error) {
return p, nil
}

// LoadPrivateKeyFromPEMForEngine loads the PEM formatted data that will be sent to the OpenSSL Engine
func LoadPrivateKeyFromPEMForEngine(engine *Engine, id string) (PrivateKey, error) {

// id is a reference for the openssl engine to use in whatever it needs to to find the private key.
// For example, for a TPM engine, this could be the full path and name of the file that holds PEM data that the
// TPM library can use to reference the private key in the TPM
if len(id) == 0 {
return nil, errors.New("LoadPrivateKeyFromPEMForEngine: Private Key ID not supplied")
}

var key *C.EVP_PKEY

pkid := C.CString(id)
defer C.free(unsafe.Pointer(pkid))

// The PEM file will bw loaded by the engine library
key = C.X_ENGINE_load_private_key(engine.e, pkid, nil, nil)

p := &pKey{key: key}
runtime.SetFinalizer(p, func(p *pKey) {
C.X_EVP_PKEY_free(p.key)
})
return p, nil
}

// LoadPrivateKeyFromPEMWithPassword loads a private key from a PEM-encoded block.
func LoadPrivateKeyFromPEMWithPassword(pem_block []byte, password string) (
PrivateKey, error) {
Expand Down Expand Up @@ -502,3 +575,4 @@ func GenerateED25519Key() (PrivateKey, error) {
})
return p, nil
}

9 changes: 9 additions & 0 deletions shim.c
Original file line number Diff line number Diff line change
Expand Up @@ -768,3 +768,12 @@ long X_X509_get_version(const X509 *x) {
int X_X509_set_version(X509 *x, long version) {
return X509_set_version(x, version);
}

int X_EVP_PKEY_CTX_set_signature_md(EVP_PKEY_CTX *ctx, const EVP_MD *md) {
return EVP_PKEY_CTX_set_signature_md(ctx, md);
}

EVP_PKEY *X_ENGINE_load_private_key(ENGINE *e, const char *key_id, UI_METHOD *ui_method, void *callback_data) {
return ENGINE_load_private_key(e, key_id, ui_method, callback_data);
}

2 changes: 2 additions & 0 deletions shim.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ extern void X_EVP_CIPHER_CTX_set_padding(EVP_CIPHER_CTX *ctx, int padding);
extern const EVP_CIPHER *X_EVP_CIPHER_CTX_cipher(EVP_CIPHER_CTX *ctx);
extern int X_EVP_CIPHER_CTX_encrypting(const EVP_CIPHER_CTX *ctx);
extern int X_EVP_PKEY_CTX_set_ec_paramgen_curve_nid(EVP_PKEY_CTX *ctx, int nid);
extern int X_EVP_PKEY_CTX_set_signature_md(EVP_PKEY_CTX *ctx, const EVP_MD *md);
extern EVP_PKEY *X_ENGINE_load_private_key(ENGINE *e, const char *key_id, UI_METHOD *ui_method, void *callback_data);

/* HMAC methods */
extern size_t X_HMAC_size(const HMAC_CTX *e);
Expand Down