From f0752e5249d3b60abe526cb117d404632b0b3556 Mon Sep 17 00:00:00 2001 From: Rob Dancer Date: Mon, 29 Apr 2019 16:12:54 +0100 Subject: [PATCH 1/3] Add signer for pre-hashed data --- key.go | 49 ++++++++++++++++++++++++++++++++++++++++++++++++- shim.c | 5 +++++ shim.h | 1 + 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/key.go b/key.go index 91ea98a7..3a9f8703 100644 --- a/key.go +++ b/key.go @@ -94,6 +94,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) @@ -165,11 +168,55 @@ func (key *pKey) SignPKCS1v15(method Method, data []byte) ([]byte, error) { } } +// SignPKCS1v15Hash signs pre-hashed data +func (key *KeyExt) 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) - if key.KeyType() == KeyTypeED25519 { + If key.KeyType() == KeyTypeED25519 { // do ED specific one-shot sign if method != nil || len(data) == 0 || len(sig) == 0 { diff --git a/shim.c b/shim.c index 6e680841..a0a6071d 100644 --- a/shim.c +++ b/shim.c @@ -768,3 +768,8 @@ 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); +} + diff --git a/shim.h b/shim.h index b792822b..7e72e939 100644 --- a/shim.h +++ b/shim.h @@ -150,6 +150,7 @@ 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); /* HMAC methods */ extern size_t X_HMAC_size(const HMAC_CTX *e); From 9888f37a85e8be55e810ff21fdd291a986d13fd3 Mon Sep 17 00:00:00 2001 From: Rob Dancer Date: Mon, 29 Apr 2019 17:42:39 +0100 Subject: [PATCH 2/3] Tested signing method using pre-hashed data --- key.go | 28 ++++++++++++++++++++++++++-- shim.c | 4 ++++ shim.h | 1 + 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/key.go b/key.go index 3a9f8703..a0669551 100644 --- a/key.go +++ b/key.go @@ -19,6 +19,7 @@ import "C" import ( "errors" + "fmt" "io/ioutil" "runtime" "unsafe" @@ -169,7 +170,7 @@ func (key *pKey) SignPKCS1v15(method Method, data []byte) ([]byte, error) { } // SignPKCS1v15Hash signs pre-hashed data -func (key *KeyExt) SignPKCS1v15Hash(method Method, digest []byte) ([]byte, error) { +func (key *pKey) SignPKCS1v15Hash(method Method, digest []byte) ([]byte, error) { var ctx *C.EVP_PKEY_CTX var ecode C.int @@ -216,7 +217,7 @@ 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) - If key.KeyType() == KeyTypeED25519 { + if key.KeyType() == KeyTypeED25519 { // do ED specific one-shot sign if method != nil || len(data) == 0 || len(sig) == 0 { @@ -343,6 +344,28 @@ 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, pemFile string) (PrivateKey, error) { + + if len(pemFile) == 0 { + return nil, errors.New("LoadPrivateKeyFromPEMForEngine: PEM file name not supplied") + } + + var key *C.EVP_PKEY + + fname := C.CString(pemFile) + defer C.free(unsafe.Pointer(fname)) + + // The PEM file will bw loaded by the engine library + key = C.X_ENGINE_load_private_key(engine.e, fname, 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) { @@ -549,3 +572,4 @@ func GenerateED25519Key() (PrivateKey, error) { }) return p, nil } + diff --git a/shim.c b/shim.c index a0a6071d..7ea3feb4 100644 --- a/shim.c +++ b/shim.c @@ -773,3 +773,7 @@ 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); +} + diff --git a/shim.h b/shim.h index 7e72e939..d3b621a0 100644 --- a/shim.h +++ b/shim.h @@ -151,6 +151,7 @@ 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); From 532a9521c1602937fc0e6a00b4c513b8023a9369 Mon Sep 17 00:00:00 2001 From: Rob Dancer Date: Mon, 29 Apr 2019 17:54:01 +0100 Subject: [PATCH 3/3] Changed names of variables in LoadPrivateKeyFromPEMForEngine --- key.go | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/key.go b/key.go index a0669551..26d0787e 100644 --- a/key.go +++ b/key.go @@ -345,19 +345,22 @@ func LoadPrivateKeyFromPEM(pem_block []byte) (PrivateKey, error) { } // LoadPrivateKeyFromPEMForEngine loads the PEM formatted data that will be sent to the OpenSSL Engine -func LoadPrivateKeyFromPEMForEngine(engine *Engine, pemFile string) (PrivateKey, error) { +func LoadPrivateKeyFromPEMForEngine(engine *Engine, id string) (PrivateKey, error) { - if len(pemFile) == 0 { - return nil, errors.New("LoadPrivateKeyFromPEMForEngine: PEM file name not supplied") + // 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 - fname := C.CString(pemFile) - defer C.free(unsafe.Pointer(fname)) + 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, fname, nil, nil) + key = C.X_ENGINE_load_private_key(engine.e, pkid, nil, nil) p := &pKey{key: key} runtime.SetFinalizer(p, func(p *pKey) {