ruby-changes:43335
From: rhe <ko1@a...>
Date: Tue, 14 Jun 2016 21:41:00 +0900 (JST)
Subject: [ruby-changes:43335] rhe:r55409 (trunk): openssl: add missing #to_der to OCSP::{CertificateId, BasicResponse}
rhe 2016-06-14 21:40:55 +0900 (Tue, 14 Jun 2016) New Revision: 55409 https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=55409 Log: openssl: add missing #to_der to OCSP::{CertificateId,BasicResponse} * ext/openssl/ossl_ocsp.c (ossl_ocspbres_to_der, ossl_ocspcid_to_der): Implement #to_der methods for OCSP::BasicResponse and OCSP::CertificateId. (ossl_ocspreq_initialize, ossl_ocspres_initialize): Use GetOCSP*() instead of raw DATA_PTR(). (ossl_ocspbres_initialize, ossl_ocspcid_initialize): Allow initializing from DER string. (Init_ossl_ocsp): Define new #to_der methods. * test/openssl/test_ocsp.rb: Test these changes. Also add missing tests for OCSP::{Response,Request}#to_der. Modified files: trunk/ChangeLog trunk/ext/openssl/ossl_ocsp.c trunk/test/openssl/test_ocsp.rb Index: test/openssl/test_ocsp.rb =================================================================== --- test/openssl/test_ocsp.rb (revision 55408) +++ test/openssl/test_ocsp.rb (revision 55409) @@ -8,6 +8,10 @@ class OpenSSL::TestOCSP < OpenSSL::TestC https://github.com/ruby/ruby/blob/trunk/test/openssl/test_ocsp.rb#L8 ca_subj = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=TestCA") ca_key = OpenSSL::TestUtils::TEST_KEY_RSA1024 ca_serial = 0xabcabcabcabc + ca_exts = [ + ["basicConstraints", "CA:TRUE", true], + ["keyUsage", "cRLSign,keyCertSign", true], + ] subj = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=TestCert") @key = OpenSSL::TestUtils::TEST_KEY_RSA1024 @@ -17,9 +21,17 @@ class OpenSSL::TestOCSP < OpenSSL::TestC https://github.com/ruby/ruby/blob/trunk/test/openssl/test_ocsp.rb#L21 dgst = OpenSSL::Digest::SHA1.new @ca_cert = OpenSSL::TestUtils.issue_cert( - ca_subj, ca_key, ca_serial, now, now+3600, [], nil, nil, dgst) + ca_subj, ca_key, ca_serial, now, now+3600, ca_exts, nil, nil, dgst) @cert = OpenSSL::TestUtils.issue_cert( - subj, @key, serial, now, now+3600, [], @ca_cert, nil, dgst) + subj, @key, serial, now, now+3600, [], @ca_cert, ca_key, dgst) + + @key2 = OpenSSL::TestUtils::TEST_KEY_RSA2048 + cert2_exts = [ + ["extendedKeyUsage", "OCSPSigning", true], + ] + @cert2 = OpenSSL::TestUtils.issue_cert( + OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=TestCert2"), + @key2, serial+1, now, now+3600, cert2_exts, @ca_cert, ca_key, "SHA256") end def test_new_certificate_id @@ -34,6 +46,30 @@ class OpenSSL::TestOCSP < OpenSSL::TestC https://github.com/ruby/ruby/blob/trunk/test/openssl/test_ocsp.rb#L46 assert_equal @cert.serial, cid.serial end if defined?(OpenSSL::Digest::SHA256) + def test_certificate_id_der + cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert) # hash algorithm defaults to SHA-1 + der = cid.to_der + asn1 = OpenSSL::ASN1.decode(der) + assert_equal OpenSSL::ASN1.ObjectId("SHA1").to_der, asn1.value[0].value[0].to_der + assert_equal OpenSSL::Digest::SHA1.digest(@cert.issuer.to_der), asn1.value[1].value + assert_equal OpenSSL::Digest::SHA1.digest(OpenSSL::ASN1.decode(@ca_cert.to_der).value[0].value[6].value[1].value), asn1.value[2].value + assert_equal @cert.serial, asn1.value[3].value + assert_equal der, OpenSSL::OCSP::CertificateId.new(der).to_der + end + + def test_request_der + request = OpenSSL::OCSP::Request.new + cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA1.new) + request.add_certid(cid) + request.sign(@cert, @key, [@ca_cert], 0) + asn1 = OpenSSL::ASN1.decode(request.to_der) + assert_equal cid.to_der, asn1.value[0].value.find { |a| a.tag_class == :UNIVERSAL }.value[0].value[0].to_der + assert_equal OpenSSL::ASN1.ObjectId("sha1WithRSAEncryption").to_der, asn1.value[1].value[0].value[0].value[0].to_der + assert_equal @cert.to_der, asn1.value[1].value[0].value[2].value[0].value[0].to_der + assert_equal @ca_cert.to_der, asn1.value[1].value[0].value[2].value[0].value[1].to_der + assert_equal asn1.to_der, OpenSSL::OCSP::Request.new(asn1.to_der).to_der + end + def test_new_ocsp_request request = OpenSSL::OCSP::Request.new cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA1.new) @@ -43,6 +79,33 @@ class OpenSSL::TestOCSP < OpenSSL::TestC https://github.com/ruby/ruby/blob/trunk/test/openssl/test_ocsp.rb#L79 # in current implementation not same instance of certificate id, but should contain same data assert_equal cid.serial, request.certid.first.serial end + + def test_basic_response_der + bres = OpenSSL::OCSP::BasicResponse.new + cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA1.new) + bres.add_status(cid, OpenSSL::OCSP::V_CERTSTATUS_GOOD, 0, nil, -300, 500, []) + bres.add_nonce("NONCE") + bres.sign(@cert2, @key2, [@ca_cert], 0) + der = bres.to_der + asn1 = OpenSSL::ASN1.decode(der) + assert_equal cid.to_der, asn1.value[0].value.find { |a| a.class == OpenSSL::ASN1::Sequence }.value[0].value[0].to_der + assert_equal OpenSSL::ASN1.Sequence([@cert2, @ca_cert]).to_der, asn1.value[3].value[0].to_der + assert_equal der, OpenSSL::OCSP::BasicResponse.new(der).to_der + end + + def test_response_der + bres = OpenSSL::OCSP::BasicResponse.new + cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA1.new) + bres.add_status(cid, OpenSSL::OCSP::V_CERTSTATUS_GOOD, 0, nil, -300, 500, []) + bres.sign(@cert2, @key2, [@ca_cert], 0) + res = OpenSSL::OCSP::Response.create(OpenSSL::OCSP::RESPONSE_STATUS_SUCCESSFUL, bres) + der = res.to_der + asn1 = OpenSSL::ASN1.decode(der) + assert_equal OpenSSL::OCSP::RESPONSE_STATUS_SUCCESSFUL, asn1.value[0].value + assert_equal OpenSSL::ASN1.ObjectId("basicOCSPResponse").to_der, asn1.value[1].value[0].value[0].to_der + assert_equal bres.to_der, asn1.value[1].value[0].value[1].value + assert_equal der, OpenSSL::OCSP::Response.new(der).to_der + end end end Index: ext/openssl/ossl_ocsp.c =================================================================== --- ext/openssl/ossl_ocsp.c (revision 55408) +++ ext/openssl/ossl_ocsp.c (revision 55409) @@ -180,15 +180,13 @@ ossl_ocspreq_initialize(int argc, VALUE https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L180 rb_scan_args(argc, argv, "01", &arg); if(!NIL_P(arg)){ - OCSP_REQUEST *req = DATA_PTR(self), *x; + OCSP_REQUEST *req; + GetOCSPReq(self, req); arg = ossl_to_der_if_possible(arg); StringValue(arg); - p = (unsigned char*)RSTRING_PTR(arg); - x = d2i_OCSP_REQUEST(&req, &p, RSTRING_LEN(arg)); - DATA_PTR(self) = req; - if(!x){ + p = (unsigned char *)RSTRING_PTR(arg); + if (!d2i_OCSP_REQUEST(&req, &p, RSTRING_LEN(arg))) ossl_raise(eOCSPError, "cannot load DER encoded request"); - } } return self; @@ -463,15 +461,13 @@ ossl_ocspres_initialize(int argc, VALUE https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L461 rb_scan_args(argc, argv, "01", &arg); if(!NIL_P(arg)){ - OCSP_RESPONSE *res = DATA_PTR(self), *x; + OCSP_RESPONSE *res; + GetOCSPRes(self, res); arg = ossl_to_der_if_possible(arg); StringValue(arg); p = (unsigned char *)RSTRING_PTR(arg); - x = d2i_OCSP_RESPONSE(&res, &p, RSTRING_LEN(arg)); - DATA_PTR(self) = res; - if(!x){ + if (!d2i_OCSP_RESPONSE(&res, &p, RSTRING_LEN(arg))) ossl_raise(eOCSPError, "cannot load DER encoded response"); - } } return self; @@ -584,14 +580,29 @@ ossl_ocspbres_alloc(VALUE klass) https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L580 /* * call-seq: - * OpenSSL::OCSP::BasicResponse.new(*) -> basic_response + * OpenSSL::OCSP::BasicResponse.new(der_string = nil) -> basic_response * - * Creates a new BasicResponse and ignores all arguments. + * Creates a new BasicResponse. If +der_string+ is given, decodes +der_string+ + * as DER. */ static VALUE ossl_ocspbres_initialize(int argc, VALUE *argv, VALUE self) { + VALUE arg; + const unsigned char *p; + + rb_scan_args(argc, argv, "01", &arg); + if (!NIL_P(arg)) { + OCSP_BASICRESP *res; + GetOCSPBasicRes(self, res); + arg = ossl_to_der_if_possible(arg); + StringValue(arg); + p = (unsigned char *)RSTRING_PTR(arg); + if (!d2i_OCSP_BASICRESP(&res, &p, RSTRING_LEN(arg))) + ossl_raise(eOCSPError, "d2i_OCSP_BASICRESP"); + } + return self; } @@ -856,6 +867,32 @@ ossl_ocspbres_verify(int argc, VALUE *ar https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L867 } /* + * call-seq: + * basic_response.to_der -> String + * + * Encodes this basic response into a DER-encoded string. + */ +static VALUE +ossl_ocspbres_to_der(VALUE self) +{ + OCSP_BASICRESP *res; + VALUE str; + long len; + unsigned char *p; + + GetOCSPBasicRes(self, res); + if ((len = i2d_OCSP_BASICRESP(res, NULL)) <= 0) + ossl_raise(eOCSPError, NULL); + str = rb_str_new(0, len); + p = (unsigned char *)RSTRING_PTR(str); + if (i2d_OCSP_BASICRESP(res, &p) <= 0) + ossl_raise(eOCSPError, NULL); + ossl_str_adjust(str, p); + + return str; +} + +/* * OCSP::CertificateId */ static VALUE @@ -875,10 +912,14 @@ ossl_ocspcid_alloc(VALUE klass) https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L912 /* * call-seq: * OpenSSL::OCSP::CertificateId.new(subject, issuer, digest = nil) -> certificate_id + * OpenSSL::OCSP::CertificateId.new(der_string) -> certificate_id * * Creates a new OpenSSL::OCSP::CertificateId for the given +subject+ and * +issuer+ X509 certificates. The +digest+ is used to compute the * certificate ID and must be an OpenSSL::Digest instance. + * + * If only one argument is given, decodes it as DER representation of a + * certificate ID. */ static VALUE @@ -889,7 +930,17 @@ ossl_ocspcid_initialize(int argc, VALUE https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L930 VALUE subject, issuer, digest; const EVP_MD *md; - if (rb_scan_args(argc, argv, "21", &subject, &issuer, &digest) == 0) { + GetOCSPCertId(self, id); + if (rb_scan_args(argc, argv, "12", &subject, &issuer, &digest) == 1) { + VALUE arg; + const unsigned char *p; + + arg = ossl_to_der_if_possible(subject); + StringValue(arg); + p = (unsigned char *)RSTRING_PTR(arg); + if (!d2i_OCSP_CERTID(&id, &p, RSTRING_LEN(arg))) + ossl_raise(eOCSPError, "d2i_OCSP_CERTID"); + return self; } @@ -904,9 +955,8 @@ ossl_ocspcid_initialize(int argc, VALUE https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L955 } if(!newid) ossl_raise(eOCSPError, NULL); - GetOCSPCertId(self, id); OCSP_CERTID_free(id); - RDATA(self)->data = newid; + SetOCSPCertId(self, newid); return self; } @@ -971,6 +1021,32 @@ ossl_ocspcid_get_serial(VALUE self) https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L1021 return asn1integer_to_num(serial); } +/* + * call-seq: + * certificate_id.to_der -> String + * + * Encodes this certificate identifier into a DER-encoded string. + */ +static VALUE +ossl_ocspcid_to_der(VALUE self) +{ + OCSP_CERTID *id; + VALUE str; + long len; + unsigned char *p; + + GetOCSPCertId(self, id); + if ((len = i2d_OCSP_CERTID(id, NULL)) <= 0) + ossl_raise(eOCSPError, NULL); + str = rb_str_new(0, len); + p = (unsigned char *)RSTRING_PTR(str); + if (i2d_OCSP_CERTID(id, &p) <= 0) + ossl_raise(eOCSPError, NULL); + ossl_str_adjust(str, p); + + return str; +} + void Init_ossl_ocsp(void) { @@ -1138,6 +1214,7 @@ Init_ossl_ocsp(void) https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L1214 rb_define_method(cOCSPBasicRes, "status", ossl_ocspbres_get_status, 0); rb_define_method(cOCSPBasicRes, "sign", ossl_ocspbres_sign, -1); rb_define_method(cOCSPBasicRes, "verify", ossl_ocspbres_verify, -1); + rb_define_method(cOCSPBasicRes, "to_der", ossl_ocspbres_to_der, 0); /* * An OpenSSL::OCSP::CertificateId identifies a certificate to the CA so @@ -1150,6 +1227,7 @@ Init_ossl_ocsp(void) https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L1227 rb_define_method(cOCSPCertId, "cmp", ossl_ocspcid_cmp, 1); rb_define_method(cOCSPCertId, "cmp_issuer", ossl_ocspcid_cmp_issuer, 1); rb_define_method(cOCSPCertId, "serial", ossl_ocspcid_get_serial, 0); + rb_define_method(cOCSPCertId, "to_der", ossl_ocspcid_to_der, 0); /* Internal error in issuer */ rb_define_const(mOCSP, "RESPONSE_STATUS_INTERNALERROR", INT2NUM(OCSP_RESPONSE_STATUS_INTERNALERROR)); Index: ChangeLog =================================================================== --- ChangeLog (revision 55408) +++ ChangeLog (revision 55409) @@ -1,3 +1,20 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1 +Tue Jun 14 21:40:42 2016 Kazuki Yamaguchi <k@r...> + + * ext/openssl/ossl_ocsp.c (ossl_ocspbres_to_der, ossl_ocspcid_to_der): + Implement #to_der methods for OCSP::BasicResponse and + OCSP::CertificateId. + + (ossl_ocspreq_initialize, ossl_ocspres_initialize): Use GetOCSP*() + instead of raw DATA_PTR(). + + (ossl_ocspbres_initialize, ossl_ocspcid_initialize): Allow + initializing from DER string. + + (Init_ossl_ocsp): Define new #to_der methods. + + * test/openssl/test_ocsp.rb: Test these changes. Also add missing tests + for OCSP::{Response,Request}#to_der. + Tue Jun 14 21:35:00 2016 Kazuki Yamaguchi <k@r...> * ext/openssl/openssl_missing.h (DH_set0_pqg, RSA_set0_key): -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/