DESERIALIZER: Implement decryption of password protected objects
authorRichard Levitte <levitte@openssl.org>
Fri, 10 Jul 2020 13:13:55 +0000 (15:13 +0200)
committerRichard Levitte <levitte@openssl.org>
Fri, 24 Jul 2020 14:43:20 +0000 (16:43 +0200)
This implements these functions:

OSSL_DESERIALIZER_CTX_set_cipher()
OSSL_DESERIALIZER_CTX_set_passphrase()
OSSL_DESERIALIZER_CTX_set_passphrase_ui()
OSSL_DESERIALIZER_CTX_set_passphrase_cb()

To be able to deal with multiple deserializers trying to work on the
same byte array and wanting to decrypt it while doing so, the
deserializer caches the passphrase.  This cache is cleared at the end
of OSSL_DESERIALIZER_from_bio().

Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Shane Lontis <shane.lontis@oracle.com>
(Merged from https://github.com/openssl/openssl/pull/12410)

crypto/serializer/build.info
crypto/serializer/deserializer_lib.c
crypto/serializer/deserializer_pkey.c
crypto/serializer/serdes_pass.c [new file with mode: 0644]
crypto/serializer/serializer_local.h
crypto/serializer/serializer_pkey.c
include/openssl/core_names.h

index c0222785e94d18837f23aba419a6b1c3f5035867..11f8889b6bb04587e7636f8801bc6cd94256fa0b 100644 (file)
@@ -1,3 +1,5 @@
+SOURCE[../../libcrypto]=serdes_pass.c
+
 SOURCE[../../libcrypto]=serializer_meth.c serializer_lib.c serializer_pkey.c
 SOURCE[../../libcrypto]=deserializer_meth.c deserializer_lib.c \
         deserializer_pkey.c
index 2d2b1b7e7a75f209983f3b0a88825a4169fe05d2..2fbb7782cf2e0af6f3ea9c6b88c53a67c6908d3b 100644 (file)
@@ -29,12 +29,19 @@ static int deser_process(const OSSL_PARAM params[], void *arg);
 int OSSL_DESERIALIZER_from_bio(OSSL_DESERIALIZER_CTX *ctx, BIO *in)
 {
     struct deser_process_data_st data;
+    int ok = 0;
 
     memset(&data, 0, sizeof(data));
     data.ctx = ctx;
     data.bio = in;
 
-    return deser_process(NULL, &data);
+    ok = deser_process(NULL, &data);
+
+    /* Clear any cached passphrase */
+    OPENSSL_clear_free(ctx->cached_passphrase, ctx->cached_passphrase_len);
+    ctx->cached_passphrase = NULL;
+    ctx->cached_passphrase_len = 0;
+    return ok;
 }
 
 #ifndef OPENSSL_NO_STDIO
index 1dc35b76a7ad5a673fb25067631ffa6b97e46315..0fafdf31aa39e6cced6b2bf1f7c9cb1da3b079e5 100644 (file)
@@ -9,11 +9,82 @@
 
 #include <openssl/core_names.h>
 #include <openssl/evp.h>
+#include <openssl/ui.h>
 #include <openssl/deserializer.h>
+#include <openssl/core_names.h>
 #include <openssl/safestack.h>
 #include "crypto/evp.h"
 #include "serializer_local.h"
 
+int OSSL_DESERIALIZER_CTX_set_cipher(OSSL_DESERIALIZER_CTX *ctx,
+                                     const char *cipher_name,
+                                     const char *propquery)
+{
+    OSSL_PARAM params[] = { OSSL_PARAM_END, OSSL_PARAM_END, OSSL_PARAM_END };
+
+    params[0] =
+        OSSL_PARAM_construct_utf8_string(OSSL_DESERIALIZER_PARAM_CIPHER,
+                                         (void *)cipher_name, 0);
+    params[1] =
+        OSSL_PARAM_construct_utf8_string(OSSL_DESERIALIZER_PARAM_PROPERTIES,
+                                         (void *)propquery, 0);
+
+    return OSSL_DESERIALIZER_CTX_set_params(ctx, params);
+}
+
+int OSSL_DESERIALIZER_CTX_set_passphrase(OSSL_DESERIALIZER_CTX *ctx,
+                                         const unsigned char *kstr,
+                                         size_t klen)
+{
+    OSSL_PARAM params[] = { OSSL_PARAM_END, OSSL_PARAM_END };
+
+    params[0] = OSSL_PARAM_construct_octet_string(OSSL_DESERIALIZER_PARAM_PASS,
+                                                  (void *)kstr, klen);
+
+    return OSSL_DESERIALIZER_CTX_set_params(ctx, params);
+}
+
+static void deserializer_ctx_reset_passphrase_ui(OSSL_DESERIALIZER_CTX *ctx)
+{
+    UI_destroy_method(ctx->allocated_ui_method);
+    ctx->allocated_ui_method = NULL;
+    ctx->ui_method = NULL;
+    ctx->ui_data = NULL;
+}
+
+int OSSL_DESERIALIZER_CTX_set_passphrase_ui(OSSL_DESERIALIZER_CTX *ctx,
+                                            const UI_METHOD *ui_method,
+                                            void *ui_data)
+{
+    if (!ossl_assert(ctx != NULL)) {
+        ERR_raise(ERR_LIB_OSSL_DESERIALIZER, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    deserializer_ctx_reset_passphrase_ui(ctx);
+    ctx->ui_method = ui_method;
+    ctx->ui_data = ui_data;
+    return 1;
+}
+
+int OSSL_DESERIALIZER_CTX_set_passphrase_cb(OSSL_DESERIALIZER_CTX *ctx,
+                                            pem_password_cb *cb, void *cbarg)
+{
+    if (!ossl_assert(ctx != NULL)) {
+        ERR_raise(ERR_LIB_OSSL_DESERIALIZER, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    deserializer_ctx_reset_passphrase_ui(ctx);
+    if (cb == NULL)
+        return 1;
+    ctx->ui_method =
+        ctx->allocated_ui_method = UI_UTIL_wrap_read_pem_callback(cb, 0);
+    ctx->ui_data = cbarg;
+
+    return ctx->ui_method != NULL;
+}
+
 /*
  * Support for OSSL_DESERIALIZER_CTX_new_by_EVP_PKEY:
  * Handle an object reference
diff --git a/crypto/serializer/serdes_pass.c b/crypto/serializer/serdes_pass.c
new file mode 100644 (file)
index 0000000..8a33af5
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/err.h>
+#include <openssl/ui.h>
+#include <openssl/core_names.h>
+#include "internal/cryptlib.h"
+#include "serializer_local.h"
+
+/* Passphrase callbacks for any who need it */
+
+/*
+ * First, define the generic passphrase function that supports both
+ * outgoing (with passphrase verify) and incoming (without passphrase
+ * verify) passphrase reading.
+ */
+static int do_passphrase(char *pass, size_t pass_size, size_t *pass_len,
+                         const OSSL_PARAM params[], void *arg, int verify,
+                         const UI_METHOD *ui_method, void *ui_data, int errlib)
+{
+    const OSSL_PARAM *p;
+    const char *prompt_info = NULL;
+    char *prompt = NULL, *vpass = NULL;
+    int prompt_idx = -1, verify_idx = -1;
+    UI *ui = NULL;
+    int ret = 0;
+
+    if (!ossl_assert(pass != NULL && pass_size != 0 && pass_len != NULL)) {
+        ERR_raise(errlib, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    if ((p = OSSL_PARAM_locate_const(params,
+                                     OSSL_PASSPHRASE_PARAM_INFO)) != NULL) {
+        if (p->data_type != OSSL_PARAM_UTF8_STRING)
+            return 0;
+        prompt_info = p->data;
+    }
+
+    if ((ui = UI_new()) == NULL) {
+        ERR_raise(errlib, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+
+    UI_set_method(ui, ui_method);
+    UI_add_user_data(ui, ui_data);
+
+    /* Get an application constructed prompt */
+    prompt = UI_construct_prompt(ui, "pass phrase", prompt_info);
+   if (prompt == NULL) {
+        ERR_raise(errlib, ERR_R_MALLOC_FAILURE);
+        goto end;
+    }
+
+    prompt_idx = UI_add_input_string(ui, prompt,
+                                     UI_INPUT_FLAG_DEFAULT_PWD,
+                                     pass, 0, pass_size - 1) - 1;
+    if (prompt_idx < 0) {
+        ERR_raise(errlib, ERR_R_UI_LIB);
+        goto end;
+    }
+
+    if (verify) {
+        /* Get a buffer for verification prompt */
+        vpass = OPENSSL_zalloc(pass_size);
+        if (vpass == NULL) {
+            ERR_raise(errlib, ERR_R_MALLOC_FAILURE);
+            goto end;
+        }
+        verify_idx = UI_add_verify_string(ui, prompt,
+                                          UI_INPUT_FLAG_DEFAULT_PWD,
+                                          vpass, 0, pass_size - 1,
+                                          pass) - 1;
+        if (verify_idx < 0) {
+            ERR_raise(errlib, ERR_R_UI_LIB);
+            goto end;
+        }
+    }
+
+    switch (UI_process(ui)) {
+    case -2:
+        ERR_raise(errlib, ERR_R_INTERRUPTED_OR_CANCELLED);
+        break;
+    case -1:
+        ERR_raise(errlib, ERR_R_UI_LIB);
+        break;
+    default:
+        *pass_len = (size_t)UI_get_result_length(ui, prompt_idx);
+        ret = 1;
+        break;
+    }
+
+ end:
+    OPENSSL_free(vpass);
+    OPENSSL_free(prompt);
+    UI_free(ui);
+    return ret;
+}
+
+/*
+ * Serializers typically want to get an outgoing passphrase, while
+ * deserializers typically want to get en incoming passphrase.
+ */
+int ossl_serializer_passphrase_out_cb(char *pass, size_t pass_size,
+                                      size_t *pass_len,
+                                      const OSSL_PARAM params[], void *arg)
+{
+    OSSL_SERIALIZER_CTX *ctx = arg;
+
+    if (!ossl_assert(ctx != NULL)) {
+        ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    return do_passphrase(pass, pass_size, pass_len, params, arg, 1,
+                         ctx->ui_method, ctx->ui_data,
+                         ERR_LIB_OSSL_SERIALIZER);
+}
+
+int ossl_deserializer_passphrase_in_cb(char *pass, size_t pass_size,
+                                       size_t *pass_len,
+                                       const OSSL_PARAM params[], void *arg)
+{
+    OSSL_DESERIALIZER_CTX *ctx = arg;
+
+    if (!ossl_assert(ctx != NULL)) {
+        ERR_raise(ERR_LIB_OSSL_DESERIALIZER, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    if (ctx->cached_passphrase != NULL) {
+        size_t len = ctx->cached_passphrase_len;
+
+        if (len > pass_size)
+            len = pass_size;
+        memcpy(pass, ctx->cached_passphrase, len);
+        *pass_len = len;
+        return 1;
+    } else {
+        if ((ctx->cached_passphrase = OPENSSL_zalloc(pass_size)) == NULL) {
+            ERR_raise(ERR_LIB_OSSL_DESERIALIZER, ERR_R_MALLOC_FAILURE);
+            return 0;
+        }
+    }
+    if (do_passphrase(pass, pass_size, pass_len, params, arg, 0,
+                      ctx->ui_method, ctx->ui_data,
+                      ERR_LIB_OSSL_DESERIALIZER)) {
+        memcpy(ctx->cached_passphrase, pass, *pass_len);
+        ctx->cached_passphrase_len = *pass_len;
+        return 1;
+    }
+    return 0;
+}
index 46ff84cc7a53df158f80e548787f92c1f5ed9d09..acf600c285653596ffbc7761cec5e2b6475540b5 100644 (file)
@@ -110,4 +110,20 @@ struct ossl_deserializer_ctx_st {
      * intermediary storage.
      */
     UI_METHOD *allocated_ui_method;
+    /*
+     * Because the same input may pass through more than one deserializer,
+     * we cache any passphrase passed to us.  The desrializing processor
+     * must clear this at the end of a run.
+     */
+    unsigned char *cached_passphrase;
+    size_t cached_passphrase_len;
 };
+
+/* Passphrase callbacks, found in serdes_pass.c */
+
+/*
+ * Serializers typically want to get an outgoing passphrase, while
+ * deserializers typically want to get en incoming passphrase.
+ */
+OSSL_PASSPHRASE_CALLBACK ossl_serializer_passphrase_out_cb;
+OSSL_PASSPHRASE_CALLBACK ossl_deserializer_passphrase_in_cb;
index 35ddb92bd4ab75e9528c62c7fe4e864223281087..6e24ed73f0d691b751b751ddb5c785ab869188df 100644 (file)
@@ -107,110 +107,6 @@ static void cache_serializers(const char *name, void *data)
         d->error = 1;
 }
 
-/*
- * Support for OSSL_SERIALIZER_CTX_new_by_TYPE and OSSL_SERIALIZER_to_bio:
- * Passphrase callbacks
- */
-
-/*
- * First, we define the generic passphrase function that supports both
- * outgoing (with passphrase verify) and incoming (without passphrase verify)
- * passphrase reading.
- */
-static int serializer_passphrase(char *pass, size_t pass_size,
-                                 size_t *pass_len, int verify,
-                                 const OSSL_PARAM params[], void *arg)
-{
-    OSSL_SERIALIZER_CTX *ctx = arg;
-    const OSSL_PARAM *p;
-    const char *prompt_info = NULL;
-    char *prompt = NULL, *vpass = NULL;
-    int prompt_idx = -1, verify_idx = -1;
-    UI *ui = NULL;
-    int ret = 0;
-
-    if (!ossl_assert(ctx != NULL && pass != NULL
-                    && pass_size != 0 && pass_len != NULL)) {
-        ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_PASSED_NULL_PARAMETER);
-        return 0;
-    }
-
-    if ((p = OSSL_PARAM_locate_const(params,
-                                     OSSL_PASSPHRASE_PARAM_INFO)) != NULL) {
-        if (p->data_type != OSSL_PARAM_UTF8_STRING)
-            return 0;
-        prompt_info = p->data;
-    }
-
-    if ((ui = UI_new()) == NULL) {
-        ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_MALLOC_FAILURE);
-        return 0;
-    }
-
-    UI_set_method(ui, ctx->ui_method);
-    UI_add_user_data(ui, ctx->ui_data);
-
-    /* Get an application constructed prompt */
-    prompt = UI_construct_prompt(ui, "pass phrase", prompt_info);
-   if (prompt == NULL) {
-        ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_MALLOC_FAILURE);
-        goto end;
-    }
-
-    prompt_idx = UI_add_input_string(ui, prompt,
-                                     UI_INPUT_FLAG_DEFAULT_PWD,
-                                     pass, 0, pass_size - 1) - 1;
-    if (prompt_idx < 0) {
-        ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_UI_LIB);
-        goto end;
-    }
-
-    if (verify) {
-        /* Get a buffer for verification prompt */
-        vpass = OPENSSL_zalloc(pass_size);
-        if (vpass == NULL) {
-            ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_MALLOC_FAILURE);
-            goto end;
-        }
-        verify_idx = UI_add_verify_string(ui, prompt,
-                                          UI_INPUT_FLAG_DEFAULT_PWD,
-                                          vpass, 0, pass_size - 1,
-                                          pass) - 1;
-        if (verify_idx < 0) {
-            ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_UI_LIB);
-            goto end;
-        }
-    }
-
-    switch (UI_process(ui)) {
-    case -2:
-        ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_INTERRUPTED_OR_CANCELLED);
-        break;
-    case -1:
-        ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_UI_LIB);
-        break;
-    default:
-        *pass_len = (size_t)UI_get_result_length(ui, prompt_idx);
-        ret = 1;
-        break;
-    }
-
- end:
-    OPENSSL_free(vpass);
-    OPENSSL_free(prompt);
-    UI_free(ui);
-    return ret;
-}
-
-/* Ensure correct function definition for outgoing passphrase reader */
-static OSSL_PASSPHRASE_CALLBACK serializer_passphrase_out_cb;
-static int serializer_passphrase_out_cb(char *pass, size_t pass_size,
-                                        size_t *pass_len,
-                                        const OSSL_PARAM params[], void *arg)
-{
-    return serializer_passphrase(pass, pass_size, pass_len, 1, params, arg);
-}
-
 /*
  * Support for OSSL_SERIALIZER_to_bio:
  * writing callback for the OSSL_PARAM (the implementation doesn't have
@@ -229,7 +125,7 @@ static int serializer_write_cb(const OSSL_PARAM params[], void *arg)
     BIO *out = write_data->out;
 
     return ctx->ser->serialize_data(ctx->serctx, params, (OSSL_CORE_BIO *)out,
-                                    serializer_passphrase_out_cb, ctx);
+                                    ossl_serializer_passphrase_out_cb, ctx);
 }
 
 /*
@@ -266,7 +162,7 @@ static int serializer_EVP_PKEY_to_bio(OSSL_SERIALIZER_CTX *ctx, BIO *out)
 
     return ctx->ser->serialize_object(ctx->serctx, keydata,
                                       (OSSL_CORE_BIO *)out,
-                                      serializer_passphrase_out_cb, ctx);
+                                      ossl_serializer_passphrase_out_cb, ctx);
 }
 
 /*
index c380b45fbbaa139a1341355147c3a416c86f8669..9ce4115a892d37abee59627434bcecb031cf6221 100644 (file)
@@ -404,6 +404,9 @@ extern "C" {
 #define OSSL_SERIALIZER_PARAM_PROPERTIES        OSSL_ALG_PARAM_PROPERTIES
 #define OSSL_SERIALIZER_PARAM_PASS              "passphrase"
 
+#define OSSL_DESERIALIZER_PARAM_CIPHER          OSSL_ALG_PARAM_CIPHER
+#define OSSL_DESERIALIZER_PARAM_PROPERTIES      OSSL_ALG_PARAM_PROPERTIES
+#define OSSL_DESERIALIZER_PARAM_PASS            "passphrase"
 #define OSSL_DESERIALIZER_PARAM_INPUT_TYPE      "input-type"
 #define OSSL_DESERIALIZER_PARAM_DATA_TYPE       "data-type"
 #define OSSL_DESERIALIZER_PARAM_DATA            "data"