[openssl-commits] [openssl] master update

Richard Levitte levitte at openssl.org
Mon Dec 10 12:19:47 UTC 2018


The branch master has been updated
       via  2bafe6cfeda5dc27fbc4c17cd351063872e0c0fd (commit)
       via  166261a5e9e141cc3850012cbb9106bcd3226393 (commit)
       via  b2db94d1d0bb79a80e188aaf459698ae5cbabe7d (commit)
       via  16e252a01b754a13e83d5e5e87afbe389997926b (commit)
       via  b5015e834aa7d3f0a5d7585a8fae05cecbdbb848 (commit)
       via  6d99e238397859f2df58c60e28905193b2dd6762 (commit)
       via  a67203a19d379a8cc8b369587c60c46eb4e19014 (commit)
       via  ae8183690fa53b978d4647563f5a521c4cafe94c (commit)
       via  4d9f99654441e36fdcb49540a1dbc9d4c70ccb68 (commit)
       via  d9d4dff5c640990d45af115353fc9f88a497a56c (commit)
       via  eb3fe0ee5411449230cff46b7f13ebef37aa950e (commit)
      from  7ffb7fbe8c1c1bc467993389478d2ea6f00e1860 (commit)


- Log -----------------------------------------------------------------
commit 2bafe6cfeda5dc27fbc4c17cd351063872e0c0fd
Author: Eneas U de Queiroz <cote2004-github at yahoo.com>
Date:   Tue Nov 6 22:54:07 2018 -0200

    eng_devcrypto: add command to dump driver info
    
    This is useful to determine the kernel driver running each algorithm.
    
    Signed-off-by: Eneas U de Queiroz <cote2004-github at yahoo.com>
    
    Reviewed-by: Matthias St. Pierre <Matthias.St.Pierre at ncp-e.com>
    Reviewed-by: Richard Levitte <levitte at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/7585)

commit 166261a5e9e141cc3850012cbb9106bcd3226393
Author: Eneas U de Queiroz <cote2004-github at yahoo.com>
Date:   Sat Nov 3 15:41:10 2018 -0300

    eng_devcrypto: add configuration options
    
    USE_SOFTDRIVERS: whether to use software (not accelerated) drivers
    CIPHERS: list of ciphers to enable
    DIGESTS: list of digests to enable
    
    Signed-off-by: Eneas U de Queiroz <cote2004-github at yahoo.com>
    
    Reviewed-by: Matthias St. Pierre <Matthias.St.Pierre at ncp-e.com>
    Reviewed-by: Richard Levitte <levitte at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/7585)

commit b2db94d1d0bb79a80e188aaf459698ae5cbabe7d
Author: Eneas U de Queiroz <cote2004-github at yahoo.com>
Date:   Mon Nov 5 15:54:17 2018 -0200

    eng_devcrypto: save ioctl if EVP_MD_..FLAG_ONESHOT
    
    Since each ioctl causes a context switch, slowing things down, if
    EVP_MD_CTX_FLAG_ONESHOT is set, then:
     - call the ioctl in digest_update, saving the result; and
     - just copy the result in digest_final, instead of using another ioctl.
    
    Signed-off-by: Eneas U de Queiroz <cote2004-github at yahoo.com>
    
    Reviewed-by: Matthias St. Pierre <Matthias.St.Pierre at ncp-e.com>
    Reviewed-by: Richard Levitte <levitte at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/7585)

commit 16e252a01b754a13e83d5e5e87afbe389997926b
Author: Eneas U de Queiroz <cote2004-github at yahoo.com>
Date:   Sat Dec 8 18:01:04 2018 -0200

    eng_devcrypto: make sure digest can do copy
    
    Digest must be able to do partial-state copy to be used.
    
    Signed-off-by: Eneas U de Queiroz <cote2004-github at yahoo.com>
    
    Reviewed-by: Matthias St. Pierre <Matthias.St.Pierre at ncp-e.com>
    Reviewed-by: Richard Levitte <levitte at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/7585)

commit b5015e834aa7d3f0a5d7585a8fae05cecbdbb848
Author: Eneas U de Queiroz <cote2004-github at yahoo.com>
Date:   Wed Nov 28 11:26:27 2018 -0200

    eng_devcrypto: fix ctr mode
    
    Make CTR mode behave like a stream cipher.
    
    Signed-off-by: Eneas U de Queiroz <cote2004-github at yahoo.com>
    
    Reviewed-by: Matthias St. Pierre <Matthias.St.Pierre at ncp-e.com>
    Reviewed-by: Richard Levitte <levitte at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/7585)

commit 6d99e238397859f2df58c60e28905193b2dd6762
Author: Eneas U de Queiroz <cote2004-github at yahoo.com>
Date:   Tue Nov 13 09:23:22 2018 -0200

    eng_devcrypto: add cipher CTX copy function
    
    The engine needs a custom cipher context copy function to open a new
    /dev/crypto session.
    
    Signed-off-by: Eneas U de Queiroz <cote2004-github at yahoo.com>
    
    Reviewed-by: Matthias St. Pierre <Matthias.St.Pierre at ncp-e.com>
    Reviewed-by: Richard Levitte <levitte at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/7585)

commit a67203a19d379a8cc8b369587c60c46eb4e19014
Author: Eneas U de Queiroz <cote2004-github at yahoo.com>
Date:   Mon Nov 5 15:59:44 2018 -0200

    eng_devcrypto: close session on cleanup, not final
    
    Close the session in digest_cleanup instead of digest_final.  A failure
    in closing the session does not mean a previous successful digest final
    has failed as well.
    
    Signed-off-by: Eneas U de Queiroz <cote2004-github at yahoo.com>
    
    Reviewed-by: Matthias St. Pierre <Matthias.St.Pierre at ncp-e.com>
    Reviewed-by: Richard Levitte <levitte at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/7585)

commit ae8183690fa53b978d4647563f5a521c4cafe94c
Author: Eneas U de Queiroz <cote2004-github at yahoo.com>
Date:   Wed Nov 14 13:58:06 2018 -0200

    eng_devcrypto: fix copy of unitilialized digest
    
    If the source ctx has not been initialized, don't initialize the copy
    either.
    
    Signed-off-by: Eneas U de Queiroz <cote2004-github at yahoo.com>
    
    Reviewed-by: Matthias St. Pierre <Matthias.St.Pierre at ncp-e.com>
    Reviewed-by: Richard Levitte <levitte at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/7585)

commit 4d9f99654441e36fdcb49540a1dbc9d4c70ccb68
Author: Eneas U de Queiroz <cote2004-github at yahoo.com>
Date:   Wed Nov 14 11:22:14 2018 -0200

    eng_devcrypto: expand digest failure cases
    
    Return failure when the digest_ctx is null in digest_update and
    digest_final, and when md is null in digest_final.
    
    Signed-off-by: Eneas U de Queiroz <cote2004-github at yahoo.com>
    
    Reviewed-by: Matthias St. Pierre <Matthias.St.Pierre at ncp-e.com>
    Reviewed-by: Richard Levitte <levitte at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/7585)

commit d9d4dff5c640990d45af115353fc9f88a497a56c
Author: Eneas U de Queiroz <cote2004-github at yahoo.com>
Date:   Mon Nov 5 17:59:42 2018 -0200

    eng_devcrypto: don't leak methods tables
    
    Call functions to prepare methods after confirming that /dev/crytpo was
    sucessfully open and that the destroy function has been set.
    
    Signed-off-by: Eneas U de Queiroz <cote2004-github at yahoo.com>
    
    Reviewed-by: Matthias St. Pierre <Matthias.St.Pierre at ncp-e.com>
    Reviewed-by: Richard Levitte <levitte at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/7585)

commit eb3fe0ee5411449230cff46b7f13ebef37aa950e
Author: Eneas U de Queiroz <cote2004-github at yahoo.com>
Date:   Thu Nov 8 11:16:20 2018 -0200

    INSTALL: add note about devcrypto engine
    
    Signed-off-by: Eneas U de Queiroz <cote2004-github at yahoo.com>
    
    Reviewed-by: Matthias St. Pierre <Matthias.St.Pierre at ncp-e.com>
    Reviewed-by: Richard Levitte <levitte at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/7585)

-----------------------------------------------------------------------

Summary of changes:
 INSTALL                       |   5 +
 crypto/engine/eng_devcrypto.c | 677 +++++++++++++++++++++++++++++++++++++-----
 2 files changed, 615 insertions(+), 67 deletions(-)

diff --git a/INSTALL b/INSTALL
index 5cca299..95fc71a 100644
--- a/INSTALL
+++ b/INSTALL
@@ -335,6 +335,11 @@
                    Don't build support for datagram based BIOs. Selecting this
                    option will also force the disabling of DTLS.
 
+  enable-devcryptoeng
+                   Build the /dev/crypto engine.  It is automatically selected
+                   on BSD implementations, in which case it can be disabled with
+                   no-devcryptoeng.
+
   no-dso
                    Don't build support for loading Dynamic Shared Objects.
 
diff --git a/crypto/engine/eng_devcrypto.c b/crypto/engine/eng_devcrypto.c
index 6bba6e2..bfdb8fc 100644
--- a/crypto/engine/eng_devcrypto.c
+++ b/crypto/engine/eng_devcrypto.c
@@ -16,6 +16,7 @@
 #include <unistd.h>
 #include <assert.h>
 
+#include <openssl/conf.h>
 #include <openssl/evp.h>
 #include <openssl/err.h>
 #include <openssl/engine.h>
@@ -34,6 +35,34 @@
  * saner...  why re-open /dev/crypto for every session?
  */
 static int cfd;
+#define DEVCRYPTO_REQUIRE_ACCELERATED 0 /* require confirmation of acceleration */
+#define DEVCRYPTO_USE_SOFTWARE        1 /* allow software drivers */
+#define DEVCRYPTO_REJECT_SOFTWARE     2 /* only disallow confirmed software drivers */
+
+#define DEVCRYPTO_DEFAULT_USE_SOFDTRIVERS DEVCRYPTO_REJECT_SOFTWARE
+static int use_softdrivers = DEVCRYPTO_DEFAULT_USE_SOFDTRIVERS;
+
+/*
+ * cipher/digest status & acceleration definitions
+ * Make sure the defaults are set to 0
+ */
+struct driver_info_st {
+    enum devcrypto_status_t {
+        DEVCRYPTO_STATUS_FAILURE         = -3, /* unusable for other reason */
+        DEVCRYPTO_STATUS_NO_CIOCCPHASH   = -2, /* hash state copy not supported */
+        DEVCRYPTO_STATUS_NO_CIOCGSESSION = -1, /* session open failed */
+        DEVCRYPTO_STATUS_UNKNOWN         =  0, /* not tested yet */
+        DEVCRYPTO_STATUS_USABLE          =  1  /* algo can be used */
+    } status;
+
+    enum devcrypto_accelerated_t {
+        DEVCRYPTO_NOT_ACCELERATED        = -1, /* software implemented */
+        DEVCRYPTO_ACCELERATION_UNKNOWN   =  0, /* acceleration support unkown */
+        DEVCRYPTO_ACCELERATED            =  1  /* hardware accelerated */
+    } accelerated;
+
+    char *driver_name;
+};
 
 /******************************************************************************
  *
@@ -47,10 +76,12 @@ static int cfd;
 
 struct cipher_ctx {
     struct session_op sess;
-
-    /* to pass from init to do_cipher */
-    const unsigned char *iv;
     int op;                      /* COP_ENCRYPT or COP_DECRYPT */
+    unsigned long mode;          /* EVP_CIPH_*_MODE */
+
+    /* to handle ctr mode being a stream cipher */
+    unsigned char partial[EVP_MAX_BLOCK_LENGTH];
+    unsigned int blocksize, num;
 };
 
 static const struct cipher_data_st {
@@ -87,9 +118,9 @@ static const struct cipher_data_st {
     { NID_aes_256_xts, 16, 256 / 8 * 2, 16, EVP_CIPH_XTS_MODE, CRYPTO_AES_XTS },
 #endif
 #if !defined(CHECK_BSD_STYLE_MACROS) || defined(CRYPTO_AES_ECB)
-    { NID_aes_128_ecb, 16, 128 / 8, 16, EVP_CIPH_ECB_MODE, CRYPTO_AES_ECB },
-    { NID_aes_192_ecb, 16, 192 / 8, 16, EVP_CIPH_ECB_MODE, CRYPTO_AES_ECB },
-    { NID_aes_256_ecb, 16, 256 / 8, 16, EVP_CIPH_ECB_MODE, CRYPTO_AES_ECB },
+    { NID_aes_128_ecb, 16, 128 / 8, 0, EVP_CIPH_ECB_MODE, CRYPTO_AES_ECB },
+    { NID_aes_192_ecb, 16, 192 / 8, 0, EVP_CIPH_ECB_MODE, CRYPTO_AES_ECB },
+    { NID_aes_256_ecb, 16, 256 / 8, 0, EVP_CIPH_ECB_MODE, CRYPTO_AES_ECB },
 #endif
 #if 0                            /* Not yet supported */
     { NID_aes_128_gcm, 16, 128 / 8, 16, EVP_CIPH_GCM_MODE, CRYPTO_AES_GCM },
@@ -106,13 +137,22 @@ static const struct cipher_data_st {
 #endif
 };
 
-static size_t get_cipher_data_index(int nid)
+static size_t find_cipher_data_index(int nid)
 {
     size_t i;
 
     for (i = 0; i < OSSL_NELEM(cipher_data); i++)
         if (nid == cipher_data[i].nid)
             return i;
+    return (size_t)-1;
+}
+
+static size_t get_cipher_data_index(int nid)
+{
+    size_t i = find_cipher_data_index(nid);
+
+    if (i != (size_t)-1)
+        return i;
 
     /*
      * Code further down must make sure that only NIDs in the table above
@@ -146,6 +186,8 @@ static int cipher_init(EVP_CIPHER_CTX *ctx, const unsigned char *key,
     cipher_ctx->sess.keylen = cipher_d->keylen;
     cipher_ctx->sess.key = (void *)key;
     cipher_ctx->op = enc ? COP_ENCRYPT : COP_DECRYPT;
+    cipher_ctx->mode = cipher_d->flags & EVP_CIPH_MODE;
+    cipher_ctx->blocksize = cipher_d->blocksize;
     if (ioctl(cfd, CIOCGSESSION, &cipher_ctx->sess) < 0) {
         SYSerr(SYS_F_IOCTL, errno);
         return 0;
@@ -160,8 +202,11 @@ static int cipher_do_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
     struct cipher_ctx *cipher_ctx =
         (struct cipher_ctx *)EVP_CIPHER_CTX_get_cipher_data(ctx);
     struct crypt_op cryp;
+    unsigned char *iv = EVP_CIPHER_CTX_iv_noconst(ctx);
 #if !defined(COP_FLAG_WRITE_IV)
     unsigned char saved_iv[EVP_MAX_IV_LENGTH];
+    const unsigned char *ivptr;
+    size_t nblocks, ivlen;
 #endif
 
     memset(&cryp, 0, sizeof(cryp));
@@ -169,19 +214,28 @@ static int cipher_do_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
     cryp.len = inl;
     cryp.src = (void *)in;
     cryp.dst = (void *)out;
-    cryp.iv = (void *)EVP_CIPHER_CTX_iv_noconst(ctx);
+    cryp.iv = (void *)iv;
     cryp.op = cipher_ctx->op;
 #if !defined(COP_FLAG_WRITE_IV)
     cryp.flags = 0;
 
-    if (EVP_CIPHER_CTX_iv_length(ctx) > 0) {
-        assert(inl >= EVP_CIPHER_CTX_iv_length(ctx));
-        if (!EVP_CIPHER_CTX_encrypting(ctx)) {
-            unsigned char *ivptr = in + inl - EVP_CIPHER_CTX_iv_length(ctx);
-
-            memcpy(saved_iv, ivptr, EVP_CIPHER_CTX_iv_length(ctx));
+    ivlen = EVP_CIPHER_CTX_iv_length(ctx);
+    if (ivlen > 0)
+        switch (cipher_ctx->mode) {
+        case EVP_CIPH_CBC_MODE:
+            assert(inl >= ivlen);
+            if (!EVP_CIPHER_CTX_encrypting(ctx)) {
+                ivptr = in + inl - ivlen;
+                memcpy(saved_iv, ivptr, ivlen);
+            }
+            break;
+
+        case EVP_CIPH_CTR_MODE:
+            break;
+
+        default: /* should not happen */
+            return 0;
         }
-    }
 #else
     cryp.flags = COP_FLAG_WRITE_IV;
 #endif
@@ -192,21 +246,94 @@ static int cipher_do_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
     }
 
 #if !defined(COP_FLAG_WRITE_IV)
-    if (EVP_CIPHER_CTX_iv_length(ctx) > 0) {
-        unsigned char *ivptr = saved_iv;
+    if (ivlen > 0)
+        switch (cipher_ctx->mode) {
+        case EVP_CIPH_CBC_MODE:
+            assert(inl >= ivlen);
+            if (EVP_CIPHER_CTX_encrypting(ctx))
+                ivptr = out + inl - ivlen;
+            else
+                ivptr = saved_iv;
+
+            memcpy(iv, ivptr, ivlen);
+            break;
+
+        case EVP_CIPH_CTR_MODE:
+            nblocks = (inl + cipher_ctx->blocksize - 1)
+                      / cipher_ctx->blocksize;
+            do {
+                ivlen--;
+                nblocks += iv[ivlen];
+                iv[ivlen] = (uint8_t) nblocks;
+                nblocks >>= 8;
+            } while (ivlen);
+            break;
+
+        default: /* should not happen */
+            return 0;
+        }
+#endif
+
+    return 1;
+}
 
-        assert(inl >= EVP_CIPHER_CTX_iv_length(ctx));
-        if (!EVP_CIPHER_CTX_encrypting(ctx))
-            ivptr = out + inl - EVP_CIPHER_CTX_iv_length(ctx);
+static int ctr_do_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                         const unsigned char *in, size_t inl)
+{
+    struct cipher_ctx *cipher_ctx =
+        (struct cipher_ctx *)EVP_CIPHER_CTX_get_cipher_data(ctx);
+    size_t nblocks, len;
 
-        memcpy(EVP_CIPHER_CTX_iv_noconst(ctx), ivptr,
-               EVP_CIPHER_CTX_iv_length(ctx));
+    /* initial partial block */
+    while (cipher_ctx->num && inl) {
+        (*out++) = *(in++) ^ cipher_ctx->partial[cipher_ctx->num];
+        --inl;
+        cipher_ctx->num = (cipher_ctx->num + 1) % cipher_ctx->blocksize;
+    }
+
+    /* full blocks */
+    if (inl > (unsigned int) cipher_ctx->blocksize) {
+        nblocks = inl/cipher_ctx->blocksize;
+        len = nblocks * cipher_ctx->blocksize;
+        if (cipher_do_cipher(ctx, out, in, len) < 1)
+            return 0;
+        inl -= len;
+        out += len;
+        in += len;
+    }
+
+    /* final partial block */
+    if (inl) {
+        memset(cipher_ctx->partial, 0, cipher_ctx->blocksize);
+        if (cipher_do_cipher(ctx, cipher_ctx->partial, cipher_ctx->partial,
+            cipher_ctx->blocksize) < 1)
+            return 0;
+        while (inl--) {
+            out[cipher_ctx->num] = in[cipher_ctx->num]
+                                   ^ cipher_ctx->partial[cipher_ctx->num];
+            cipher_ctx->num++;
+        }
     }
-#endif
 
     return 1;
 }
 
+static int cipher_ctrl(EVP_CIPHER_CTX *ctx, int type, int p1, void* p2)
+{
+    EVP_CIPHER_CTX *to_ctx = (EVP_CIPHER_CTX *)p2;
+    struct cipher_ctx *cipher_ctx;
+
+    if (type == EVP_CTRL_COPY) {
+        /* when copying the context, a new session needs to be initialized */
+        cipher_ctx = (struct cipher_ctx *)EVP_CIPHER_CTX_get_cipher_data(ctx);
+        return (cipher_ctx == NULL)
+            || cipher_init(to_ctx, cipher_ctx->sess.key, EVP_CIPHER_CTX_iv(ctx),
+                           (cipher_ctx->op == COP_ENCRYPT));
+    }
+
+    return -1;
+}
+
 static int cipher_cleanup(EVP_CIPHER_CTX *ctx)
 {
     struct cipher_ctx *cipher_ctx =
@@ -221,18 +348,40 @@ static int cipher_cleanup(EVP_CIPHER_CTX *ctx)
 }
 
 /*
- * Keep a table of known nids and associated methods.
+ * Keep tables of known nids, associated methods, selected ciphers, and driver
+ * info.
  * Note that known_cipher_nids[] isn't necessarily indexed the same way as
- * cipher_data[] above, which known_cipher_methods[] is.
+ * cipher_data[] above, which the other tables are.
  */
 static int known_cipher_nids[OSSL_NELEM(cipher_data)];
 static int known_cipher_nids_amount = -1; /* -1 indicates not yet initialised */
 static EVP_CIPHER *known_cipher_methods[OSSL_NELEM(cipher_data)] = { NULL, };
+static int selected_ciphers[OSSL_NELEM(cipher_data)];
+static struct driver_info_st cipher_driver_info[OSSL_NELEM(cipher_data)];
+
+
+static int devcrypto_test_cipher(size_t cipher_data_index)
+{
+    return (cipher_driver_info[cipher_data_index].status == DEVCRYPTO_STATUS_USABLE
+            && selected_ciphers[cipher_data_index] == 1
+            && (cipher_driver_info[cipher_data_index].accelerated
+                    == DEVCRYPTO_ACCELERATED
+                || use_softdrivers == DEVCRYPTO_USE_SOFTWARE
+                || (cipher_driver_info[cipher_data_index].accelerated
+                        != DEVCRYPTO_NOT_ACCELERATED
+                    && use_softdrivers == DEVCRYPTO_REJECT_SOFTWARE)));
+}
 
 static void prepare_cipher_methods(void)
 {
     size_t i;
     struct session_op sess;
+    unsigned long cipher_mode;
+#ifdef CIOCGSESSINFO
+    struct session_info_op siop;
+#endif
+
+    memset(&cipher_driver_info, 0, sizeof(cipher_driver_info));
 
     memset(&sess, 0, sizeof(sess));
     sess.key = (void *)"01234567890123456789012345678901234567890123456789";
@@ -240,41 +389,80 @@ static void prepare_cipher_methods(void)
     for (i = 0, known_cipher_nids_amount = 0;
          i < OSSL_NELEM(cipher_data); i++) {
 
+        selected_ciphers[i] = 1;
         /*
-         * Check that the algo is really availably by trying to open and close
-         * a session.
+         * Check that the cipher is usable
          */
         sess.cipher = cipher_data[i].devcryptoid;
         sess.keylen = cipher_data[i].keylen;
-        if (ioctl(cfd, CIOCGSESSION, &sess) < 0
-            || ioctl(cfd, CIOCFSESSION, &sess.ses) < 0)
+        if (ioctl(cfd, CIOCGSESSION, &sess) < 0) {
+            cipher_driver_info[i].status = DEVCRYPTO_STATUS_NO_CIOCGSESSION;
             continue;
+        }
+
+        cipher_mode = cipher_data[i].flags & EVP_CIPH_MODE;
 
         if ((known_cipher_methods[i] =
                  EVP_CIPHER_meth_new(cipher_data[i].nid,
-                                     cipher_data[i].blocksize,
+                                     cipher_mode == EVP_CIPH_CTR_MODE ? 1 :
+                                                    cipher_data[i].blocksize,
                                      cipher_data[i].keylen)) == NULL
             || !EVP_CIPHER_meth_set_iv_length(known_cipher_methods[i],
                                               cipher_data[i].ivlen)
             || !EVP_CIPHER_meth_set_flags(known_cipher_methods[i],
                                           cipher_data[i].flags
+                                          | EVP_CIPH_CUSTOM_COPY
                                           | EVP_CIPH_FLAG_DEFAULT_ASN1)
             || !EVP_CIPHER_meth_set_init(known_cipher_methods[i], cipher_init)
             || !EVP_CIPHER_meth_set_do_cipher(known_cipher_methods[i],
+                                     cipher_mode == EVP_CIPH_CTR_MODE ?
+                                              ctr_do_cipher :
                                               cipher_do_cipher)
+            || !EVP_CIPHER_meth_set_ctrl(known_cipher_methods[i], cipher_ctrl)
             || !EVP_CIPHER_meth_set_cleanup(known_cipher_methods[i],
                                             cipher_cleanup)
             || !EVP_CIPHER_meth_set_impl_ctx_size(known_cipher_methods[i],
                                                   sizeof(struct cipher_ctx))) {
+            cipher_driver_info[i].status = DEVCRYPTO_STATUS_FAILURE;
             EVP_CIPHER_meth_free(known_cipher_methods[i]);
             known_cipher_methods[i] = NULL;
         } else {
+            cipher_driver_info[i].status = DEVCRYPTO_STATUS_USABLE;
+#ifdef CIOCGSESSINFO
+            siop.ses = sess.ses;
+            if (ioctl(cfd, CIOCGSESSINFO, &siop) < 0) {
+                cipher_driver_info[i].accelerated = DEVCRYPTO_ACCELERATION_UNKNOWN;
+            } else {
+                cipher_driver_info[i].driver_name =
+                    OPENSSL_strndup(siop.cipher_info.cra_driver_name,
+                                    CRYPTODEV_MAX_ALG_NAME);
+                if (!(siop.flags & SIOP_FLAG_KERNEL_DRIVER_ONLY))
+                    cipher_driver_info[i].accelerated = DEVCRYPTO_NOT_ACCELERATED;
+                else
+                    cipher_driver_info[i].accelerated = DEVCRYPTO_ACCELERATED;
+            }
+#endif /* CIOCGSESSINFO */
+        }
+        ioctl(cfd, CIOCFSESSION, &sess.ses);
+        if (devcrypto_test_cipher(i)) {
             known_cipher_nids[known_cipher_nids_amount++] =
                 cipher_data[i].nid;
         }
     }
 }
 
+static void rebuild_known_cipher_nids(ENGINE *e)
+{
+    size_t i;
+
+    for (i = 0, known_cipher_nids_amount = 0; i < OSSL_NELEM(cipher_data); i++) {
+        if (devcrypto_test_cipher(i))
+            known_cipher_nids[known_cipher_nids_amount++] = cipher_data[i].nid;
+    }
+    ENGINE_unregister_ciphers(e);
+    ENGINE_register_ciphers(e);
+}
+
 static const EVP_CIPHER *get_cipher_method(int nid)
 {
     size_t i = get_cipher_data_index(nid);
@@ -302,8 +490,11 @@ static void destroy_all_cipher_methods(void)
 {
     size_t i;
 
-    for (i = 0; i < OSSL_NELEM(cipher_data); i++)
+    for (i = 0; i < OSSL_NELEM(cipher_data); i++) {
         destroy_cipher_method(cipher_data[i].nid);
+        OPENSSL_free(cipher_driver_info[i].driver_name);
+        cipher_driver_info[i].driver_name = NULL;
+    }
 }
 
 static int devcrypto_ciphers(ENGINE *e, const EVP_CIPHER **cipher,
@@ -317,6 +508,70 @@ static int devcrypto_ciphers(ENGINE *e, const EVP_CIPHER **cipher,
     return *cipher != NULL;
 }
 
+static void devcrypto_select_all_ciphers(int *cipher_list)
+{
+    size_t i;
+
+    for (i = 0; i < OSSL_NELEM(cipher_data); i++)
+        cipher_list[i] = 1;
+}
+
+static int cryptodev_select_cipher_cb(const char *str, int len, void *usr)
+{
+    int *cipher_list = (int *)usr;
+    char *name;
+    const EVP_CIPHER *EVP;
+    size_t i;
+
+    if (len == 0)
+        return 1;
+    if (usr == NULL || (name = OPENSSL_strndup(str, len)) == NULL)
+        return 0;
+    EVP = EVP_get_cipherbyname(name);
+    if (EVP == NULL)
+        fprintf(stderr, "devcrypto: unknown cipher %s\n", name);
+    else if ((i = find_cipher_data_index(EVP_CIPHER_nid(EVP))) != (size_t)-1)
+        cipher_list[i] = 1;
+    else
+        fprintf(stderr, "devcrypto: cipher %s not available\n", name);
+    OPENSSL_free(name);
+    return 1;
+}
+
+static void dump_cipher_info(void)
+{
+    size_t i;
+    const char *name;
+
+    fprintf (stderr, "Information about ciphers supported by the /dev/crypto"
+             " engine:\n");
+#ifndef CIOCGSESSINFO
+    fprintf(stderr, "CIOCGSESSINFO (session info call) unavailable\n");
+#endif
+    for (i = 0; i < OSSL_NELEM(cipher_data); i++) {
+        name = OBJ_nid2sn(cipher_data[i].nid);
+        fprintf (stderr, "Cipher %s, NID=%d, /dev/crypto info: id=%d, ",
+                 name ? name : "unknown", cipher_data[i].nid,
+                 cipher_data[i].devcryptoid);
+        if (cipher_driver_info[i].status == DEVCRYPTO_STATUS_NO_CIOCGSESSION ) {
+            fprintf (stderr, "CIOCGSESSION (session open call) failed\n");
+            continue;
+        }
+        fprintf (stderr, "driver=%s ", cipher_driver_info[i].driver_name ?
+                 cipher_driver_info[i].driver_name : "unknown");
+        if (cipher_driver_info[i].accelerated == DEVCRYPTO_ACCELERATED)
+            fprintf(stderr, "(hw accelerated)");
+        else if (cipher_driver_info[i].accelerated == DEVCRYPTO_NOT_ACCELERATED)
+            fprintf(stderr, "(software)");
+        else
+            fprintf(stderr, "(acceleration status unknown)");
+        if (cipher_driver_info[i].status == DEVCRYPTO_STATUS_FAILURE)
+            fprintf (stderr, ". Cipher setup failed");
+        fprintf(stderr, "\n");
+    }
+    fprintf(stderr, "\n");
+}
+
 /*
  * We only support digests if the cryptodev implementation supports multiple
  * data updates and session copying.  Otherwise, we would be forced to maintain
@@ -338,7 +593,9 @@ static int devcrypto_ciphers(ENGINE *e, const EVP_CIPHER **cipher,
 
 struct digest_ctx {
     struct session_op sess;
-    int init;
+    /* This signals that the init function was called, not that it succeeded. */
+    int init_called;
+    unsigned char digest_res[HASH_MAX_LEN];
 };
 
 static const struct digest_data_st {
@@ -369,13 +626,22 @@ static const struct digest_data_st {
 #endif
 };
 
-static size_t get_digest_data_index(int nid)
+static size_t find_digest_data_index(int nid)
 {
     size_t i;
 
     for (i = 0; i < OSSL_NELEM(digest_data); i++)
         if (nid == digest_data[i].nid)
             return i;
+    return (size_t)-1;
+}
+
+static size_t get_digest_data_index(int nid)
+{
+    size_t i = find_digest_data_index(nid);
+
+    if (i != (size_t)-1)
+        return i;
 
     /*
      * Code further down must make sure that only NIDs in the table above
@@ -392,8 +658,8 @@ static const struct digest_data_st *get_digest_data(int nid)
 }
 
 /*
- * Following are the four necessary functions to map OpenSSL functionality
- * with cryptodev.
+ * Following are the five necessary functions to map OpenSSL functionality
+ * with cryptodev: init, update, final, cleanup, and copy.
  */
 
 static int digest_init(EVP_MD_CTX *ctx)
@@ -403,7 +669,7 @@ static int digest_init(EVP_MD_CTX *ctx)
     const struct digest_data_st *digest_d =
         get_digest_data(EVP_MD_CTX_type(ctx));
 
-    digest_ctx->init = 1;
+    digest_ctx->init_called = 1;
 
     memset(&digest_ctx->sess, 0, sizeof(digest_ctx->sess));
     digest_ctx->sess.mac = digest_d->devcryptoid;
@@ -438,12 +704,18 @@ static int digest_update(EVP_MD_CTX *ctx, const void *data, size_t count)
     if (count == 0)
         return 1;
 
-    if (digest_op(digest_ctx, data, count, NULL, COP_FLAG_UPDATE) < 0) {
-        SYSerr(SYS_F_IOCTL, errno);
+    if (digest_ctx == NULL)
         return 0;
+
+    if (EVP_MD_CTX_test_flags(ctx, EVP_MD_CTX_FLAG_ONESHOT)) {
+        if (digest_op(digest_ctx, data, count, digest_ctx->digest_res, 0) >= 0)
+            return 1;
+    } else if (digest_op(digest_ctx, data, count, NULL, COP_FLAG_UPDATE) >= 0) {
+        return 1;
     }
 
-    return 1;
+    SYSerr(SYS_F_IOCTL, errno);
+    return 0;
 }
 
 static int digest_final(EVP_MD_CTX *ctx, unsigned char *md)
@@ -451,11 +723,12 @@ static int digest_final(EVP_MD_CTX *ctx, unsigned char *md)
     struct digest_ctx *digest_ctx =
         (struct digest_ctx *)EVP_MD_CTX_md_data(ctx);
 
-    if (digest_op(digest_ctx, NULL, 0, md, COP_FLAG_FINAL) < 0) {
-        SYSerr(SYS_F_IOCTL, errno);
+    if (md == NULL || digest_ctx == NULL)
         return 0;
-    }
-    if (ioctl(cfd, CIOCFSESSION, &digest_ctx->sess.ses) < 0) {
+
+    if (EVP_MD_CTX_test_flags(ctx, EVP_MD_CTX_FLAG_ONESHOT)) {
+        memcpy(md, digest_ctx->digest_res, EVP_MD_CTX_size(ctx));
+    } else if (digest_op(digest_ctx, NULL, 0, md, COP_FLAG_FINAL) < 0) {
         SYSerr(SYS_F_IOCTL, errno);
         return 0;
     }
@@ -471,14 +744,9 @@ static int digest_copy(EVP_MD_CTX *to, const EVP_MD_CTX *from)
         (struct digest_ctx *)EVP_MD_CTX_md_data(to);
     struct cphash_op cphash;
 
-    if (digest_from == NULL)
+    if (digest_from == NULL || digest_from->init_called != 1)
         return 1;
 
-    if (digest_from->init != 1) {
-        SYSerr(SYS_F_IOCTL, EINVAL);
-        return 0;
-    }
-
     if (!digest_init(to)) {
         SYSerr(SYS_F_IOCTL, errno);
         return 0;
@@ -495,37 +763,111 @@ static int digest_copy(EVP_MD_CTX *to, const EVP_MD_CTX *from)
 
 static int digest_cleanup(EVP_MD_CTX *ctx)
 {
+    struct digest_ctx *digest_ctx =
+        (struct digest_ctx *)EVP_MD_CTX_md_data(ctx);
+
+    if (digest_ctx == NULL)
+        return 1;
+    if (ioctl(cfd, CIOCFSESSION, &digest_ctx->sess.ses) < 0) {
+        SYSerr(SYS_F_IOCTL, errno);
+        return 0;
+    }
     return 1;
 }
 
 /*
- * Keep a table of known nids and associated methods.
+ * Keep tables of known nids, associated methods, selected digests, and
+ * driver info.
  * Note that known_digest_nids[] isn't necessarily indexed the same way as
- * digest_data[] above, which known_digest_methods[] is.
+ * digest_data[] above, which the other tables are.
  */
 static int known_digest_nids[OSSL_NELEM(digest_data)];
 static int known_digest_nids_amount = -1; /* -1 indicates not yet initialised */
 static EVP_MD *known_digest_methods[OSSL_NELEM(digest_data)] = { NULL, };
+static int selected_digests[OSSL_NELEM(digest_data)];
+static struct driver_info_st digest_driver_info[OSSL_NELEM(digest_data)];
+
+static int devcrypto_test_digest(size_t digest_data_index)
+{
+    return (digest_driver_info[digest_data_index].status == DEVCRYPTO_STATUS_USABLE
+            && selected_digests[digest_data_index] == 1
+            && (digest_driver_info[digest_data_index].accelerated
+                    == DEVCRYPTO_ACCELERATED
+                || use_softdrivers == DEVCRYPTO_USE_SOFTWARE
+                || (digest_driver_info[digest_data_index].accelerated
+                        != DEVCRYPTO_NOT_ACCELERATED
+                    && use_softdrivers == DEVCRYPTO_REJECT_SOFTWARE)));
+}
+
+static void rebuild_known_digest_nids(ENGINE *e)
+{
+    size_t i;
+
+    for (i = 0, known_digest_nids_amount = 0; i < OSSL_NELEM(digest_data); i++) {
+        if (devcrypto_test_digest(i))
+            known_digest_nids[known_digest_nids_amount++] = digest_data[i].nid;
+    }
+    ENGINE_unregister_digests(e);
+    ENGINE_register_digests(e);
+}
 
 static void prepare_digest_methods(void)
 {
     size_t i;
-    struct session_op sess;
+    struct session_op sess1, sess2;
+#ifdef CIOCGSESSINFO
+    struct session_info_op siop;
+#endif
+    struct cphash_op cphash;
 
-    memset(&sess, 0, sizeof(sess));
+    memset(&digest_driver_info, 0, sizeof(digest_driver_info));
+
+    memset(&sess1, 0, sizeof(sess1));
+    memset(&sess2, 0, sizeof(sess2));
 
     for (i = 0, known_digest_nids_amount = 0; i < OSSL_NELEM(digest_data);
          i++) {
 
+        selected_digests[i] = 1;
+
         /*
-         * Check that the algo is really availably by trying to open and close
-         * a session.
+         * Check that the digest is usable
          */
-        sess.mac = digest_data[i].devcryptoid;
-        if (ioctl(cfd, CIOCGSESSION, &sess) < 0
-            || ioctl(cfd, CIOCFSESSION, &sess.ses) < 0)
-            continue;
+        sess1.mac = digest_data[i].devcryptoid;
+        sess2.ses = 0;
+        if (ioctl(cfd, CIOCGSESSION, &sess1) < 0) {
+            digest_driver_info[i].status = DEVCRYPTO_STATUS_NO_CIOCGSESSION;
+            goto finish;
+        }
 
+#ifdef CIOCGSESSINFO
+        /* gather hardware acceleration info from the driver */
+        siop.ses = sess1.ses;
+        if (ioctl(cfd, CIOCGSESSINFO, &siop) < 0) {
+            digest_driver_info[i].accelerated = DEVCRYPTO_ACCELERATION_UNKNOWN;
+        } else {
+            digest_driver_info[i].driver_name =
+                OPENSSL_strndup(siop.hash_info.cra_driver_name,
+                                CRYPTODEV_MAX_ALG_NAME);
+            if (siop.flags & SIOP_FLAG_KERNEL_DRIVER_ONLY)
+                digest_driver_info[i].accelerated = DEVCRYPTO_ACCELERATED;
+            else
+                digest_driver_info[i].accelerated = DEVCRYPTO_NOT_ACCELERATED;
+        }
+#endif
+
+        /* digest must be capable of hash state copy */
+        sess2.mac = sess1.mac;
+        if (ioctl(cfd, CIOCGSESSION, &sess2) < 0) {
+            digest_driver_info[i].status = DEVCRYPTO_STATUS_FAILURE;
+            goto finish;
+        }
+        cphash.src_ses = sess1.ses;
+        cphash.dst_ses = sess2.ses;
+        if (ioctl(cfd, CIOCCPHASH, &cphash) < 0) {
+            digest_driver_info[i].status = DEVCRYPTO_STATUS_NO_CIOCCPHASH;
+            goto finish;
+        }
         if ((known_digest_methods[i] = EVP_MD_meth_new(digest_data[i].nid,
                                                        NID_undef)) == NULL
             || !EVP_MD_meth_set_result_size(known_digest_methods[i],
@@ -537,11 +879,18 @@ static void prepare_digest_methods(void)
             || !EVP_MD_meth_set_cleanup(known_digest_methods[i], digest_cleanup)
             || !EVP_MD_meth_set_app_datasize(known_digest_methods[i],
                                              sizeof(struct digest_ctx))) {
+            digest_driver_info[i].status = DEVCRYPTO_STATUS_FAILURE;
             EVP_MD_meth_free(known_digest_methods[i]);
             known_digest_methods[i] = NULL;
-        } else {
-            known_digest_nids[known_digest_nids_amount++] = digest_data[i].nid;
+            goto finish;
         }
+        digest_driver_info[i].status = DEVCRYPTO_STATUS_USABLE;
+finish:
+        ioctl(cfd, CIOCFSESSION, &sess1.ses);
+        if (sess2.ses != 0)
+            ioctl(cfd, CIOCFSESSION, &sess2.ses);
+        if (devcrypto_test_digest(i))
+            known_digest_nids[known_digest_nids_amount++] = digest_data[i].nid;
     }
 }
 
@@ -572,8 +921,11 @@ static void destroy_all_digest_methods(void)
 {
     size_t i;
 
-    for (i = 0; i < OSSL_NELEM(digest_data); i++)
+    for (i = 0; i < OSSL_NELEM(digest_data); i++) {
         destroy_digest_method(digest_data[i].nid);
+        OPENSSL_free(digest_driver_info[i].driver_name);
+        digest_driver_info[i].driver_name = NULL;
+    }
 }
 
 static int devcrypto_digests(ENGINE *e, const EVP_MD **digest,
@@ -587,10 +939,199 @@ static int devcrypto_digests(ENGINE *e, const EVP_MD **digest,
     return *digest != NULL;
 }
 
+static void devcrypto_select_all_digests(int *digest_list)
+{
+    size_t i;
+
+    for (i = 0; i < OSSL_NELEM(digest_data); i++)
+        digest_list[i] = 1;
+}
+
+static int cryptodev_select_digest_cb(const char *str, int len, void *usr)
+{
+    int *digest_list = (int *)usr;
+    char *name;
+    const EVP_MD *EVP;
+    size_t i;
+
+    if (len == 0)
+        return 1;
+    if (usr == NULL || (name = OPENSSL_strndup(str, len)) == NULL)
+        return 0;
+    EVP = EVP_get_digestbyname(name);
+    if (EVP == NULL)
+        fprintf(stderr, "devcrypto: unknown digest %s\n", name);
+    else if ((i = find_digest_data_index(EVP_MD_type(EVP))) != (size_t)-1)
+        digest_list[i] = 1;
+    else
+        fprintf(stderr, "devcrypto: digest %s not available\n", name);
+    OPENSSL_free(name);
+    return 1;
+}
+
+static void dump_digest_info(void)
+{
+    size_t i;
+    const char *name;
+
+    fprintf (stderr, "Information about digests supported by the /dev/crypto"
+             " engine:\n");
+#ifndef CIOCGSESSINFO
+    fprintf(stderr, "CIOCGSESSINFO (session info call) unavailable\n");
+#endif
+
+    for (i = 0; i < OSSL_NELEM(digest_data); i++) {
+        name = OBJ_nid2sn(digest_data[i].nid);
+        fprintf (stderr, "Digest %s, NID=%d, /dev/crypto info: id=%d, driver=%s",
+                 name ? name : "unknown", digest_data[i].nid,
+                 digest_data[i].devcryptoid,
+                 digest_driver_info[i].driver_name ? digest_driver_info[i].driver_name : "unknown");
+        if (digest_driver_info[i].status == DEVCRYPTO_STATUS_NO_CIOCGSESSION) {
+            fprintf (stderr, ". CIOCGSESSION (session open) failed\n");
+            continue;
+        }
+        if (digest_driver_info[i].accelerated == DEVCRYPTO_ACCELERATED)
+            fprintf(stderr, " (hw accelerated)");
+        else if (digest_driver_info[i].accelerated == DEVCRYPTO_NOT_ACCELERATED)
+            fprintf(stderr, " (software)");
+        else
+            fprintf(stderr, " (acceleration status unknown)");
+        if (cipher_driver_info[i].status == DEVCRYPTO_STATUS_FAILURE)
+            fprintf (stderr, ". Cipher setup failed\n");
+        else if (digest_driver_info[i].status == DEVCRYPTO_STATUS_NO_CIOCCPHASH)
+            fprintf(stderr, ", CIOCCPHASH failed\n");
+        else
+            fprintf(stderr, ", CIOCCPHASH capable\n");
+    }
+    fprintf(stderr, "\n");
+}
+
 #endif
 
 /******************************************************************************
  *
+ * CONTROL COMMANDS
+ *
+ *****/
+
+#define DEVCRYPTO_CMD_USE_SOFTDRIVERS ENGINE_CMD_BASE
+#define DEVCRYPTO_CMD_CIPHERS (ENGINE_CMD_BASE + 1)
+#define DEVCRYPTO_CMD_DIGESTS (ENGINE_CMD_BASE + 2)
+#define DEVCRYPTO_CMD_DUMP_INFO (ENGINE_CMD_BASE + 3)
+
+static const ENGINE_CMD_DEFN devcrypto_cmds[] = {
+#ifdef CIOCGSESSINFO
+   {DEVCRYPTO_CMD_USE_SOFTDRIVERS,
+    "USE_SOFTDRIVERS",
+    "specifies whether to use software (not accelerated) drivers ("
+        OPENSSL_MSTR(DEVCRYPTO_REQUIRE_ACCELERATED) "=use only accelerated drivers, "
+        OPENSSL_MSTR(DEVCRYPTO_USE_SOFTWARE) "=allow all drivers, "
+        OPENSSL_MSTR(DEVCRYPTO_REJECT_SOFTWARE)
+        "=use if acceleration can't be determined) [default="
+        OPENSSL_MSTR(DEVCRYPTO_DEFAULT_USE_SOFDTRIVERS) "]",
+    ENGINE_CMD_FLAG_NUMERIC},
+#endif
+
+   {DEVCRYPTO_CMD_CIPHERS,
+    "CIPHERS",
+    "either ALL, NONE, or a comma-separated list of ciphers to enable [default=ALL]",
+    ENGINE_CMD_FLAG_STRING},
+
+#ifdef IMPLEMENT_DIGEST
+   {DEVCRYPTO_CMD_DIGESTS,
+    "DIGESTS",
+    "either ALL, NONE, or a comma-separated list of digests to enable [default=ALL]",
+    ENGINE_CMD_FLAG_STRING},
+#endif
+
+   {DEVCRYPTO_CMD_DUMP_INFO,
+    "DUMP_INFO",
+    "dump info about each algorithm to stderr; use 'openssl engine -pre DUMP_INFO devcrypto'",
+    ENGINE_CMD_FLAG_NO_INPUT},
+
+   {0, NULL, NULL, 0}
+};
+
+static int devcrypto_ctrl(ENGINE *e, int cmd, long i, void *p, void (*f) (void))
+{
+    int *new_list;
+    switch (cmd) {
+#ifdef CIOCGSESSINFO
+    case DEVCRYPTO_CMD_USE_SOFTDRIVERS:
+        switch (i) {
+        case DEVCRYPTO_REQUIRE_ACCELERATED:
+        case DEVCRYPTO_USE_SOFTWARE:
+        case DEVCRYPTO_REJECT_SOFTWARE:
+            break;
+        default:
+            fprintf(stderr, "devcrypto: invalid value (%ld) for USE_SOFTDRIVERS\n", i);
+            return 0;
+        }
+        if (use_softdrivers == i)
+            return 1;
+        use_softdrivers = i;
+#ifdef IMPLEMENT_DIGEST
+        rebuild_known_digest_nids(e);
+#endif
+        rebuild_known_cipher_nids(e);
+        return 1;
+#endif /* CIOCGSESSINFO */
+
+    case DEVCRYPTO_CMD_CIPHERS:
+        if (p == NULL)
+            return 1;
+        if (strcasecmp((const char *)p, "ALL") == 0) {
+            devcrypto_select_all_ciphers(selected_ciphers);
+        } else if (strcasecmp((const char*)p, "NONE") == 0) {
+            memset(selected_ciphers, 0, sizeof(selected_ciphers));
+        } else {
+            new_list=OPENSSL_zalloc(sizeof(selected_ciphers));
+            if (!CONF_parse_list(p, ',', 1, cryptodev_select_cipher_cb, new_list)) {
+                OPENSSL_free(new_list);
+                return 0;
+            }
+            memcpy(selected_ciphers, new_list, sizeof(selected_ciphers));
+            OPENSSL_free(new_list);
+        }
+        rebuild_known_cipher_nids(e);
+        return 1;
+
+#ifdef IMPLEMENT_DIGEST
+    case DEVCRYPTO_CMD_DIGESTS:
+        if (p == NULL)
+            return 1;
+        if (strcasecmp((const char *)p, "ALL") == 0) {
+            devcrypto_select_all_digests(selected_digests);
+        } else if (strcasecmp((const char*)p, "NONE") == 0) {
+            memset(selected_digests, 0, sizeof(selected_digests));
+        } else {
+            new_list=OPENSSL_zalloc(sizeof(selected_digests));
+            if (!CONF_parse_list(p, ',', 1, cryptodev_select_digest_cb, new_list)) {
+                OPENSSL_free(new_list);
+                return 0;
+            }
+            memcpy(selected_digests, new_list, sizeof(selected_digests));
+            OPENSSL_free(new_list);
+        }
+        rebuild_known_digest_nids(e);
+        return 1;
+#endif /* IMPLEMENT_DIGEST */
+
+    case DEVCRYPTO_CMD_DUMP_INFO:
+        dump_cipher_info();
+#ifdef IMPLEMENT_DIGEST
+        dump_digest_info();
+#endif
+        return 1;
+
+    default:
+        break;
+    }
+    return 0;
+}
+
+/******************************************************************************
+ *
  * LOAD / UNLOAD
  *
  *****/
@@ -619,11 +1160,6 @@ void engine_load_devcrypto_int()
         return;
     }
 
-    prepare_cipher_methods();
-#ifdef IMPLEMENT_DIGEST
-    prepare_digest_methods();
-#endif
-
     if ((e = ENGINE_new()) == NULL
         || !ENGINE_set_destroy_function(e, devcrypto_unload)) {
         ENGINE_free(e);
@@ -636,8 +1172,15 @@ void engine_load_devcrypto_int()
         return;
     }
 
+    prepare_cipher_methods();
+#ifdef IMPLEMENT_DIGEST
+    prepare_digest_methods();
+#endif
+
     if (!ENGINE_set_id(e, "devcrypto")
         || !ENGINE_set_name(e, "/dev/crypto engine")
+        || !ENGINE_set_cmd_defns(e, devcrypto_cmds)
+        || !ENGINE_set_ctrl_function(e, devcrypto_ctrl)
 
 /*
  * Asymmetric ciphers aren't well supported with /dev/crypto.  Among the BSD


More information about the openssl-commits mailing list