Files
heimdal/lib/hx509/cms.c
Nicolas Williams db7763ca7b asn1: X.681/682/683 magic handling of open types
Status:

 - And it works!

 - We have an extensive test based on decoding a rich EK certficate.

   This test exercises all of:

    - decoding
    - encoding with and without decoded open types
    - copying of decoded values with decoded open types
    - freeing of decoded values with decoded open types

   Valgrind finds no memory errors.

 - Added a manual page for the compiler.

 - rfc2459.asn1 now has all three primary PKIX types that we care about
   defined as in RFC5912, with IOS constraints and parameterization:

    - `Extension`       (embeds open type in an `OCTET STRING`)
    - `OtherName`       (embeds open type in an        `ANY`-like type)
    - `SingleAttribute` (embeds open type in an        `ANY`-like type)
    - `AttributeSet`    (embeds open type in a  `SET OF ANY`-like type)

   All of these use OIDs as the open type type ID field, but integer
   open type type ID fields are also supported (and needed, for
   Kerberos).

   That will cover every typed hole pattern in all our ASN.1 modules.

   With this we'll be able to automatically and recursively decode
   through all subject DN attributes even when the subject DN is a
   directoryName SAN, and subjectDirectoryAttributes, and all
   extensions, and all SANs, and all authorization-data elements, and
   PA-data, and...

   We're not really using `SingleAttribute` and `AttributeSet` yet
   because various changes are needed in `lib/hx509` for that.

 - `asn1_compile` builds and recognizes the subset of X.681/682/683 that
   we need for, and now use in, rfc2459.asn1.  It builds the necessary
   AST, generates the correct C types, and generates templating for
   object sets and open types!

 - See READMEs for details.

 - Codegen backend not tested; I won't make it implement automatic open
   type handling, but it should at least not crash by substituting
   `heim_any` for open types not embedded in `OCTET STRING`.

 - We're _really_ starting to have problems with the ITU-T ASN.1
   grammar and our version of it...

   Type names have to start with upper-case, value names with
   lower-case, but it's not enough to disambiguate.

   The fact the we've allowed value and type names to violate their
   respective start-with case rules is causing us trouble now that we're
   adding grammar from X.681/682/683, and we're going to have to undo
   that.

   In preparation for that I'm capitalizing the `heim_any` and
   `heim_any_set` types, and doing some additional cleanup, which
   requires changes to other parts of Heimdal (all in this same commit
   for now).

   Problems we have because of this:

    - We cannot IMPORT values into modules because we have no idea if a
      symbol being imported refers to a value or a type because the only
      clue we would have is the symbol's name, so we assume IMPORTed
      symbols are for types.

      This means we can't import OIDs, for example, which is super
      annoying.

      One thing we might be able to do here is mark imported symbols as
      being of an undetermined-but-not-undefined type, then coerce the
      symbol's type the first time it's used in a context where its type
      is inferred as type, value, object, object set, or class.  (Though
      since we don't generate C symbols for objects or classes, we won't
      be able to import them, especially since we need to know them at
      compile time and cannot defer their handling to link- or
      run-time.)

    - The `NULL` type name, and the `NULL` value name now cause two
      reduce/reduce conflicts via the `FieldSetting` production.

    - Various shift/reduce conflicts involving `NULL` values in
      non-top-level contexts (in constraints, for example).

 - Currently I have a bug where to disambiguate the grammar I have a
   CLASS_IDENTIFIER token that is all caps, while TYPE_IDENTIFIER must
   start with a capital but not be all caps, but this breaks Kerberos
   since all its types are all capitalized -- oof!

   To fix this I made it so class names have to be all caps and
   start with an underscore (ick).

TBD:

 - Check all the XXX comments and address them
 - Apply this treatment to Kerberos!  Automatic handling of authz-data
   sounds useful :)
 - Apply this treatment to PKCS#10 (CSRs) and other ASN.1 modules too.
 - Replace various bits of code in `lib/hx509/` with uses of this
   feature.
 - Add JER.
 - Enhance `hxtool` and `asn1_print`.

Getting there!
2021-02-28 18:13:08 -06:00

1664 lines
42 KiB
C

/*
* Copyright (c) 2003 - 2007 Kungliga Tekniska Högskolan
* (Royal Institute of Technology, Stockholm, Sweden).
* 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. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS 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 INSTITUTE OR 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.
*/
#include "hx_locl.h"
/**
* @page page_cms CMS/PKCS7 message functions.
*
* CMS is defined in RFC 3369 and is an continuation of the RSA Labs
* standard PKCS7. The basic messages in CMS is
*
* - SignedData
* Data signed with private key (RSA, DSA, ECDSA) or secret
* (symmetric) key
* - EnvelopedData
* Data encrypted with private key (RSA)
* - EncryptedData
* Data encrypted with secret (symmetric) key.
* - ContentInfo
* Wrapper structure including type and data.
*
*
* See the library functions here: @ref hx509_cms
*/
#define ALLOC(X, N) (X) = calloc((N), sizeof(*(X)))
#define ALLOC_SEQ(X, N) do { (X)->len = (N); ALLOC((X)->val, (N)); } while(0)
/**
* Wrap data and oid in a ContentInfo and encode it.
*
* @param oid type of the content.
* @param buf data to be wrapped. If a NULL pointer is passed in, the
* optional content field in the ContentInfo is not going be filled
* in.
* @param res the encoded buffer, the result should be freed with
* der_free_octet_string().
*
* @return Returns an hx509 error code.
*
* @ingroup hx509_cms
*/
HX509_LIB_FUNCTION int HX509_LIB_CALL
hx509_cms_wrap_ContentInfo(const heim_oid *oid,
const heim_octet_string *buf,
heim_octet_string *res)
{
ContentInfo ci;
size_t size;
int ret;
memset(res, 0, sizeof(*res));
memset(&ci, 0, sizeof(ci));
ret = der_copy_oid(oid, &ci.contentType);
if (ret)
return ret;
if (buf) {
ALLOC(ci.content, 1);
if (ci.content == NULL) {
free_ContentInfo(&ci);
return ENOMEM;
}
ci.content->data = malloc(buf->length);
if (ci.content->data == NULL) {
free_ContentInfo(&ci);
return ENOMEM;
}
memcpy(ci.content->data, buf->data, buf->length);
ci.content->length = buf->length;
}
ASN1_MALLOC_ENCODE(ContentInfo, res->data, res->length, &ci, &size, ret);
free_ContentInfo(&ci);
if (ret)
return ret;
if (res->length != size)
_hx509_abort("internal ASN.1 encoder error");
return 0;
}
/**
* Decode an ContentInfo and unwrap data and oid it.
*
* @param in the encoded buffer.
* @param oid type of the content.
* @param out data to be wrapped.
* @param have_data since the data is optional, this flags show dthe
* diffrence between no data and the zero length data.
*
* @return Returns an hx509 error code.
*
* @ingroup hx509_cms
*/
HX509_LIB_FUNCTION int HX509_LIB_CALL
hx509_cms_unwrap_ContentInfo(const heim_octet_string *in,
heim_oid *oid,
heim_octet_string *out,
int *have_data)
{
ContentInfo ci;
size_t size;
int ret;
memset(oid, 0, sizeof(*oid));
memset(out, 0, sizeof(*out));
ret = decode_ContentInfo(in->data, in->length, &ci, &size);
if (ret)
return ret;
ret = der_copy_oid(&ci.contentType, oid);
if (ret) {
free_ContentInfo(&ci);
return ret;
}
if (ci.content) {
ret = der_copy_octet_string(ci.content, out);
if (ret) {
der_free_oid(oid);
free_ContentInfo(&ci);
return ret;
}
} else
memset(out, 0, sizeof(*out));
if (have_data)
*have_data = (ci.content != NULL) ? 1 : 0;
free_ContentInfo(&ci);
return 0;
}
#define CMS_ID_SKI 0
#define CMS_ID_NAME 1
static int
fill_CMSIdentifier(const hx509_cert cert,
int type,
CMSIdentifier *id)
{
int ret;
switch (type) {
case CMS_ID_SKI:
id->element = choice_CMSIdentifier_subjectKeyIdentifier;
ret = _hx509_find_extension_subject_key_id(_hx509_get_cert(cert),
&id->u.subjectKeyIdentifier);
if (ret == 0)
break;
/* FALLTHROUGH */
case CMS_ID_NAME: {
hx509_name name;
id->element = choice_CMSIdentifier_issuerAndSerialNumber;
ret = hx509_cert_get_issuer(cert, &name);
if (ret)
return ret;
ret = hx509_name_to_Name(name, &id->u.issuerAndSerialNumber.issuer);
hx509_name_free(&name);
if (ret)
return ret;
ret = hx509_cert_get_serialnumber(cert, &id->u.issuerAndSerialNumber.serialNumber);
break;
}
default:
_hx509_abort("CMS fill identifier with unknown type");
}
return ret;
}
static int
unparse_CMSIdentifier(hx509_context context,
CMSIdentifier *id,
char **str)
{
int ret = -1;
*str = NULL;
switch (id->element) {
case choice_CMSIdentifier_issuerAndSerialNumber: {
IssuerAndSerialNumber *iasn;
char *serial, *name;
iasn = &id->u.issuerAndSerialNumber;
ret = _hx509_Name_to_string(&iasn->issuer, &name);
if(ret)
return ret;
ret = der_print_hex_heim_integer(&iasn->serialNumber, &serial);
if (ret) {
free(name);
return ret;
}
ret = asprintf(str, "certificate issued by %s with serial number %s",
name, serial);
free(name);
free(serial);
break;
}
case choice_CMSIdentifier_subjectKeyIdentifier: {
KeyIdentifier *ki = &id->u.subjectKeyIdentifier;
char *keyid;
ssize_t len;
len = hex_encode(ki->data, ki->length, &keyid);
if (len < 0)
return ENOMEM;
ret = asprintf(str, "certificate with id %s", keyid);
free(keyid);
break;
}
default:
ret = asprintf(str, "certificate have unknown CMSidentifier type");
break;
}
/*
* In the following if, we check ret and *str which should be returned/set
* by asprintf(3) in every branch of the switch statement.
*/
if (ret == -1 || *str == NULL)
return ENOMEM;
return 0;
}
static int
find_CMSIdentifier(hx509_context context,
CMSIdentifier *client,
hx509_certs certs,
time_t time_now,
hx509_cert *signer_cert,
int match)
{
hx509_query q;
hx509_cert cert;
Certificate c;
int ret;
memset(&c, 0, sizeof(c));
_hx509_query_clear(&q);
*signer_cert = NULL;
switch (client->element) {
case choice_CMSIdentifier_issuerAndSerialNumber:
q.serial = &client->u.issuerAndSerialNumber.serialNumber;
q.issuer_name = &client->u.issuerAndSerialNumber.issuer;
q.match = HX509_QUERY_MATCH_SERIALNUMBER|HX509_QUERY_MATCH_ISSUER_NAME;
break;
case choice_CMSIdentifier_subjectKeyIdentifier:
q.subject_id = &client->u.subjectKeyIdentifier;
q.match = HX509_QUERY_MATCH_SUBJECT_KEY_ID;
break;
default:
hx509_set_error_string(context, 0, HX509_CMS_NO_RECIPIENT_CERTIFICATE,
"unknown CMS identifier element");
return HX509_CMS_NO_RECIPIENT_CERTIFICATE;
}
q.match |= match;
q.match |= HX509_QUERY_MATCH_TIME;
if (time_now)
q.timenow = time_now;
else
q.timenow = time(NULL);
ret = hx509_certs_find(context, certs, &q, &cert);
if (ret == HX509_CERT_NOT_FOUND) {
char *str;
ret = unparse_CMSIdentifier(context, client, &str);
if (ret == 0) {
hx509_set_error_string(context, 0,
HX509_CMS_NO_RECIPIENT_CERTIFICATE,
"Failed to find %s", str);
} else
hx509_clear_error_string(context);
return HX509_CMS_NO_RECIPIENT_CERTIFICATE;
} else if (ret) {
hx509_set_error_string(context, HX509_ERROR_APPEND,
HX509_CMS_NO_RECIPIENT_CERTIFICATE,
"Failed to find CMS id in cert store");
return HX509_CMS_NO_RECIPIENT_CERTIFICATE;
}
*signer_cert = cert;
return 0;
}
/**
* Decode and unencrypt EnvelopedData.
*
* Extract data and parameteres from from the EnvelopedData. Also
* supports using detached EnvelopedData.
*
* @param context A hx509 context.
* @param certs Certificate that can decrypt the EnvelopedData
* encryption key.
* @param flags HX509_CMS_UE flags to control the behavior.
* @param data pointer the structure the contains the DER/BER encoded
* EnvelopedData stucture.
* @param length length of the data that data point to.
* @param encryptedContent in case of detached signature, this
* contains the actual encrypted data, othersize its should be NULL.
* @param time_now set the current time, if zero the library uses now as the date.
* @param contentType output type oid, should be freed with der_free_oid().
* @param content the data, free with der_free_octet_string().
*
* @return an hx509 error code.
*
* @ingroup hx509_cms
*/
HX509_LIB_FUNCTION int HX509_LIB_CALL
hx509_cms_unenvelope(hx509_context context,
hx509_certs certs,
int flags,
const void *data,
size_t length,
const heim_octet_string *encryptedContent,
time_t time_now,
heim_oid *contentType,
heim_octet_string *content)
{
heim_octet_string key;
EnvelopedData ed;
hx509_cert cert;
AlgorithmIdentifier *ai;
const heim_octet_string *enccontent;
heim_octet_string *params, params_data;
heim_octet_string ivec;
size_t size;
int ret, matched = 0, findflags = 0;
size_t i;
memset(&key, 0, sizeof(key));
memset(&ed, 0, sizeof(ed));
memset(&ivec, 0, sizeof(ivec));
memset(content, 0, sizeof(*content));
memset(contentType, 0, sizeof(*contentType));
if ((flags & HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT) == 0)
findflags |= HX509_QUERY_KU_ENCIPHERMENT;
ret = decode_EnvelopedData(data, length, &ed, &size);
if (ret) {
hx509_set_error_string(context, 0, ret,
"Failed to decode EnvelopedData");
return ret;
}
if (ed.recipientInfos.len == 0) {
ret = HX509_CMS_NO_RECIPIENT_CERTIFICATE;
hx509_set_error_string(context, 0, ret,
"No recipient info in enveloped data");
goto out;
}
enccontent = ed.encryptedContentInfo.encryptedContent;
if (enccontent == NULL) {
if (encryptedContent == NULL) {
ret = HX509_CMS_NO_DATA_AVAILABLE;
hx509_set_error_string(context, 0, ret,
"Content missing from encrypted data");
goto out;
}
enccontent = encryptedContent;
} else if (encryptedContent != NULL) {
ret = HX509_CMS_NO_DATA_AVAILABLE;
hx509_set_error_string(context, 0, ret,
"Both internal and external encrypted data");
goto out;
}
cert = NULL;
for (i = 0; i < ed.recipientInfos.len; i++) {
KeyTransRecipientInfo *ri;
char *str;
int ret2;
ri = &ed.recipientInfos.val[i];
ret = find_CMSIdentifier(context, &ri->rid, certs,
time_now, &cert,
HX509_QUERY_PRIVATE_KEY|findflags);
if (ret)
continue;
matched = 1; /* found a matching certificate, let decrypt */
ret = _hx509_cert_private_decrypt(context,
&ri->encryptedKey,
&ri->keyEncryptionAlgorithm.algorithm,
cert, &key);
hx509_cert_free(cert);
if (ret == 0)
break; /* succuessfully decrypted cert */
cert = NULL;
ret2 = unparse_CMSIdentifier(context, &ri->rid, &str);
if (ret2 == 0) {
hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
"Failed to decrypt with %s", str);
free(str);
}
}
if (!matched) {
ret = HX509_CMS_NO_RECIPIENT_CERTIFICATE;
hx509_set_error_string(context, 0, ret,
"No private key matched any certificate");
goto out;
}
if (cert == NULL) {
ret = HX509_CMS_NO_RECIPIENT_CERTIFICATE;
hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
"No private key decrypted the transfer key");
goto out;
}
ret = der_copy_oid(&ed.encryptedContentInfo.contentType, contentType);
if (ret) {
hx509_set_error_string(context, 0, ret,
"Failed to copy EnvelopedData content oid");
goto out;
}
ai = &ed.encryptedContentInfo.contentEncryptionAlgorithm;
if (ai->parameters) {
params_data.data = ai->parameters->data;
params_data.length = ai->parameters->length;
params = &params_data;
} else
params = NULL;
{
hx509_crypto crypto;
ret = hx509_crypto_init(context, NULL, &ai->algorithm, &crypto);
if (ret)
goto out;
if (flags & HX509_CMS_UE_ALLOW_WEAK)
hx509_crypto_allow_weak(crypto);
if (params) {
ret = hx509_crypto_set_params(context, crypto, params, &ivec);
if (ret) {
hx509_crypto_destroy(crypto);
goto out;
}
}
ret = hx509_crypto_set_key_data(crypto, key.data, key.length);
if (ret) {
hx509_crypto_destroy(crypto);
hx509_set_error_string(context, 0, ret,
"Failed to set key for decryption "
"of EnvelopedData");
goto out;
}
ret = hx509_crypto_decrypt(crypto,
enccontent->data,
enccontent->length,
ivec.length ? &ivec : NULL,
content);
hx509_crypto_destroy(crypto);
if (ret) {
hx509_set_error_string(context, 0, ret,
"Failed to decrypt EnvelopedData");
goto out;
}
}
out:
free_EnvelopedData(&ed);
der_free_octet_string(&key);
if (ivec.length)
der_free_octet_string(&ivec);
if (ret) {
der_free_oid(contentType);
der_free_octet_string(content);
}
return ret;
}
/**
* Encrypt end encode EnvelopedData.
*
* Encrypt and encode EnvelopedData. The data is encrypted with a
* random key and the the random key is encrypted with the
* certificates private key. This limits what private key type can be
* used to RSA.
*
* @param context A hx509 context.
* @param flags flags to control the behavior.
* - HX509_CMS_EV_NO_KU_CHECK - Don't check KU on certificate
* - HX509_CMS_EV_ALLOW_WEAK - Allow weak crytpo
* - HX509_CMS_EV_ID_NAME - prefer issuer name and serial number
* @param cert Certificate to encrypt the EnvelopedData encryption key
* with.
* @param data pointer the data to encrypt.
* @param length length of the data that data point to.
* @param encryption_type Encryption cipher to use for the bulk data,
* use NULL to get default.
* @param contentType type of the data that is encrypted
* @param content the output of the function,
* free with der_free_octet_string().
*
* @return an hx509 error code.
*
* @ingroup hx509_cms
*/
HX509_LIB_FUNCTION int HX509_LIB_CALL
hx509_cms_envelope_1(hx509_context context,
int flags,
hx509_cert cert,
const void *data,
size_t length,
const heim_oid *encryption_type,
const heim_oid *contentType,
heim_octet_string *content)
{
KeyTransRecipientInfo *ri;
heim_octet_string ivec;
heim_octet_string key;
hx509_crypto crypto = NULL;
int ret, cmsidflag;
EnvelopedData ed;
size_t size;
memset(&ivec, 0, sizeof(ivec));
memset(&key, 0, sizeof(key));
memset(&ed, 0, sizeof(ed));
memset(content, 0, sizeof(*content));
if (encryption_type == NULL)
encryption_type = &asn1_oid_id_aes_256_cbc;
if ((flags & HX509_CMS_EV_NO_KU_CHECK) == 0) {
ret = _hx509_check_key_usage(context, cert, 1 << 2, TRUE);
if (ret)
goto out;
}
ret = hx509_crypto_init(context, NULL, encryption_type, &crypto);
if (ret)
goto out;
if (flags & HX509_CMS_EV_ALLOW_WEAK)
hx509_crypto_allow_weak(crypto);
ret = hx509_crypto_set_random_key(crypto, &key);
if (ret) {
hx509_set_error_string(context, 0, ret,
"Create random key for EnvelopedData content");
goto out;
}
ret = hx509_crypto_random_iv(crypto, &ivec);
if (ret) {
hx509_set_error_string(context, 0, ret,
"Failed to create a random iv");
goto out;
}
ret = hx509_crypto_encrypt(crypto,
data,
length,
&ivec,
&ed.encryptedContentInfo.encryptedContent);
if (ret) {
hx509_set_error_string(context, 0, ret,
"Failed to encrypt EnvelopedData content");
goto out;
}
{
AlgorithmIdentifier *enc_alg;
enc_alg = &ed.encryptedContentInfo.contentEncryptionAlgorithm;
ret = der_copy_oid(encryption_type, &enc_alg->algorithm);
if (ret) {
hx509_set_error_string(context, 0, ret,
"Failed to set crypto oid "
"for EnvelopedData");
goto out;
}
ALLOC(enc_alg->parameters, 1);
if (enc_alg->parameters == NULL) {
ret = ENOMEM;
hx509_set_error_string(context, 0, ret,
"Failed to allocate crypto parameters "
"for EnvelopedData");
goto out;
}
ret = hx509_crypto_get_params(context,
crypto,
&ivec,
enc_alg->parameters);
if (ret) {
goto out;
}
}
ALLOC_SEQ(&ed.recipientInfos, 1);
if (ed.recipientInfos.val == NULL) {
ret = ENOMEM;
hx509_set_error_string(context, 0, ret,
"Failed to allocate recipients info "
"for EnvelopedData");
goto out;
}
ri = &ed.recipientInfos.val[0];
if (flags & HX509_CMS_EV_ID_NAME) {
ri->version = 0;
cmsidflag = CMS_ID_NAME;
} else {
ri->version = 2;
cmsidflag = CMS_ID_SKI;
}
ret = fill_CMSIdentifier(cert, cmsidflag, &ri->rid);
if (ret) {
hx509_set_error_string(context, 0, ret,
"Failed to set CMS identifier info "
"for EnvelopedData");
goto out;
}
ret = hx509_cert_public_encrypt(context,
&key, cert,
&ri->keyEncryptionAlgorithm.algorithm,
&ri->encryptedKey);
if (ret) {
hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
"Failed to encrypt transport key for "
"EnvelopedData");
goto out;
}
/*
*
*/
ed.version = 0;
ed.originatorInfo = NULL;
ret = der_copy_oid(contentType, &ed.encryptedContentInfo.contentType);
if (ret) {
hx509_set_error_string(context, 0, ret,
"Failed to copy content oid for "
"EnvelopedData");
goto out;
}
ed.unprotectedAttrs = NULL;
ASN1_MALLOC_ENCODE(EnvelopedData, content->data, content->length,
&ed, &size, ret);
if (ret) {
hx509_set_error_string(context, 0, ret,
"Failed to encode EnvelopedData");
goto out;
}
if (size != content->length)
_hx509_abort("internal ASN.1 encoder error");
out:
if (crypto)
hx509_crypto_destroy(crypto);
if (ret)
der_free_octet_string(content);
der_free_octet_string(&key);
der_free_octet_string(&ivec);
free_EnvelopedData(&ed);
return ret;
}
static int
any_to_certs(hx509_context context, const SignedData *sd, hx509_certs certs)
{
int ret;
size_t i;
if (sd->certificates == NULL)
return 0;
for (i = 0; i < sd->certificates->len; i++) {
heim_error_t error;
hx509_cert c;
c = hx509_cert_init_data(context,
sd->certificates->val[i].data,
sd->certificates->val[i].length,
&error);
if (c == NULL) {
ret = heim_error_get_code(error);
heim_release(error);
return ret;
}
ret = hx509_certs_add(context, certs, c);
hx509_cert_free(c);
if (ret)
return ret;
}
return 0;
}
static const Attribute *
find_attribute(const CMSAttributes *attr, const heim_oid *oid)
{
size_t i;
for (i = 0; i < attr->len; i++)
if (der_heim_oid_cmp(&attr->val[i].type, oid) == 0)
return &attr->val[i];
return NULL;
}
/**
* Decode SignedData and verify that the signature is correct.
*
* @param context A hx509 context.
* @param ctx a hx509 verify context.
* @param flags to control the behaivor of the function.
* - HX509_CMS_VS_NO_KU_CHECK - Don't check KeyUsage
* - HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH - allow oid mismatch
* - HX509_CMS_VS_ALLOW_ZERO_SIGNER - no signer, see below.
* @param data pointer to CMS SignedData encoded data.
* @param length length of the data that data point to.
* @param signedContent external data used for signature.
* @param pool certificate pool to build certificates paths.
* @param contentType free with der_free_oid().
* @param content the output of the function, free with
* der_free_octet_string().
* @param signer_certs list of the cerficates used to sign this
* request, free with hx509_certs_free().
*
* @return an hx509 error code.
*
* @ingroup hx509_cms
*/
HX509_LIB_FUNCTION int HX509_LIB_CALL
hx509_cms_verify_signed(hx509_context context,
hx509_verify_ctx ctx,
unsigned int flags,
const void *data,
size_t length,
const heim_octet_string *signedContent,
hx509_certs pool,
heim_oid *contentType,
heim_octet_string *content,
hx509_certs *signer_certs)
{
SignerInfo *signer_info;
hx509_cert cert = NULL;
hx509_certs certs = NULL;
SignedData sd;
size_t size;
int ret, found_valid_sig;
size_t i;
*signer_certs = NULL;
content->data = NULL;
content->length = 0;
contentType->length = 0;
contentType->components = NULL;
memset(&sd, 0, sizeof(sd));
ret = decode_SignedData(data, length, &sd, &size);
if (ret) {
hx509_set_error_string(context, 0, ret,
"Failed to decode SignedData");
goto out;
}
if (sd.encapContentInfo.eContent == NULL && signedContent == NULL) {
ret = HX509_CMS_NO_DATA_AVAILABLE;
hx509_set_error_string(context, 0, ret,
"No content data in SignedData");
goto out;
}
if (sd.encapContentInfo.eContent && signedContent) {
ret = HX509_CMS_NO_DATA_AVAILABLE;
hx509_set_error_string(context, 0, ret,
"Both external and internal SignedData");
goto out;
}
if (sd.encapContentInfo.eContent)
ret = der_copy_octet_string(sd.encapContentInfo.eContent, content);
else
ret = der_copy_octet_string(signedContent, content);
if (ret) {
hx509_set_error_string(context, 0, ret, "malloc: out of memory");
goto out;
}
ret = hx509_certs_init(context, "MEMORY:cms-cert-buffer",
0, NULL, &certs);
if (ret)
goto out;
ret = hx509_certs_init(context, "MEMORY:cms-signer-certs",
0, NULL, signer_certs);
if (ret)
goto out;
/* XXX Check CMS version */
ret = any_to_certs(context, &sd, certs);
if (ret)
goto out;
if (pool) {
ret = hx509_certs_merge(context, certs, pool);
if (ret)
goto out;
}
for (found_valid_sig = 0, i = 0; i < sd.signerInfos.len; i++) {
heim_octet_string signed_data = { 0, 0 };
const heim_oid *match_oid;
heim_oid decode_oid;
signer_info = &sd.signerInfos.val[i];
match_oid = NULL;
if (signer_info->signature.length == 0) {
ret = HX509_CMS_MISSING_SIGNER_DATA;
hx509_set_error_string(context, 0, ret,
"SignerInfo %d in SignedData "
"missing sigature", i);
continue;
}
ret = find_CMSIdentifier(context, &signer_info->sid, certs,
_hx509_verify_get_time(ctx), &cert,
HX509_QUERY_KU_DIGITALSIGNATURE);
if (ret) {
/**
* If HX509_CMS_VS_NO_KU_CHECK is set, allow more liberal
* search for matching certificates by not considering
* KeyUsage bits on the certificates.
*/
if ((flags & HX509_CMS_VS_NO_KU_CHECK) == 0)
continue;
ret = find_CMSIdentifier(context, &signer_info->sid, certs,
_hx509_verify_get_time(ctx), &cert,
0);
if (ret)
continue;
}
if (signer_info->signedAttrs) {
const Attribute *attr;
CMSAttributes sa;
heim_octet_string os;
sa.val = signer_info->signedAttrs->val;
sa.len = signer_info->signedAttrs->len;
/* verify that sigature exists */
attr = find_attribute(&sa, &asn1_oid_id_pkcs9_messageDigest);
if (attr == NULL) {
ret = HX509_CRYPTO_SIGNATURE_MISSING;
hx509_set_error_string(context, 0, ret,
"SignerInfo have signed attributes "
"but messageDigest (signature) "
"is missing");
goto next_sigature;
}
if (attr->value.len != 1) {
ret = HX509_CRYPTO_SIGNATURE_MISSING;
hx509_set_error_string(context, 0, ret,
"SignerInfo have more then one "
"messageDigest (signature)");
goto next_sigature;
}
ret = decode_MessageDigest(attr->value.val[0].data,
attr->value.val[0].length,
&os,
&size);
if (ret) {
hx509_set_error_string(context, 0, ret,
"Failed to decode "
"messageDigest (signature)");
goto next_sigature;
}
ret = _hx509_verify_signature(context,
NULL,
&signer_info->digestAlgorithm,
content,
&os);
der_free_octet_string(&os);
if (ret) {
hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
"Failed to verify messageDigest");
goto next_sigature;
}
/*
* Fetch content oid inside signedAttrs or set it to
* id-pkcs7-data.
*/
attr = find_attribute(&sa, &asn1_oid_id_pkcs9_contentType);
if (attr == NULL) {
match_oid = &asn1_oid_id_pkcs7_data;
} else {
if (attr->value.len != 1) {
ret = HX509_CMS_DATA_OID_MISMATCH;
hx509_set_error_string(context, 0, ret,
"More then one oid in signedAttrs");
goto next_sigature;
}
ret = decode_ContentType(attr->value.val[0].data,
attr->value.val[0].length,
&decode_oid,
&size);
if (ret) {
hx509_set_error_string(context, 0, ret,
"Failed to decode "
"oid in signedAttrs");
goto next_sigature;
}
match_oid = &decode_oid;
}
ASN1_MALLOC_ENCODE(CMSAttributes,
signed_data.data,
signed_data.length,
&sa,
&size, ret);
if (ret) {
if (match_oid == &decode_oid)
der_free_oid(&decode_oid);
hx509_clear_error_string(context);
goto next_sigature;
}
if (size != signed_data.length)
_hx509_abort("internal ASN.1 encoder error");
} else {
signed_data.data = content->data;
signed_data.length = content->length;
match_oid = &asn1_oid_id_pkcs7_data;
}
/**
* If HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH, allow
* encapContentInfo mismatch with the oid in signedAttributes
* (or if no signedAttributes where use, pkcs7-data oid).
* This is only needed to work with broken CMS implementations
* that doesn't follow CMS signedAttributes rules.
*/
if (der_heim_oid_cmp(match_oid, &sd.encapContentInfo.eContentType) &&
(flags & HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH) == 0) {
ret = HX509_CMS_DATA_OID_MISMATCH;
hx509_set_error_string(context, 0, ret,
"Oid in message mismatch from the expected");
}
if (match_oid == &decode_oid)
der_free_oid(&decode_oid);
if (ret == 0) {
ret = hx509_verify_signature(context,
cert,
&signer_info->signatureAlgorithm,
&signed_data,
&signer_info->signature);
if (ret)
hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
"Failed to verify signature in "
"CMS SignedData");
}
if (signed_data.data != NULL && content->data != signed_data.data) {
free(signed_data.data);
signed_data.data = NULL;
}
if (ret)
goto next_sigature;
/**
* If HX509_CMS_VS_NO_VALIDATE flags is set, do not verify the
* signing certificates and leave that up to the caller.
*/
if ((flags & HX509_CMS_VS_NO_VALIDATE) == 0) {
ret = hx509_verify_path(context, ctx, cert, certs);
if (ret)
goto next_sigature;
}
ret = hx509_certs_add(context, *signer_certs, cert);
if (ret)
goto next_sigature;
found_valid_sig++;
next_sigature:
if (cert)
hx509_cert_free(cert);
cert = NULL;
}
/**
* If HX509_CMS_VS_ALLOW_ZERO_SIGNER is set, allow empty
* SignerInfo (no signatures). If SignedData have no signatures,
* the function will return 0 with signer_certs set to NULL. Zero
* signers is allowed by the standard, but since its only useful
* in corner cases, it make into a flag that the caller have to
* turn on.
*/
if (sd.signerInfos.len == 0 && (flags & HX509_CMS_VS_ALLOW_ZERO_SIGNER)) {
if (*signer_certs)
hx509_certs_free(signer_certs);
} else if (found_valid_sig == 0) {
if (ret == 0) {
ret = HX509_CMS_SIGNER_NOT_FOUND;
hx509_set_error_string(context, 0, ret,
"No signers where found");
}
goto out;
}
ret = der_copy_oid(&sd.encapContentInfo.eContentType, contentType);
if (ret) {
hx509_clear_error_string(context);
goto out;
}
out:
free_SignedData(&sd);
if (certs)
hx509_certs_free(&certs);
if (ret) {
if (content->data)
der_free_octet_string(content);
if (*signer_certs)
hx509_certs_free(signer_certs);
der_free_oid(contentType);
der_free_octet_string(content);
}
return ret;
}
static int
add_one_attribute(Attribute **attr,
unsigned int *len,
const heim_oid *oid,
heim_octet_string *data)
{
void *d;
int ret;
d = realloc(*attr, sizeof((*attr)[0]) * (*len + 1));
if (d == NULL)
return ENOMEM;
(*attr) = d;
ret = der_copy_oid(oid, &(*attr)[*len].type);
if (ret)
return ret;
ALLOC_SEQ(&(*attr)[*len].value, 1);
if ((*attr)[*len].value.val == NULL) {
der_free_oid(&(*attr)[*len].type);
return ENOMEM;
}
(*attr)[*len].value.val[0].data = data->data;
(*attr)[*len].value.val[0].length = data->length;
*len += 1;
return 0;
}
/**
* Decode SignedData and verify that the signature is correct.
*
* @param context A hx509 context.
* @param flags
* @param eContentType the type of the data.
* @param data data to sign
* @param length length of the data that data point to.
* @param digest_alg digest algorithm to use, use NULL to get the
* default or the peer determined algorithm.
* @param cert certificate to use for sign the data.
* @param peer info about the peer the message to send the message to,
* like what digest algorithm to use.
* @param anchors trust anchors that the client will use, used to
* polulate the certificates included in the message
* @param pool certificates to use in try to build the path to the
* trust anchors.
* @param signed_data the output of the function, free with
* der_free_octet_string().
*
* @return Returns an hx509 error code.
*
* @ingroup hx509_cms
*/
HX509_LIB_FUNCTION int HX509_LIB_CALL
hx509_cms_create_signed_1(hx509_context context,
int flags,
const heim_oid *eContentType,
const void *data, size_t length,
const AlgorithmIdentifier *digest_alg,
hx509_cert cert,
hx509_peer_info peer,
hx509_certs anchors,
hx509_certs pool,
heim_octet_string *signed_data)
{
hx509_certs certs;
int ret = 0;
signed_data->data = NULL;
signed_data->length = 0;
ret = hx509_certs_init(context, "MEMORY:certs", 0, NULL, &certs);
if (ret)
return ret;
ret = hx509_certs_add(context, certs, cert);
if (ret)
goto out;
ret = hx509_cms_create_signed(context, flags, eContentType, data, length,
digest_alg, certs, peer, anchors, pool,
signed_data);
out:
hx509_certs_free(&certs);
return ret;
}
struct sigctx {
SignedData sd;
const AlgorithmIdentifier *digest_alg;
const heim_oid *eContentType;
heim_octet_string content;
hx509_peer_info peer;
int cmsidflag;
int leafonly;
hx509_certs certs;
hx509_certs anchors;
hx509_certs pool;
};
static int HX509_LIB_CALL
sig_process(hx509_context context, void *ctx, hx509_cert cert)
{
struct sigctx *sigctx = ctx;
heim_octet_string buf, sigdata = { 0, NULL };
SignerInfo *signer_info = NULL;
AlgorithmIdentifier digest;
size_t size;
void *ptr;
int ret;
SignedData *sd = &sigctx->sd;
hx509_path path;
memset(&digest, 0, sizeof(digest));
memset(&path, 0, sizeof(path));
if (_hx509_cert_private_key(cert) == NULL) {
hx509_set_error_string(context, 0, HX509_PRIVATE_KEY_MISSING,
"Private key missing for signing");
return HX509_PRIVATE_KEY_MISSING;
}
if (sigctx->digest_alg) {
ret = copy_AlgorithmIdentifier(sigctx->digest_alg, &digest);
if (ret)
hx509_clear_error_string(context);
} else {
ret = hx509_crypto_select(context, HX509_SELECT_DIGEST,
_hx509_cert_private_key(cert),
sigctx->peer, &digest);
}
if (ret)
goto out;
/*
* Allocate on more signerInfo and do the signature processing
*/
ptr = realloc(sd->signerInfos.val,
(sd->signerInfos.len + 1) * sizeof(sd->signerInfos.val[0]));
if (ptr == NULL) {
ret = ENOMEM;
goto out;
}
sd->signerInfos.val = ptr;
signer_info = &sd->signerInfos.val[sd->signerInfos.len];
memset(signer_info, 0, sizeof(*signer_info));
signer_info->version = 1;
ret = fill_CMSIdentifier(cert, sigctx->cmsidflag, &signer_info->sid);
if (ret) {
hx509_clear_error_string(context);
goto out;
}
signer_info->signedAttrs = NULL;
signer_info->unsignedAttrs = NULL;
ret = copy_AlgorithmIdentifier(&digest, &signer_info->digestAlgorithm);
if (ret) {
hx509_clear_error_string(context);
goto out;
}
/*
* If it isn't pkcs7-data send signedAttributes
*/
if (der_heim_oid_cmp(sigctx->eContentType, &asn1_oid_id_pkcs7_data) != 0) {
CMSAttributes sa;
heim_octet_string sig;
ALLOC(signer_info->signedAttrs, 1);
if (signer_info->signedAttrs == NULL) {
ret = ENOMEM;
goto out;
}
ret = _hx509_create_signature(context,
NULL,
&digest,
&sigctx->content,
NULL,
&sig);
if (ret)
goto out;
ASN1_MALLOC_ENCODE(MessageDigest,
buf.data,
buf.length,
&sig,
&size,
ret);
der_free_octet_string(&sig);
if (ret) {
hx509_clear_error_string(context);
goto out;
}
if (size != buf.length)
_hx509_abort("internal ASN.1 encoder error");
ret = add_one_attribute(&signer_info->signedAttrs->val,
&signer_info->signedAttrs->len,
&asn1_oid_id_pkcs9_messageDigest,
&buf);
if (ret) {
free(buf.data);
hx509_clear_error_string(context);
goto out;
}
ASN1_MALLOC_ENCODE(ContentType,
buf.data,
buf.length,
sigctx->eContentType,
&size,
ret);
if (ret)
goto out;
if (size != buf.length)
_hx509_abort("internal ASN.1 encoder error");
ret = add_one_attribute(&signer_info->signedAttrs->val,
&signer_info->signedAttrs->len,
&asn1_oid_id_pkcs9_contentType,
&buf);
if (ret) {
free(buf.data);
hx509_clear_error_string(context);
goto out;
}
sa.val = signer_info->signedAttrs->val;
sa.len = signer_info->signedAttrs->len;
ASN1_MALLOC_ENCODE(CMSAttributes,
sigdata.data,
sigdata.length,
&sa,
&size,
ret);
if (ret) {
hx509_clear_error_string(context);
goto out;
}
if (size != sigdata.length)
_hx509_abort("internal ASN.1 encoder error");
} else {
sigdata.data = sigctx->content.data;
sigdata.length = sigctx->content.length;
}
{
AlgorithmIdentifier sigalg;
ret = hx509_crypto_select(context, HX509_SELECT_PUBLIC_SIG,
_hx509_cert_private_key(cert), sigctx->peer,
&sigalg);
if (ret)
goto out;
ret = _hx509_create_signature(context,
_hx509_cert_private_key(cert),
&sigalg,
&sigdata,
&signer_info->signatureAlgorithm,
&signer_info->signature);
free_AlgorithmIdentifier(&sigalg);
if (ret)
goto out;
}
sigctx->sd.signerInfos.len++;
signer_info = NULL;
/*
* Provide best effort path
*/
if (sigctx->certs) {
unsigned int i;
if (sigctx->pool && sigctx->leafonly == 0) {
_hx509_calculate_path(context,
HX509_CALCULATE_PATH_NO_ANCHOR,
time(NULL),
sigctx->anchors,
0,
cert,
sigctx->pool,
&path);
} else
_hx509_path_append(context, &path, cert);
for (i = 0; i < path.len; i++) {
/* XXX remove dups */
ret = hx509_certs_add(context, sigctx->certs, path.val[i]);
if (ret) {
hx509_clear_error_string(context);
goto out;
}
}
}
out:
if (signer_info)
free_SignerInfo(signer_info);
if (sigdata.data != sigctx->content.data)
der_free_octet_string(&sigdata);
_hx509_path_free(&path);
free_AlgorithmIdentifier(&digest);
return ret;
}
static int HX509_LIB_CALL
cert_process(hx509_context context, void *ctx, hx509_cert cert)
{
struct sigctx *sigctx = ctx;
const unsigned int i = sigctx->sd.certificates->len;
void *ptr;
int ret;
ptr = realloc(sigctx->sd.certificates->val,
(i + 1) * sizeof(sigctx->sd.certificates->val[0]));
if (ptr == NULL)
return ENOMEM;
sigctx->sd.certificates->val = ptr;
ret = hx509_cert_binary(context, cert,
&sigctx->sd.certificates->val[i]);
if (ret == 0)
sigctx->sd.certificates->len++;
return ret;
}
static int
cmp_AlgorithmIdentifier(const AlgorithmIdentifier *p, const AlgorithmIdentifier *q)
{
return der_heim_oid_cmp(&p->algorithm, &q->algorithm);
}
HX509_LIB_FUNCTION int HX509_LIB_CALL
hx509_cms_create_signed(hx509_context context,
int flags,
const heim_oid *eContentType,
const void *data, size_t length,
const AlgorithmIdentifier *digest_alg,
hx509_certs certs,
hx509_peer_info peer,
hx509_certs anchors,
hx509_certs pool,
heim_octet_string *signed_data)
{
unsigned int i, j;
hx509_name name;
int ret;
size_t size;
struct sigctx sigctx;
memset(&sigctx, 0, sizeof(sigctx));
memset(&name, 0, sizeof(name));
if (eContentType == NULL)
eContentType = &asn1_oid_id_pkcs7_data;
sigctx.digest_alg = digest_alg;
sigctx.content.data = rk_UNCONST(data);
sigctx.content.length = length;
sigctx.eContentType = eContentType;
sigctx.peer = peer;
/**
* Use HX509_CMS_SIGNATURE_ID_NAME to preferred use of issuer name
* and serial number if possible. Otherwise subject key identifier
* will preferred.
*/
if (flags & HX509_CMS_SIGNATURE_ID_NAME)
sigctx.cmsidflag = CMS_ID_NAME;
else
sigctx.cmsidflag = CMS_ID_SKI;
/**
* Use HX509_CMS_SIGNATURE_LEAF_ONLY to only request leaf
* certificates to be added to the SignedData.
*/
sigctx.leafonly = (flags & HX509_CMS_SIGNATURE_LEAF_ONLY) ? 1 : 0;
/**
* Use HX509_CMS_NO_CERTS to make the SignedData contain no
* certificates, overrides HX509_CMS_SIGNATURE_LEAF_ONLY.
*/
if ((flags & HX509_CMS_SIGNATURE_NO_CERTS) == 0) {
ret = hx509_certs_init(context, "MEMORY:certs", 0, NULL, &sigctx.certs);
if (ret)
return ret;
}
sigctx.anchors = anchors;
sigctx.pool = pool;
sigctx.sd.version = cMSVersion_v3;
der_copy_oid(eContentType, &sigctx.sd.encapContentInfo.eContentType);
/**
* Use HX509_CMS_SIGNATURE_DETACHED to create detached signatures.
*/
if ((flags & HX509_CMS_SIGNATURE_DETACHED) == 0) {
ALLOC(sigctx.sd.encapContentInfo.eContent, 1);
if (sigctx.sd.encapContentInfo.eContent == NULL) {
hx509_clear_error_string(context);
ret = ENOMEM;
goto out;
}
sigctx.sd.encapContentInfo.eContent->data = malloc(length);
if (sigctx.sd.encapContentInfo.eContent->data == NULL) {
hx509_clear_error_string(context);
ret = ENOMEM;
goto out;
}
memcpy(sigctx.sd.encapContentInfo.eContent->data, data, length);
sigctx.sd.encapContentInfo.eContent->length = length;
}
/**
* Use HX509_CMS_SIGNATURE_NO_SIGNER to create no sigInfo (no
* signatures).
*/
if ((flags & HX509_CMS_SIGNATURE_NO_SIGNER) == 0) {
ret = hx509_certs_iter_f(context, certs, sig_process, &sigctx);
if (ret)
goto out;
}
if (sigctx.sd.signerInfos.len) {
/*
* For each signerInfo, collect all different digest types.
*/
for (i = 0; i < sigctx.sd.signerInfos.len; i++) {
AlgorithmIdentifier *di =
&sigctx.sd.signerInfos.val[i].digestAlgorithm;
for (j = 0; j < sigctx.sd.digestAlgorithms.len; j++)
if (cmp_AlgorithmIdentifier(di, &sigctx.sd.digestAlgorithms.val[j]) == 0)
break;
if (j == sigctx.sd.digestAlgorithms.len) {
ret = add_DigestAlgorithmIdentifiers(&sigctx.sd.digestAlgorithms, di);
if (ret) {
hx509_clear_error_string(context);
goto out;
}
}
}
}
/*
* Add certs we think are needed, build as part of sig_process
*/
if (sigctx.certs) {
ALLOC(sigctx.sd.certificates, 1);
if (sigctx.sd.certificates == NULL) {
hx509_clear_error_string(context);
ret = ENOMEM;
goto out;
}
ret = hx509_certs_iter_f(context, sigctx.certs, cert_process, &sigctx);
if (ret)
goto out;
}
ASN1_MALLOC_ENCODE(SignedData,
signed_data->data, signed_data->length,
&sigctx.sd, &size, ret);
if (ret) {
hx509_clear_error_string(context);
goto out;
}
if (signed_data->length != size)
_hx509_abort("internal ASN.1 encoder error");
out:
hx509_certs_free(&sigctx.certs);
free_SignedData(&sigctx.sd);
return ret;
}
HX509_LIB_FUNCTION int HX509_LIB_CALL
hx509_cms_decrypt_encrypted(hx509_context context,
hx509_lock lock,
const void *data,
size_t length,
heim_oid *contentType,
heim_octet_string *content)
{
heim_octet_string cont;
CMSEncryptedData ed;
AlgorithmIdentifier *ai;
int ret;
memset(content, 0, sizeof(*content));
memset(&cont, 0, sizeof(cont));
ret = decode_CMSEncryptedData(data, length, &ed, NULL);
if (ret) {
hx509_set_error_string(context, 0, ret,
"Failed to decode CMSEncryptedData");
return ret;
}
if (ed.encryptedContentInfo.encryptedContent == NULL) {
ret = HX509_CMS_NO_DATA_AVAILABLE;
hx509_set_error_string(context, 0, ret,
"No content in EncryptedData");
goto out;
}
ret = der_copy_oid(&ed.encryptedContentInfo.contentType, contentType);
if (ret) {
hx509_clear_error_string(context);
goto out;
}
ai = &ed.encryptedContentInfo.contentEncryptionAlgorithm;
if (ai->parameters == NULL) {
ret = HX509_ALG_NOT_SUPP;
hx509_clear_error_string(context);
goto out;
}
ret = _hx509_pbe_decrypt(context,
lock,
ai,
ed.encryptedContentInfo.encryptedContent,
&cont);
if (ret)
goto out;
*content = cont;
out:
if (ret) {
if (cont.data)
free(cont.data);
}
free_CMSEncryptedData(&ed);
return ret;
}