+
+ _assert(PKCS7_set_type(value_, NID_pkcs7_signed));
+ _assert(PKCS7_content_new(value_, NID_pkcs7_data));
+
+ STACK_OF(X509) *certs(stuff);
+ for (unsigned i(0), e(sk_X509_num(certs)); i != e; i++)
+ _assert(PKCS7_add_certificate(value_, sk_X509_value(certs, e - i - 1)));
+
+ STACK_OF(X509_ATTRIBUTE) *attributes(sk_X509_ATTRIBUTE_new_null());
+ _assert(attributes != NULL);
+ _scope({ sk_X509_ATTRIBUTE_pop_free(attributes, X509_ATTRIBUTE_free); });
+
+
+ // XXX: this is the same as PKCS7_sign_add_signer(value_, stuff, stuff, NULL, PKCS7_NOSMIMECAP)
+ _assert(X509_check_private_key(stuff, stuff));
+ auto info(PKCS7_add_signature(value_, stuff, stuff, EVP_sha1()));
+ _assert(info != NULL);
+ _assert(PKCS7_add_certificate(value_, stuff));
+
+ {
+ auto attribute(X509_ATTRIBUTE_create(NID_pkcs9_contentType, V_ASN1_OBJECT, OBJ_nid2obj(NID_pkcs7_data)));
+ _assert(attribute != NULL);
+ _assert(sk_X509_ATTRIBUTE_push(attributes, attribute) != 0);
+ }
+
+ {
+ auto cdattr(APPLE_CDATTR_new());
+ _assert(cdattr != NULL);
+ _scope({ APPLE_CDATTR_free(cdattr); });
+
+ static auto nid(OBJ_create("1.2.840.113635.100.9.2", "", ""));
+ cdattr->object = OBJ_nid2obj(nid);
+
+ for (Algorithm *pointer : GetAlgorithms()) {
+ Algorithm &algorithm(*pointer);
+ APPLE_CDHASH *cdhash(APPLE_CDHASH_new());
+ _assert(cdhash != NULL);
+ _assert(sk_push((_STACK *) cdattr->cdhashes, cdhash) != 0);
+ cdhash->algorithm = OBJ_nid2obj(algorithm.nid_);
+ Octet string(algorithm[hash], algorithm.size_);
+ cdhash->value = string;
+ string.release();
+ }
+
+ // in e20b57270dece66ce2c68aeb5d14dd6d9f3c5d68 OpenSSL removed a "hack"
+ // in the process, they introduced a useful bug in X509_ATTRIBUTE_set1_data
+ // however, I don't want to rely on that or detect the bypass before it
+ // so, instead, I create my own compatible attribute and re-serialize it :/
+
+ ASN1_STRING *seq(ASN1_STRING_new());
+ _assert(seq != NULL);
+ _scope({ ASN1_STRING_free(seq); });
+ seq->length = ASN1_item_i2d((ASN1_VALUE *) cdattr, &seq->data, ASN1_ITEM_rptr(APPLE_CDATTR));
+
+ X509_ATTRIBUTE *attribute(NULL);
+ const unsigned char *data(seq->data);
+ _assert(d2i_X509_ATTRIBUTE(&attribute, &data, seq->length) != 0);
+ _assert(attribute != NULL);
+ _assert(sk_X509_ATTRIBUTE_push(attributes, attribute) != 0);
+ }
+
+ {
+ // XXX: move the "cdhashes" plist code to here and remove xml argument
+
+ Octet string(xml);
+ static auto nid(OBJ_create("1.2.840.113635.100.9.1", "", ""));
+ auto attribute(X509_ATTRIBUTE_create(nid, V_ASN1_OCTET_STRING, string));
+ _assert(attribute != NULL);
+ string.release();
+ _assert(sk_X509_ATTRIBUTE_push(attributes, attribute) != 0);
+ }
+
+ _assert(PKCS7_set_signed_attributes(info, attributes) != 0);
+ PKCS7_set_detached(value_, 1);
+
+ // XXX: this is the same as PKCS7_final(value_, data, PKCS7_BINARY)
+ BIO *bio(PKCS7_dataInit(value_, NULL));
+ _assert(bio != NULL);
+ _scope({ BIO_free_all(bio); });
+ SMIME_crlf_copy(data, bio, PKCS7_BINARY);
+ BIO_flush(bio);
+ _assert(PKCS7_dataFinal(value_, bio));