diff options
author | Kazuki Yamaguchi <k@rhe.jp> | 2017-09-14 17:05:24 +0900 |
---|---|---|
committer | Kazuki Yamaguchi <k@rhe.jp> | 2017-09-15 14:33:08 +0900 |
commit | 76bee74729b8d1b11628cdd93bf9cfaf09a99ba7 (patch) | |
tree | 4a1f4f9fd92feb14b1dbde063d57ff1e5675b79f | |
parent | 0cb5fb03658b29ba4b7a0c38959ddcfa6a2b737c (diff) | |
download | ruby-openssl-wip/ocsp-accessors.tar.gz |
ocsp: add OCSP::OneRequest, a wrapper of OCSP_ONEREQwip/ocsp-accessors
OCSP::Request#add_request and #requests that return OCSP::OneRequest is
also added. The existing methods #add_certid and #certid could not
handle singleRequestExtensions which is a field of the Request structure
(OCSP_ONEREQ corresponds to it).
-rw-r--r-- | ext/openssl/ossl_ocsp.c | 189 | ||||
-rw-r--r-- | test/test_ocsp.rb | 30 |
2 files changed, 209 insertions, 10 deletions
diff --git a/ext/openssl/ossl_ocsp.c b/ext/openssl/ossl_ocsp.c index a51884d7..ae09fcde 100644 --- a/ext/openssl/ossl_ocsp.c +++ b/ext/openssl/ossl_ocsp.c @@ -17,6 +17,12 @@ if(!(req)) ossl_raise(rb_eRuntimeError, "Request wasn't initialized!"); \ } while (0) +#define GetOCSPOneReq(obj, onereq) do { \ + TypedData_Get_Struct(obj, OCSP_ONEREQ, &ossl_ocsp_onereq_type, onereq); \ + if (!(onereq)) \ + rb_raise(rb_eRuntimeError, "OneRequest wasn't initialized!"); \ +} while (0) + #define GetOCSPRes(obj, res) do { \ TypedData_Get_Struct((obj), OCSP_RESPONSE, &ossl_ocsp_response_type, (res)); \ if(!(res)) ossl_raise(rb_eRuntimeError, "Response wasn't initialized!"); \ @@ -38,10 +44,11 @@ } while (0) static VALUE mOCSP, eOCSPError; -static VALUE cOCSPReq; +static VALUE cOCSPReq, cOCSPOneReq; static VALUE cOCSPRes, cOCSPBasicRes, cOCSPSingleRes; static VALUE cOCSPCertId; +static VALUE ocsp_onereq_new_wrap(OCSP_ONEREQ *onereq, VALUE req); static VALUE ocsp_sres_new_dup(const OCSP_SINGLERESP *sres); static VALUE ocsp_certid_new_dup(const OCSP_CERTID *cid); @@ -59,6 +66,14 @@ static const rb_data_type_t ossl_ocsp_request_type = { 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, }; +static const rb_data_type_t ossl_ocsp_onereq_type = { + "OpenSSL/OCSP/ONEREQ", + { + 0, /* nofree */0, + }, + 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, +}; + static void ossl_ocsp_response_free(void *ptr) { @@ -253,32 +268,98 @@ ossl_ocspreq_check_nonce(VALUE self, VALUE basic_resp) /* * call-seq: - * request.add_certid(certificate_id) -> request + * request.add_request(cert_id [, extensions]) -> OneRequest * - * Adds _certificate_id_ to the request. + * Adds a Request (not to be confused with OpenSSL::OCSP::Request, which + * corresponds to the OCSPRequest structure) to the requestList of the + * OCSPRequest. Extensions, that would be stored in the singleRequestExtensions, + * can be optionally specified. + * + * Note that the returned OneRequest is a "dependent object", which is + * contained in the Request. Currently, it is not possible to modify the + * content afterwards. */ - static VALUE -ossl_ocspreq_add_certid(VALUE self, VALUE certid) +ocsp_req_add_request(int argc, VALUE *argv, VALUE self) { OCSP_REQUEST *req; OCSP_CERTID *id, *id_new; + OCSP_ONEREQ *onereq; + VALUE certid, exts, tmp = 0; + X509_EXTENSION **buf = NULL; + long i, num = 0; GetOCSPReq(self, req); + rb_check_frozen(self); + rb_scan_args(argc, argv, "11", &certid, &exts); GetOCSPCertId(certid, id); + if (argc >= 2) { + Check_Type(exts, T_ARRAY); + num = RARRAY_LEN(exts); + buf = ALLOCV_N(X509_EXTENSION *, tmp, num); + for (i = 0; i < num; i++) + buf[i] = GetX509ExtPtr(RARRAY_AREF(exts, i)); + } - if (!(id_new = OCSP_CERTID_dup(id))) + id_new = OCSP_CERTID_dup(id); + if (!id_new) ossl_raise(eOCSPError, "OCSP_CERTID_dup"); - if (!OCSP_request_add0_id(req, id_new)) { - OCSP_CERTID_free(id_new); + onereq = OCSP_request_add0_id(req, id_new); + if (!onereq) ossl_raise(eOCSPError, "OCSP_request_add0_id"); - } + for (i = 0; i < num; i++) + if (!OCSP_ONEREQ_add_ext(onereq, buf[i], -1)) + ossl_raise(eOCSPError, "OCSP_ONEREQ_add_ext"); + ALLOCV_END(tmp); + + return ocsp_onereq_new_wrap(onereq, self); +} + +/* + * call-seq: + * request.add_certid(certificate_id) -> request + * + * Adds _certificate_id_ to the request. + * + * See also #add_request. + */ +static VALUE +ossl_ocspreq_add_certid(VALUE self, VALUE certid) +{ + ocsp_req_add_request(1, &certid, self); return self; } /* * call-seq: + * request.requests -> [onereq1, onereq2, ...] + * + * Returns the list of Request (OpenSSL::OCSP::OneRequest) contained in the + * requestList field. + * + * Note that the OneRequest instances are "dependent objects", and cannot be + * modified. + */ +static VALUE +ocsp_req_requests(VALUE self) +{ + OCSP_REQUEST *req; + VALUE ary; + int i, num; + + GetOCSPReq(self, req); + num = OCSP_request_onereq_count(req); + ary = rb_ary_new_capa(num); + for(i = 0; i < num; i++){ + OCSP_ONEREQ *onereq = OCSP_request_onereq_get0(req, i); + rb_ary_push(ary, ocsp_onereq_new_wrap(onereq, self)); + } + return ary; +} + +/* + * call-seq: * request.certid -> [certificate_id, ...] * * Returns all certificate IDs in this request. @@ -293,7 +374,7 @@ ossl_ocspreq_get_certid(VALUE self) GetOCSPReq(self, req); count = OCSP_request_onereq_count(req); - ary = (count > 0) ? rb_ary_new() : Qnil; + ary = rb_ary_new_capa(count); for(i = 0; i < count; i++){ OCSP_ONEREQ *one = OCSP_request_onereq_get0(req, i); rb_ary_push(ary, ocsp_certid_new_dup(OCSP_onereq_get0_id(one))); @@ -464,6 +545,78 @@ ossl_ocspreq_signed_p(VALUE self) } /* + * OCSP::OneRequest + * + * An OCSP::OneRequest is immutable and always a dependent object of + * OCSP::Request. + */ +static VALUE +ocsp_onereq_new_wrap(OCSP_ONEREQ *onereq, VALUE req) +{ + /* We can raise NoMemoryError here without freeing onereq. */ + VALUE obj = TypedData_Wrap_Struct(cOCSPOneReq, &ossl_ocsp_onereq_type, 0); + /* Prevent req from being GCed */ + rb_ivar_set(obj, rb_intern("ocsp_request"), req); + RTYPEDDATA_DATA(obj) = onereq; + return obj; +} + +/* + * call-seq: + * one_request.to_der -> String + */ +static VALUE +ocsp_onereq_to_der(VALUE self) +{ + OCSP_ONEREQ *onereq; + VALUE str; + int len; + unsigned char *p; + + GetOCSPOneReq(self, onereq); + len = i2d_OCSP_ONEREQ(onereq, NULL); + if (len <= 0) + ossl_raise(eOCSPError, "i2d_OCSP_ONEREQ"); + str = rb_str_new(NULL, len); + p = (unsigned char *)RSTRING_PTR(str); + if (i2d_OCSP_ONEREQ(onereq, &p) <= 0) + ossl_raise(eOCSPError, "i2d_OCSP_ONEREQ"); + ossl_str_adjust(str, p); + return str; +} + +/* + * call-seq: + * one_request.cert_id -> CertificateId + * + * Returns the a + */ +static VALUE +ocsp_onereq_cert_id(VALUE self) +{ + OCSP_ONEREQ *onereq; + + GetOCSPOneReq(self, onereq); + return ocsp_certid_new_dup(OCSP_onereq_get0_id(onereq)); +} + +/* + * call-seq: + * one_request.extensions -> [ext1, ext2, ...] + * + * Returns the singleRequestExtensions of the Request. + */ +static VALUE +ocsp_onereq_extensions(VALUE self) +{ + OCSP_ONEREQ *onereq; + + GetOCSPOneReq(self, onereq); + OSSL_IMPL_GET_EXTENSIONS(onereq, OCSP_ONEREQ_get_ext_count, + OCSP_ONEREQ_get_ext); +} + +/* * OCSP::Response */ static VALUE @@ -1855,7 +2008,9 @@ Init_ossl_ocsp(void) rb_define_method(cOCSPReq, "initialize", ossl_ocspreq_initialize, -1); rb_define_method(cOCSPReq, "add_nonce", ossl_ocspreq_add_nonce, -1); rb_define_method(cOCSPReq, "check_nonce", ossl_ocspreq_check_nonce, 1); + rb_define_method(cOCSPReq, "add_request", ocsp_req_add_request, -1); rb_define_method(cOCSPReq, "add_certid", ossl_ocspreq_add_certid, 1); + rb_define_method(cOCSPReq, "requests", ocsp_req_requests, 0); rb_define_method(cOCSPReq, "certid", ossl_ocspreq_get_certid, 0); rb_define_method(cOCSPReq, "extensions", ocsp_req_extensions, 0); rb_define_method(cOCSPReq, "extensions=", ocsp_req_set_extensions, 1); @@ -1865,6 +2020,20 @@ Init_ossl_ocsp(void) rb_define_method(cOCSPReq, "to_der", ossl_ocspreq_to_der, 0); /* + * An OpenSSL::OCSP::OneRequest represents a Request structure. This + * contains a CertID that identifies a certificate, and optionally includes + * extensions. + * + * Currently this type cannot be directly instantiated. + */ + cOCSPOneReq = rb_define_class_under(mOCSP, "OneRequest", rb_cObject); + rb_undef_alloc_func(cOCSPOneReq); + rb_define_method(cOCSPOneReq, "to_der", ocsp_onereq_to_der, 0); + rb_define_method(cOCSPOneReq, "cert_id", ocsp_onereq_cert_id, 0); + rb_define_alias(cOCSPOneReq, "certid", "cert_id"); + rb_define_method(cOCSPOneReq, "extensions", ocsp_onereq_extensions, 0); + + /* * An OpenSSL::OCSP::Response represents an OCSPResponse structure, which * consists of the processing status of the prior request, and the bytes of * actual response. At the moment, the basic response type is supported. diff --git a/test/test_ocsp.rb b/test/test_ocsp.rb index 05b10bc0..4b13be14 100644 --- a/test/test_ocsp.rb +++ b/test/test_ocsp.rb @@ -188,6 +188,36 @@ class OpenSSL::TestOCSP < OpenSSL::TestCase assert_equal request.to_der, request.dup.to_der end + def test_one_request + cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, "SHA1") + + # Initially no Request is added + req = OpenSSL::OCSP::Request.new + assert_equal [], req.certid + assert_equal [], req.requests + + # Add a Request using #add_certid + req = OpenSSL::OCSP::Request.new + assert_same req, req.add_certid(cid) + onereq, *rest = req.requests + assert_equal [], rest + assert_instance_of OpenSSL::OCSP::OneRequest, onereq + assert_equal cid.to_der, onereq.certid.to_der + assert_equal [], onereq.extensions + assert_equal [cid.to_der], req.certid.map(&:to_der) + + # Add a Request using #add_request + req = OpenSSL::OCSP::Request.new + sl = OpenSSL::ASN1::Sequence([@cert.subject]) + ext = OpenSSL::X509::Extension.new("1.3.6.1.5.5.7.48.1.7", sl) + onereq1 = req.add_request(cid, [ext]) + onereq2, *rest = req.requests + assert_equal [], rest + assert_equal onereq1.to_der, onereq2.to_der + assert_equal cid.to_der, onereq1.certid.to_der + assert_equal [ext.to_der], onereq1.extensions.map(&:to_der) + end + def test_basic_response_der bres = OpenSSL::OCSP::BasicResponse.new cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA1.new) |