ruby-changes:33491
From: drbrain <ko1@a...>
Date: Sat, 12 Apr 2014 09:35:09 +0900 (JST)
Subject: [ruby-changes:33491] drbrain:r45572 (trunk): * ext/openssl/ossl_ocsp.c: [DOC] Document OpenSSL::OCSP.
drbrain 2014-04-12 09:35:01 +0900 (Sat, 12 Apr 2014) New Revision: 45572 http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=45572 Log: * ext/openssl/ossl_ocsp.c: [DOC] Document OpenSSL::OCSP. Modified files: trunk/ChangeLog trunk/ext/openssl/ossl_ocsp.c Index: ChangeLog =================================================================== --- ChangeLog (revision 45571) +++ ChangeLog (revision 45572) @@ -1,3 +1,7 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1 +Sat Apr 12 09:26:48 2014 Eric Hodel <drbrain@s...> + + * ext/openssl/ossl_ocsp.c: [DOC] Document OpenSSL::OCSP. + Fri Apr 11 18:52:38 2014 Koichi Sasada <ko1@a...> * array.c (ARY_SET): added. Index: ext/openssl/ossl_ocsp.c =================================================================== --- ext/openssl/ossl_ocsp.c (revision 45571) +++ ext/openssl/ossl_ocsp.c (revision 45572) @@ -99,6 +99,15 @@ ossl_ocspreq_alloc(VALUE klass) https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L99 return obj; } +/* + * call-seq: + * OpenSSL::OCSP::Request.new -> request + * OpenSSL::OCSP::Request.new(request_der) -> request + * + * Creates a new OpenSSL::OCSP::Request. The request may be created empty or + * from a +request_der+ string. + */ + static VALUE ossl_ocspreq_initialize(int argc, VALUE *argv, VALUE self) { @@ -121,6 +130,17 @@ ossl_ocspreq_initialize(int argc, VALUE https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L130 return self; } +/* + * call-seq: + * request.add_nonce(nonce = nil) -> request + * + * Adds a +nonce+ to the OCSP request. If no nonce is given a random one will + * be generated. + * + * The nonce is used to prevent replay attacks but some servers do not support + * it. + */ + static VALUE ossl_ocspreq_add_nonce(int argc, VALUE *argv, VALUE self) { @@ -143,18 +163,25 @@ ossl_ocspreq_add_nonce(int argc, VALUE * https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L163 return self; } -/* Check nonce validity in a request and response. - * Return value reflects result: - * 1: nonces present and equal. - * 2: nonces both absent. - * 3: nonce present in response only. - * 0: nonces both present and not equal. - * -1: nonce in request only. - * - * For most responders clients can check return > 0. - * If responder doesn't handle nonces return != 0 may be - * necessary. return == 0 is always an error. +/* + * call-seq: + * request.check_nonce(response) -> result + * + * Checks the nonce validity for this request and +response+. + * + * The return value is one of the following: + * + * -1 :: nonce in request only. + * 0 :: nonces both present and not equal. + * 1 :: nonces present and equal. + * 2 :: nonces both absent. + * 3 :: nonce present in response only. + * + * For most responses, clients can check +result+ > 0. If a responder doesn't + * handle nonces <code>result.nonzero?</code> may be necessary. A result of + * <code>0</code> is always an error. */ + static VALUE ossl_ocspreq_check_nonce(VALUE self, VALUE basic_resp) { @@ -169,6 +196,13 @@ ossl_ocspreq_check_nonce(VALUE self, VAL https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L196 return INT2NUM(res); } +/* + * call-seq: + * request.add_certid(certificate_id) -> request + * + * Adds +certificate_id+ to the request. + */ + static VALUE ossl_ocspreq_add_certid(VALUE self, VALUE certid) { @@ -183,6 +217,13 @@ ossl_ocspreq_add_certid(VALUE self, VALU https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L217 return self; } +/* + * call-seq: + * request.certid -> [certificate_id, ...] + * + * Returns all certificate IDs in this request. + */ + static VALUE ossl_ocspreq_get_certid(VALUE self) { @@ -206,6 +247,17 @@ ossl_ocspreq_get_certid(VALUE self) https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L247 return ary; } +/* + * call-seq: + * request.sign(signer_cert, signer_key) -> self + * request.sign(signer_cert, signer_key, certificates) -> self + * request.sign(signer_cert, signer_key, certificates, flags) -> self + * + * Signs this OCSP request using +signer_cert+ and +signer_key+. + * +certificates+ is an optional Array of certificates that may be included in + * the request. + */ + static VALUE ossl_ocspreq_sign(int argc, VALUE *argv, VALUE self) { @@ -234,6 +286,14 @@ ossl_ocspreq_sign(int argc, VALUE *argv, https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L286 return self; } +/* + * call-seq: + * request.verify(certificates, store) -> true or false + * request.verify(certificates, store, flags) -> true or false + * + * Verifies this request using the given +certificates+ and X509 +store+. + */ + static VALUE ossl_ocspreq_verify(int argc, VALUE *argv, VALUE self) { @@ -255,6 +315,10 @@ ossl_ocspreq_verify(int argc, VALUE *arg https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L315 return result ? Qtrue : Qfalse; } +/* + * Returns this request as a DER-encoded string + */ + static VALUE ossl_ocspreq_to_der(VALUE self) { @@ -278,6 +342,13 @@ ossl_ocspreq_to_der(VALUE self) https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L342 /* * OCSP::Response */ + +/* call-seq: + * OpenSSL::OCSP::Response.create(status, basic_response = nil) -> response + * + * Creates an OpenSSL::OCSP::Response from +status+ and +basic_response+. + */ + static VALUE ossl_ocspres_s_create(VALUE klass, VALUE status, VALUE basic_resp) { @@ -308,6 +379,15 @@ ossl_ocspres_alloc(VALUE klass) https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L379 return obj; } +/* + * call-seq: + * OpenSSL::OCSP::Response.new -> response + * OpenSSL::OCSP::Response.new(response_der) -> response + * + * Creates a new OpenSSL::OCSP::Response. The response may be created empty or + * from a +response_der+ string. + */ + static VALUE ossl_ocspres_initialize(int argc, VALUE *argv, VALUE self) { @@ -330,6 +410,13 @@ ossl_ocspres_initialize(int argc, VALUE https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L410 return self; } +/* + * call-seq: + * response.status -> Integer + * + * Returns the status of the response. + */ + static VALUE ossl_ocspres_status(VALUE self) { @@ -342,6 +429,13 @@ ossl_ocspres_status(VALUE self) https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L429 return INT2NUM(st); } +/* + * call-seq: + * response.status_string -> String + * + * Returns a status string for the response. + */ + static VALUE ossl_ocspres_status_string(VALUE self) { @@ -354,6 +448,13 @@ ossl_ocspres_status_string(VALUE self) https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L448 return rb_str_new2(OCSP_response_status_str(st)); } +/* + * call-seq: + * response.basic + * + * Returns a BasicResponse for this response + */ + static VALUE ossl_ocspres_get_basic(VALUE self) { @@ -369,6 +470,13 @@ ossl_ocspres_get_basic(VALUE self) https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L470 return ret; } +/* + * call-seq: + * response.to_der -> String + * + * Returns this response as a DER-encoded string. + */ + static VALUE ossl_ocspres_to_der(VALUE self) { @@ -405,12 +513,27 @@ ossl_ocspbres_alloc(VALUE klass) https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L513 return obj; } +/* + * call-seq: + * OpenSSL::OCSP::BasicResponse.new(*) -> basic_response + * + * Creates a new BasicResponse and ignores all arguments. + */ + static VALUE ossl_ocspbres_initialize(int argc, VALUE *argv, VALUE self) { return self; } +/* + * call-seq: + * basic_response.copy_nonce(request) -> Integer + * + * Copies the nonce from +request+ into this response. Returns 1 on success + * and 0 on failure. + */ + static VALUE ossl_ocspbres_copy_nonce(VALUE self, VALUE request) { @@ -425,6 +548,14 @@ ossl_ocspbres_copy_nonce(VALUE self, VAL https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L548 return INT2NUM(ret); } +/* + * call-seq: + * basic_response.add_nonce(nonce = nil) + * + * Adds +nonce+ to this response. If no nonce was provided a random nonce + * will be added. + */ + static VALUE ossl_ocspbres_add_nonce(int argc, VALUE *argv, VALUE self) { @@ -447,6 +578,22 @@ ossl_ocspbres_add_nonce(int argc, VALUE https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L578 return self; } +/* + * call-seq: + * basic_response.add_status(certificate_id, status, reason, revocation_time, this_update, next_update, extensions) -> basic_response + * + * Adds a validation +status+ (0 for revoked, 1 for success) to this + * response for +certificate_id+. +reason+ describes the reason for the + * revocation, if any. + * + * The +revocation_time+, +this_update+ and +next_update+ are times for the + * certificate's revocation time, the time of this status and the next update + * time for a new status, respectively. + * + * +extensions+ may be an Array of OpenSSL::X509::Extension that will + * be added to this response or nil. + */ + static VALUE ossl_ocspbres_add_status(VALUE self, VALUE cid, VALUE status, VALUE reason, VALUE revtime, @@ -515,6 +662,16 @@ ossl_ocspbres_add_status(VALUE self, VAL https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L662 return self; } +/* + * call-seq: + * basic_response.status -> statuses + * + * Returns an Array of statuses for this response. Each status contains a + * CertificateId, the status (0 for success, 1 for revoked), the reason for + * the status, the revocation time, the time of this update, the time for the + * next update and a list of OpenSSL::X509::Extensions. + */ + static VALUE ossl_ocspbres_get_status(VALUE self) { @@ -560,6 +717,16 @@ ossl_ocspbres_get_status(VALUE self) https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L717 return ret; } +/* + * call-seq: + * basic_response.sign(signer_cert, signer_key) -> self + * basic_response.sign(signer_cert, signer_key, certificates) -> self + * basic_response.sign(signer_cert, signer_key, certificates, flags) -> self + * + * Signs this response using the +signer_cert+ and +signer_key+. Additional + * +certificates+ may be added to the signature along with a set of +flags+. + */ + static VALUE ossl_ocspbres_sign(int argc, VALUE *argv, VALUE self) { @@ -590,6 +757,14 @@ ossl_ocspbres_sign(int argc, VALUE *argv https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L757 return self; } +/* + * call-seq: + * basic_response.verify(certificates, store) -> true or false + * basic_response.verify(certificates, store, flags) -> true or false + * + * Verifies the signature of the response using the given +certificates+, + * +store+ and +flags+. + */ static VALUE ossl_ocspbres_verify(int argc, VALUE *argv, VALUE self) { @@ -627,6 +802,15 @@ ossl_ocspcid_alloc(VALUE klass) https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L802 return obj; } +/* + * call-seq: + * OpenSSL::OCSP::CertificateId.new(subject, issuer, digest = nil) -> 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. + */ + static VALUE ossl_ocspcid_initialize(int argc, VALUE *argv, VALUE self) { @@ -657,6 +841,13 @@ ossl_ocspcid_initialize(int argc, VALUE https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L841 return self; } +/* + * call-seq: + * certificate_id.cmp(other) -> true or false + * + * Compares this certificate id with +other+ and returns true if they are the + * same. + */ static VALUE ossl_ocspcid_cmp(VALUE self, VALUE other) { @@ -670,6 +861,14 @@ ossl_ocspcid_cmp(VALUE self, VALUE other https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L861 return (result == 0) ? Qtrue : Qfalse; } +/* + * call-seq: + * certificate_id.cmp_issuer(other) -> true or false + * + * Compares this certificate id's issuer with +other+ and returns true if + * they are the same. + */ + static VALUE ossl_ocspcid_cmp_issuer(VALUE self, VALUE other) { @@ -683,6 +882,13 @@ ossl_ocspcid_cmp_issuer(VALUE self, VALU https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L882 return (result == 0) ? Qtrue : Qfalse; } +/* + * call-seq: + * certificate_id.get_serial -> Integer + * + * Returns the serial number of the issuing certificate. + */ + static VALUE ossl_ocspcid_get_serial(VALUE self) { @@ -696,10 +902,130 @@ ossl_ocspcid_get_serial(VALUE self) https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L902 void Init_ossl_ocsp() { + /* + * OpenSSL::OCSP implements Online Certificate Status Protocol requests + * and responses. + * + * Creating and sending an OCSP request requires a subject certificate + * that contains an OCSP URL in an authorityInfoAccess extension and the + * issuer certificate for the subject certificate. First, load the issuer + * and subject certificates: + * + * subject = OpenSSL::X509::Certificate.new subject_pem + * issuer = OpenSSL::X509::Certificate.new issuer_pem + * + * To create the request we need to create a certificate ID for the + * subject certificate so the CA knows which certificate we are asking + * about: + * + * digest = OpenSSL::Digest::SHA1.new + * certificate_id = + * OpenSSL::OCSP::CertificateId.new subject, issuer, digest + * + * Then create a request and add the certificate ID to it: + * + * request = OpenSSL::OCSP::Request.new + * request.add_certid certificate_id + * + * Adding a nonce to the request protects against replay attacks but not + * all CA process the nonce. + * + * request.add_nonce + * + * To submit the request to the CA for verification we need to extract the + * OCSP URI from the subject certificate: + * + * authority_info_access = subject.extensions.find do |extension| + * extension.oid == 'authorityInfoAccess' + * end + * + * descriptions = authority_info_access.value.split "\n" + * ocsp = descriptions.find do |description| + * description.start_with? 'OCSP' + * end + * + * require 'uri' + * + * ocsp_uri = URI ocsp[/URI:(.*)/, 1] + * + * To submit the request we'll POST the request to the OCSP URI (per RFC + * 2560). Note that we only handle HTTP requests and don't handle any + * redirects in this example, so this is insufficient for serious use. + * + * require 'net/http' + * + * http_response = + * Net::HTTP.start ocsp_uri.hostname, ocsp.port do |http| + * http.post ocsp_uri.path, request.to_der, + * 'content-type' => 'application/ocsp-request' + * end + * + * response = OpenSSL::OCSP::Response.new http_response.body + * response_basic = response.basic + * + * First we check if the response has a valid signature. Without a valid + * signature we cannot trust it. If you get a failure here you may be + * missing a system certificate store or may be missing the intermediate + * certificates. + * + * store = OpenSSL::X509::Store.new + * store.set_default_paths + * + * unless response.verify [], store then + * raise 'response is not signed by a trusted certificate' + * end + * + * The response contains the status information (success/fail). We can + * display the status as a string: + * + * puts response.status_string #=> successful + * + * Next we need to know the response details to determine if the response + * matches our request. First we check the nonce. Again, not all CAs + * support a nonce. See Request#check_nonce for the meanings of the + * return values. + * + * p request.check_nonce basic_response #=> value from -1 to 3 + * + * Then extract the status information from the basic response. (You can + * check multiple certificates in a request, but for this example we only + * submitted one.) + * + * response_certificate_id, status, reason, revocation_time, + * this_update, next_update, extensions = basic_response.status + * + * Then check the various fields. + * + * unless response_certificate_id == certificate_id then + * raise 'certificate id mismatch' + * end + * + * now = Time.now + * + * if this_update > now then + * raise 'update date is in the future' + * end + * + * if now > next_update then + * raise 'next update time has passed' + * end + */ + mOCSP = rb_define_module_under(mOSSL, "OCSP"); + /* + * OCSP error class. + */ + eOCSPError = rb_define_class_under(mOCSP, "OCSPError", eOSSLError); + /* + * An OpenSSL::OCSP::Request contains the certificate information for + * determining if a certificate has been revoked or not. A Request can be + * created for a certificate or from a DER-encoded request created + * elsewhere. + */ + cOCSPReq = rb_define_class_under(mOCSP, "Request", rb_cObject); rb_define_alloc_func(cOCSPReq, ossl_ocspreq_alloc); rb_define_method(cOCSPReq, "initialize", ossl_ocspreq_initialize, -1); @@ -711,6 +1037,11 @@ Init_ossl_ocsp() https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L1037 rb_define_method(cOCSPReq, "verify", ossl_ocspreq_verify, -1); rb_define_method(cOCSPReq, "to_der", ossl_ocspreq_to_der, 0); + /* + * An OpenSSL::OCSP::Response contains the status of a certificate check + * which is created from an OpenSSL::OCSP::Request. + */ + cOCSPRes = rb_define_class_under(mOCSP, "Response", rb_cObject); rb_define_singleton_method(cOCSPRes, "create", ossl_ocspres_s_create, 2); rb_define_alloc_func(cOCSPRes, ossl_ocspres_alloc); @@ -720,6 +1051,12 @@ Init_ossl_ocsp() https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L1051 rb_define_method(cOCSPRes, "basic", ossl_ocspres_get_basic, 0); rb_define_method(cOCSPRes, "to_der", ossl_ocspres_to_der, 0); + /* + * An OpenSSL::OCSP::BasicResponse contains the status of a certificate + * check which is created from an OpenSSL::OCSP::Request. A + * BasicResponse is more detailed than a Response. + */ + cOCSPBasicRes = rb_define_class_under(mOCSP, "BasicResponse", rb_cObject); rb_define_alloc_func(cOCSPBasicRes, ossl_ocspbres_alloc); rb_define_method(cOCSPBasicRes, "initialize", ossl_ocspbres_initialize, -1); @@ -730,6 +1067,11 @@ Init_ossl_ocsp() https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L1067 rb_define_method(cOCSPBasicRes, "sign", ossl_ocspbres_sign, -1); rb_define_method(cOCSPBasicRes, "verify", ossl_ocspbres_verify, -1); + /* + * An OpenSSL::OCSP::CertificateId identifies a certificate to the CA so + * that a status check can be performed. + */ + cOCSPCertId = rb_define_class_under(mOCSP, "CertificateId", rb_cObject); rb_define_alloc_func(cOCSPCertId, ossl_ocspcid_alloc); rb_define_method(cOCSPCertId, "initialize", ossl_ocspcid_initialize, -1); @@ -739,43 +1081,107 @@ Init_ossl_ocsp() https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L1081 #define DefOCSPConst(x) rb_define_const(mOCSP, #x, INT2NUM(OCSP_##x)) - DefOCSPConst(RESPONSE_STATUS_SUCCESSFUL); - DefOCSPConst(RESPONSE_STATUS_MALFORMEDREQUEST); - DefOCSPConst(RESPONSE_STATUS_INTERNALERROR); - DefOCSPConst(RESPONSE_STATUS_TRYLATER); - DefOCSPConst(RESPONSE_STATUS_SIGREQUIRED); - DefOCSPConst(RESPONSE_STATUS_UNAUTHORIZED); - - DefOCSPConst(REVOKED_STATUS_NOSTATUS); - DefOCSPConst(REVOKED_STATUS_UNSPECIFIED); - DefOCSPConst(REVOKED_STATUS_KEYCOMPROMISE); - DefOCSPConst(REVOKED_STATUS_CACOMPROMISE); - DefOCSPConst(REVOKED_STATUS_AFFILIATIONCHANGED); - DefOCSPConst(REV (... truncated) -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/