ENGINE_* functions
Pavel Shramov <[email protected]> Fri, 14 Mar 2008 14:19:46 +0300
| Newsgroups | gmane.comp.python.cryptography |
|---|---|
| Message-ID | <[email protected]> |
M2Crypto still lacks ENGINE_* functions and bug [1] seem to be
abandoned so I've done another patch. It seem to be superset of one
mentioned in [1] but without Context and init functions modifications.
Pavel
--
[1] https://bugzilla.osafoundation.org/show_bug.cgi?id=7585
engine.patch
(text/x-diff, 8.4 KB)
diff --git a/M2Crypto/Engine.py b/M2Crypto/Engine.py
new file mode 100644
index 0000000..457e8ff
--- /dev/null
+++ b/M2Crypto/Engine.py
@@ -0,0 +1,74 @@
+#!/usr/bin/env python
+# vim: sts=4 sw=4 et
+"""
+M2Crypto wrapper for OpenSSL ENGINE API.
+
+Pavel Shramov
+IMEC MSU
+"""
+
+from M2Crypto import m2, EVP, X509
+
+class Engine:
+ """ Wrapper for ENGINE object. """
+ def __init__(self, id = None, _ptr = None, _pyfree = 1):
+ """ Create new Engine from ENGINE pointer or obtain by id """
+ self._ptr = _ptr
+ self._pyfree = _pyfree
+ if not self._ptr and not id:
+ raise ValueError("No engine id specified")
+ if not self._ptr:
+ self._ptr = m2.engine_by_id(id)
+ if not self._ptr:
+ raise ValueError("Unknown engine: %s" % id)
+
+ def __del__(self):
+ if self._ptr and self._pyfree:
+ m2.engine_free(self._ptr)
+
+ def ctrl_cmd_string(self, cmd, arg, optional = 0):
+ return m2.engine_ctrl_cmd_string(self._ptr, cmd, arg, optional)
+
+ def get_name(self):
+ return m2.engine_get_name(self._ptr)
+
+ def get_id(self):
+ return m2.engine_get_id(self._ptr)
+
+ def set_default(self, methods = m2.ENGINE_METHOD_ALL):
+ return m2.engine_set_default(self._ptr, methods)
+
+ def __engine_load_key(self, func, name, pin = None):
+ ui = m2.ui_openssl()
+ cbd = m2.engine_pkcs11_data_new(pin)
+ kptr = func(self._ptr, name, ui, cbd)
+ if not kptr:
+ return None
+ key = EVP.PKey(kptr, _pyfree = 1)
+ m2.engine_pkcs11_data_free(cbd)
+ return key
+
+ def load_private_key(self, name, pin = None):
+ return self.__engine_load_key(m2.engine_load_private_key, name, pin)
+
+ def load_public_key(self, name, pin = None):
+ return self.__engine_load_key(m2.engine_load_public_key, name, pin)
+
+ def load_certificate(self, name):
+ """ This function may be not implemented by engine!
+ """
+ cptr = m2.engine_load_certificate(self._ptr, name)
+ if not cptr:
+ return None
+ return X509.X509(cptr, _pyfree = 1)
+
+def load_dynamic_engine(id, sopath):
+ m2.engine_load_dynamic()
+ e = Engine('dynamic')
+ e.ctrl_cmd_string("SO_PATH", sopath)
+ e.ctrl_cmd_string("ID", id)
+ e.ctrl_cmd_string("LIST_ADD", "1")
+ e.ctrl_cmd_string("LOAD", None)
+ return e
+
+
diff --git a/SWIG/_engine.i b/SWIG/_engine.i
new file mode 100644
index 0000000..3c05885
--- /dev/null
+++ b/SWIG/_engine.i
@@ -0,0 +1,174 @@
+/*
+ * -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: syntax=c sts=4 sw=4
+ *
+ * ENGINE functions from engine(3SSL).
+ *
+ * Pavel Shramov
+ * IMEC MSU
+ */
+%{
+#include <openssl/engine.h>
+#include <openssl/ui.h>
+#include <stdio.h>
+%}
+
+%apply Pointer NONNULL { ENGINE * };
+%apply Pointer NONNULL { const ENGINE * };
+%apply Pointer NONNULL { const char * };
+
+/*
+ * Functions to load different engines
+ */
+%rename(engine_load_builtin_engines) ENGINE_load_builtin_engines;
+extern void ENGINE_load_builtin_engines(void);
+
+%rename(engine_load_dynamic) ENGINE_load_dynamic;
+extern void ENGINE_load_dynamic(void);
+
+%rename(engine_load_openssl) ENGINE_load_openssl;
+extern void ENGINE_load_openssl(void);
+
+/*
+ * Engine allocation functions
+ */
+%rename(engine_new) ENGINE_new;
+extern ENGINE * ENGINE_new();
+
+%rename(engine_by_id) ENGINE_by_id;
+extern ENGINE * ENGINE_by_id(const char *);
+
+%rename(engine_free) ENGINE_free;
+extern int ENGINE_free(ENGINE *);
+
+/*
+ * Engine id/name functions
+ */
+%rename(engine_get_id) ENGINE_get_id;
+extern const char * ENGINE_get_id(const ENGINE *);
+
+%rename(engine_get_name) ENGINE_get_name;
+extern const char * ENGINE_get_name(const ENGINE *);
+
+/*
+ * Engine control functions
+ */
+%clear const char *;
+%rename(engine_ctrl_cmd_string) ENGINE_ctrl_cmd_string;
+extern int ENGINE_ctrl_cmd_string(ENGINE *e, const char *NONNULL,
+ const char *arg, int cmd_optional);
+
+%apply Pointer NONNULL { const char * };
+
+/*
+ * UI methods.
+ * XXX: UI_OpenSSL method is static and UI_destroy_method is not needed.
+ */
+%rename(ui_openssl) UI_OpenSSL;
+extern UI_METHOD * UI_OpenSSL();
+
+%rename(ui_destroy_method) UI_destroy_method;
+extern void UI_destroy_method(UI_METHOD *ui_method);
+
+%clear const char *;
+%inline %{
+
+/*
+ * Code from engine-pkcs11 1.4.0 in engine-pkcs11.c
+ *
+
+99 static char *get_pin(UI_METHOD * ui_method, void *callback_data, char *sc_pin,
+100 int maxlen)
+101 {
+102 UI *ui;
+103 struct {
+104 const void *password;
+105 const char *prompt_info;
+106 } *mycb = callback_data;
+107
+108 if (mycb->password) {
+109 sc_pin = set_pin(mycb->password);
+110 return sc_pin;
+111 }
+
+ *
+ * So callback_data need to be always provided and have fixed type.
+ * UI method still may be NULL.
+ *
+ * Following functions allocate and free callback data structure with
+ * optional password set.
+ */
+
+typedef struct {
+ char * password;
+ char * prompt;
+} _cbd_t;
+
+void * engine_pkcs11_data_new(const char *pin) {
+ _cbd_t * cb = (_cbd_t *) malloc(sizeof(_cbd_t));
+ cb->password = 0;
+ if (pin)
+ cb->password = strdup(pin);
+ cb->prompt = 0;
+ return cb;
+}
+
+void engine_pkcs11_data_free(void * vcb) {
+ _cbd_t * cb = (_cbd_t *) vcb;
+ if (!cb)
+ return;
+ if (cb->password)
+ free(cb->password);
+ free(cb);
+}
+
+%}
+%apply Pointer NONNULL { const char * };
+
+/*
+ * Engine key/cert load functions.
+ * See above notice about callback_data.
+ */
+%rename(engine_load_private_key) ENGINE_load_private_key;
+extern EVP_PKEY *ENGINE_load_private_key(ENGINE *e, const char *key_id,
+ UI_METHOD *ui_method, void *callback_data);
+%rename(engine_load_public_key) ENGINE_load_public_key;
+extern EVP_PKEY *ENGINE_load_public_key(ENGINE *e, const char *key_id,
+ UI_METHOD *ui_method, void *callback_data);
+
+/*
+ * This function may be not implemented in engine.
+ * pkcs11 engine has this control.
+ */
+%inline %{
+X509 * engine_load_certificate(ENGINE *e, const char * slot) {
+ struct {
+ const char * slot;
+ X509 * cert;
+ } cbd;
+ cbd.slot = slot;
+ cbd.cert = NULL;
+ if (!ENGINE_ctrl_cmd(e, "LOAD_CERT_CTRL", 0, &cbd, NULL, 0))
+ return NULL;
+ return cbd.cert;
+}
+%}
+
+/* These flags are used to control combinations of algorithm (methods)
+ * by bitwise "OR"ing. */
+#define ENGINE_METHOD_RSA 0x0001
+#define ENGINE_METHOD_DSA 0x0002
+#define ENGINE_METHOD_DH 0x0004
+#define ENGINE_METHOD_RAND 0x0008
+#define ENGINE_METHOD_ECDH 0x0010
+#define ENGINE_METHOD_ECDSA 0x0020
+#define ENGINE_METHOD_CIPHERS 0x0040
+#define ENGINE_METHOD_DIGESTS 0x0080
+#define ENGINE_METHOD_STORE 0x0100
+/* Obvious all-or-nothing cases. */
+#define ENGINE_METHOD_ALL 0xFFFF
+#define ENGINE_METHOD_NONE 0x0000
+
+%rename(engine_set_default) ENGINE_set_default;
+extern int ENGINE_set_default(ENGINE *e, unsigned int flags);
+
diff --git a/SWIG/_m2crypto.i b/SWIG/_m2crypto.i
index 41e633c..3c15ac9 100644
--- a/SWIG/_m2crypto.i
+++ b/SWIG/_m2crypto.i
@@ -51,6 +51,7 @@ static PyObject *ssl_set_tmp_rsa_cb_func;
%include _pkcs7.i
%include _util.i
%include _ec.i
+%include _engine.i
#ifdef SWIG_VERSION
%constant int encrypt = 1;
diff --git a/tests/test_engine.py b/tests/test_engine.py
new file mode 100644
index 0000000..954d807
--- /dev/null
+++ b/tests/test_engine.py
@@ -0,0 +1,36 @@
+#!/usr/bin/env python
+
+""" Unit tests for M2Crypto.Engine. """
+
+import unittest
+from M2Crypto import Engine, m2
+
+class EngineTestCase(unittest.TestCase):
+
+ privkey = 'tests/rsa.priv.pem'
+ bad_id = '1bea1edfeb97'
+
+ def test_by_id_junk(self):
+ self.assertRaises(ValueError, Engine.Engine, self.bad_id)
+
+ def test_by_id_openssl(self):
+ m2.engine_load_openssl()
+ Engine.Engine('openssl')
+
+ def test_by_id_dynamic(self):
+ m2.engine_load_dynamic()
+ Engine.Engine('dynamic')
+
+ def test_load_private(self):
+ e = Engine.Engine('openssl')
+ e.set_default()
+ key = e.load_private_key(self.privkey)
+ assert key is not None
+
+def suite():
+ return unittest.makeSuite(EngineTestCase)
+
+
+if __name__ == '__main__':
+ unittest.TextTestRunner().run(suite())
+