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 */
};
goto err;
}
+ X509_free(cert);
return true;
err:
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
}
/*
- * 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;
- if (!cr->cryptoData) {
+ /* Allocate a CryptoData structure */
+ cs->cryptoData = CryptoData_new();
+
+ 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
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;
}
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);
}
/*
ri = RecipientInfo_new();
if (!ri) {
/* Allocation failed in OpenSSL */
- crypto_recipients_free(cr);
+ crypto_session_free(cs);
return NULL;
}
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;
}
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;
}
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 cr;
+ return cs;
}
/*
- * Free memory associated with a crypto recipient object.
+ * 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.
*/
-void crypto_recipients_free (CRYPTO_RECIPIENTS *cr)
+bool crypto_session_encode(CRYPTO_SESSION *cs, void *dest, size_t *length)
{
- CryptoData_free(cr->cryptoData);
- free(cr);
+ if (*length == 0) {
+ *length = i2d_CryptoData(cs->cryptoData, NULL);
+ return true;
+ }
+
+ *length = i2d_CryptoData(cs->cryptoData, (unsigned char **) &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 void *data, size_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
+
+ cs = (CRYPTO_SESSION *) malloc(sizeof(CRYPTO_SESSION));
+ if (!cs) {
+ return CRYPTO_ERROR_INTERNAL;
+ }
+
+ /* 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);
}
/*
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; }
-SIGNATURE *crypto_sign_decode (const void *sigData, size_t length) { return false; }
+SIGNATURE *crypto_sign_decode (const void *sigData, size_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, void *dest, size_t *length) { return false; }
+crypto_error_t crypto_session_decode(const void *data, size_t length, alist *keypairs, CRYPTO_SESSION **session) { return CRYPTO_ERROR_INTERNAL; }
#endif /* HAVE_CRYPTO */
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";