/* * build attribute type */ static chunk_t build_attribute_type(const chunk_t type, chunk_t content) { return asn1_wrap(ASN1_SEQUENCE, "cm" , type , asn1_wrap(ASN1_SET, "m", content)); }
/* * build authorityKeyIdentifier */ static chunk_t build_authorityKeyID(x509cert_t *signer) { chunk_t keyIdentifier = (signer->subjectKeyID.ptr == NULL) ? empty_chunk : asn1_simple_object(ASN1_CONTEXT_S_0 , signer->subjectKeyID); chunk_t authorityCertIssuer = build_directoryName(ASN1_CONTEXT_C_1 , signer->issuer); chunk_t authorityCertSerialNumber = asn1_simple_object(ASN1_CONTEXT_S_2 , signer->serialNumber); return asn1_wrap(ASN1_SEQUENCE, "cm" , ASN1_authorityKeyIdentifier_oid , asn1_wrap(ASN1_OCTET_STRING, "m" , asn1_wrap(ASN1_SEQUENCE, "mmm" , keyIdentifier , authorityCertIssuer , authorityCertSerialNumber ) ) ); }
/** * @brief Builds a contentType attribute * * @return ASN.1 encoded contentType attribute */ chunk_t pkcs7_contentType_attribute(void) { return asn1_wrap(ASN1_SEQUENCE, "mm", asn1_build_known_oid(OID_PKCS9_CONTENT_TYPE), asn1_wrap(ASN1_SET, "m", asn1_build_known_oid(OID_PKCS7_DATA))); }
/** * Build OCSP request * * @v ocsp OCSP check * @ret rc Return status code */ static int ocsp_request ( struct ocsp_check *ocsp ) { struct digest_algorithm *digest = &ocsp_digest_algorithm; struct asn1_builder *builder = &ocsp->request.builder; struct asn1_cursor *cert_id_tail = &ocsp->request.cert_id_tail; uint8_t digest_ctx[digest->ctxsize]; uint8_t name_digest[digest->digestsize]; uint8_t pubkey_digest[digest->digestsize]; int rc; /* Generate digests */ digest_init ( digest, digest_ctx ); digest_update ( digest, digest_ctx, ocsp->cert->issuer.raw.data, ocsp->cert->issuer.raw.len ); digest_final ( digest, digest_ctx, name_digest ); digest_init ( digest, digest_ctx ); digest_update ( digest, digest_ctx, ocsp->issuer->subject.public_key.raw_bits.data, ocsp->issuer->subject.public_key.raw_bits.len ); digest_final ( digest, digest_ctx, pubkey_digest ); /* Construct request */ if ( ( rc = ( asn1_prepend_raw ( builder, ocsp->cert->serial.raw.data, ocsp->cert->serial.raw.len ), asn1_prepend ( builder, ASN1_OCTET_STRING, pubkey_digest, sizeof ( pubkey_digest ) ), asn1_prepend ( builder, ASN1_OCTET_STRING, name_digest, sizeof ( name_digest ) ), asn1_prepend ( builder, ASN1_SEQUENCE, ocsp_algorithm_id, sizeof ( ocsp_algorithm_id ) ), asn1_wrap ( builder, ASN1_SEQUENCE ), asn1_wrap ( builder, ASN1_SEQUENCE ), asn1_wrap ( builder, ASN1_SEQUENCE ), asn1_wrap ( builder, ASN1_SEQUENCE ), asn1_wrap ( builder, ASN1_SEQUENCE ) ) ) != 0 ) { DBGC ( ocsp, "OCSP %p \"%s\" could not build request: %s\n", ocsp, x509_name ( ocsp->cert ), strerror ( rc ) ); return rc; } DBGC2 ( ocsp, "OCSP %p \"%s\" request is:\n", ocsp, x509_name ( ocsp->cert ) ); DBGC2_HDA ( ocsp, 0, builder->data, builder->len ); /* Parse certificate ID for comparison with response */ cert_id_tail->data = builder->data; cert_id_tail->len = builder->len; if ( ( rc = ( asn1_enter ( cert_id_tail, ASN1_SEQUENCE ), asn1_enter ( cert_id_tail, ASN1_SEQUENCE ), asn1_enter ( cert_id_tail, ASN1_SEQUENCE ), asn1_enter ( cert_id_tail, ASN1_SEQUENCE ), asn1_enter ( cert_id_tail, ASN1_SEQUENCE ), asn1_skip ( cert_id_tail, ASN1_SEQUENCE ) ) ) != 0 ) { DBGC ( ocsp, "OCSP %p \"%s\" could not locate certID: %s\n", ocsp, x509_name ( ocsp->cert ), strerror ( rc ) ); return rc; } return 0; }
/** * Builds a transId attribute */ chunk_t scep_transId_attribute(chunk_t transID) { return asn1_wrap(ASN1_SEQUENCE, "cm" , ASN1_transId_oid , asn1_wrap(ASN1_SET, "m" , asn1_simple_object(ASN1_PRINTABLESTRING, transID) ) ); }
/* * build holder */ static chunk_t build_holder(void) { return asn1_wrap(ASN1_SEQUENCE, "mm" , asn1_wrap(ASN1_CONTEXT_C_0, "mm" , build_directoryName(ASN1_SEQUENCE, user->issuer) , asn1_simple_object(ASN1_INTEGER, user->serialNumber) ) , build_directoryName(ASN1_CONTEXT_C_1, user->subject)); }
/** * Builds a messageType attribute */ chunk_t scep_messageType_attribute(scep_msg_t m) { chunk_t msgType = { (u_char*)msgType_values[m], strlen(msgType_values[m]) }; return asn1_wrap(ASN1_SEQUENCE, "cm" , ASN1_messageType_oid , asn1_wrap(ASN1_SET, "m" , asn1_simple_object(ASN1_PRINTABLESTRING, msgType) ) ); }
/** * Parse an EC domain parameter identifier as defined in RFC 5656 */ static chunk_t parse_ec_identifier(chunk_t identifier) { chunk_t oid = chunk_empty; if (chunk_equals(identifier, chunk_from_str("nistp256"))) { oid = asn1_build_known_oid(OID_PRIME256V1); } else if (chunk_equals(identifier, chunk_from_str("nistp384"))) { oid = asn1_build_known_oid(OID_SECT384R1); } else if (chunk_equals(identifier, chunk_from_str("nistp521"))) { oid = asn1_build_known_oid(OID_SECT521R1); } else { char ascii[64]; if (snprintf(ascii, sizeof(ascii), "%.*s", (int)identifier.len, identifier.ptr) < sizeof(ascii)) { oid = asn1_wrap(ASN1_OID, "m", asn1_oid_from_string(ascii)); } } return oid; }
/* * build extensions */ static chunk_t build_extensions(void) { return asn1_wrap(ASN1_SEQUENCE, "mc" , build_authorityKeyID(signer) , ASN1_noRevAvail_ext); }
/* * build attributes */ static chunk_t build_attributes(void) { return asn1_wrap(ASN1_SEQUENCE, "m" , build_attribute_type(ASN1_group_oid , build_ietfAttributes(groups))); }
/* * build attrCertValidityPeriod */ static chunk_t build_attr_cert_validity(void) { return asn1_wrap(ASN1_SEQUENCE, "mm" , timetoasn1(¬Before, ASN1_GENERALIZEDTIME) , timetoasn1(¬After, ASN1_GENERALIZEDTIME)); }
/** * @brief Builds a messageDigest attribute * * * @param[in] blob content to create digest of * @param[in] digest_alg digest algorithm to be used * @return ASN.1 encoded messageDigest attribute * */ chunk_t pkcs7_messageDigest_attribute(chunk_t content, int digest_alg) { chunk_t digest; hash_algorithm_t hash_alg; hasher_t *hasher; hash_alg = hasher_algorithm_from_oid(digest_alg); hasher = lib->crypto->create_hasher(lib->crypto, hash_alg); hasher->allocate_hash(hasher, content, &digest); hasher->destroy(hasher); return asn1_wrap(ASN1_SEQUENCE, "mm", asn1_build_known_oid(OID_PKCS9_MESSAGE_DIGEST), asn1_wrap(ASN1_SET, "m", asn1_wrap(ASN1_OCTET_STRING, "m", digest))); }
/* * builds a senderNonce attribute */ chunk_t scep_senderNonce_attribute(void) { const size_t nonce_len = 16; u_char nonce_buf[nonce_len]; chunk_t senderNonce = { nonce_buf, nonce_len }; get_rnd_bytes(nonce_buf, nonce_len); return asn1_wrap(ASN1_SEQUENCE, "cm" , ASN1_senderNonce_oid , asn1_wrap(ASN1_SET, "m" , asn1_simple_object(ASN1_OCTET_STRING, senderNonce) ) ); }
/** * build a DER-encoded contentInfo object */ static chunk_t pkcs7_build_contentInfo(contentInfo_t *cInfo) { return (cInfo->content.ptr) ? asn1_wrap(ASN1_SEQUENCE, "mm", asn1_build_known_oid(cInfo->type), asn1_simple_object(ASN1_CONTEXT_C_0, cInfo->content)) : asn1_build_known_oid(cInfo->type); }
/** * build issuerAndSerialNumber object */ chunk_t pkcs7_build_issuerAndSerialNumber(certificate_t *cert) { identification_t *issuer = cert->get_issuer(cert); x509_t *x509 = (x509_t*)cert; return asn1_wrap(ASN1_SEQUENCE, "cm", issuer->get_encoding(issuer), asn1_integer("c", x509->get_serial(x509))); }
/** * Builds a senderNonce attribute */ chunk_t scep_senderNonce_attribute(void) { const size_t nonce_len = 16; u_char nonce_buf[nonce_len]; chunk_t senderNonce = { nonce_buf, nonce_len }; rng_t *rng; rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); rng->get_bytes(rng, nonce_len, nonce_buf); rng->destroy(rng); return asn1_wrap(ASN1_SEQUENCE, "cm" , ASN1_senderNonce_oid , asn1_wrap(ASN1_SET, "m" , asn1_simple_object(ASN1_OCTET_STRING, senderNonce) ) ); }
/* * build an X.509 attribute certificate */ chunk_t build_attr_cert(void) { chunk_t attributeCertificateInfo = build_attr_cert_info(); chunk_t signatureValue = pkcs1_build_signature(attributeCertificateInfo , OID_SHA1, signerkey, TRUE); return asn1_wrap(ASN1_SEQUENCE, "mcm" , attributeCertificateInfo , ASN1_sha1WithRSA_id , signatureValue); }
/** * create a signed pkcs7 contentInfo object */ chunk_t pkcs7_build_signedData(chunk_t data, chunk_t attributes, certificate_t *cert, int digest_alg, private_key_t *key) { contentInfo_t pkcs7Data, signedData; chunk_t authenticatedAttributes = chunk_empty; chunk_t encryptedDigest = chunk_empty; chunk_t signerInfo, cInfo, signature, encoding = chunk_empty;; signature_scheme_t scheme = signature_scheme_from_oid(digest_alg); if (attributes.ptr) { if (key->sign(key, scheme, attributes, &signature)) { encryptedDigest = asn1_wrap(ASN1_OCTET_STRING, "m", signature); authenticatedAttributes = chunk_clone(attributes); *authenticatedAttributes.ptr = ASN1_CONTEXT_C_0; } } else if (data.ptr) { if (key->sign(key, scheme, data, &signature)) { encryptedDigest = asn1_wrap(ASN1_OCTET_STRING, "m", signature); } } signerInfo = asn1_wrap(ASN1_SEQUENCE, "cmmmmm" , ASN1_INTEGER_1 , pkcs7_build_issuerAndSerialNumber(cert) , asn1_algorithmIdentifier(digest_alg) , authenticatedAttributes , asn1_algorithmIdentifier(OID_RSA_ENCRYPTION) , encryptedDigest); pkcs7Data.type = OID_PKCS7_DATA; pkcs7Data.content = (data.ptr == NULL)? chunk_empty : asn1_simple_object(ASN1_OCTET_STRING, data); cert->get_encoding(cert, CERT_ASN1_DER, &encoding); signedData.type = OID_PKCS7_SIGNED_DATA; signedData.content = asn1_wrap(ASN1_SEQUENCE, "cmmmm" , ASN1_INTEGER_1 , asn1_wrap(ASN1_SET, "m", asn1_algorithmIdentifier(digest_alg)) , pkcs7_build_contentInfo(&pkcs7Data) , asn1_wrap(ASN1_CONTEXT_C_0, "m", encoding) , asn1_wrap(ASN1_SET, "m", signerInfo)); cInfo = pkcs7_build_contentInfo(&signedData); DBG3(DBG_LIB, "signedData %B", &cInfo); free(pkcs7Data.content.ptr); free(signedData.content.ptr); return cInfo; }
/* * build attributeCertificateInfo */ static chunk_t build_attr_cert_info(void) { return asn1_wrap(ASN1_SEQUENCE, "cmmcmmmm" , ASN1_INTEGER_1 , build_holder() , build_v2_form() , ASN1_sha1WithRSA_id , asn1_simple_object(ASN1_INTEGER, serial) , build_attr_cert_validity() , build_attributes() , build_extensions()); }
END_TEST /******************************************************************************* * parse_algorithm_identifier */ START_TEST(test_asn1_parse_algorithmIdentifier) { typedef struct { int alg; bool empty; chunk_t parameters; } testdata_t; testdata_t test[] = { { OID_ECDSA_WITH_SHA1, TRUE, chunk_empty }, { OID_SHA1_WITH_RSA, TRUE, chunk_from_chars(0x05, 0x00) }, { OID_3DES_EDE_CBC, FALSE, chunk_from_chars(0x04, 0x01, 0xaa) }, { OID_PBKDF2, FALSE, chunk_from_chars(0x30, 0x01, 0xaa) } }; chunk_t algid, parameters; int i, alg; for (i = 0; i < countof(test); i++) { algid = asn1_wrap(ASN1_SEQUENCE, "mc", asn1_build_known_oid(test[i].alg), test[i].parameters); parameters = chunk_empty; if (i == 2) { alg = asn1_parse_algorithmIdentifier(algid, 0, NULL); } else { alg = asn1_parse_algorithmIdentifier(algid, 0, ¶meters); if (test[i].empty) { ck_assert(parameters.len == 0 && parameters.ptr == NULL); } else { ck_assert(chunk_equals(parameters, test[i].parameters)); } } ck_assert(alg == test[i].alg); chunk_free(&algid); } }
/* * build attributes */ static chunk_t build_ietfAttributes(ietfAttrList_t *list) { chunk_t ietfAttributes; ietfAttrList_t *item = list; size_t size = 0; u_char *pos; /* precalculate the total size of all values */ while (item != NULL) { size_t len = item->attr->value.len; size += 1 + (len > 0) + (len >= 128) + (len >= 256) + (len >= 65536) + len; item = item->next; } pos = build_asn1_object(&ietfAttributes, ASN1_SEQUENCE, size); while (list != NULL) { ietfAttr_t *attr = list->attr; asn1_t type = ASN1_NULL; switch (attr->kind) { case IETF_ATTRIBUTE_OCTETS: type = ASN1_OCTET_STRING; break; case IETF_ATTRIBUTE_STRING: type = ASN1_UTF8STRING; break; case IETF_ATTRIBUTE_OID: type = ASN1_OID; break; } mv_chunk(&pos, asn1_simple_object(type, attr->value)); list = list->next; } return asn1_wrap(ASN1_SEQUENCE, "m", ietfAttributes); }
/* * Defined in header. */ chunk_t asn1_algorithmIdentifier(int oid) { chunk_t parameters; /* some algorithmIdentifiers have a NULL parameters field and some do not */ switch (oid) { case OID_ECDSA_WITH_SHA1: case OID_ECDSA_WITH_SHA224: case OID_ECDSA_WITH_SHA256: case OID_ECDSA_WITH_SHA384: case OID_ECDSA_WITH_SHA512: parameters = chunk_empty; break; default: parameters = asn1_simple_object(ASN1_NULL, chunk_empty); break; } return asn1_wrap(ASN1_SEQUENCE, "mm", asn1_build_known_oid(oid), parameters); }
/** * Generate a transaction id as the MD5 hash of an public key * the transaction id is also used as a unique serial number */ void scep_generate_transaction_id(public_key_t *key, chunk_t *transID, chunk_t *serialNumber) { chunk_t digest = chunk_alloca(HASH_SIZE_MD5); chunk_t keyEncoding = chunk_empty, keyInfo; hasher_t *hasher; bool msb_set; u_char *pos; key->get_encoding(key, PUBKEY_ASN1_DER, &keyEncoding); keyInfo = asn1_wrap(ASN1_SEQUENCE, "mm", asn1_algorithmIdentifier(OID_RSA_ENCRYPTION), asn1_bitstring("m", keyEncoding)); hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5); if (!hasher || !hasher->get_hash(hasher, keyInfo, digest.ptr)) { memset(digest.ptr, 0, digest.len); } DESTROY_IF(hasher); free(keyInfo.ptr); /* is the most significant bit of the digest set? */ msb_set = (*digest.ptr & 0x80) == 0x80; /* allocate space for the serialNumber */ serialNumber->len = msb_set + digest.len; serialNumber->ptr = malloc(serialNumber->len); /* the serial number as the two's complement of the digest */ pos = serialNumber->ptr; if (msb_set) { *pos++ = 0x00; } memcpy(pos, digest.ptr, digest.len); /* the transaction id is the serial number in hex format */ *transID = chunk_to_hex(digest, NULL, TRUE); }
/** * Builds a pkcs7 enveloped and signed scep request */ chunk_t scep_build_request(chunk_t data, chunk_t transID, scep_msg_t msg, certificate_t *enc_cert, int enc_alg, certificate_t *signer_cert, int digest_alg, private_key_t *private_key) { chunk_t envelopedData, attributes, request; envelopedData = pkcs7_build_envelopedData(data, enc_cert, enc_alg); attributes = asn1_wrap(ASN1_SET, "mmmmm" , pkcs7_contentType_attribute() , pkcs7_messageDigest_attribute(envelopedData , digest_alg) , scep_transId_attribute(transID) , scep_messageType_attribute(msg) , scep_senderNonce_attribute()); request = pkcs7_build_signedData(envelopedData, attributes , signer_cert, digest_alg, private_key); free(envelopedData.ptr); free(attributes.ptr); return request; }
/** * Load a generic public key from an SSH key blob */ static sshkey_public_key_t *parse_public_key(chunk_t blob) { bio_reader_t *reader; chunk_t format; reader = bio_reader_create(blob); if (!reader->read_data32(reader, &format)) { DBG1(DBG_LIB, "invalid key format in SSH key"); reader->destroy(reader); return NULL; } if (chunk_equals(format, chunk_from_str("ssh-rsa"))) { chunk_t n, e; if (!reader->read_data32(reader, &e) || !reader->read_data32(reader, &n)) { DBG1(DBG_LIB, "invalid RSA key in SSH key"); reader->destroy(reader); return NULL; } reader->destroy(reader); return lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_RSA, BUILD_RSA_MODULUS, n, BUILD_RSA_PUB_EXP, e, BUILD_END); } else if (format.len > strlen(ECDSA_PREFIX) && strneq(format.ptr, ECDSA_PREFIX, strlen(ECDSA_PREFIX))) { chunk_t ec_blob, identifier, q, oid, encoded; sshkey_public_key_t *key; ec_blob = reader->peek(reader); reader->destroy(reader); reader = bio_reader_create(ec_blob); if (!reader->read_data32(reader, &identifier) || !reader->read_data32(reader, &q)) { DBG1(DBG_LIB, "invalid ECDSA key in SSH key"); reader->destroy(reader); return NULL; } oid = parse_ec_identifier(identifier); if (!oid.ptr) { DBG1(DBG_LIB, "invalid ECDSA key identifier in SSH key"); reader->destroy(reader); return NULL; } reader->destroy(reader); /* build key from subjectPublicKeyInfo */ encoded = asn1_wrap(ASN1_SEQUENCE, "mm", asn1_wrap(ASN1_SEQUENCE, "mm", asn1_build_known_oid(OID_EC_PUBLICKEY), oid), asn1_bitstring("c", q)); key = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_ECDSA, BUILD_BLOB_ASN1_DER, encoded, BUILD_END); chunk_free(&encoded); return key; } DBG1(DBG_LIB, "unsupported SSH key format %.*s", (int)format.len, format.ptr); reader->destroy(reader); return NULL; }
/* * build directoryName */ static chunk_t build_directoryName(asn1_t tag, chunk_t name) { return asn1_wrap(tag, "m" , asn1_simple_object(ASN1_CONTEXT_C_4, name)); }
/* * build v2Form */ static chunk_t build_v2_form(void) { return asn1_wrap(ASN1_CONTEXT_C_0, "m" , build_directoryName(ASN1_SEQUENCE, signer->subject)); }
static chunk_t build_attributes (void) { return asn1_wrap (build_attribute_type (ASN1_group_oid, ietfAttr_list_encode (groups))); }
void build_attr_cert (void) { asn1_wrap (build_attributes ()); }
/** * create a symmetrically encrypted pkcs7 contentInfo object */ chunk_t pkcs7_build_envelopedData(chunk_t data, certificate_t *cert, int enc_alg) { encryption_algorithm_t alg; size_t alg_key_size; chunk_t symmetricKey, protectedKey, iv, in, out; crypter_t *crypter; alg = encryption_algorithm_from_oid(enc_alg, &alg_key_size); crypter = lib->crypto->create_crypter(lib->crypto, alg, alg_key_size/BITS_PER_BYTE); if (crypter == NULL) { DBG1(DBG_LIB, "crypter for %N not available", encryption_algorithm_names, alg); return chunk_empty; } /* generate a true random symmetric encryption key and a pseudo-random iv */ { rng_t *rng; rng = lib->crypto->create_rng(lib->crypto, RNG_TRUE); rng->allocate_bytes(rng, crypter->get_key_size(crypter), &symmetricKey); DBG4(DBG_LIB, "symmetric encryption key %B", &symmetricKey); rng->destroy(rng); rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); rng->allocate_bytes(rng, crypter->get_iv_size(crypter), &iv); DBG4(DBG_LIB, "initialization vector: %B", &iv); rng->destroy(rng); } /* pad the data to a multiple of the block size */ { size_t block_size = crypter->get_block_size(crypter); size_t padding = block_size - data.len % block_size; in.len = data.len + padding; in.ptr = malloc(in.len); DBG2(DBG_LIB, "padding %u bytes of data to multiple block size of %u bytes", data.len, in.len); /* copy data */ memcpy(in.ptr, data.ptr, data.len); /* append padding */ memset(in.ptr + data.len, padding, padding); } DBG3(DBG_LIB, "padded unencrypted data %B", &in); /* symmetric encryption of data object */ crypter->set_key(crypter, symmetricKey); crypter->encrypt(crypter, in, iv, &out); crypter->destroy(crypter); chunk_clear(&in); DBG3(DBG_LIB, "encrypted data %B", &out); /* protect symmetric key by public key encryption */ { public_key_t *key = cert->get_public_key(cert); if (key == NULL) { DBG1(DBG_LIB, "public key not found in encryption certificate"); chunk_clear(&symmetricKey); chunk_free(&iv); chunk_free(&out); return chunk_empty; } key->encrypt(key, ENCRYPT_RSA_PKCS1, symmetricKey, &protectedKey); key->destroy(key); } /* build pkcs7 enveloped data object */ { chunk_t contentEncryptionAlgorithm = asn1_wrap(ASN1_SEQUENCE, "mm" , asn1_build_known_oid(enc_alg) , asn1_simple_object(ASN1_OCTET_STRING, iv)); chunk_t encryptedContentInfo = asn1_wrap(ASN1_SEQUENCE, "mmm" , asn1_build_known_oid(OID_PKCS7_DATA) , contentEncryptionAlgorithm , asn1_wrap(ASN1_CONTEXT_S_0, "m", out)); chunk_t encryptedKey = asn1_wrap(ASN1_OCTET_STRING, "m" , protectedKey); chunk_t recipientInfo = asn1_wrap(ASN1_SEQUENCE, "cmmm" , ASN1_INTEGER_0 , pkcs7_build_issuerAndSerialNumber(cert) , asn1_algorithmIdentifier(OID_RSA_ENCRYPTION) , encryptedKey); chunk_t cInfo; contentInfo_t envelopedData; envelopedData.type = OID_PKCS7_ENVELOPED_DATA; envelopedData.content = asn1_wrap(ASN1_SEQUENCE, "cmm" , ASN1_INTEGER_0 , asn1_wrap(ASN1_SET, "m", recipientInfo) , encryptedContentInfo); cInfo = pkcs7_build_contentInfo(&envelopedData); DBG3(DBG_LIB, "envelopedData %B", &cInfo); chunk_free(&envelopedData.content); chunk_free(&iv); chunk_clear(&symmetricKey); return cInfo; } }