]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/lib/crypto.c
kes If doing a mount, look for a slot, and if specified pass it to
[bacula/bacula] / bacula / src / lib / crypto.c
index f3bcbb8fb0109a8c6413e0d56e9c779c129f93ab..af206985dc53493558904b4c873cc89ae105fda8 100644 (file)
@@ -19,7 +19,7 @@
  * license please contact Landon Fuller <landonf@opendarwin.org>.
  */
 /*
-   Copyright (C) 2005 Kern Sibbald
+   Copyright (C) 2005-2006 Kern Sibbald
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
@@ -176,6 +176,7 @@ ASN1_SEQUENCE(SignatureData) = {
 
 ASN1_SEQUENCE(CryptoData) = {
    ASN1_SIMPLE(CryptoData, version, ASN1_INTEGER),
+   ASN1_SIMPLE(CryptoData, contentEncryptionAlgorithm, ASN1_OBJECT),
    ASN1_SIMPLE(CryptoData, iv, ASN1_OCTET_STRING),
    ASN1_SET_OF(CryptoData, recipientInfo, RecipientInfo)
 } ASN1_SEQUENCE_END(CryptoData);
@@ -269,14 +270,18 @@ struct Signature {
    SignatureData *sigData;
 };
 
-/* Encryption Key Data */
-struct Crypto_Recipients {
+/* Encryption Session Data */
+struct Crypto_Session {
    CryptoData *cryptoData;                        /* ASN.1 Structure */
-   EVP_CIPHER *openssl_cipher;                    /* OpenSSL Cipher Object */
-   unsigned char session_key[EVP_MAX_KEY_LENGTH]; /* Private symmetric session key */
+   unsigned char *session_key;                    /* Private symmetric session key */
    size_t session_key_len;                        /* Symmetric session key length */
 };
 
+/* Symmetric Cipher Context */
+struct Cipher_Context {
+   EVP_CIPHER_CTX ctx;
+};
+
 /* PEM Password Dispatch Context */
 typedef struct PEM_CB_Context {
    CRYPTO_PEM_PASSWD_CB *pem_callback;
@@ -444,6 +449,7 @@ int crypto_keypair_load_cert (X509_KEYPAIR *keypair, const char *file)
        goto err;
    }
 
+   X509_free(cert);
    return true;
 
 err:
@@ -461,6 +467,55 @@ static int crypto_pem_callback_dispatch (char *buf, int size, int rwflag, void *
    return (ctx->pem_callback(buf, size, ctx->pem_userdata));
 }
 
+/*
+ * Check a PEM-encoded file
+ * for the existence of a private key.
+ * Returns: true if a private key is found
+ *          false otherwise
+ */
+bool crypto_keypair_has_key (const char *file) {
+   BIO *bio;
+   char *name = NULL;
+   char *header = NULL;
+   unsigned char *data = NULL;
+   bool retval = false;
+   long len;
+
+   if (!(bio = BIO_new_file(file, "r"))) {
+      openssl_post_errors(M_ERROR, _("Unable to open private key file"));
+      return false;
+   }
+
+   while (PEM_read_bio(bio, &name, &header, &data, &len)) {
+      /* We don't care what the data is, just that it's there */
+      OPENSSL_free(header);
+      OPENSSL_free(data);
+
+      /*
+       * PEM Header Found, check for a private key
+       * Due to OpenSSL limitations, we must specifically
+       * list supported PEM private key encodings.
+       */
+      if (strcmp(name, PEM_STRING_RSA) == 0
+            || strcmp(name, PEM_STRING_DSA) == 0
+            || strcmp(name, PEM_STRING_PKCS8) == 0
+            || strcmp(name, PEM_STRING_PKCS8INF) == 0) {
+         retval = true;
+         OPENSSL_free(name);
+         break;
+      } else {
+         OPENSSL_free(name);
+      }
+   }
+
+   /* Free our bio */
+   BIO_free(bio);
+
+   /* Post PEM-decoding error messages, if any */
+   openssl_post_errors(M_ERROR, _("Unable to read private key from file"));
+   return retval;
+}
+
 /*
  * Load a PEM-encoded private key.
  *  Returns: true on success
@@ -525,7 +580,7 @@ DIGEST *crypto_digest_new (crypto_digest_t type)
    DIGEST *digest;
    const EVP_MD *md = NULL; /* Quell invalid uninitialized warnings */
 
-   digest = (DIGEST *) malloc(sizeof(DIGEST));
+   digest = (DIGEST *)malloc(sizeof(DIGEST));
    digest->type = type;
 
    /* Initialize the OpenSSL message digest context */
@@ -571,7 +626,8 @@ err:
  * Returns: true on success
  *          false on failure
  */
-bool crypto_digest_update (DIGEST *digest, const void *data, size_t length) {
+bool crypto_digest_update(DIGEST *digest, const uint8_t *data, uint32_t length)
+{
    if (EVP_DigestUpdate(&digest->ctx, data, length) == 0) {
       return true;
    } else { 
@@ -586,8 +642,8 @@ bool crypto_digest_update (DIGEST *digest, const void *data, size_t length) {
  * Returns: true on success
  *          false on failure
  */
-bool crypto_digest_finalize (DIGEST *digest, void *dest, size_t *length) {
-   if (!EVP_DigestFinal(&digest->ctx, (unsigned char *) dest, length)) {
+bool crypto_digest_finalize (DIGEST *digest, uint8_t *dest, uint32_t *length) {
+   if (!EVP_DigestFinal(&digest->ctx, dest, (unsigned int *)length)) {
       return false;
    } else {
       return true;
@@ -819,14 +875,14 @@ err:
  * Returns: true on success, stores the encoded data in dest, and the size in length.
  *          false on failure.
  */
-int crypto_sign_encode(SIGNATURE *sig, void *dest, size_t *length)
+int crypto_sign_encode(SIGNATURE *sig, uint8_t *dest, uint32_t *length)
 {
    if (*length == 0) {
       *length = i2d_SignatureData(sig->sigData, NULL);
       return true;
    }
 
-   *length = i2d_SignatureData(sig->sigData, (unsigned char **) &dest);
+   *length = i2d_SignatureData(sig->sigData, (unsigned char **)&dest);
    return true;
 }
 
@@ -839,16 +895,16 @@ int crypto_sign_encode(SIGNATURE *sig, void *dest, size_t *length)
 
  */
 
-SIGNATURE *crypto_sign_decode(const void *sigData, size_t length)
+SIGNATURE *crypto_sign_decode(const uint8_t *sigData, uint32_t length)
 {
    SIGNATURE *sig;
 #if (OPENSSL_VERSION_NUMBER >= 0x0090800FL)
    const unsigned char *p = (const unsigned char *) sigData;
 #else
-   unsigned char *p = (unsigned char *) sigData;
+   unsigned char *p = (unsigned char *)sigData;
 #endif
 
-   sig = (SIGNATURE *) malloc(sizeof(SIGNATURE));
+   sig = (SIGNATURE *)malloc(sizeof(SIGNATURE));
    if (!sig) {
       return NULL;
    }
@@ -859,6 +915,7 @@ SIGNATURE *crypto_sign_decode(const void *sigData, size_t length)
    if (!sig->sigData) {
       /* Allocation / Decoding failed in OpenSSL */
       openssl_post_errors(M_ERROR, _("Signature decoding failed"));
+      free(sig);
       return NULL;
    }
 
@@ -875,34 +932,38 @@ void crypto_sign_free(SIGNATURE *sig)
 }
 
 /*
- * Create a new encryption recipient.
- *  Returns: A pointer to a CRYPTO_RECIPIENTS object on success.
+ * Create a new encryption session.
+ *  Returns: A pointer to a CRYPTO_SESSION object on success.
  *           NULL on failure.
  */
-CRYPTO_RECIPIENTS *crypto_recipients_new (crypto_cipher_t cipher, alist *pubkeys)
+CRYPTO_SESSION *crypto_session_new (crypto_cipher_t cipher, alist *pubkeys)
 {
-   CRYPTO_RECIPIENTS *cr;
+   CRYPTO_SESSION *cs;
    X509_KEYPAIR *keypair;
    const EVP_CIPHER *ec;
    unsigned char *iv;
    int iv_len;
 
-   /* Allocate our recipient description structures */
-   cr = (CRYPTO_RECIPIENTS *) malloc(sizeof(CRYPTO_RECIPIENTS));
-   if (!cr) {
+   /* Allocate our session description structures */
+   cs = (CRYPTO_SESSION *) malloc(sizeof(CRYPTO_SESSION));
+   if (!cs) {
       return NULL;
    }
 
-   cr->cryptoData = CryptoData_new();
+   /* Initialize required fields */
+   cs->session_key = NULL;
+
+   /* Allocate a CryptoData structure */
+   cs->cryptoData = CryptoData_new();
 
-   if (!cr->cryptoData) {
+   if (!cs->cryptoData) {
       /* Allocation failed in OpenSSL */
-      free(cr);
+      free(cs);
       return NULL;
    }
 
    /* Set the ASN.1 structure version number */
-   ASN1_INTEGER_set(cr->cryptoData->version, BACULA_ASN1_VERSION);
+   ASN1_INTEGER_set(cs->cryptoData->version, BACULA_ASN1_VERSION);
 
    /*
     * Acquire a cipher instance and set the ASN.1 cipher NID
@@ -910,35 +971,36 @@ CRYPTO_RECIPIENTS *crypto_recipients_new (crypto_cipher_t cipher, alist *pubkeys
    switch (cipher) {
    case CRYPTO_CIPHER_AES_128_CBC:
       /* AES 128 bit CBC */
-      cr->cryptoData->contentEncryptionAlgorithm = OBJ_nid2obj(NID_aes_128_cbc);
+      cs->cryptoData->contentEncryptionAlgorithm = OBJ_nid2obj(NID_aes_128_cbc);
       ec = EVP_aes_128_cbc();
       break;
    case CRYPTO_CIPHER_AES_192_CBC:
       /* AES 192 bit CBC */
-      cr->cryptoData->contentEncryptionAlgorithm = OBJ_nid2obj(NID_aes_192_cbc);
+      cs->cryptoData->contentEncryptionAlgorithm = OBJ_nid2obj(NID_aes_192_cbc);
       ec = EVP_aes_192_cbc();
       break;
    case CRYPTO_CIPHER_AES_256_CBC:
       /* AES 256 bit CBC */
-      cr->cryptoData->contentEncryptionAlgorithm = OBJ_nid2obj(NID_aes_256_cbc);
+      cs->cryptoData->contentEncryptionAlgorithm = OBJ_nid2obj(NID_aes_256_cbc);
       ec = EVP_aes_256_cbc();
       break;
    case CRYPTO_CIPHER_BLOWFISH_CBC:
       /* Blowfish CBC */
-      cr->cryptoData->contentEncryptionAlgorithm = OBJ_nid2obj(NID_bf_cbc);
+      cs->cryptoData->contentEncryptionAlgorithm = OBJ_nid2obj(NID_bf_cbc);
       ec = EVP_bf_cbc();
       break;
    default:
       Emsg0(M_ERROR, 0, _("Unsupported cipher type specified\n"));
-      crypto_recipients_free(cr);
+      crypto_session_free(cs);
       return NULL;
    }
 
    /* Generate a symmetric session key */
-   cr->session_key_len = EVP_CIPHER_key_length(ec);
-   if (RAND_bytes(cr->session_key, cr->session_key_len) <= 0) {
+   cs->session_key_len = EVP_CIPHER_key_length(ec);
+   cs->session_key = (unsigned char *) malloc(cs->session_key_len);
+   if (RAND_bytes(cs->session_key, cs->session_key_len) <= 0) {
       /* OpenSSL failure */
-      crypto_recipients_free(cr);
+      crypto_session_free(cs);
       return NULL;
    }
 
@@ -947,23 +1009,26 @@ CRYPTO_RECIPIENTS *crypto_recipients_new (crypto_cipher_t cipher, alist *pubkeys
       iv = (unsigned char *) malloc(iv_len);
       if (!iv) {
          /* Malloc failure */
-         crypto_recipients_free(cr);
+         crypto_session_free(cs);
          return NULL;
       }
 
       /* Generate random IV */
       if (RAND_bytes(iv, iv_len) <= 0) {
          /* OpenSSL failure */
-         crypto_recipients_free(cr);
+         crypto_session_free(cs);
+         free(iv);
          return NULL;
       }
 
       /* Store it in our ASN.1 structure */
-      if (!M_ASN1_OCTET_STRING_set(cr->cryptoData->iv, iv, iv_len)) {
+      if (!M_ASN1_OCTET_STRING_set(cs->cryptoData->iv, iv, iv_len)) {
          /* Allocation failed in OpenSSL */
-         crypto_recipients_free(cr);
+         crypto_session_free(cs);
+         free(iv);
          return NULL;
       }
+      free(iv);
    }
 
    /*
@@ -978,7 +1043,7 @@ CRYPTO_RECIPIENTS *crypto_recipients_new (crypto_cipher_t cipher, alist *pubkeys
       ri = RecipientInfo_new();
       if (!ri) {
          /* Allocation failed in OpenSSL */
-         crypto_recipients_free(cr);
+         crypto_session_free(cs);
          return NULL;
       }
 
@@ -997,14 +1062,14 @@ CRYPTO_RECIPIENTS *crypto_recipients_new (crypto_cipher_t cipher, alist *pubkeys
       ekey = (unsigned char *) malloc(EVP_PKEY_size(keypair->pubkey));
       if (!ekey) {
          RecipientInfo_free(ri);
-         crypto_recipients_free(cr);
+         crypto_session_free(cs);
          return NULL;
       }
 
-      if ((ekey_len = EVP_PKEY_encrypt(ekey, cr->session_key, cr->session_key_len, keypair->pubkey)) <= 0) {
+      if ((ekey_len = EVP_PKEY_encrypt(ekey, cs->session_key, cs->session_key_len, keypair->pubkey)) <= 0) {
          /* OpenSSL failure */
          RecipientInfo_free(ri);
-         crypto_recipients_free(cr);
+         crypto_session_free(cs);
          free(ekey);
          return NULL;
       }
@@ -1013,7 +1078,7 @@ CRYPTO_RECIPIENTS *crypto_recipients_new (crypto_cipher_t cipher, alist *pubkeys
       if (!M_ASN1_OCTET_STRING_set(ri->encryptedKey, ekey, ekey_len)) {
          /* Allocation failed in OpenSSL */
          RecipientInfo_free(ri);
-         crypto_recipients_free(cr);
+         crypto_session_free(cs);
          free(ekey);
          return NULL;
       }
@@ -1022,21 +1087,260 @@ CRYPTO_RECIPIENTS *crypto_recipients_new (crypto_cipher_t cipher, alist *pubkeys
       free(ekey);
 
       /* Push the new RecipientInfo structure onto the stack */
-      sk_RecipientInfo_push(cr->cryptoData->recipientInfo, ri);
+      sk_RecipientInfo_push(cs->cryptoData->recipientInfo, ri);
+   }
+
+   return cs;
+}
+
+/*
+ * Encodes the CryptoData structure. The length argument is used to specify the
+ * size of dest. A length of 0 will cause no data to be written to dest, and the
+ * required length to be written to length. The caller can then allocate sufficient
+ * space for the output.
+ *
+ * Returns: true on success, stores the encoded data in dest, and the size in length.
+ *          false on failure.
+ */
+bool crypto_session_encode(CRYPTO_SESSION *cs, uint8_t *dest, uint32_t *length)
+{
+   if (*length == 0) {
+      *length = i2d_CryptoData(cs->cryptoData, NULL);
+      return true;
+   }
+
+   *length = i2d_CryptoData(cs->cryptoData, &dest);
+   return true;
+}
+
+/*
+ * Decodes the CryptoData structure. The length argument is
+ * used to specify the size of data.
+ *
+ * Returns: CRYPTO_SESSION instance on success.
+ *          NULL on failure.
+ * Returns: CRYPTO_ERROR_NONE and a pointer to a newly allocated CRYPTO_SESSION structure in *session on success.
+ *          A crypto_error_t value on failure.
+ */
+crypto_error_t crypto_session_decode(const uint8_t *data, uint32_t length, alist *keypairs, CRYPTO_SESSION **session)
+{
+   CRYPTO_SESSION *cs;
+   X509_KEYPAIR *keypair;
+   STACK_OF(RecipientInfo) *recipients;
+   crypto_error_t retval = CRYPTO_ERROR_NONE;
+#if (OPENSSL_VERSION_NUMBER >= 0x0090800FL)
+   const unsigned char *p = (const unsigned char *)data;
+#else
+   unsigned char *p = (unsigned char *)data;
+#endif
+
+   /* bacula-fd.conf doesn't contains any key */
+   if (!keypairs) {
+      return CRYPTO_ERROR_NORECIPIENT;
+   }
+
+   cs = (CRYPTO_SESSION *) malloc(sizeof(CRYPTO_SESSION));
+   if (!cs) {
+      return CRYPTO_ERROR_INTERNAL;
+   }
+
+   /* Initialize required fields */
+   cs->session_key = NULL;
+
+   /* d2i_CryptoData modifies the supplied pointer */
+   cs->cryptoData = d2i_CryptoData(NULL, &p, length);
+
+   if (!cs->cryptoData) {
+      /* Allocation / Decoding failed in OpenSSL */
+      openssl_post_errors(M_ERROR, _("CryptoData decoding failed"));
+      retval = CRYPTO_ERROR_INTERNAL;
+      goto err;
+   }
+
+   recipients = cs->cryptoData->recipientInfo;
+
+   /*
+    * Find a matching RecipientInfo structure for a supplied
+    * public key
+    */
+   foreach_alist(keypair, keypairs) {
+      RecipientInfo *ri;
+      int i;
+
+      /* Private key available? */
+      if (keypair->privkey == NULL) {
+         continue;
+      }
+
+      for (i = 0; i < sk_RecipientInfo_num(recipients); i++) {
+         ri = sk_RecipientInfo_value(recipients, i);
+
+         /* Match against the subjectKeyIdentifier */
+         if (M_ASN1_OCTET_STRING_cmp(keypair->keyid, ri->subjectKeyIdentifier) == 0) {
+            /* Match found, extract symmetric encryption session data */
+            
+            /* RSA is required. */
+            assert(EVP_PKEY_type(keypair->privkey->type) == EVP_PKEY_RSA);
+
+            /* If we recieve a RecipientInfo structure that does not use
+             * RSA, return an error */
+            if (OBJ_obj2nid(ri->keyEncryptionAlgorithm) != NID_rsaEncryption) {
+               retval = CRYPTO_ERROR_INVALID_CRYPTO;
+               goto err;
+            }
+
+            /* Decrypt the session key */
+            /* Allocate sufficient space for the largest possible decrypted data */
+            cs->session_key = (unsigned char *) malloc(EVP_PKEY_size(keypair->privkey));
+            cs->session_key_len = EVP_PKEY_decrypt(cs->session_key, M_ASN1_STRING_data(ri->encryptedKey),
+                                  M_ASN1_STRING_length(ri->encryptedKey), keypair->privkey);
+
+            if (cs->session_key_len <= 0) {
+               openssl_post_errors(M_ERROR, _("Failure decrypting the session key"));
+               retval = CRYPTO_ERROR_DECRYPTION;
+               goto err;
+            }
+
+            /* Session key successfully extracted, return the CRYPTO_SESSION structure */
+            *session = cs;
+            return CRYPTO_ERROR_NONE;
+         }
+      }
+   }
+
+   /* No matching recipient found */
+   return CRYPTO_ERROR_NORECIPIENT;
+
+err:
+   crypto_session_free(cs);
+   return retval;
+}
+
+/*
+ * Free memory associated with a crypto session object.
+ */
+void crypto_session_free (CRYPTO_SESSION *cs)
+{
+   if (cs->cryptoData) {
+      CryptoData_free(cs->cryptoData);
+   }
+   if (cs->session_key){
+      free(cs->session_key);
+   }
+   free(cs);
+}
+
+/*
+ * Create a new crypto cipher context with the specified session object
+ *  Returns: A pointer to a CIPHER_CONTEXT object on success. The cipher block size is returned in blocksize.
+ *           NULL on failure.
+ */
+CIPHER_CONTEXT *crypto_cipher_new (CRYPTO_SESSION *cs, bool encrypt, uint32_t *blocksize)
+{
+   CIPHER_CONTEXT *cipher_ctx;
+   const EVP_CIPHER *ec;
+
+   cipher_ctx = (CIPHER_CONTEXT *) malloc(sizeof(CIPHER_CONTEXT));
+   if (!cipher_ctx) {
+      return NULL;
+   }
+
+   /*
+    * Acquire a cipher instance for the given ASN.1 cipher NID
+    */
+   if ((ec = EVP_get_cipherbyobj(cs->cryptoData->contentEncryptionAlgorithm)) == NULL) {
+      Emsg1(M_ERROR, 0, _("Unsupported contentEncryptionAlgorithm: %d\n"), OBJ_obj2nid(cs->cryptoData->contentEncryptionAlgorithm));
+      free(cipher_ctx);
+      return NULL;
+   }
+
+   /* Initialize the OpenSSL cipher context */
+   EVP_CIPHER_CTX_init(&cipher_ctx->ctx);
+   if (encrypt) {
+      /* Initialize for encryption */
+      if (!EVP_CipherInit_ex(&cipher_ctx->ctx, ec, NULL, NULL, NULL, 1)) {
+         openssl_post_errors(M_ERROR, _("OpenSSL cipher context initialization failed"));
+         goto err;
+      }
+   } else {
+      /* Initialize for decryption */
+      if (!EVP_CipherInit_ex(&cipher_ctx->ctx, ec, NULL, NULL, NULL, 0)) {
+         openssl_post_errors(M_ERROR, _("OpenSSL cipher context initialization failed"));
+         goto err;
+      }
+   }
+
+   /* Set the key size */
+   if (!EVP_CIPHER_CTX_set_key_length(&cipher_ctx->ctx, cs->session_key_len)) {
+      openssl_post_errors(M_ERROR, _("Encryption session provided an invalid symmetric key"));
+      goto err;
+   }
+
+   /* Validate the IV length */
+   if (EVP_CIPHER_iv_length(ec) != M_ASN1_STRING_length(cs->cryptoData->iv)) {
+      openssl_post_errors(M_ERROR, _("Encryption session provided an invalid IV"));
+      goto err;
    }
+   
+   /* Add the key and IV to the cipher context */
+   if (!EVP_CipherInit_ex(&cipher_ctx->ctx, NULL, NULL, cs->session_key, M_ASN1_STRING_data(cs->cryptoData->iv), -1)) {
+      openssl_post_errors(M_ERROR, _("OpenSSL cipher context key/IV initialization failed"));
+      goto err;
+   }
+
+   *blocksize = EVP_CIPHER_CTX_block_size(&cipher_ctx->ctx);
+   return cipher_ctx;
 
-   return cr;
+err:
+   crypto_cipher_free(cipher_ctx);
+   return NULL;
 }
 
+
 /*
- * Free memory associated with a crypto recipient object.
+ * Encrypt/Decrypt length bytes of data using the provided cipher context
+ * Returns: true on success, number of bytes output in written
+ *          false on failure
  */
-void crypto_recipients_free (CRYPTO_RECIPIENTS *cr)
+bool crypto_cipher_update(CIPHER_CONTEXT *cipher_ctx, const uint8_t *data, uint32_t length, const uint8_t *dest, uint32_t *written)
 {
-   CryptoData_free(cr->cryptoData);
-   free(cr);
+   if (!EVP_CipherUpdate(&cipher_ctx->ctx, (unsigned char *)dest, (int *)written, (const unsigned char *)data, length)) {
+      /* This really shouldn't fail */
+      return false;
+   } else {
+      return true;
+   }
 }
 
+/*
+ * Finalize the cipher context, writing any remaining data and necessary padding
+ * to dest, and the size in written.
+ * The result size will either be one block of data or zero.
+ *
+ * Returns: true on success
+ *          false on failure
+ */
+bool crypto_cipher_finalize (CIPHER_CONTEXT *cipher_ctx, uint8_t *dest, uint32_t *written)
+{
+   if (!EVP_CipherFinal_ex(&cipher_ctx->ctx, (unsigned char *)dest, (int *) written)) {
+      /* This really shouldn't fail */
+      return false;
+   } else {
+      return true;
+   }
+}
+
+
+/*
+ * Free memory associated with a cipher context.
+ */
+void crypto_cipher_free (CIPHER_CONTEXT *cipher_ctx)
+{
+   EVP_CIPHER_CTX_cleanup(&cipher_ctx->ctx);
+   free (cipher_ctx);
+}
+
+
 /*
  * Perform global initialization of OpenSSL
  * This function is not thread safe.
@@ -1054,9 +1358,12 @@ int init_crypto (void)
    /* Load libssl and libcrypto human-readable error strings */
    SSL_load_error_strings();
 
-   /* Register OpenSSL ciphers */
+   /* Initialize OpenSSL SSL  library */
    SSL_library_init();
 
+   /* Register OpenSSL ciphers and digests */
+   OpenSSL_add_all_algorithms();
+
    if (!openssl_seed_prng()) {
       Emsg0(M_ERROR_TERM, 0, _("Failed to seed OpenSSL PRNG\n"));
    }
@@ -1092,6 +1399,9 @@ int cleanup_crypto (void)
    /* Free libssl and libcrypto error strings */
    ERR_free_strings();
 
+   /* Free all ciphers and digests */
+   EVP_cleanup();
+
    /* Free memory used by PRNG */
    RAND_cleanup();
 
@@ -1128,7 +1438,7 @@ DIGEST *crypto_digest_new (crypto_digest_t type)
 {
    DIGEST *digest;
 
-   digest = (DIGEST *) malloc(sizeof(DIGEST));
+   digest = (DIGEST *)malloc(sizeof(DIGEST));
    digest->type = type;
 
    switch (type) {
@@ -1147,7 +1457,8 @@ DIGEST *crypto_digest_new (crypto_digest_t type)
    return (digest);
 }
 
-bool crypto_digest_update (DIGEST *digest, const void *data, size_t length) {
+bool crypto_digest_update(DIGEST *digest, const uint8_t *data, uint32_t length)
+{
    switch (digest->type) {
    case CRYPTO_DIGEST_MD5:
       /* Doesn't return anything ... */
@@ -1167,8 +1478,8 @@ bool crypto_digest_update (DIGEST *digest, const void *data, size_t length) {
    }
 }
 
-bool crypto_digest_finalize (DIGEST *digest, void *dest, size_t *length) {
-
+bool crypto_digest_finalize(DIGEST *digest, uint8_t *dest, uint32_t *length) 
+{
    switch (digest->type) {
    case CRYPTO_DIGEST_MD5:
       /* Guard against programmer error by either the API client or
@@ -1176,7 +1487,7 @@ bool crypto_digest_finalize (DIGEST *digest, void *dest, size_t *length) {
       assert(*length >= CRYPTO_DIGEST_MD5_SIZE);
       *length = CRYPTO_DIGEST_MD5_SIZE;
       /* Doesn't return anything ... */
-      MD5Final((unsigned char *) dest, &digest->md5);
+      MD5Final((unsigned char *)dest, &digest->md5);
       return true;
    case CRYPTO_DIGEST_SHA1:
       /* Guard against programmer error by either the API client or
@@ -1196,7 +1507,7 @@ bool crypto_digest_finalize (DIGEST *digest, void *dest, size_t *length) {
    return false;
 }
 
-void crypto_digest_free (DIGEST *digest)
+void crypto_digest_free(DIGEST *digest)
 {
    free (digest);
 }
@@ -1211,20 +1522,28 @@ crypto_error_t crypto_sign_get_digest (SIGNATURE *sig, X509_KEYPAIR *keypair, DI
 crypto_error_t crypto_sign_verify (SIGNATURE *sig, X509_KEYPAIR *keypair, DIGEST *digest) { return CRYPTO_ERROR_INTERNAL; }
 
 int crypto_sign_add_signer (SIGNATURE *sig, DIGEST *digest, X509_KEYPAIR *keypair) { return false; }
-int crypto_sign_encode (SIGNATURE *sig, void *dest, size_t *length) { return false; }
+int crypto_sign_encode (SIGNATURE *sig, uint8_t *dest, uint32_t *length) { return false; }
 
-SIGNATURE *crypto_sign_decode (const void *sigData, size_t length) { return false; }
+SIGNATURE *crypto_sign_decode (const uint8_t *sigData, uint32_t length) { return NULL; }
 void crypto_sign_free (SIGNATURE *sig) { }
 
 
 X509_KEYPAIR *crypto_keypair_new (void) { return NULL; }
 X509_KEYPAIR *crypto_keypair_dup (X509_KEYPAIR *keypair) { return NULL; }
 int crypto_keypair_load_cert (X509_KEYPAIR *keypair, const char *file) { return false; }
+bool crypto_keypair_has_key (const char *file) { return false; }
 int crypto_keypair_load_key (X509_KEYPAIR *keypair, const char *file, CRYPTO_PEM_PASSWD_CB *pem_callback, const void *pem_userdata) { return false; }
 void crypto_keypair_free (X509_KEYPAIR *keypair) { }
 
-CRYPTO_RECIPIENTS *crypto_recipients_new (crypto_cipher_t cipher, alist *pubkeys) { return NULL; }
-void crypto_recipients_free (CRYPTO_RECIPIENTS *cr) { }
+CRYPTO_SESSION *crypto_session_new (crypto_cipher_t cipher, alist *pubkeys) { return NULL; }
+void crypto_session_free (CRYPTO_SESSION *cs) { }
+bool crypto_session_encode (CRYPTO_SESSION *cs, uint8_t *dest, uint32_t *length) { return false; }
+crypto_error_t crypto_session_decode(const uint8_t *data, uint32_t length, alist *keypairs, CRYPTO_SESSION **session) { return CRYPTO_ERROR_INTERNAL; }
+
+CIPHER_CONTEXT *crypto_cipher_new (CRYPTO_SESSION *cs, bool encrypt, uint32_t *blocksize) { return NULL; }
+bool crypto_cipher_update (CIPHER_CONTEXT *cipher_ctx, const uint8_t *data, uint32_t length, const uint8_t *dest, uint32_t *written) { return false; }
+bool crypto_cipher_finalize (CIPHER_CONTEXT *cipher_ctx, uint8_t *dest, uint32_t *written) { return false; }
+void crypto_cipher_free (CIPHER_CONTEXT *cipher_ctx) { }
 
 #endif /* HAVE_CRYPTO */
 
@@ -1291,10 +1610,16 @@ const char *crypto_strerror(crypto_error_t error) {
       return "No error";
    case CRYPTO_ERROR_NOSIGNER:
       return "Signer not found";
+   case CRYPTO_ERROR_NORECIPIENT:
+      return "Recipient not found";
    case CRYPTO_ERROR_INVALID_DIGEST:
       return "Unsupported digest algorithm";
+   case CRYPTO_ERROR_INVALID_CRYPTO:
+      return "Unsupported encryption algorithm";
    case CRYPTO_ERROR_BAD_SIGNATURE:
       return "Signature is invalid";
+   case CRYPTO_ERROR_DECRYPTION:
+      return "Decryption error";
    case CRYPTO_ERROR_INTERNAL:
       /* This shouldn't happen */
       return "Internal error";