- Always use callback.
- Return INVALID_PASSWORD for combinations that do support password,
it was not required for import but was given in params.
- Return INVALID_PARAM for combinations that do not support password
while it was given in params. For both, import and export.
- PKCS8 always requires a password.
- Added few special cases to differentiate INV_PARAM and INV_PASSWORD.
Change-Id: I171e89125600151e33178eadc3df6b6004987f3c
* used. If it's not known if the key is encrypted one should pass NULL as
* password and check for the #YACA_ERROR_INVALID_PASSWORD return code.
*
+ * @remarks If the imported key will be detected as a format that does not support
+ * encryption and password was passed #YACA_ERROR_INVALID_PARAMETER will
+ * be returned. For a list of keys and formats that do support encryption
+ * see yaca_key_export() documentation.
+ *
* @remarks The @a key should be released using yaca_key_destroy()
*
* @param[in] key_type Type of the key
* @remarks For key formats two values are allowed:
* - #YACA_KEY_FORMAT_DEFAULT: this is the only option possible in case of symmetric
* keys (or IV), for asymmetric keys it will choose PKCS#1
- * for RSA and SSLeay for DSA.
+ * for RSA keys and SSLeay for DSA keys.
* - #YACA_KEY_FORMAT_PKCS8: this will only work for private asymmetric keys.
*
* @remarks The following file formats are supported:
* - #YACA_KEY_FILE_FORMAT_PEM: used only for asymmetric, PEM file format
* - #YACA_KEY_FILE_FORMAT_DER: used only for asymmetric, DER file format
*
- * @remarks If no password is provided the exported key will be unencrypted. Only private
- * RSA/DSA exported as PEM can be encrypted.
+ * @remarks Encryption is supported and optional for RSA/DSA private keys in the
+ * #YACA_KEY_FORMAT_DEFAULT with #YACA_KEY_FILE_FORMAT_PEM format. If no password is
+ * provided the exported key will be unencrypted. The encryption algorithm used
+ * in this case is AES-256-CBC.
+ *
+ * @remarks Encryption is obligatory for #YACA_KEY_FORMAT_PKCS8 format (for both, PEM and DER
+ * file formats). If no password is provided the #YACA_ERROR_INVALID_PARAMETER will
+ * be returned. The encryption algorithm used in this case is PBE with DES-CBC.
*
- * @remarks TODO:document the default encryption algorithm (AES256 for FORMAT_DEFAULT,
- * unknown yet for the FORMAT_PKCS8).
+ * @remakrs Encryption is not supported for the symmetric and public keys in all their
+ * supported formats. If a password is provided in such case the
+ * #YACA_ERROR_INVALID_PARAMETER will be returned.
*
* @param[in] key Key to be exported
* @param[in] key_fmt Format of the key
#include "misc.h"
#include "../src/debug.h"
-void example_password(const yaca_key_h key, yaca_key_format_e key_fmt)
+void example_password(const yaca_key_h key, yaca_key_format_e key_fmt,
+ yaca_key_file_format_e key_file_fmt)
{
char *k = NULL;
size_t kl;
if (ret != YACA_ERROR_NONE)
goto exit;
- ret = yaca_key_export(key, key_fmt, YACA_KEY_FILE_FORMAT_PEM, password, &k, &kl);
+ ret = yaca_key_export(key, key_fmt, key_file_fmt, password, &k, &kl);
+ if (ret == YACA_ERROR_INVALID_PARAMETER)
+ printf("invalid parameter, probably a missing password for PKCS8\n");
if (ret != YACA_ERROR_NONE)
goto exit;
ret = yaca_key_import(YACA_KEY_TYPE_RSA_PRIV, password, k, kl, &lkey);
if (ret == YACA_ERROR_INVALID_PASSWORD)
printf("invalid password\n");
-
- yaca_free(password);
- password = NULL;
}
if (ret != YACA_ERROR_NONE)
yaca_free(k);
k = NULL;
- ret = yaca_key_export(lkey, key_fmt, YACA_KEY_FILE_FORMAT_PEM, NULL, &k, &kl);
+ ret = yaca_key_export(lkey, key_fmt, YACA_KEY_FILE_FORMAT_PEM, password, &k, &kl);
if (ret != YACA_ERROR_NONE)
goto exit;
if (ret != YACA_ERROR_NONE)
goto exit;
- printf("Default format:\n");
- example_password(key, YACA_KEY_FORMAT_DEFAULT);
- printf("\nPKCS8 format:\n");
- example_password(key, YACA_KEY_FORMAT_PKCS8);
+ printf("Default format with PEM:\n");
+ example_password(key, YACA_KEY_FORMAT_DEFAULT, YACA_KEY_FILE_FORMAT_PEM);
+ printf("\nPKCS8 format with PEM:\n");
+ example_password(key, YACA_KEY_FORMAT_PKCS8, YACA_KEY_FILE_FORMAT_PEM);
+ printf("\nPKCS8 format with DER:\n");
+ example_password(key, YACA_KEY_FORMAT_PKCS8, YACA_KEY_FILE_FORMAT_DER);
exit:
yaca_key_destroy(key);
#include <openssl/err.h>
#include <openssl/pem.h>
+#include <openssl/pkcs12.h>
+#include <openssl/dsa.h>
#include <yaca_error.h>
case ERR_PACK(ERR_LIB_RSA, RSA_F_PKEY_RSA_CTRL, RSA_R_INVALID_KEYBITS):
case ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_PKEY_CTX_CTRL, EVP_R_COMMAND_NOT_SUPPORTED):
case ERR_PACK(ERR_LIB_PEM, PEM_F_PEM_READ_BIO, PEM_R_NO_START_LINE):
- case ERR_PACK(ERR_LIB_ASN1, ASN1_F_ASN1_CHECK_TLEN, ASN1_R_WRONG_TAG):
case ERR_PACK(ERR_LIB_ASN1, ASN1_F_ASN1_D2I_READ_BIO, ASN1_R_NOT_ENOUGH_DATA):
ret = YACA_ERROR_INVALID_PARAMETER;
break;
+ case ERR_PACK(ERR_LIB_ASN1, ASN1_F_ASN1_GET_OBJECT, ASN1_R_TOO_LONG):
+ case ERR_PACK(ERR_LIB_ASN1, ASN1_F_ASN1_GET_OBJECT, ASN1_R_HEADER_TOO_LONG):
+ case ERR_PACK(ERR_LIB_ASN1, ASN1_F_ASN1_CHECK_TLEN, ASN1_R_WRONG_TAG):
+ {
+ bool found_crypto_error = false;
+
+ while ((err = ERR_get_error()) != 0)
+ if (err == ERR_PACK(ERR_LIB_PKCS12, PKCS12_F_PKCS12_ITEM_DECRYPT_D2I, PKCS12_R_DECODE_ERROR) ||
+ err == ERR_PACK(ERR_LIB_PKCS12, PKCS12_F_PKCS12_PBE_CRYPT, PKCS12_R_PKCS12_CIPHERFINAL_ERROR) ||
+ err == ERR_PACK(ERR_LIB_DSA, DSA_F_OLD_DSA_PRIV_DECODE, ERR_R_DSA_LIB) ||
+ err == ERR_PACK(ERR_LIB_RSA, RSA_F_OLD_RSA_PRIV_DECODE, ERR_R_RSA_LIB)) {
+ found_crypto_error = true;
+ break;
+ }
+
+ if (found_crypto_error)
+ ret = YACA_ERROR_INVALID_PASSWORD;
+ else
+ ret = YACA_ERROR_INVALID_PARAMETER;
+
+ break;
+ }
case ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_DECRYPTFINAL_EX, EVP_R_BAD_DECRYPT):
case ERR_PACK(ERR_LIB_PEM, PEM_F_PEM_DO_HEADER, PEM_R_BAD_DECRYPT):
+ case ERR_PACK(ERR_LIB_PEM, PEM_F_PEM_DO_HEADER, PEM_R_BAD_PASSWORD_READ):
+ case ERR_PACK(ERR_LIB_PEM, PEM_F_PEM_READ_BIO_PRIVATEKEY, PEM_R_BAD_PASSWORD_READ):
+ case ERR_PACK(ERR_LIB_PEM, PEM_F_D2I_PKCS8PRIVATEKEY_BIO, PEM_R_BAD_PASSWORD_READ):
ret = YACA_ERROR_INVALID_PASSWORD;
break;
}
#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, UNUSED int size, UNUSED int rwflag, UNUSED void *u)
+struct openssl_password_data {
+ bool password_requested;
+ const char *password;
+};
+
+int openssl_password_cb(char *buf, int size, UNUSED int rwflag, void *u)
{
- const char empty[] = "";
+ struct openssl_password_data *cb_data = u;
+
+ if (cb_data->password == NULL)
+ return 0;
- memcpy(buf, empty, sizeof(empty));
+ size_t pass_len = strlen(cb_data->password);
- return sizeof(empty);
+ if (pass_len > INT_MAX || (int)pass_len > size)
+ return 0;
+
+ memcpy(buf, cb_data->password, pass_len);
+ cb_data->password_requested = true;
+
+ return pass_len;
+}
+
+int openssl_password_cb_error(UNUSED char *buf, UNUSED int size, UNUSED int rwflag, UNUSED void *u)
+{
+ return 0;
}
int base64_decode_length(const char *data, size_t data_len, size_t *len)
int ret;
BIO *src = NULL;
EVP_PKEY *pkey = NULL;
- bool wrong_pass = false;
- pem_password_cb *cb = NULL;
+ pem_password_cb *cb = openssl_password_cb;
+ struct openssl_password_data cb_data = {false, password};
bool private;
+ bool password_supported;
yaca_key_type_e type;
struct yaca_key_evp_s *nk = NULL;
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 && !wrong_pass) {
+ if (pkey == NULL) {
BIO_reset(src);
- pkey = PEM_read_bio_PrivateKey(src, NULL, cb, (void*)password);
+ pkey = PEM_read_bio_PrivateKey(src, NULL, cb, (void*)&cb_data);
if (ERROR_HANDLE() == YACA_ERROR_INVALID_PASSWORD)
- wrong_pass = true;
+ return YACA_ERROR_INVALID_PASSWORD;
private = true;
+ password_supported = true;
}
- if (pkey == NULL && !wrong_pass) {
+ if (pkey == NULL) {
BIO_reset(src);
- pkey = PEM_read_bio_PUBKEY(src, NULL, cb, (void*)password);
- if (ERROR_HANDLE() == YACA_ERROR_INVALID_PASSWORD)
- wrong_pass = true;
+ pkey = PEM_read_bio_PUBKEY(src, NULL, openssl_password_cb_error, NULL);
+ ERROR_CLEAR();
private = false;
+ password_supported = false;
}
- if (pkey == NULL && !wrong_pass) {
+ if (pkey == NULL) {
BIO_reset(src);
- X509 *x509 = PEM_read_bio_X509(src, NULL, cb, (void*)password);
- if (ERROR_HANDLE() == YACA_ERROR_INVALID_PASSWORD)
- wrong_pass = true;
+ X509 *x509 = PEM_read_bio_X509(src, NULL, openssl_password_cb_error, NULL);
if (x509 != NULL) {
pkey = X509_get_pubkey(x509);
X509_free(x509);
- ERROR_CLEAR();
}
+ ERROR_CLEAR();
private = false;
+ password_supported = false;
}
}
/* Possible DER */
else {
- if (pkey == NULL && !wrong_pass) {
+ if (pkey == NULL) {
BIO_reset(src);
- pkey = d2i_PKCS8PrivateKey_bio(src, NULL, cb, (void*)password);
+ pkey = d2i_PKCS8PrivateKey_bio(src, NULL, cb, (void*)&cb_data);
if (ERROR_HANDLE() == YACA_ERROR_INVALID_PASSWORD)
- wrong_pass = true;
+ return YACA_ERROR_INVALID_PASSWORD;
private = true;
+ password_supported = true;
}
- if (pkey == NULL && !wrong_pass) {
+ if (pkey == NULL) {
BIO_reset(src);
pkey = d2i_PrivateKey_bio(src, NULL);
ERROR_CLEAR();
private = true;
+ password_supported = false;
}
- if (pkey == NULL && !wrong_pass) {
+ if (pkey == NULL) {
BIO_reset(src);
pkey = d2i_PUBKEY_bio(src, NULL);
ERROR_CLEAR();
private = false;
+ password_supported = false;
}
}
BIO_free(src);
- if (wrong_pass)
- return YACA_ERROR_INVALID_PASSWORD;
-
if (pkey == NULL)
return YACA_ERROR_INVALID_PARAMETER;
+ /* password was given, but it was not required to perform import */
+ if (password != NULL && !cb_data.password_requested) {
+ if (password_supported)
+ ret = YACA_ERROR_INVALID_PASSWORD;
+ else
+ ret = YACA_ERROR_INVALID_PARAMETER;
+ goto exit;
+ }
+
switch (EVP_PKEY_type(pkey->type)) {
case EVP_PKEY_RSA:
type = private ? YACA_KEY_TYPE_RSA_PRIV : YACA_KEY_TYPE_RSA_PUB;
case YACA_KEY_TYPE_RSA_PUB:
case YACA_KEY_TYPE_DSA_PUB:
+ if (password != NULL)
+ return YACA_ERROR_INVALID_PARAMETER;
ret = PEM_write_bio_PUBKEY(mem, evp_key->evp);
break;
switch (evp_key->key.type) {
case YACA_KEY_TYPE_RSA_PRIV:
+ if (password != NULL)
+ return YACA_ERROR_INVALID_PARAMETER;
ret = i2d_RSAPrivateKey_bio(mem, EVP_PKEY_get0(evp_key->evp));
break;
case YACA_KEY_TYPE_DSA_PRIV:
+ if (password != NULL)
+ return YACA_ERROR_INVALID_PARAMETER;
ret = i2d_DSAPrivateKey_bio(mem, EVP_PKEY_get0(evp_key->evp));
break;
case YACA_KEY_TYPE_RSA_PUB:
case YACA_KEY_TYPE_DSA_PUB:
+ if (password != NULL)
+ return YACA_ERROR_INVALID_PARAMETER;
ret = i2d_PUBKEY_bio(mem, evp_key->evp);
break;
assert(mem != NULL);
int ret;
- int nid = -1;
+ int nid = NID_pbeWithMD5AndDES_CBC;
- if (password != NULL)
- nid = NID_pbeWithMD5AndDES_CBC;
+ /* PKCS8 export requires a password */
+ if (password == NULL)
+ return YACA_ERROR_INVALID_PARAMETER;
switch (key_file_fmt) {
- yaca_key_wrap(), yaca_key_unwrap()
- We need a way to import keys encrypted with hw (or other) keys. New
function like yaca_key_load or sth?
-- Add extended description and examples in documentation.
\ No newline at end of file
+- Add extended description and examples in documentation.
+- Check PKCS8 with PKCS5 2.0 (EVP cipher instead of PBE)