yaca_key_import() implementation of password handling 02/70802/5
authorLukasz Pawelczyk <l.pawelczyk@samsung.com>
Wed, 18 May 2016 12:43:21 +0000 (14:43 +0200)
committerLukasz Pawelczyk <l.pawelczyk@samsung.com>
Fri, 27 May 2016 11:18:14 +0000 (13:18 +0200)
Change-Id: I3586b8038a711942d0998cd7130509cbbc6388ff

src/key.c

index f31f34b..a197842 100755 (executable)
--- a/src/key.c
+++ b/src/key.c
@@ -32,6 +32,7 @@
 #include <openssl/bio.h>
 #include <openssl/pem.h>
 #include <openssl/des.h>
+#include <openssl/err.h>
 
 #include <yaca/crypto.h>
 #include <yaca/error.h>
 
 #include "internal.h"
 
+/* This callback only exists to block the default OpenSSL one and
+ * allow us to check for a proper error code when the key is encrypted
+ */
+int password_dummy_cb(char *buf, int size, int rwflag, void *u)
+{
+       const char empty[] = "";
+
+       memcpy(buf, empty, sizeof(empty));
+
+       return sizeof(empty);
+}
+
 int base64_decode_length(const char *data, size_t data_len, size_t *len)
 {
        assert(data != NULL);
@@ -224,18 +237,34 @@ out:
        return ret;
 }
 
+bool check_import_wrong_pass()
+{
+       unsigned long err = ERR_peek_last_error();
+       unsigned long err_bad_password_1 = ERR_PACK(ERR_LIB_PEM, PEM_F_PEM_DO_HEADER, PEM_R_BAD_DECRYPT);
+       unsigned long err_bad_password_2 = ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_DECRYPTFINAL_EX, EVP_R_BAD_DECRYPT);
+
+       if (err == err_bad_password_1 || err == err_bad_password_2)
+               return true;
+
+       return false;
+}
+
 int import_evp(yaca_key_h *key,
                yaca_key_type_e key_type,
+               const char *password,
                const char *data,
                size_t data_len)
 {
        assert(key != NULL);
+       assert(password == NULL || password[0] != '\0');
        assert(data != NULL);
        assert(data_len != 0);
 
        int ret;
        BIO *src = NULL;
        EVP_PKEY *pkey = NULL;
+       bool wrong_pass = false;
+       pem_password_cb *cb = NULL;
        bool private;
        yaca_key_type_e type;
        struct yaca_key_evp_s *nk = NULL;
@@ -257,46 +286,64 @@ int import_evp(yaca_key_h *key,
                return YACA_ERROR_INTERNAL;
        }
 
+       /* Block the default OpenSSL password callback */
+       if (password == NULL)
+               cb = password_dummy_cb;
+
        /* Possible PEM */
        if (strncmp("----", data, 4) == 0) {
-               if (pkey == NULL) {
+               if (pkey == NULL && !wrong_pass) {
                        BIO_reset(src);
-                       pkey = PEM_read_bio_PrivateKey(src, NULL, NULL, NULL);
+                       pkey = PEM_read_bio_PrivateKey(src, NULL, cb, (void*)password);
+                       if (check_import_wrong_pass())
+                               wrong_pass = true;
                        private = true;
+                       ERROR_CLEAR();
                }
 
-               if (pkey == NULL) {
+               if (pkey == NULL && !wrong_pass) {
                        BIO_reset(src);
-                       pkey = PEM_read_bio_PUBKEY(src, NULL, NULL, NULL);
+                       pkey = PEM_read_bio_PUBKEY(src, NULL, cb, (void*)password);
+                       if (check_import_wrong_pass())
+                               wrong_pass = true;
                        private = false;
+                       ERROR_CLEAR();
                }
 
-               if (pkey == NULL) {
+               if (pkey == NULL && !wrong_pass) {
                        BIO_reset(src);
-                       X509 *x509 = PEM_read_bio_X509(src, NULL, NULL, NULL);
+                       X509 *x509 = PEM_read_bio_X509(src, NULL, cb, (void*)password);
+                       if (check_import_wrong_pass())
+                               wrong_pass = true;
                        if (x509 != NULL)
                                pkey = X509_get_pubkey(x509);
-                       private = false;
                        X509_free(x509);
+                       private = false;
+                       ERROR_CLEAR();
                }
        }
        /* Possible DER */
        else {
-               if (pkey == NULL) {
+               if (pkey == NULL && !wrong_pass) {
                        BIO_reset(src);
                        pkey = d2i_PrivateKey_bio(src, NULL);
                        private = true;
+                       ERROR_CLEAR();
                }
 
-               if (pkey == NULL) {
+               if (pkey == NULL && !wrong_pass) {
                        BIO_reset(src);
                        pkey = d2i_PUBKEY_bio(src, NULL);
                        private = false;
+                       ERROR_CLEAR();
                }
        }
 
        BIO_free(src);
 
+       if (wrong_pass)
+               return YACA_ERROR_PASSWORD_INVALID;
+
        if (pkey == NULL)
                return YACA_ERROR_INVALID_ARGUMENT;
 
@@ -872,16 +919,22 @@ API int yaca_key_import(yaca_key_h *key,
        if (key == NULL || data == NULL || data_len == 0)
                return YACA_ERROR_INVALID_ARGUMENT;
 
+       /* allow an empty password, OpenSSL returns an error with "" */
+       if (password != NULL && password[0] == '\0')
+               password = NULL;
+
        switch (key_type) {
        case YACA_KEY_TYPE_SYMMETRIC:
        case YACA_KEY_TYPE_DES:
        case YACA_KEY_TYPE_IV:
+               if (password != NULL)
+                       return YACA_ERROR_INVALID_ARGUMENT;
                return import_simple(key, key_type, data, data_len);
        case YACA_KEY_TYPE_RSA_PUB:
        case YACA_KEY_TYPE_RSA_PRIV:
        case YACA_KEY_TYPE_DSA_PUB:
        case YACA_KEY_TYPE_DSA_PRIV:
-               return import_evp(key, key_type, data, data_len);
+               return import_evp(key, key_type, password, data, data_len);
        case YACA_KEY_TYPE_DH_PUB:
        case YACA_KEY_TYPE_DH_PRIV:
        case YACA_KEY_TYPE_EC_PUB: