From 0c1862732fec0f6c9a4ed8221865d3e048f8c87c Mon Sep 17 00:00:00 2001 From: "@rubynerd" Date: Mon, 15 Mar 2021 00:37:49 +0000 Subject: [PATCH 1/4] add PKCS7 "partial" flag support to OpenSSL::PKCS7.sign --- ext/openssl/ossl_pkcs7.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/ext/openssl/ossl_pkcs7.c b/ext/openssl/ossl_pkcs7.c index 0bcc76a9f..5448287e9 100644 --- a/ext/openssl/ossl_pkcs7.c +++ b/ext/openssl/ossl_pkcs7.c @@ -230,12 +230,16 @@ ossl_pkcs7_s_sign(int argc, VALUE *argv, VALUE klass) pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */ flg = NIL_P(flags) ? 0 : NUM2INT(flags); ret = NewPKCS7(cPKCS7); - in = ossl_obj2bio(&data); + + if (!(flg & PKCS7_PARTIAL)) + in = ossl_obj2bio(&data); + if(NIL_P(certs)) x509s = NULL; else{ x509s = ossl_protect_x509_ary2sk(certs, &status); if(status){ - BIO_free(in); + if (!(flg & PKCS7_PARTIAL)) + BIO_free(in); rb_jump_tag(status); } } @@ -244,10 +248,17 @@ ossl_pkcs7_s_sign(int argc, VALUE *argv, VALUE klass) sk_X509_pop_free(x509s, X509_free); ossl_raise(ePKCS7Error, NULL); } + SetPKCS7(ret, pkcs7); - ossl_pkcs7_set_data(ret, data); + + if (!(flg & PKCS7_PARTIAL)) + ossl_pkcs7_set_data(ret, data); + ossl_pkcs7_set_err_string(ret, Qnil); - BIO_free(in); + + if (!(flg & PKCS7_PARTIAL)) + BIO_free(in); + sk_X509_pop_free(x509s, X509_free); return ret; @@ -1080,4 +1091,5 @@ Init_ossl_pkcs7(void) DefPKCS7Const(BINARY); DefPKCS7Const(NOATTR); DefPKCS7Const(NOSMIMECAP); + DefPKCS7Const(PARTIAL); } From 879c13988e848cbbc520056559c2bb6dfe7442f2 Mon Sep 17 00:00:00 2001 From: "@rubynerd" Date: Mon, 15 Mar 2021 00:39:21 +0000 Subject: [PATCH 2/4] add draft implementation of OpenSSL::PKCS7#finalize / PKCS7_final --- ext/openssl/ossl_pkcs7.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/ext/openssl/ossl_pkcs7.c b/ext/openssl/ossl_pkcs7.c index 5448287e9..c18cbb637 100644 --- a/ext/openssl/ossl_pkcs7.c +++ b/ext/openssl/ossl_pkcs7.c @@ -874,6 +874,24 @@ ossl_pkcs7_to_pem(VALUE self) return str; } +static VALUE +ossl_pkcs7_finalize(VALUE self, VALUE data, VALUE flags) +{ + PKCS7 *pkcs7; + BIO *in; + + GetPKCS7(self, pkcs7); + + int flg = NIL_P(flags) ? 0 : NUM2INT(flags); + in = ossl_obj2bio(&data); + + if (!PKCS7_final(pkcs7, in, flg)) { + ossl_raise(ePKCS7Error, NULL); + }; + + return self; +} + /* * SIGNER INFO */ @@ -1063,6 +1081,7 @@ Init_ossl_pkcs7(void) rb_define_method(cPKCS7, "to_pem", ossl_pkcs7_to_pem, 0); rb_define_alias(cPKCS7, "to_s", "to_pem"); rb_define_method(cPKCS7, "to_der", ossl_pkcs7_to_der, 0); + rb_define_method(cPKCS7, "finalize", ossl_pkcs7_finalize, 2); cPKCS7Signer = rb_define_class_under(cPKCS7, "SignerInfo", rb_cObject); rb_define_const(cPKCS7, "Signer", cPKCS7Signer); From 45d19cba2ae0460098d834dd6eddc26ba7f7d849 Mon Sep 17 00:00:00 2001 From: "@rubynerd" Date: Mon, 15 Mar 2021 00:40:00 +0000 Subject: [PATCH 3/4] WIP implementation of PKCS7::SignerInfo#add_signed_attribute --- ext/openssl/ossl_pkcs7.c | 84 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/ext/openssl/ossl_pkcs7.c b/ext/openssl/ossl_pkcs7.c index c18cbb637..9e362b07e 100644 --- a/ext/openssl/ossl_pkcs7.c +++ b/ext/openssl/ossl_pkcs7.c @@ -972,6 +972,89 @@ ossl_pkcs7si_get_signed_time(VALUE self) return Qnil; } +static VALUE +ossl_pkcs7si_add_signed_attribute(VALUE self, VALUE oid, VALUE value) { + // PKCS7_add_signed_attribute(PKCS7_SIGNER_INFO *si, int nid, int attrtype, void *value) + // + // argument sources: + // si - signer_info from OpenSSL, OpenSSL::PKCS7#signers.first, then rubyobj -> OpenSSL struct + // nid - "numerical id" of OID, how to pivot from OpenSSL::ASN1::ObjectId??? + // - nightmare + // - ObjectId has little connection with reality, it's a subclass of Primitive + // attrtype - "and it adds a new ASN.1 ANY object of type attrtype with the given value to the new attribute." + // - what is this? + // value - void PTR, ossl_asn1_get_asn1type has a case statement that produces a correct value + + PKCS7_SIGNER_INFO *p7si; + ASN1_OBJECT *a1obj; + ASN1_TYPE *value_as_type; // hacks to use ossl_asn1_get_asn1type's case statement + int nid, tag = 0; + + GetPKCS7si(self, p7si); + + // convert OpenSSL::ASN1::ObjectId to a nid + a1obj = obj_to_asn1obj(ossl_asn1_get_value(oid)); // TODO: error check + nid = OBJ_obj2nid(a1obj); // TODO: error check (NID_undef) + // it's completely possible someone's using an unknown NID here + // we should raise an informative error if this happens + + // so about attrtype... + // i'm assuming this would be something like "OpenSSL::ASN1::Sequence" in Ruby + // we can determine attrtype from the "value", as essentially the "value" is an attrtype plus value + // so attrtype is eventually passed into ASN1_TYPE_set + // ASN1_type_set docs makes explicit references to "V_ASN1_SEQUENCE"/"V_ASN1_BOOLEAN"/"V_ASN1_OTHER" + // so i'm now pretty convinced i should be able to get this from a1obj + + // though should we use ossl_asn1_get_tag? + // ossl_asn1_tag takes VALUE obj -> int + tag = ossl_asn1_tag(value); // TODO: error check + + // how tf do we go from value -> ruby -> openssl somehow? -> "value pointer" + // maybe this: + // value = ossl_asn1_get_value(obj); // no, becaue value is a ruby — ossl_asn1_get_asn1type might have something useful + + // struct asn1_object_st { + // const char *sn, *ln; + // int nid; + // int length; + // const unsigned char *data; + // int flags; + // } + + + // "void *value" goes PKCS7_signed_attribute -> add_attribute -> X509_ATTRIBUTE_create (x_attrib.c) + // -> ASN1_TYPE_set(val, atrtype, value) + // -> a->value.ptr = value; + // a is an ASN1_TYPE struct (asn1_type_st) + // include/openssl/asn1.h.in as: + // + // struct asn1_type_st { + // int type; + // union { + // char *ptr; + // // a bunch of other specifically typed attributes like: + // ASN1_BOOLEAN boolean; + // ASN1_UNIVERSALSTRING *universalstring; + // } + // very unclear on if this matters though? + // + // so what do we need? i'm still no closer to actually answering that question + // i can get an ASN1_OBJECT but that's still not much use to me + // + // i can actually get an ASN1_TYPE from ossl_asn1_get_asn1type, which calls + // ASN1_TYPE_set under the hood via a crazy case statement. + value_as_type = ossl_asn1_get_asn1type(value); + + // method sig would be .add_signed_attribute(oid/type, value) + // where ObjectId is actually a Primitive (???) so how do I handle that? + // both oid and value are ultimately primitives tbh + PKCS7_add_signed_attribute(p7si, nid, tag, value_as_type.value); + + // return the value of the attribute we've just stuck in + return value; +} + + /* * RECIPIENT INFO */ @@ -1090,6 +1173,7 @@ Init_ossl_pkcs7(void) rb_define_method(cPKCS7Signer, "issuer", ossl_pkcs7si_get_issuer, 0); rb_define_method(cPKCS7Signer, "serial", ossl_pkcs7si_get_serial,0); rb_define_method(cPKCS7Signer,"signed_time",ossl_pkcs7si_get_signed_time,0); + rb_define_method(cPKCS7Signer, "add_signed_attribute", ossl_pkcs7si_add_signed_attribute, 2); cPKCS7Recipient = rb_define_class_under(cPKCS7,"RecipientInfo",rb_cObject); rb_define_alloc_func(cPKCS7Recipient, ossl_pkcs7ri_alloc); From 4b95ee660ba6b2ed952859382faac8f31cea9002 Mon Sep 17 00:00:00 2001 From: "@rubynerd" Date: Mon, 22 Mar 2021 21:35:31 +0000 Subject: [PATCH 4/4] replace add_signed_attribute with set_signed_attributes --- ext/openssl/ossl.c | 1 + ext/openssl/ossl.h | 1 + ext/openssl/ossl_pkcs7.c | 93 +++++++------------------------------ ext/openssl/ossl_x509.h | 1 + ext/openssl/ossl_x509attr.c | 15 ++++++ 5 files changed, 34 insertions(+), 77 deletions(-) diff --git a/ext/openssl/ossl.c b/ext/openssl/ossl.c index 70b6a6a80..cf838c7cb 100644 --- a/ext/openssl/ossl.c +++ b/ext/openssl/ossl.c @@ -61,6 +61,7 @@ ossl_##name##_ary2sk(VALUE ary) \ return sk; \ } OSSL_IMPL_ARY2SK(x509, X509, cX509Cert, DupX509CertPtr) +OSSL_IMPL_ARY2SK(x509_attr, X509_ATTRIBUTE, cX509Attr, DupX509AttrPtr) #define OSSL_IMPL_SK2ARY(name, type) \ VALUE \ diff --git a/ext/openssl/ossl.h b/ext/openssl/ossl.h index 577eb6d6b..214404174 100644 --- a/ext/openssl/ossl.h +++ b/ext/openssl/ossl.h @@ -80,6 +80,7 @@ extern VALUE eOSSLError; */ STACK_OF(X509) *ossl_x509_ary2sk(VALUE); STACK_OF(X509) *ossl_protect_x509_ary2sk(VALUE,int*); +STACK_OF(X509_ATTRIBUTE) *ossl_protect_x509_attr_ary2sk(VALUE,int*); VALUE ossl_x509_sk2ary(const STACK_OF(X509) *certs); VALUE ossl_x509crl_sk2ary(const STACK_OF(X509_CRL) *crl); VALUE ossl_x509name_sk2ary(const STACK_OF(X509_NAME) *names); diff --git a/ext/openssl/ossl_pkcs7.c b/ext/openssl/ossl_pkcs7.c index 9e362b07e..ba01a6308 100644 --- a/ext/openssl/ossl_pkcs7.c +++ b/ext/openssl/ossl_pkcs7.c @@ -973,87 +973,26 @@ ossl_pkcs7si_get_signed_time(VALUE self) } static VALUE -ossl_pkcs7si_add_signed_attribute(VALUE self, VALUE oid, VALUE value) { - // PKCS7_add_signed_attribute(PKCS7_SIGNER_INFO *si, int nid, int attrtype, void *value) - // - // argument sources: - // si - signer_info from OpenSSL, OpenSSL::PKCS7#signers.first, then rubyobj -> OpenSSL struct - // nid - "numerical id" of OID, how to pivot from OpenSSL::ASN1::ObjectId??? - // - nightmare - // - ObjectId has little connection with reality, it's a subclass of Primitive - // attrtype - "and it adds a new ASN.1 ANY object of type attrtype with the given value to the new attribute." - // - what is this? - // value - void PTR, ossl_asn1_get_asn1type has a case statement that produces a correct value - +ossl_pkcs7si_set_signed_attributes(VALUE self, VALUE ary) +{ PKCS7_SIGNER_INFO *p7si; - ASN1_OBJECT *a1obj; - ASN1_TYPE *value_as_type; // hacks to use ossl_asn1_get_asn1type's case statement - int nid, tag = 0; + STACK_OF(X509_ATTRIBUTE) *sk; + int status, result; GetPKCS7si(self, p7si); + Check_Type(ary, T_ARRAY); - // convert OpenSSL::ASN1::ObjectId to a nid - a1obj = obj_to_asn1obj(ossl_asn1_get_value(oid)); // TODO: error check - nid = OBJ_obj2nid(a1obj); // TODO: error check (NID_undef) - // it's completely possible someone's using an unknown NID here - // we should raise an informative error if this happens - - // so about attrtype... - // i'm assuming this would be something like "OpenSSL::ASN1::Sequence" in Ruby - // we can determine attrtype from the "value", as essentially the "value" is an attrtype plus value - // so attrtype is eventually passed into ASN1_TYPE_set - // ASN1_type_set docs makes explicit references to "V_ASN1_SEQUENCE"/"V_ASN1_BOOLEAN"/"V_ASN1_OTHER" - // so i'm now pretty convinced i should be able to get this from a1obj - - // though should we use ossl_asn1_get_tag? - // ossl_asn1_tag takes VALUE obj -> int - tag = ossl_asn1_tag(value); // TODO: error check - - // how tf do we go from value -> ruby -> openssl somehow? -> "value pointer" - // maybe this: - // value = ossl_asn1_get_value(obj); // no, becaue value is a ruby — ossl_asn1_get_asn1type might have something useful - - // struct asn1_object_st { - // const char *sn, *ln; - // int nid; - // int length; - // const unsigned char *data; - // int flags; - // } - - - // "void *value" goes PKCS7_signed_attribute -> add_attribute -> X509_ATTRIBUTE_create (x_attrib.c) - // -> ASN1_TYPE_set(val, atrtype, value) - // -> a->value.ptr = value; - // a is an ASN1_TYPE struct (asn1_type_st) - // include/openssl/asn1.h.in as: - // - // struct asn1_type_st { - // int type; - // union { - // char *ptr; - // // a bunch of other specifically typed attributes like: - // ASN1_BOOLEAN boolean; - // ASN1_UNIVERSALSTRING *universalstring; - // } - // very unclear on if this matters though? - // - // so what do we need? i'm still no closer to actually answering that question - // i can get an ASN1_OBJECT but that's still not much use to me - // - // i can actually get an ASN1_TYPE from ossl_asn1_get_asn1type, which calls - // ASN1_TYPE_set under the hood via a crazy case statement. - value_as_type = ossl_asn1_get_asn1type(value); - - // method sig would be .add_signed_attribute(oid/type, value) - // where ObjectId is actually a Primitive (???) so how do I handle that? - // both oid and value are ultimately primitives tbh - PKCS7_add_signed_attribute(p7si, nid, tag, value_as_type.value); - - // return the value of the attribute we've just stuck in - return value; -} + // TODO: reset attributes + + // build list of x509 attrs of length RARRAY_LEN(ary) + sk = ossl_protect_x509_attr_ary2sk(ary, &status); + result = PKCS7_set_signed_attributes(p7si, sk); + + fprintf(stderr, "set signed attributes result is: '%d'\n", result); + + return Qtrue; +} /* * RECIPIENT INFO @@ -1173,7 +1112,7 @@ Init_ossl_pkcs7(void) rb_define_method(cPKCS7Signer, "issuer", ossl_pkcs7si_get_issuer, 0); rb_define_method(cPKCS7Signer, "serial", ossl_pkcs7si_get_serial,0); rb_define_method(cPKCS7Signer,"signed_time",ossl_pkcs7si_get_signed_time,0); - rb_define_method(cPKCS7Signer, "add_signed_attribute", ossl_pkcs7si_add_signed_attribute, 2); + rb_define_method(cPKCS7Signer, "signed_attributes=", ossl_pkcs7si_set_signed_attributes, 1); cPKCS7Recipient = rb_define_class_under(cPKCS7,"RecipientInfo",rb_cObject); rb_define_alloc_func(cPKCS7Recipient, ossl_pkcs7ri_alloc); diff --git a/ext/openssl/ossl_x509.h b/ext/openssl/ossl_x509.h index 4fadfa6b8..40252ce73 100644 --- a/ext/openssl/ossl_x509.h +++ b/ext/openssl/ossl_x509.h @@ -32,6 +32,7 @@ extern VALUE eX509AttrError; VALUE ossl_x509attr_new(X509_ATTRIBUTE *); X509_ATTRIBUTE *GetX509AttrPtr(VALUE); +X509_ATTRIBUTE *DupX509AttrPtr(VALUE); void Init_ossl_x509attr(void); /* diff --git a/ext/openssl/ossl_x509attr.c b/ext/openssl/ossl_x509attr.c index 60846cfe9..184ca2a1e 100644 --- a/ext/openssl/ossl_x509attr.c +++ b/ext/openssl/ossl_x509attr.c @@ -77,6 +77,21 @@ GetX509AttrPtr(VALUE obj) return attr; } +X509_ATTRIBUTE * +DupX509AttrPtr(VALUE obj) +{ + X509_ATTRIBUTE *attr, *new; + + GetX509Attr(obj, attr); + + // XXX: maybe missing an up_ref here? + if (!(new = X509_ATTRIBUTE_dup(attr))) { + fprintf(stderr, "dup failed unlucky\n"); + } + + return new; +} + /* * Private */