#include <openssl/err.h>
#include <openssl/cms.h>
#include <openssl/aes.h>
-#include "cms_lcl.h"
+#include "cms_local.h"
#include "crypto/asn1.h"
/* Key Agreement Recipient Info (KARI) routines */
return -1;
}
-int CMS_RecipientInfo_kari_set0_pkey(CMS_RecipientInfo *ri, EVP_PKEY *pk)
+int CMS_RecipientInfo_kari_set0_pkey_and_peer(CMS_RecipientInfo *ri, EVP_PKEY *pk, X509 *peer)
{
EVP_PKEY_CTX *pctx;
CMS_KeyAgreeRecipientInfo *kari = ri->d.kari;
EVP_PKEY_CTX_free(kari->pctx);
kari->pctx = NULL;
- if (!pk)
+ if (pk == NULL)
return 1;
+
pctx = EVP_PKEY_CTX_new(pk, NULL);
- if (!pctx || !EVP_PKEY_derive_init(pctx))
+ if (pctx == NULL || EVP_PKEY_derive_init(pctx) <= 0)
goto err;
+
+ if (peer != NULL) {
+ EVP_PKEY *pub_pkey = X509_get0_pubkey(peer);
+
+ if (EVP_PKEY_derive_set_peer(pctx, pub_pkey) <= 0)
+ goto err;
+ }
+
kari->pctx = pctx;
return 1;
err:
return 0;
}
+int CMS_RecipientInfo_kari_set0_pkey(CMS_RecipientInfo *ri, EVP_PKEY *pk)
+{
+ return CMS_RecipientInfo_kari_set0_pkey_and_peer(ri, pk, NULL);
+}
+
EVP_CIPHER_CTX *CMS_RecipientInfo_kari_get0_ctx(CMS_RecipientInfo *ri)
{
if (ri->type == CMS_RECIPINFO_AGREE)
size_t enckeylen;
size_t ceklen;
CMS_EncryptedContentInfo *ec;
+
+ {
+ /*
+ * TODO(3.0) Remove this when we have functionality to deserialize
+ * parameters in EVP_PKEY form from an X509_ALGOR.
+ * This is needed to be able to replace the EC_KEY specific decoding
+ * that happens in ecdh_cms_set_peerkey() (crypto/ec/ec_ameth.c)
+ *
+ * THIS IS TEMPORARY
+ */
+ EVP_PKEY_CTX *pctx = CMS_RecipientInfo_get0_pkey_ctx(ri);
+ EVP_PKEY *pkey = EVP_PKEY_CTX_get0_pkey(pctx);
+
+ EVP_PKEY_get0(pkey);
+ if (EVP_PKEY_id(pkey) == EVP_PKEY_NONE) {
+ CMSerr(CMS_F_CMS_RECIPIENTINFO_KARI_DECRYPT,
+ CMS_R_NOT_SUPPORTED_FOR_THIS_KEY_TYPE);
+ goto err;
+ }
+ }
+
enckeylen = rek->encryptedKey->length;
enckey = rek->encryptedKey->data;
/* Setup all parameters to derive KEK */
EVP_PKEY_CTX *pctx = NULL;
EVP_PKEY *ekey = NULL;
int rv = 0;
+
pctx = EVP_PKEY_CTX_new(pk, NULL);
- if (!pctx)
+ if (pctx == NULL)
goto err;
if (EVP_PKEY_keygen_init(pctx) <= 0)
goto err;
goto err;
EVP_PKEY_CTX_free(pctx);
pctx = EVP_PKEY_CTX_new(ekey, NULL);
- if (!pctx)
+ if (pctx == NULL)
goto err;
if (EVP_PKEY_derive_init(pctx) <= 0)
goto err;
return rv;
}
+/* Set originator private key and initialise context based on it */
+static int cms_kari_set_originator_private_key(CMS_KeyAgreeRecipientInfo *kari, EVP_PKEY *originatorPrivKey )
+{
+ EVP_PKEY_CTX *pctx = NULL;
+ int rv = 0;
+
+ pctx = EVP_PKEY_CTX_new(originatorPrivKey, NULL);
+ if (pctx == NULL)
+ goto err;
+ if (EVP_PKEY_derive_init(pctx) <= 0)
+ goto err;
+
+ kari->pctx = pctx;
+ rv = 1;
+ err:
+ if (rv == 0)
+ EVP_PKEY_CTX_free(pctx);
+ return rv;
+}
+
/* Initialise a kari based on passed certificate and key */
-int cms_RecipientInfo_kari_init(CMS_RecipientInfo *ri, X509 *recip,
- EVP_PKEY *pk, unsigned int flags)
+int cms_RecipientInfo_kari_init(CMS_RecipientInfo *ri, X509 *recip, EVP_PKEY *recipPubKey, X509 * originator, EVP_PKEY *originatorPrivKey, unsigned int flags)
{
CMS_KeyAgreeRecipientInfo *kari;
CMS_RecipientEncryptedKey *rek = NULL;
return 0;
}
- /* Create ephemeral key */
- if (!cms_kari_create_ephemeral_key(kari, pk))
- return 0;
+ if (originatorPrivKey == NULL && originator == NULL) {
+ /* Create ephemeral key */
+ if (!cms_kari_create_ephemeral_key(kari, recipPubKey))
+ return 0;
+ } else {
+ /* Use originator key */
+ CMS_OriginatorIdentifierOrKey *oik = ri->d.kari->originator;
- EVP_PKEY_up_ref(pk);
- rek->pkey = pk;
+ if (originatorPrivKey == NULL && originator == NULL)
+ return 0;
+
+ if (flags & CMS_USE_ORIGINATOR_KEYID) {
+ oik->type = CMS_OIK_KEYIDENTIFIER;
+ oik->d.subjectKeyIdentifier = ASN1_OCTET_STRING_new();
+ if (oik->d.subjectKeyIdentifier == NULL)
+ return 0;
+ if (!cms_set1_keyid(&oik->d.subjectKeyIdentifier, originator))
+ return 0;
+ } else {
+ oik->type = CMS_REK_ISSUER_SERIAL;
+ if (!cms_set1_ias(&oik->d.issuerAndSerialNumber, originator))
+ return 0;
+ }
+
+ if (!cms_kari_set_originator_private_key(kari, originatorPrivKey))
+ return 0;
+ }
+
+ EVP_PKEY_up_ref(recipPubKey);
+ rek->pkey = recipPubKey;
return 1;
}
EVP_CIPHER_CTX *ctx = kari->ctx;
const EVP_CIPHER *kekcipher;
int keylen = EVP_CIPHER_key_length(cipher);
+ int ret;
+
/* If a suitable wrap algorithm is already set nothing to do */
kekcipher = EVP_CIPHER_CTX_cipher(ctx);
-
- if (kekcipher) {
+ if (kekcipher != NULL) {
if (EVP_CIPHER_CTX_mode(ctx) != EVP_CIPH_WRAP_MODE)
return 0;
return 1;
}
+ else if (cipher != NULL
+ && (EVP_CIPHER_flags(cipher) & EVP_CIPH_FLAG_GET_WRAP_CIPHER)) {
+ ret = EVP_CIPHER_meth_get_ctrl(cipher)(NULL, EVP_CTRL_GET_WRAP_CIPHER,
+ 0, &kekcipher);
+ if (ret <= 0)
+ return 0;
+
+ if (kekcipher != NULL) {
+ if (EVP_CIPHER_mode(kekcipher) != EVP_CIPH_WRAP_MODE)
+ return 0;
+
+ return EVP_EncryptInit_ex(ctx, kekcipher, NULL, NULL, NULL);
+ }
+ }
+
/*
* Pick a cipher based on content encryption cipher. If it is DES3 use
* DES3 wrap otherwise use AES wrap similar to key size.
STACK_OF(CMS_RecipientEncryptedKey) *reks;
int i;
+ {
+ /*
+ * TODO(3.0) Remove this when we have figured out all the details
+ * need to set up encryption right. With legacy keys, a *lot* is
+ * happening in the CMS specific EVP_PKEY_ASN1_METHOD functions,
+ * such as automatically setting a default KDF type, KDF digest,
+ * all that kind of stuff.
+ * With EVP_SIGNATURE, setting a default digest is done by getting
+ * the default MD for the key, and then inject that back into the
+ * signature implementation... we could do something similar with
+ * CMS, possibly using CMS specific OSSL_PARAM keys, just like we
+ * have for certain AlgorithmIdentifier retrievals.
+ *
+ * THIS IS TEMPORARY
+ */
+ EVP_PKEY_CTX *pctx = CMS_RecipientInfo_get0_pkey_ctx(ri);
+ EVP_PKEY *pkey = EVP_PKEY_CTX_get0_pkey(pctx);
+
+ EVP_PKEY_get0(pkey);
+ if (EVP_PKEY_id(pkey) == EVP_PKEY_NONE) {
+ CMSerr(CMS_F_CMS_RECIPIENTINFO_KARI_ENCRYPT,
+ CMS_R_NOT_SUPPORTED_FOR_THIS_KEY_TYPE);
+ return 0;
+ }
+ }
+
if (ri->type != CMS_RECIPINFO_AGREE) {
CMSerr(CMS_F_CMS_RECIPIENTINFO_KARI_ENCRYPT, CMS_R_NOT_KEY_AGREEMENT);
return 0;