aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKazuki Yamaguchi <k@rhe.jp>2017-09-14 17:05:24 +0900
committerKazuki Yamaguchi <k@rhe.jp>2017-09-15 14:33:08 +0900
commit76bee74729b8d1b11628cdd93bf9cfaf09a99ba7 (patch)
tree4a1f4f9fd92feb14b1dbde063d57ff1e5675b79f
parent0cb5fb03658b29ba4b7a0c38959ddcfa6a2b737c (diff)
downloadruby-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.c189
-rw-r--r--test/test_ocsp.rb30
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)