/*
- * Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
- * project.
- */
-/* ====================================================================
- * Copyright (c) 2013 The OpenSSL Project. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * 3. All advertising materials mentioning features or use of this
- * software must display the following acknowledgment:
- * "This product includes software developed by the OpenSSL Project
- * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
- *
- * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
- * endorse or promote products derived from this software without
- * prior written permission. For written permission, please contact
- * licensing@OpenSSL.org.
- *
- * 5. Products derived from this software may not be called "OpenSSL"
- * nor may "OpenSSL" appear in their names without prior written
- * permission of the OpenSSL Project.
+ * Copyright 2013-2020 The OpenSSL Project Authors. All Rights Reserved.
*
- * 6. Redistributions of any form whatsoever must retain the following
- * acknowledgment:
- * "This product includes software developed by the OpenSSL Project
- * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
- *
- * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
- * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
- * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
- * OF THE POSSIBILITY OF SUCH DAMAGE.
- * ====================================================================
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
*/
#include "internal/cryptlib.h"
#include <openssl/x509v3.h>
#include <openssl/err.h>
#include <openssl/cms.h>
-#include <openssl/rand.h>
#include <openssl/aes.h>
-#include "cms_lcl.h"
-#include "internal/asn1_int.h"
+#include "cms_local.h"
+#include "crypto/asn1.h"
+
+DEFINE_STACK_OF(CMS_RecipientEncryptedKey)
/* 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;
}
-/* Initialise a ktri based on passed certificate and key */
+/* 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;
kari->version = 3;
rek = M_ASN1_new_of(CMS_RecipientEncryptedKey);
+ if (rek == NULL)
+ return 0;
+
if (!sk_CMS_RecipientEncryptedKey_push(kari->recipientEncryptedKeys, rek)) {
M_ASN1_free_of(rek, CMS_RecipientEncryptedKey);
return 0;
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;
+
+ if (originatorPrivKey == NULL || originator == NULL)
+ return 0;
- EVP_PKEY_up_ref(pk);
- rek->pkey = pk;
+ 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.
/* Encrypt content key in key agreement recipient info */
-int cms_RecipientInfo_kari_encrypt(CMS_ContentInfo *cms,
+int cms_RecipientInfo_kari_encrypt(const CMS_ContentInfo *cms,
CMS_RecipientInfo *ri)
{
CMS_KeyAgreeRecipientInfo *kari;
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;