diff options
Diffstat (limited to 'ext/openssl/ossl_ocsp.c')
-rw-r--r-- | ext/openssl/ossl_ocsp.c | 929 |
1 files changed, 807 insertions, 122 deletions
diff --git a/ext/openssl/ossl_ocsp.c b/ext/openssl/ossl_ocsp.c index ae15d93b..bb5eb5a8 100644 --- a/ext/openssl/ossl_ocsp.c +++ b/ext/openssl/ossl_ocsp.c @@ -57,6 +57,21 @@ GetOCSPBasicRes((obj), (res)); \ } while (0) +#define NewOCSPSingleRes(klass) \ + TypedData_Wrap_Struct((klass), &ossl_ocsp_singleresp_type, 0) +#define SetOCSPSingleRes(obj, res) do { \ + if(!(res)) ossl_raise(rb_eRuntimeError, "SingleResponse wasn't initialized!"); \ + RTYPEDDATA_DATA(obj) = (res); \ +} while (0) +#define GetOCSPSingleRes(obj, res) do { \ + TypedData_Get_Struct((obj), OCSP_SINGLERESP, &ossl_ocsp_singleresp_type, (res)); \ + if(!(res)) ossl_raise(rb_eRuntimeError, "SingleResponse wasn't initialized!"); \ +} while (0) +#define SafeGetOCSPSingleRes(obj, res) do { \ + OSSL_Check_Kind((obj), cOCSPSingleRes); \ + GetOCSPSingleRes((obj), (res)); \ +} while (0) + #define NewOCSPCertId(klass) \ TypedData_Wrap_Struct((klass), &ossl_ocsp_certid_type, 0) #define SetOCSPCertId(obj, cid) do { \ @@ -77,6 +92,7 @@ VALUE eOCSPError; VALUE cOCSPReq; VALUE cOCSPRes; VALUE cOCSPBasicRes; +VALUE cOCSPSingleRes; VALUE cOCSPCertId; static void @@ -122,6 +138,20 @@ static const rb_data_type_t ossl_ocsp_basicresp_type = { }; static void +ossl_ocsp_singleresp_free(void *ptr) +{ + OCSP_SINGLERESP_free(ptr); +} + +static const rb_data_type_t ossl_ocsp_singleresp_type = { + "OpenSSL/OCSP/SINGLERESP", + { + 0, ossl_ocsp_singleresp_free, + }, + 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, +}; + +static void ossl_ocsp_certid_free(void *ptr) { OCSP_CERTID_free(ptr); @@ -163,6 +193,25 @@ ossl_ocspreq_alloc(VALUE klass) return obj; } +static VALUE +ossl_ocspreq_initialize_copy(VALUE self, VALUE other) +{ + OCSP_REQUEST *req, *req_old, *req_new; + + rb_check_frozen(self); + GetOCSPReq(self, req_old); + SafeGetOCSPReq(other, req); + + req_new = ASN1_item_dup(ASN1_ITEM_rptr(OCSP_REQUEST), req); + if (!req_new) + ossl_raise(eOCSPError, "ASN1_item_dup"); + + SetOCSPReq(self, req_new); + OCSP_REQUEST_free(req_old); + + return self; +} + /* * call-seq: * OpenSSL::OCSP::Request.new -> request @@ -180,15 +229,13 @@ ossl_ocspreq_initialize(int argc, VALUE *argv, VALUE self) 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; @@ -314,49 +361,59 @@ ossl_ocspreq_get_certid(VALUE self) /* * 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 + * request.sign(cert, key, certs = nil, flags = 0, digest = nil) -> self + * + * Signs this OCSP request using +cert+, +key+ and optional +digest+. If + * +digest+ is not specified, SHA-1 is used. +certs+ is an optional Array of + * additional certificates that will be included in the request. If +certs+ is + * not specified, flag OpenSSL::OCSP::NOCERTS is set. Pass an empty array to + * include only the signer certificate. * - * Signs this OCSP request using +signer_cert+ and +signer_key+. - * +certificates+ is an optional Array of certificates that may be included in - * the request. + * +flags+ can include: + * OpenSSL::OCSP::NOCERTS:: don't include certificates */ static VALUE ossl_ocspreq_sign(int argc, VALUE *argv, VALUE self) { - VALUE signer_cert, signer_key, certs, flags; + VALUE signer_cert, signer_key, certs, flags, digest; OCSP_REQUEST *req; X509 *signer; EVP_PKEY *key; - STACK_OF(X509) *x509s; - unsigned long flg; + STACK_OF(X509) *x509s = NULL; + unsigned long flg = 0; + const EVP_MD *md; int ret; - rb_scan_args(argc, argv, "22", &signer_cert, &signer_key, &certs, &flags); + rb_scan_args(argc, argv, "23", &signer_cert, &signer_key, &certs, &flags, &digest); + GetOCSPReq(self, req); signer = GetX509CertPtr(signer_cert); key = GetPrivPKeyPtr(signer_key); - flg = NIL_P(flags) ? 0 : NUM2INT(flags); - if(NIL_P(certs)){ - x509s = sk_X509_new_null(); + if (!NIL_P(flags)) + flg = NUM2INT(flags); + if (NIL_P(digest)) + md = EVP_sha1(); + else + md = GetDigestPtr(digest); + if (NIL_P(certs)) flags |= OCSP_NOCERTS; - } - else x509s = ossl_x509_ary2sk(certs); - GetOCSPReq(self, req); - ret = OCSP_request_sign(req, signer, key, EVP_sha1(), x509s, flg); + else + x509s = ossl_x509_ary2sk(certs); + + ret = OCSP_request_sign(req, signer, key, md, x509s, flg); sk_X509_pop_free(x509s, X509_free); - if(!ret) ossl_raise(eOCSPError, NULL); + if (!ret) ossl_raise(eOCSPError, NULL); return self; } /* * call-seq: - * request.verify(certificates, store) -> true or false - * request.verify(certificates, store, flags) -> true or false + * request.verify(certificates, store, flags = 0) -> true or false * - * Verifies this request using the given +certificates+ and X509 +store+. + * Verifies this request using the given +certificates+ and +store+. + * +certificates+ is an array of OpenSSL::X509::Certificate, +store+ is an + * OpenSSL::X509::Store. */ static VALUE @@ -369,15 +426,16 @@ ossl_ocspreq_verify(int argc, VALUE *argv, VALUE self) int flg, result; rb_scan_args(argc, argv, "21", &certs, &store, &flags); + GetOCSPReq(self, req); x509st = GetX509StorePtr(store); flg = NIL_P(flags) ? 0 : NUM2INT(flags); x509s = ossl_x509_ary2sk(certs); - GetOCSPReq(self, req); result = OCSP_request_verify(req, x509s, x509st, flg); sk_X509_pop_free(x509s, X509_free); - if(!result) rb_warn("%s", ERR_error_string(ERR_peek_error(), NULL)); + if (!result) + ossl_clear_error(); - return result ? Qtrue : Qfalse; + return result > 0 ? Qtrue : Qfalse; } /* @@ -446,6 +504,25 @@ ossl_ocspres_alloc(VALUE klass) return obj; } +static VALUE +ossl_ocspres_initialize_copy(VALUE self, VALUE other) +{ + OCSP_RESPONSE *res, *res_old, *res_new; + + rb_check_frozen(self); + GetOCSPRes(self, res_old); + SafeGetOCSPRes(other, res); + + res_new = ASN1_item_dup(ASN1_ITEM_rptr(OCSP_RESPONSE), res); + if (!res_new) + ossl_raise(eOCSPError, "ASN1_item_dup"); + + SetOCSPRes(self, res_new); + OCSP_RESPONSE_free(res_old); + + return self; +} + /* * call-seq: * OpenSSL::OCSP::Response.new -> response @@ -463,15 +540,13 @@ ossl_ocspres_initialize(int argc, VALUE *argv, VALUE self) 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; @@ -582,16 +657,50 @@ ossl_ocspbres_alloc(VALUE klass) return obj; } +static VALUE +ossl_ocspbres_initialize_copy(VALUE self, VALUE other) +{ + OCSP_BASICRESP *bs, *bs_old, *bs_new; + + rb_check_frozen(self); + GetOCSPBasicRes(self, bs_old); + SafeGetOCSPBasicRes(other, bs); + + bs_new = ASN1_item_dup(ASN1_ITEM_rptr(OCSP_BASICRESP), bs); + if (!bs_new) + ossl_raise(eOCSPError, "ASN1_item_dup"); + + SetOCSPBasicRes(self, bs_new); + OCSP_BASICRESP_free(bs_old); + + return self; +} + /* * 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; } @@ -647,22 +756,49 @@ ossl_ocspbres_add_nonce(int argc, VALUE *argv, VALUE self) return self; } +static VALUE +add_status_convert_time(VALUE obj) +{ + ASN1_TIME *time; + + if (RB_INTEGER_TYPE_P(obj)) + time = X509_gmtime_adj(NULL, NUM2INT(obj)); + else + time = ossl_x509_time_adjust(NULL, obj); + + if (!time) + ossl_raise(eOCSPError, NULL); + + return (VALUE)time; +} + /* * 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 good, 1 for revoked, 2 for unknown) to this - * response for +certificate_id+. +reason+ describes the reason for the - * revocation, if any. + * Adds a certificate status for +certificate_id+. +status+ is the status, and + * must be one of these: + * + * - OpenSSL::OCSP::V_CERTSTATUS_GOOD + * - OpenSSL::OCSP::V_CERTSTATUS_REVOKED + * - OpenSSL::OCSP::V_CERTSTATUS_UNKNOWN * - * 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. + * +reason+ and +revocation_time+ can be given only when +status+ is + * OpenSSL::OCSP::V_CERTSTATUS_REVOKED. +reason+ describes the reason for the + * revocation, and must be one of OpenSSL::OCSP::REVOKED_STATUS_* constants. + * +revocation_time+ is the time when the certificate is revoked. * - * +extensions+ may be an Array of OpenSSL::X509::Extension that will - * be added to this response or nil. + * +this_update+ and +next_update+ indicate the time at which ths status is + * verified to be correct and the time at or before which newer information + * will be available, respectively. +next_update+ is optional. + * + * +extensions+ is an Array of OpenSSL::X509::Extension to be included in the + * SingleResponse. This is also optional. + * + * Note that the times, +revocation_time+, +this_update+ and +next_update+ + * can be specified in either of Integer or Time object. If they are Integer, it + * is treated as the relative seconds from the current time. */ - static VALUE ossl_ocspbres_add_status(VALUE self, VALUE cid, VALUE status, VALUE reason, VALUE revtime, @@ -671,36 +807,37 @@ ossl_ocspbres_add_status(VALUE self, VALUE cid, VALUE status, OCSP_BASICRESP *bs; OCSP_SINGLERESP *single; OCSP_CERTID *id; - ASN1_TIME *ths, *nxt, *rev; - int st, rsn, error, rstatus = 0; + ASN1_TIME *ths = NULL, *nxt = NULL, *rev = NULL; + int st, rsn = 0, error = 0, rstatus = 0; long i; VALUE tmp; + GetOCSPBasicRes(self, bs); + SafeGetOCSPCertId(cid, id); st = NUM2INT(status); - rsn = NIL_P(status) ? 0 : NUM2INT(reason); - if(!NIL_P(ext)){ - /* All ary's members should be X509Extension */ - Check_Type(ext, T_ARRAY); + if (!NIL_P(ext)) { /* All ext's members must be X509::Extension */ + ext = rb_check_array_type(ext); for (i = 0; i < RARRAY_LEN(ext); i++) OSSL_Check_Kind(RARRAY_AREF(ext, i), cX509Ext); } - error = 0; - ths = nxt = rev = NULL; - if(!NIL_P(revtime)){ - tmp = rb_protect(rb_Integer, revtime, &rstatus); - if(rstatus) goto err; - rev = X509_gmtime_adj(NULL, NUM2INT(tmp)); + if (st == V_OCSP_CERTSTATUS_REVOKED) { + rsn = NUM2INT(reason); + tmp = rb_protect(add_status_convert_time, revtime, &rstatus); + if (rstatus) goto err; + rev = (ASN1_TIME *)tmp; + } + + tmp = rb_protect(add_status_convert_time, thisupd, &rstatus); + if (rstatus) goto err; + ths = (ASN1_TIME *)tmp; + + if (!NIL_P(nextupd)) { + tmp = rb_protect(add_status_convert_time, nextupd, &rstatus); + if (rstatus) goto err; + nxt = (ASN1_TIME *)tmp; } - tmp = rb_protect(rb_Integer, thisupd, &rstatus); - if(rstatus) goto err; - ths = X509_gmtime_adj(NULL, NUM2INT(tmp)); - tmp = rb_protect(rb_Integer, nextupd, &rstatus); - if(rstatus) goto err; - nxt = X509_gmtime_adj(NULL, NUM2INT(tmp)); - GetOCSPBasicRes(self, bs); - SafeGetOCSPCertId(cid, id); if(!(single = OCSP_basic_add1_status(bs, id, st, rsn, rev, ths, nxt))){ error = 1; goto err; @@ -708,8 +845,7 @@ ossl_ocspbres_add_status(VALUE self, VALUE cid, VALUE status, if(!NIL_P(ext)){ X509_EXTENSION *x509ext; - while ((x509ext = OCSP_SINGLERESP_delete_ext(single, 0))) - X509_EXTENSION_free(x509ext); + for(i = 0; i < RARRAY_LEN(ext); i++){ x509ext = DupX509ExtPtr(RARRAY_AREF(ext, i)); if(!OCSP_SINGLERESP_add_ext(single, x509ext, -1)){ @@ -736,11 +872,13 @@ ossl_ocspbres_add_status(VALUE self, VALUE cid, VALUE status, * basic_response.status -> statuses * * Returns an Array of statuses for this response. Each status contains a - * CertificateId, the status (0 for good, 1 for revoked, 2 for unknown), 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. + * CertificateId, the status (0 for good, 1 for revoked, 2 for unknown), 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. + * + * This should be superseded by BasicResponse#responses and #find_response that + * return SingleResponse. */ - static VALUE ossl_ocspbres_get_status(VALUE self) { @@ -786,76 +924,462 @@ ossl_ocspbres_get_status(VALUE self) return ret; } +static VALUE ossl_ocspsres_new(OCSP_SINGLERESP *); + +/* + * call-seq: + * basic_response.responses -> Array of SingleResponse + * + * Returns an Array of SingleResponse for this BasicResponse. + */ + +static VALUE +ossl_ocspbres_get_responses(VALUE self) +{ + OCSP_BASICRESP *bs; + VALUE ret; + int count, i; + + GetOCSPBasicRes(self, bs); + count = OCSP_resp_count(bs); + ret = rb_ary_new2(count); + + for (i = 0; i < count; i++) { + OCSP_SINGLERESP *sres, *sres_new; + + sres = OCSP_resp_get0(bs, i); + sres_new = ASN1_item_dup(ASN1_ITEM_rptr(OCSP_SINGLERESP), sres); + if (!sres_new) + ossl_raise(eOCSPError, "ASN1_item_dup"); + + rb_ary_push(ret, ossl_ocspsres_new(sres_new)); + } + + 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 + * basic_response.find_response(certificate_id) -> SingleResponse | nil * - * Signs this response using the +signer_cert+ and +signer_key+. Additional - * +certificates+ may be added to the signature along with a set of +flags+. + * Returns a SingleResponse whose CertId matches with +certificate_id+, or nil + * if this BasicResponse does not contain it. + */ +static VALUE +ossl_ocspbres_find_response(VALUE self, VALUE target) +{ + OCSP_BASICRESP *bs; + OCSP_SINGLERESP *sres, *sres_new; + OCSP_CERTID *id; + int n; + + SafeGetOCSPCertId(target, id); + GetOCSPBasicRes(self, bs); + + if ((n = OCSP_resp_find(bs, id, -1)) == -1) + return Qnil; + + sres = OCSP_resp_get0(bs, n); + sres_new = ASN1_item_dup(ASN1_ITEM_rptr(OCSP_SINGLERESP), sres); + if (!sres_new) + ossl_raise(eOCSPError, "ASN1_item_dup"); + + return ossl_ocspsres_new(sres_new); +} + +/* + * call-seq: + * basic_response.sign(cert, key, certs = nil, flags = 0, digest = nil) -> self + * + * Signs this OCSP response using the +cert+, +key+ and optional +digest+. This + * behaves in the similar way as OpenSSL::OCSP::Request#sign. + * + * +flags+ can include: + * OpenSSL::OCSP::NOCERTS:: don't include certificates + * OpenSSL::OCSP::NOTIME:: don't set producedAt + * OpenSSL::OCSP::RESPID_KEY:: use signer's public key hash as responderID */ static VALUE ossl_ocspbres_sign(int argc, VALUE *argv, VALUE self) { - VALUE signer_cert, signer_key, certs, flags; + VALUE signer_cert, signer_key, certs, flags, digest; OCSP_BASICRESP *bs; X509 *signer; EVP_PKEY *key; - STACK_OF(X509) *x509s; - unsigned long flg; + STACK_OF(X509) *x509s = NULL; + unsigned long flg = 0; + const EVP_MD *md; int ret; - rb_scan_args(argc, argv, "22", &signer_cert, &signer_key, &certs, &flags); + rb_scan_args(argc, argv, "23", &signer_cert, &signer_key, &certs, &flags, &digest); + GetOCSPBasicRes(self, bs); signer = GetX509CertPtr(signer_cert); key = GetPrivPKeyPtr(signer_key); - flg = NIL_P(flags) ? 0 : NUM2INT(flags); - if(NIL_P(certs)){ - x509s = sk_X509_new_null(); + if (!NIL_P(flags)) + flg = NUM2INT(flags); + if (NIL_P(digest)) + md = EVP_sha1(); + else + md = GetDigestPtr(digest); + if (NIL_P(certs)) flg |= OCSP_NOCERTS; - } - else{ + else x509s = ossl_x509_ary2sk(certs); - } - GetOCSPBasicRes(self, bs); - ret = OCSP_basic_sign(bs, signer, key, EVP_sha1(), x509s, flg); + + ret = OCSP_basic_sign(bs, signer, key, md, x509s, flg); sk_X509_pop_free(x509s, X509_free); - if(!ret) ossl_raise(eOCSPError, NULL); + if (!ret) ossl_raise(eOCSPError, NULL); return self; } /* * call-seq: - * basic_response.verify(certificates, store) -> true or false - * basic_response.verify(certificates, store, flags) -> true or false + * basic_response.verify(certificates, store, flags = 0) -> true or false * - * Verifies the signature of the response using the given +certificates+, - * +store+ and +flags+. + * Verifies the signature of the response using the given +certificates+ and + * +store+. This works in the similar way as OpenSSL::OCSP::Request#verify. */ static VALUE ossl_ocspbres_verify(int argc, VALUE *argv, VALUE self) { - VALUE certs, store, flags, result; + VALUE certs, store, flags; OCSP_BASICRESP *bs; STACK_OF(X509) *x509s; X509_STORE *x509st; - int flg; + int flg, result; rb_scan_args(argc, argv, "21", &certs, &store, &flags); + GetOCSPBasicRes(self, bs); x509st = GetX509StorePtr(store); flg = NIL_P(flags) ? 0 : NUM2INT(flags); x509s = ossl_x509_ary2sk(certs); - GetOCSPBasicRes(self, bs); - result = OCSP_basic_verify(bs, x509s, x509st, flg) > 0 ? Qtrue : Qfalse; + result = OCSP_basic_verify(bs, x509s, x509st, flg); sk_X509_pop_free(x509s, X509_free); - if(!result) rb_warn("%s", ERR_error_string(ERR_peek_error(), NULL)); + if (!result) + ossl_clear_error(); - return result; + return result > 0 ? Qtrue : Qfalse; } /* + * 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::SingleResponse + */ +static VALUE +ossl_ocspsres_new(OCSP_SINGLERESP *sres) +{ + VALUE obj; + + obj = NewOCSPSingleRes(cOCSPSingleRes); + SetOCSPSingleRes(obj, sres); + + return obj; +} + +static VALUE +ossl_ocspsres_alloc(VALUE klass) +{ + OCSP_SINGLERESP *sres; + VALUE obj; + + obj = NewOCSPSingleRes(klass); + if (!(sres = OCSP_SINGLERESP_new())) + ossl_raise(eOCSPError, NULL); + SetOCSPSingleRes(obj, sres); + + return obj; +} + +/* + * call-seq: + * OpenSSL::OCSP::SingleResponse.new(der_string) -> SingleResponse + * + * Creates a new SingleResponse from +der_string+. + */ +static VALUE +ossl_ocspsres_initialize(VALUE self, VALUE arg) +{ + OCSP_SINGLERESP *res; + const unsigned char *p; + + arg = ossl_to_der_if_possible(arg); + StringValue(arg); + GetOCSPSingleRes(self, res); + + p = (unsigned char*)RSTRING_PTR(arg); + if (!d2i_OCSP_SINGLERESP(&res, &p, RSTRING_LEN(arg))) + ossl_raise(eOCSPError, "d2i_OCSP_SINGLERESP"); + + return self; +} + +static VALUE +ossl_ocspsres_initialize_copy(VALUE self, VALUE other) +{ + OCSP_SINGLERESP *sres, *sres_old, *sres_new; + + rb_check_frozen(self); + GetOCSPSingleRes(self, sres_old); + SafeGetOCSPSingleRes(other, sres); + + sres_new = ASN1_item_dup(ASN1_ITEM_rptr(OCSP_SINGLERESP), sres); + if (!sres_new) + ossl_raise(eOCSPError, "ASN1_item_dup"); + + SetOCSPSingleRes(self, sres_new); + OCSP_SINGLERESP_free(sres_old); + + return self; +} + +/* + * call-seq: + * single_response.check_validity(nsec = 0, maxsec = -1) -> true | false + * + * Checks the validity of thisUpdate and nextUpdate fields of this + * SingleResponse. This checks the current time is within the range thisUpdate + * to nextUpdate. + * + * It is possible that the OCSP request takes a few seconds or the time is not + * accurate. To avoid rejecting a valid response, this method allows the times + * to be within +nsec+ of the current time. + * + * Some responders don't set the nextUpdate field. This may cause a very old + * response to be considered valid. The +maxsec+ parameter can be used to limit + * the age of responses. + */ +static VALUE +ossl_ocspsres_check_validity(int argc, VALUE *argv, VALUE self) +{ + OCSP_SINGLERESP *sres; + ASN1_GENERALIZEDTIME *this_update, *next_update; + VALUE nsec_v, maxsec_v; + int nsec, maxsec, status, ret; + + rb_scan_args(argc, argv, "02", &nsec_v, &maxsec_v); + nsec = NIL_P(nsec_v) ? 0 : NUM2INT(nsec_v); + maxsec = NIL_P(maxsec_v) ? -1 : NUM2INT(maxsec_v); + + GetOCSPSingleRes(self, sres); + status = OCSP_single_get0_status(sres, NULL, NULL, &this_update, &next_update); + if (status < 0) + ossl_raise(eOCSPError, "OCSP_single_get0_status"); + + ret = OCSP_check_validity(this_update, next_update, nsec, maxsec); + + if (ret) + return Qtrue; + else { + ossl_clear_error(); + return Qfalse; + } +} + +/* + * call-seq: + * single_response.certid -> CertificateId + * + * Returns the CertificateId for which this SingleResponse is. + */ +static VALUE +ossl_ocspsres_get_certid(VALUE self) +{ + OCSP_SINGLERESP *sres; + OCSP_CERTID *id; + + GetOCSPSingleRes(self, sres); + id = OCSP_CERTID_dup(OCSP_SINGLERESP_get0_id(sres)); + + return ossl_ocspcertid_new(id); +} + +/* + * call-seq: + * single_response.cert_status -> Integer + * + * Returns the status of the certificate identified by the certid. + * The return value may be one of these constant: + * + * - V_CERTSTATUS_GOOD + * - V_CERTSTATUS_REVOKED + * - V_CERTSTATUS_UNKNOWN + * + * When the status is V_CERTSTATUS_REVOKED, the time at which the certificate + * was revoked can be retrieved by #revocation_time. + */ +static VALUE +ossl_ocspsres_get_cert_status(VALUE self) +{ + OCSP_SINGLERESP *sres; + int status; + + GetOCSPSingleRes(self, sres); + status = OCSP_single_get0_status(sres, NULL, NULL, NULL, NULL); + if (status < 0) + ossl_raise(eOCSPError, "OCSP_single_get0_status"); + + return INT2NUM(status); +} + +/* + * call-seq: + * single_response.this_update -> Time + */ +static VALUE +ossl_ocspsres_get_this_update(VALUE self) +{ + OCSP_SINGLERESP *sres; + int status; + ASN1_GENERALIZEDTIME *time; + + GetOCSPSingleRes(self, sres); + status = OCSP_single_get0_status(sres, NULL, NULL, &time, NULL); + if (status < 0) + ossl_raise(eOCSPError, "OCSP_single_get0_status"); + + return asn1time_to_time(time); /* will handle NULL */ +} + +/* + * call-seq: + * single_response.next_update -> Time | nil + */ +static VALUE +ossl_ocspsres_get_next_update(VALUE self) +{ + OCSP_SINGLERESP *sres; + int status; + ASN1_GENERALIZEDTIME *time; + + GetOCSPSingleRes(self, sres); + status = OCSP_single_get0_status(sres, NULL, NULL, NULL, &time); + if (status < 0) + ossl_raise(eOCSPError, "OCSP_single_get0_status"); + + return asn1time_to_time(time); +} + +/* + * call-seq: + * single_response.revocation_time -> Time | nil + */ +static VALUE +ossl_ocspsres_get_revocation_time(VALUE self) +{ + OCSP_SINGLERESP *sres; + int status; + ASN1_GENERALIZEDTIME *time; + + GetOCSPSingleRes(self, sres); + status = OCSP_single_get0_status(sres, NULL, &time, NULL, NULL); + if (status < 0) + ossl_raise(eOCSPError, "OCSP_single_get0_status"); + if (status != V_OCSP_CERTSTATUS_REVOKED) + ossl_raise(eOCSPError, "certificate is not revoked"); + + return asn1time_to_time(time); +} + +/* + * call-seq: + * single_response.revocation_reason -> Integer | nil + */ +static VALUE +ossl_ocspsres_get_revocation_reason(VALUE self) +{ + OCSP_SINGLERESP *sres; + int status, reason; + + GetOCSPSingleRes(self, sres); + status = OCSP_single_get0_status(sres, &reason, NULL, NULL, NULL); + if (status < 0) + ossl_raise(eOCSPError, "OCSP_single_get0_status"); + if (status != V_OCSP_CERTSTATUS_REVOKED) + ossl_raise(eOCSPError, "certificate is not revoked"); + + return INT2NUM(reason); +} + +/* + * call-seq: + * single_response.extensions -> Array of X509::Extension + */ +static VALUE +ossl_ocspsres_get_extensions(VALUE self) +{ + OCSP_SINGLERESP *sres; + X509_EXTENSION *ext; + int count, i; + VALUE ary; + + GetOCSPSingleRes(self, sres); + + count = OCSP_SINGLERESP_get_ext_count(sres); + ary = rb_ary_new2(count); + for (i = 0; i < count; i++) { + ext = OCSP_SINGLERESP_get_ext(sres, i); + rb_ary_push(ary, ossl_x509ext_new(ext)); /* will dup */ + } + + return ary; +} + +/* + * call-seq: + * single_response.to_der -> String + * + * Encodes this SingleResponse into a DER-encoded string. + */ +static VALUE +ossl_ocspsres_to_der(VALUE self) +{ + OCSP_SINGLERESP *sres; + VALUE str; + long len; + unsigned char *p; + + GetOCSPSingleRes(self, sres); + if ((len = i2d_OCSP_SINGLERESP(sres, NULL)) <= 0) + ossl_raise(eOCSPError, NULL); + str = rb_str_new(0, len); + p = (unsigned char *)RSTRING_PTR(str); + if (i2d_OCSP_SINGLERESP(sres, &p) <= 0) + ossl_raise(eOCSPError, NULL); + ossl_str_adjust(str, p); + + return str; +} + + +/* * OCSP::CertificateId */ static VALUE @@ -872,13 +1396,36 @@ ossl_ocspcid_alloc(VALUE klass) return obj; } +static VALUE +ossl_ocspcid_initialize_copy(VALUE self, VALUE other) +{ + OCSP_CERTID *cid, *cid_old, *cid_new; + + rb_check_frozen(self); + GetOCSPCertId(self, cid_old); + SafeGetOCSPCertId(other, cid); + + cid_new = OCSP_CERTID_dup(cid); + if (!cid_new) + ossl_raise(eOCSPError, "OCSP_CERTID_dup"); + + SetOCSPCertId(self, cid_new); + OCSP_CERTID_free(cid_old); + + return self; +} + /* * 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 +1436,17 @@ ossl_ocspcid_initialize(int argc, VALUE *argv, VALUE self) 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 +1461,8 @@ ossl_ocspcid_initialize(int argc, VALUE *argv, VALUE self) } if(!newid) ossl_raise(eOCSPError, NULL); - GetOCSPCertId(self, id); OCSP_CERTID_free(id); - RDATA(self)->data = newid; + SetOCSPCertId(self, newid); return self; } @@ -954,11 +1510,11 @@ ossl_ocspcid_cmp_issuer(VALUE self, VALUE other) /* * call-seq: - * certificate_id.get_serial -> Integer + * certificate_id.serial -> Integer * - * Returns the serial number of the issuing certificate. + * Returns the serial number of the certificate for which status is being + * requested. */ - static VALUE ossl_ocspcid_get_serial(VALUE self) { @@ -971,6 +1527,105 @@ ossl_ocspcid_get_serial(VALUE self) return asn1integer_to_num(serial); } +/* + * call-seq: + * certificate_id.issuer_name_hash -> String + * + * Returns the issuerNameHash of this certificate ID, the hash of the + * issuer's distinguished name calculated with the hashAlgorithm. + */ +static VALUE +ossl_ocspcid_get_issuer_name_hash(VALUE self) +{ + OCSP_CERTID *id; + ASN1_OCTET_STRING *name_hash; + char *hexbuf; + + GetOCSPCertId(self, id); + OCSP_id_get0_info(&name_hash, NULL, NULL, NULL, id); + + if (string2hex(name_hash->data, name_hash->length, &hexbuf, NULL) < 0) + ossl_raise(eOCSPError, "string2hex"); + + return ossl_buf2str(hexbuf, name_hash->length * 2); +} + +/* + * call-seq: + * certificate_id.issuer_key_hash -> String + * + * Returns the issuerKeyHash of this certificate ID, the hash of the issuer's + * public key. + */ +static VALUE +ossl_ocspcid_get_issuer_key_hash(VALUE self) +{ + OCSP_CERTID *id; + ASN1_OCTET_STRING *key_hash; + char *hexbuf; + + GetOCSPCertId(self, id); + OCSP_id_get0_info(NULL, NULL, &key_hash, NULL, id); + + if (string2hex(key_hash->data, key_hash->length, &hexbuf, NULL) < 0) + ossl_raise(eOCSPError, "string2hex"); + + return ossl_buf2str(hexbuf, key_hash->length * 2); +} + +/* + * call-seq: + * certificate_id.hash_algorithm -> String + * + * Returns the ln (long name) of the hash algorithm used to generate + * the issuerNameHash and the issuerKeyHash values. + */ +static VALUE +ossl_ocspcid_get_hash_algorithm(VALUE self) +{ + OCSP_CERTID *id; + ASN1_OBJECT *oid; + BIO *out; + + GetOCSPCertId(self, id); + OCSP_id_get0_info(NULL, &oid, NULL, NULL, id); + + if (!(out = BIO_new(BIO_s_mem()))) + ossl_raise(eOCSPError, "BIO_new"); + + if (!i2a_ASN1_OBJECT(out, oid)) { + BIO_free(out); + ossl_raise(eOCSPError, "i2a_ASN1_OBJECT"); + } + return ossl_membio2str(out); +} + +/* + * 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) { @@ -1043,7 +1698,7 @@ Init_ossl_ocsp(void) * store = OpenSSL::X509::Store.new * store.set_default_paths * - * unless response.verify [], store then + * unless response_basic.verify [], store then * raise 'response is not signed by a trusted certificate' * end * @@ -1059,27 +1714,28 @@ Init_ossl_ocsp(void) * * 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 extract the status information for the certificate from the basic + * response. * - * Then check the various fields. + * single_response = basic_response.find_response(certificate_id) * - * unless response_certificate_id == certificate_id then - * raise 'certificate id mismatch' + * unless single_response + * raise 'basic_response does not have the status for the certificiate' * end * - * now = Time.now + * Then check the validity. A status issued in the future must be rejected. * - * if this_update > now then - * raise 'update date is in the future' + * unless single_response.check_validity + * raise 'this_update is in the future or next_update time has passed' * end * - * if now > next_update then - * raise 'next update time has passed' + * case single_response.cert_status + * when OpenSSL::OCSP::V_CERTSTATUS_GOOD + * puts 'certificate is still valid' + * when OpenSSL::OCSP::V_CERTSTATUS_REVOKED + * puts "certificate has been revoked at #{single_response.revocation_time}" + * when OpenSSL::OCSP::V_CERTSTATUS_UNKNOWN + * puts 'responder doesn't know about the certificate' * end */ @@ -1100,6 +1756,7 @@ Init_ossl_ocsp(void) cOCSPReq = rb_define_class_under(mOCSP, "Request", rb_cObject); rb_define_alloc_func(cOCSPReq, ossl_ocspreq_alloc); + rb_define_copy_func(cOCSPReq, ossl_ocspreq_initialize_copy); 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); @@ -1117,6 +1774,7 @@ Init_ossl_ocsp(void) 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); + rb_define_copy_func(cOCSPRes, ossl_ocspres_initialize_copy); rb_define_method(cOCSPRes, "initialize", ossl_ocspres_initialize, -1); rb_define_method(cOCSPRes, "status", ossl_ocspres_status, 0); rb_define_method(cOCSPRes, "status_string", ossl_ocspres_status_string, 0); @@ -1131,13 +1789,36 @@ Init_ossl_ocsp(void) cOCSPBasicRes = rb_define_class_under(mOCSP, "BasicResponse", rb_cObject); rb_define_alloc_func(cOCSPBasicRes, ossl_ocspbres_alloc); + rb_define_copy_func(cOCSPBasicRes, ossl_ocspbres_initialize_copy); rb_define_method(cOCSPBasicRes, "initialize", ossl_ocspbres_initialize, -1); rb_define_method(cOCSPBasicRes, "copy_nonce", ossl_ocspbres_copy_nonce, 1); rb_define_method(cOCSPBasicRes, "add_nonce", ossl_ocspbres_add_nonce, -1); rb_define_method(cOCSPBasicRes, "add_status", ossl_ocspbres_add_status, 7); rb_define_method(cOCSPBasicRes, "status", ossl_ocspbres_get_status, 0); + rb_define_method(cOCSPBasicRes, "responses", ossl_ocspbres_get_responses, 0); + rb_define_method(cOCSPBasicRes, "find_response", ossl_ocspbres_find_response, 1); 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::SingleResponse represents an OCSP SingleResponse + * structure, which contains the basic information of the status of the + * certificate. + */ + cOCSPSingleRes = rb_define_class_under(mOCSP, "SingleResponse", rb_cObject); + rb_define_alloc_func(cOCSPSingleRes, ossl_ocspsres_alloc); + rb_define_copy_func(cOCSPSingleRes, ossl_ocspsres_initialize_copy); + rb_define_method(cOCSPSingleRes, "initialize", ossl_ocspsres_initialize, 1); + rb_define_method(cOCSPSingleRes, "check_validity", ossl_ocspsres_check_validity, -1); + rb_define_method(cOCSPSingleRes, "certid", ossl_ocspsres_get_certid, 0); + rb_define_method(cOCSPSingleRes, "cert_status", ossl_ocspsres_get_cert_status, 0); + rb_define_method(cOCSPSingleRes, "this_update", ossl_ocspsres_get_this_update, 0); + rb_define_method(cOCSPSingleRes, "next_update", ossl_ocspsres_get_next_update, 0); + rb_define_method(cOCSPSingleRes, "revocation_time", ossl_ocspsres_get_revocation_time, 0); + rb_define_method(cOCSPSingleRes, "revocation_reason", ossl_ocspsres_get_revocation_reason, 0); + rb_define_method(cOCSPSingleRes, "extensions", ossl_ocspsres_get_extensions, 0); + rb_define_method(cOCSPSingleRes, "to_der", ossl_ocspsres_to_der, 0); /* * An OpenSSL::OCSP::CertificateId identifies a certificate to the CA so @@ -1146,10 +1827,15 @@ Init_ossl_ocsp(void) cOCSPCertId = rb_define_class_under(mOCSP, "CertificateId", rb_cObject); rb_define_alloc_func(cOCSPCertId, ossl_ocspcid_alloc); + rb_define_copy_func(cOCSPCertId, ossl_ocspcid_initialize_copy); rb_define_method(cOCSPCertId, "initialize", ossl_ocspcid_initialize, -1); 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, "issuer_name_hash", ossl_ocspcid_get_issuer_name_hash, 0); + rb_define_method(cOCSPCertId, "issuer_key_hash", ossl_ocspcid_get_issuer_key_hash, 0); + rb_define_method(cOCSPCertId, "hash_algorithm", ossl_ocspcid_get_hash_algorithm, 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)); @@ -1251,7 +1937,6 @@ Init_ossl_ocsp(void) /* The responder ID is based on the public key. */ rb_define_const(mOCSP, "V_RESPID_KEY", INT2NUM(V_OCSP_RESPID_KEY)); } - #else void Init_ossl_ocsp(void) |