diff --git a/key.go b/key.go index 91ea98a7..26d0787e 100644 --- a/key.go +++ b/key.go @@ -19,6 +19,7 @@ import "C" import ( "errors" + "fmt" "io/ioutil" "runtime" "unsafe" @@ -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) @@ -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) @@ -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) { @@ -502,3 +575,4 @@ func GenerateED25519Key() (PrivateKey, error) { }) return p, nil } + diff --git a/shim.c b/shim.c index 6e680841..7ea3feb4 100644 --- a/shim.c +++ b/shim.c @@ -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); +} + diff --git a/shim.h b/shim.h index b792822b..d3b621a0 100644 --- a/shim.h +++ b/shim.h @@ -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);