eng_devcrypto: add cipher CTX copy function
[openssl.git] / crypto / engine / eng_devcrypto.c
index d54ac5ec5b199fe4769b7d61701d405a97146e76..90ce67268051ed82f97fdcaf0ff63daab8c3a722 100644 (file)
@@ -207,6 +207,22 @@ static int cipher_do_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
     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 =
@@ -258,10 +274,12 @@ static void prepare_cipher_methods(void)
                                               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_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],
@@ -338,7 +356,8 @@ 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;
 };
 
 static const struct digest_data_st {
@@ -403,7 +422,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,6 +457,9 @@ static int digest_update(EVP_MD_CTX *ctx, const void *data, size_t count)
     if (count == 0)
         return 1;
 
+    if (digest_ctx == NULL)
+        return 0;
+
     if (digest_op(digest_ctx, data, count, NULL, COP_FLAG_UPDATE) < 0) {
         SYSerr(SYS_F_IOCTL, errno);
         return 0;
@@ -451,11 +473,9 @@ 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 (digest_op(digest_ctx, NULL, 0, md, COP_FLAG_FINAL) < 0) {
         SYSerr(SYS_F_IOCTL, errno);
         return 0;
     }
@@ -471,14 +491,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,6 +510,15 @@ 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;
 }