diff options
author | Kazuki Yamaguchi <k@rhe.jp> | 2016-06-19 23:25:32 +0900 |
---|---|---|
committer | Kazuki Yamaguchi <k@rhe.jp> | 2016-06-19 23:32:17 +0900 |
commit | e1c7ee1bb3e13fa4fe0f200cf9a8cd3531c45d39 (patch) | |
tree | 0e0f781de70fa607c5275401ba7a881d4039bdbe | |
parent | 3b8046d83ce3271d20c182054e44caf9270d4429 (diff) | |
parent | 9ead0ecaa2f500cf19e7cfb484d21c401c4c4ca4 (diff) | |
download | ruby-openssl-e1c7ee1bb3e13fa4fe0f200cf9a8cd3531c45d39.tar.gz |
Merge changes from Ruby trunk r55335..r55457
Also adjust tests.
* ruby-trunk r55335..r55457: (15 commits)
(r55457) openssl: add OpenSSL::OCSP::SingleResponse
(r55456) openssl: allow passing absolute times in OCSP::BasicRespons..
(r55455) openssl: implement initialize_copy for OpenSSL::OCSP::*
(r55454) openssl: implement initialize_copy method for PKey classes
(r55450) openssl: add 'const's required in OpenSSL master
(r55444) openssl: avoid test crash on Ubuntu 16.04
(r55423) openssl: refactor OpenSSL::OCSP::*#verify
(r55422) openssl: allow specifying hash algorithm in OCSP::*#sign
(r55411) openssl: add some accessor methods for OCSP::CertificateId
(r55409) openssl: add missing #to_der to OCSP::{CertificateId,BasicR..
(r55408) openssl: fix acesssor functions for RSA and DH in openssl_m..
(r55388) openssl: support non AES-GCM AEAD ciphers in OpenSSL::Cipher
(r55387) openssl: avoid test failure in test_engine.rb
(r55344) openssl: use ASN1_ENUMERATED_to_BN() if needed
(r55342) openssl: fix build with OPENSSL_NO_EC
Sync-with-trunk: r55457
-rw-r--r-- | ext/openssl/openssl_missing.h | 13 | ||||
-rw-r--r-- | ext/openssl/ossl_asn1.c | 8 | ||||
-rw-r--r-- | ext/openssl/ossl_cipher.c | 72 | ||||
-rw-r--r-- | ext/openssl/ossl_ocsp.c | 929 | ||||
-rw-r--r-- | ext/openssl/ossl_pkey.h | 2 | ||||
-rw-r--r-- | ext/openssl/ossl_pkey_dh.c | 42 | ||||
-rw-r--r-- | ext/openssl/ossl_pkey_dsa.c | 29 | ||||
-rw-r--r-- | ext/openssl/ossl_pkey_ec.c | 83 | ||||
-rw-r--r-- | ext/openssl/ossl_pkey_rsa.c | 33 | ||||
-rw-r--r-- | ext/openssl/ossl_ssl.c | 6 | ||||
-rw-r--r-- | ext/openssl/ruby_missing.h | 5 | ||||
-rw-r--r-- | test/envutil.rb | 7 | ||||
-rw-r--r-- | test/test_asn1.rb | 6 | ||||
-rw-r--r-- | test/test_engine.rb | 8 | ||||
-rw-r--r-- | test/test_ocsp.rb | 213 | ||||
-rw-r--r-- | test/test_pair.rb | 7 | ||||
-rw-r--r-- | test/test_pkey_dh.rb | 10 | ||||
-rw-r--r-- | test/test_pkey_dsa.rb | 8 | ||||
-rw-r--r-- | test/test_pkey_ec.rb | 29 | ||||
-rw-r--r-- | test/test_pkey_rsa.rb | 8 | ||||
-rw-r--r-- | test/utils.rb | 5 |
21 files changed, 1290 insertions, 233 deletions
diff --git a/ext/openssl/openssl_missing.h b/ext/openssl/openssl_missing.h index 67df70a5..769c7c2d 100644 --- a/ext/openssl/openssl_missing.h +++ b/ext/openssl/openssl_missing.h @@ -179,7 +179,7 @@ void X509_REQ_get0_signature(ASN1_BIT_STRING **psig, X509_ALGOR **palg, X509_REQ static inline _type *EVP_PKEY_get0_##_type(EVP_PKEY *pkey) { \ return pkey->pkey._name; } #define IMPL_KEY_ACCESSOR2(_type, _group, a1, a2, _fail_cond) \ -static inline void _type##_get0_##_group(_type *obj, BIGNUM **a1, BIGNUM **a2) { \ +static inline void _type##_get0_##_group(_type *obj, const BIGNUM **a1, const BIGNUM **a2) { \ if (a1) *a1 = obj->a1; \ if (a2) *a2 = obj->a2; } \ static inline int _type##_set0_##_group(_type *obj, BIGNUM *a1, BIGNUM *a2) { \ @@ -188,7 +188,7 @@ static inline int _type##_set0_##_group(_type *obj, BIGNUM *a1, BIGNUM *a2) { \ BN_clear_free(obj->a2); obj->a2 = a2; \ return 1; } #define IMPL_KEY_ACCESSOR3(_type, _group, a1, a2, a3, _fail_cond) \ -static inline void _type##_get0_##_group(_type *obj, BIGNUM **a1, BIGNUM **a2, BIGNUM **a3) { \ +static inline void _type##_get0_##_group(_type *obj, const BIGNUM **a1, const BIGNUM **a2, const BIGNUM **a3) { \ if (a1) *a1 = obj->a1; \ if (a2) *a2 = obj->a2; \ if (a3) *a3 = obj->a3; } \ @@ -201,7 +201,7 @@ static inline int _type##_set0_##_group(_type *obj, BIGNUM *a1, BIGNUM *a2, BIGN #if !defined(OPENSSL_NO_RSA) IMPL_PKEY_GETTER(RSA, rsa) -IMPL_KEY_ACCESSOR3(RSA, key, n, e, d, (n == obj->n || e == obj->e || (obj->d && e == obj->d))) +IMPL_KEY_ACCESSOR3(RSA, key, n, e, d, (n == obj->n || e == obj->e || (obj->d && d == obj->d))) IMPL_KEY_ACCESSOR2(RSA, factors, p, q, (p == obj->p || q == obj->q)) IMPL_KEY_ACCESSOR3(RSA, crt_params, dmp1, dmq1, iqmp, (dmp1 == obj->dmp1 || dmq1 == obj->dmq1 || iqmp == obj->iqmp)) #endif @@ -215,7 +215,7 @@ IMPL_KEY_ACCESSOR3(DSA, pqg, p, q, g, (p == obj->p || q == obj->q || g == obj->g #if !defined(OPENSSL_NO_DH) IMPL_PKEY_GETTER(DH, dh) IMPL_KEY_ACCESSOR2(DH, key, pub_key, priv_key, (pub_key == obj->pub_key || (obj->priv_key && priv_key == obj->priv_key))) -IMPL_KEY_ACCESSOR3(DH, pqg, p, q, g, (p == obj->p || q == obj->q || g == obj->g)) +IMPL_KEY_ACCESSOR3(DH, pqg, p, q, g, (p == obj->p || obj->q && q == obj->q || g == obj->g)) static inline ENGINE *DH_get0_engine(DH *dh) { return dh->engine; } #endif @@ -228,4 +228,9 @@ IMPL_PKEY_GETTER(EC_KEY, ec) #undef IMPL_KEY_ACCESSOR3 #endif /* HAVE_OPAQUE_OPENSSL */ +#if defined(HAVE_AUTHENTICATED_ENCRYPTION) && !defined(EVP_CTRL_AEAD_GET_TAG) +# define EVP_CTRL_AEAD_GET_TAG EVP_CTRL_GCM_GET_TAG +# define EVP_CTRL_AEAD_SET_TAG EVP_CTRL_GCM_SET_TAG +#endif + #endif /* _OSSL_OPENSSL_MISSING_H_ */ diff --git a/ext/openssl/ossl_asn1.c b/ext/openssl/ossl_asn1.c index 03822b5c..cbedd59e 100644 --- a/ext/openssl/ossl_asn1.c +++ b/ext/openssl/ossl_asn1.c @@ -125,9 +125,13 @@ asn1integer_to_num(ASN1_INTEGER *ai) if (!ai) { ossl_raise(rb_eTypeError, "ASN1_INTEGER is NULL!"); } - if (!(bn = ASN1_INTEGER_to_BN(ai, NULL))) { + if (ai->type == V_ASN1_ENUMERATED) + bn = ASN1_ENUMERATED_to_BN(ai, NULL); + else + bn = ASN1_INTEGER_to_BN(ai, NULL); + + if (!bn) ossl_raise(eOSSLError, NULL); - } #if DO_IT_VIA_RUBY if (!(txt = BN_bn2dec(bn))) { BN_free(bn); diff --git a/ext/openssl/ossl_cipher.c b/ext/openssl/ossl_cipher.c index e1408b91..be7a0321 100644 --- a/ext/openssl/ossl_cipher.c +++ b/ext/openssl/ossl_cipher.c @@ -553,29 +553,9 @@ ossl_cipher_set_auth_data(VALUE self, VALUE data) return data; } -#define ossl_is_gcm(nid) (nid) == NID_aes_128_gcm || \ - (nid) == NID_aes_192_gcm || \ - (nid) == NID_aes_256_gcm - -static VALUE -ossl_get_gcm_auth_tag(EVP_CIPHER_CTX *ctx, int len) -{ - unsigned char *tag; - VALUE ret; - - tag = ALLOC_N(unsigned char, len); - - if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, len, tag)) - ossl_raise(eCipherError, "retrieving the authentication tag failed"); - - ret = rb_str_new((const char *) tag, len); - xfree(tag); - return ret; -} - /* * call-seq: - * cipher.auth_tag([ tag_len ] -> string + * cipher.auth_tag(tag_len = 16) -> String * * Gets the authentication tag generated by Authenticated Encryption Cipher * modes (GCM for example). This tag may be stored along with the ciphertext, @@ -590,32 +570,23 @@ ossl_get_gcm_auth_tag(EVP_CIPHER_CTX *ctx, int len) static VALUE ossl_cipher_get_auth_tag(int argc, VALUE *argv, VALUE self) { - VALUE vtag_len; + VALUE vtag_len, ret; EVP_CIPHER_CTX *ctx; - int nid, tag_len; + int tag_len = 16; - if (rb_scan_args(argc, argv, "01", &vtag_len) == 0) { - tag_len = 16; - } else { + if (rb_scan_args(argc, argv, "01", &vtag_len) == 1) tag_len = NUM2INT(vtag_len); - } GetCipher(self, ctx); - nid = EVP_CIPHER_CTX_nid(ctx); - if (ossl_is_gcm(nid)) { - return ossl_get_gcm_auth_tag(ctx, tag_len); - } else { + if (!(EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER)) ossl_raise(eCipherError, "authentication tag not supported by this cipher"); - return Qnil; /* dummy */ - } -} -static inline void -ossl_set_gcm_auth_tag(EVP_CIPHER_CTX *ctx, unsigned char *tag, int tag_len) -{ - if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, tag_len, tag)) - ossl_raise(eCipherError, "unable to set GCM tag"); + ret = rb_str_new(NULL, tag_len); + if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, tag_len, RSTRING_PTR(ret))) + ossl_raise(eCipherError, "retrieving the authentication tag failed"); + + return ret; } /* @@ -634,7 +605,6 @@ static VALUE ossl_cipher_set_auth_tag(VALUE self, VALUE vtag) { EVP_CIPHER_CTX *ctx; - int nid; unsigned char *tag; int tag_len; @@ -643,13 +613,11 @@ ossl_cipher_set_auth_tag(VALUE self, VALUE vtag) tag_len = RSTRING_LENINT(vtag); GetCipher(self, ctx); - nid = EVP_CIPHER_CTX_nid(ctx); - - if (ossl_is_gcm(nid)) { - ossl_set_gcm_auth_tag(ctx, tag, tag_len); - } else { + if (!(EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER)) ossl_raise(eCipherError, "authentication tag not supported by this cipher"); - } + + if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tag_len, tag)) + ossl_raise(eCipherError, "unable to set AEAD tag"); return vtag; } @@ -665,16 +633,10 @@ static VALUE ossl_cipher_is_authenticated(VALUE self) { EVP_CIPHER_CTX *ctx; - int nid; GetCipher(self, ctx); - nid = EVP_CIPHER_CTX_nid(ctx); - if (ossl_is_gcm(nid)) { - return Qtrue; - } else { - return Qfalse; - } + return (EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER) ? Qtrue : Qfalse; } #else #define ossl_cipher_set_auth_data rb_f_notimplement @@ -948,7 +910,7 @@ Init_ossl_cipher(void) * the OpenSSL library still requires a value to be set - "" may be used in * case none is available. An example using the GCM (Galois Counter Mode): * - * cipher = OpenSSL::Cipher::AES.new(128, :GCM) + * cipher = OpenSSL::Cipher.new("aes-128-gcm") * cipher.encrypt * key = cipher.random_key * iv = cipher.random_iv @@ -957,7 +919,7 @@ Init_ossl_cipher(void) * encrypted = cipher.update(data) + cipher.final * tag = cipher.auth_tag * - * decipher = OpenSSL::Cipher::AES.new(128, :GCM) + * decipher = OpenSSL::Cipher.new("aes-128-gcm") * decipher.decrypt * decipher.key = key * decipher.iv = iv 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) diff --git a/ext/openssl/ossl_pkey.h b/ext/openssl/ossl_pkey.h index 6f9555e5..1f68352c 100644 --- a/ext/openssl/ossl_pkey.h +++ b/ext/openssl/ossl_pkey.h @@ -103,7 +103,7 @@ void Init_ossl_ec(void); static VALUE ossl_##_keytype##_get_##_name(VALUE self) \ { \ _type *obj; \ - BIGNUM *bn; \ + const BIGNUM *bn; \ \ Get##_type(self, obj); \ _get; \ diff --git a/ext/openssl/ossl_pkey_dh.c b/ext/openssl/ossl_pkey_dh.c index 74402fb9..139af152 100644 --- a/ext/openssl/ossl_pkey_dh.c +++ b/ext/openssl/ossl_pkey_dh.c @@ -238,6 +238,39 @@ ossl_dh_initialize(int argc, VALUE *argv, VALUE self) return self; } +static VALUE +ossl_dh_initialize_copy(VALUE self, VALUE other) +{ + EVP_PKEY *pkey; + DH *dh, *dh_other; + const BIGNUM *pub, *priv; + + GetPKey(self, pkey); + if (EVP_PKEY_base_id(pkey) != EVP_PKEY_NONE) + ossl_raise(eDHError, "DH already initialized"); + GetDH(other, dh_other); + + dh = DHparams_dup(dh_other); + if (!dh) + ossl_raise(eDHError, "DHparams_dup"); + EVP_PKEY_assign_DH(pkey, dh); + + DH_get0_key(dh_other, &pub, &priv); + if (pub) { + BIGNUM *pub2 = BN_dup(pub); + BIGNUM *priv2 = BN_dup(priv); + + if (!pub2 || priv && !priv2) { + BN_clear_free(pub2); + BN_clear_free(priv2); + ossl_raise(eDHError, "BN_dup"); + } + DH_set0_key(dh, pub2, priv2); + } + + return self; +} + /* * call-seq: * dh.public? -> true | false @@ -249,7 +282,7 @@ static VALUE ossl_dh_is_public(VALUE self) { DH *dh; - BIGNUM *bn; + const BIGNUM *bn; GetDH(self, dh); DH_get0_key(dh, &bn, NULL); @@ -268,7 +301,7 @@ static VALUE ossl_dh_is_private(VALUE self) { DH *dh; - BIGNUM *bn; + const BIGNUM *bn; GetDH(self, dh); DH_get0_key(dh, NULL, &bn); @@ -352,7 +385,7 @@ ossl_dh_get_params(VALUE self) { DH *dh; VALUE hash; - BIGNUM *p, *q, *g, *pub_key, *priv_key; + const BIGNUM *p, *q, *g, *pub_key, *priv_key; GetDH(self, dh); DH_get0_pqg(dh, &p, &q, &g); @@ -498,7 +531,7 @@ static VALUE ossl_dh_compute_key(VALUE self, VALUE pub) { DH *dh; - BIGNUM *pub_key, *dh_p; + const BIGNUM *pub_key, *dh_p; VALUE str; int len; @@ -568,6 +601,7 @@ Init_ossl_dh(void) cDH = rb_define_class_under(mPKey, "DH", cPKey); rb_define_singleton_method(cDH, "generate", ossl_dh_s_generate, -1); rb_define_method(cDH, "initialize", ossl_dh_initialize, -1); + rb_define_copy_func(cDH, ossl_dh_initialize_copy); rb_define_method(cDH, "public?", ossl_dh_is_public, 0); rb_define_method(cDH, "private?", ossl_dh_is_private, 0); rb_define_method(cDH, "to_text", ossl_dh_to_text, 0); diff --git a/ext/openssl/ossl_pkey_dsa.c b/ext/openssl/ossl_pkey_dsa.c index 28246790..1ddc0d48 100644 --- a/ext/openssl/ossl_pkey_dsa.c +++ b/ext/openssl/ossl_pkey_dsa.c @@ -26,7 +26,7 @@ static inline int DSA_HAS_PRIVATE(DSA *dsa) { - BIGNUM *bn; + const BIGNUM *bn; DSA_get0_key(dsa, NULL, &bn); return !!bn; } @@ -269,6 +269,26 @@ ossl_dsa_initialize(int argc, VALUE *argv, VALUE self) return self; } +static VALUE +ossl_dsa_initialize_copy(VALUE self, VALUE other) +{ + EVP_PKEY *pkey; + DSA *dsa, *dsa_new; + + GetPKey(self, pkey); + if (EVP_PKEY_base_id(pkey) != EVP_PKEY_NONE) + ossl_raise(eDSAError, "DSA already initialized"); + GetDSA(other, dsa); + + dsa_new = ASN1_dup((i2d_of_void *)i2d_DSAPrivateKey, (d2i_of_void *)d2i_DSAPrivateKey, (char *)dsa); + if (!dsa_new) + ossl_raise(eDSAError, "ASN1_dup"); + + EVP_PKEY_assign_DSA(pkey, dsa_new); + + return self; +} + /* * call-seq: * dsa.public? -> true | false @@ -280,7 +300,7 @@ static VALUE ossl_dsa_is_public(VALUE self) { DSA *dsa; - BIGNUM *bn; + const BIGNUM *bn; GetDSA(self, dsa); DSA_get0_key(dsa, &bn, NULL); @@ -402,7 +422,7 @@ ossl_dsa_get_params(VALUE self) { DSA *dsa; VALUE hash; - BIGNUM *p, *q, *g, *pub_key, *priv_key; + const BIGNUM *p, *q, *g, *pub_key, *priv_key; GetDSA(self, dsa); DSA_get0_pqg(dsa, &p, &q, &g); @@ -509,7 +529,7 @@ static VALUE ossl_dsa_sign(VALUE self, VALUE data) { DSA *dsa; - BIGNUM *dsa_q; + const BIGNUM *dsa_q; unsigned int buf_len; VALUE str; @@ -610,6 +630,7 @@ Init_ossl_dsa(void) rb_define_singleton_method(cDSA, "generate", ossl_dsa_s_generate, 1); rb_define_method(cDSA, "initialize", ossl_dsa_initialize, -1); + rb_define_copy_func(cDSA, ossl_dsa_initialize_copy); rb_define_method(cDSA, "public?", ossl_dsa_is_public, 0); rb_define_method(cDSA, "private?", ossl_dsa_is_private, 0); diff --git a/ext/openssl/ossl_pkey_ec.c b/ext/openssl/ossl_pkey_ec.c index 30ded33c..43eebd26 100644 --- a/ext/openssl/ossl_pkey_ec.c +++ b/ext/openssl/ossl_pkey_ec.c @@ -285,6 +285,29 @@ static VALUE ossl_ec_key_initialize(int argc, VALUE *argv, VALUE self) return self; } +static VALUE +ossl_ec_key_initialize_copy(VALUE self, VALUE other) +{ + EVP_PKEY *pkey; + EC_KEY *ec, *ec_new; + + GetPKey(self, pkey); + if (EVP_PKEY_base_id(pkey) != EVP_PKEY_NONE) + ossl_raise(eECError, "EC already initialized"); + SafeRequire_EC_KEY(other, ec); + + ec_new = EC_KEY_dup(ec); + if (!ec_new) + ossl_raise(eECError, "EC_KEY_dup"); + if (!EVP_PKEY_assign_EC_KEY(pkey, ec_new)) { + EC_KEY_free(ec_new); + ossl_raise(eECError, "EVP_PKEY_assign_EC_KEY"); + } + rb_iv_set(self, "@group", Qnil); /* EC_KEY_dup() also copies the EC_GROUP */ + + return self; +} + /* * call-seq: * key.group => group @@ -903,6 +926,26 @@ static VALUE ossl_ec_group_initialize(int argc, VALUE *argv, VALUE self) return self; } +static VALUE +ossl_ec_group_initialize_copy(VALUE self, VALUE other) +{ + ossl_ec_group *ec_group; + EC_GROUP *orig; + + TypedData_Get_Struct(self, ossl_ec_group, &ossl_ec_group_type, ec_group); + if (ec_group->group) + ossl_raise(eEC_GROUP, "EC::Group already initialized"); + SafeRequire_EC_GROUP(other, orig); + + ec_group->group = EC_GROUP_dup(orig); + if (!ec_group->group) + ossl_raise(eEC_GROUP, "EC_GROUP_dup"); + + rb_iv_set(self, "@key", Qnil); + + return self; +} + /* call-seq: * group1.eql?(group2) => true | false * group1 == group2 => true | false @@ -1381,6 +1424,31 @@ static VALUE ossl_ec_point_initialize(int argc, VALUE *argv, VALUE self) return self; } +static VALUE +ossl_ec_point_initialize_copy(VALUE self, VALUE other) +{ + ossl_ec_point *ec_point; + EC_POINT *orig; + EC_GROUP *group; + VALUE group_v; + + TypedData_Get_Struct(self, ossl_ec_point, &ossl_ec_point_type, ec_point); + if (ec_point->point) + ossl_raise(eEC_POINT, "EC::Point already initialized"); + SafeRequire_EC_POINT(other, orig); + + group_v = rb_obj_dup(rb_iv_get(other, "@group")); + SafeRequire_EC_GROUP(group_v, group); + + ec_point->point = EC_POINT_dup(orig, group); + if (!ec_point->point) + ossl_raise(eEC_POINT, "EC_POINT_dup"); + rb_iv_set(self, "@key", Qnil); + rb_iv_set(self, "@group", group_v); + + return self; +} + /* * call-seq: * point1.eql?(point2) => true | false @@ -1624,14 +1692,6 @@ static VALUE ossl_ec_point_mul(int argc, VALUE *argv, VALUE self) return result; } -static void no_copy(VALUE klass) -{ - rb_undef_method(klass, "copy"); - rb_undef_method(klass, "clone"); - rb_undef_method(klass, "dup"); - rb_undef_method(klass, "initialize_copy"); -} - void Init_ossl_ec(void) { #ifdef DONT_NEED_RDOC_WORKAROUND @@ -1664,6 +1724,7 @@ void Init_ossl_ec(void) rb_define_singleton_method(cEC, "generate", ossl_ec_key_s_generate, 1); rb_define_method(cEC, "initialize", ossl_ec_key_initialize, -1); + rb_define_copy_func(cEC, ossl_ec_key_initialize_copy); /* copy/dup/cmp */ rb_define_method(cEC, "group", ossl_ec_key_get_group, 0); @@ -1700,6 +1761,7 @@ void Init_ossl_ec(void) rb_define_alloc_func(cEC_GROUP, ossl_ec_group_alloc); rb_define_method(cEC_GROUP, "initialize", ossl_ec_group_initialize, -1); + rb_define_copy_func(cEC_GROUP, ossl_ec_group_initialize_copy); rb_define_method(cEC_GROUP, "eql?", ossl_ec_group_eql, 1); rb_define_alias(cEC_GROUP, "==", "eql?"); /* copy/dup/cmp */ @@ -1735,6 +1797,7 @@ void Init_ossl_ec(void) rb_define_alloc_func(cEC_POINT, ossl_ec_point_alloc); rb_define_method(cEC_POINT, "initialize", ossl_ec_point_initialize, -1); + rb_define_copy_func(cEC_POINT, ossl_ec_point_initialize_copy); rb_attr(cEC_POINT, rb_intern("group"), 1, 0, 0); rb_define_method(cEC_POINT, "eql?", ossl_ec_point_eql, 1); rb_define_alias(cEC_POINT, "==", "eql?"); @@ -1748,10 +1811,6 @@ void Init_ossl_ec(void) rb_define_method(cEC_POINT, "to_bn", ossl_ec_point_to_bn, 0); rb_define_method(cEC_POINT, "mul", ossl_ec_point_mul, -1); - - no_copy(cEC); - no_copy(cEC_GROUP); - no_copy(cEC_POINT); } #else /* defined NO_EC */ diff --git a/ext/openssl/ossl_pkey_rsa.c b/ext/openssl/ossl_pkey_rsa.c index 1ee45d0f..2326a70f 100644 --- a/ext/openssl/ossl_pkey_rsa.c +++ b/ext/openssl/ossl_pkey_rsa.c @@ -26,7 +26,7 @@ static inline int RSA_HAS_PRIVATE(RSA *rsa) { - BIGNUM *p, *q; + const BIGNUM *p, *q; RSA_get0_factors(rsa, &p, &q); return p && q; /* d? why? */ @@ -271,6 +271,26 @@ ossl_rsa_initialize(int argc, VALUE *argv, VALUE self) return self; } +static VALUE +ossl_rsa_initialize_copy(VALUE self, VALUE other) +{ + EVP_PKEY *pkey; + RSA *rsa, *rsa_new; + + GetPKey(self, pkey); + if (EVP_PKEY_base_id(pkey) != EVP_PKEY_NONE) + ossl_raise(eRSAError, "RSA already initialized"); + GetRSA(other, rsa); + + rsa_new = ASN1_dup((i2d_of_void *)i2d_RSAPrivateKey, (d2i_of_void *)d2i_RSAPrivateKey, (char *)rsa); + if (!rsa_new) + ossl_raise(eRSAError, "ASN1_dup"); + + EVP_PKEY_assign_RSA(pkey, rsa_new); + + return self; +} + /* * call-seq: * rsa.public? => true @@ -398,7 +418,7 @@ static VALUE ossl_rsa_public_encrypt(int argc, VALUE *argv, VALUE self) { RSA *rsa; - BIGNUM *rsa_n; + const BIGNUM *rsa_n; int buf_len, pad; VALUE str, buffer, padding; @@ -430,7 +450,7 @@ static VALUE ossl_rsa_public_decrypt(int argc, VALUE *argv, VALUE self) { RSA *rsa; - BIGNUM *rsa_n; + const BIGNUM *rsa_n; int buf_len, pad; VALUE str, buffer, padding; @@ -462,7 +482,7 @@ static VALUE ossl_rsa_private_encrypt(int argc, VALUE *argv, VALUE self) { RSA *rsa; - BIGNUM *rsa_n; + const BIGNUM *rsa_n; int buf_len, pad; VALUE str, buffer, padding; @@ -496,7 +516,7 @@ static VALUE ossl_rsa_private_decrypt(int argc, VALUE *argv, VALUE self) { RSA *rsa; - BIGNUM *rsa_n; + const BIGNUM *rsa_n; int buf_len, pad; VALUE str, buffer, padding; @@ -534,7 +554,7 @@ ossl_rsa_get_params(VALUE self) { RSA *rsa; VALUE hash; - BIGNUM *n, *e, *d, *p, *q, *dmp1, *dmq1, *iqmp; + const BIGNUM *n, *e, *d, *p, *q, *dmp1, *dmq1, *iqmp; GetRSA(self, rsa); RSA_get0_key(rsa, &n, &e, &d); @@ -675,6 +695,7 @@ Init_ossl_rsa(void) rb_define_singleton_method(cRSA, "generate", ossl_rsa_s_generate, -1); rb_define_method(cRSA, "initialize", ossl_rsa_initialize, -1); + rb_define_copy_func(cRSA, ossl_rsa_initialize_copy); rb_define_method(cRSA, "public?", ossl_rsa_is_public, 0); rb_define_method(cRSA, "private?", ossl_rsa_is_private, 0); diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c index 1ee06583..58769461 100644 --- a/ext/openssl/ossl_ssl.c +++ b/ext/openssl/ossl_ssl.c @@ -163,7 +163,7 @@ ossl_sslctx_s_alloc(VALUE klass) RTYPEDDATA_DATA(obj) = ctx; SSL_CTX_set_ex_data(ctx, ossl_ssl_ex_ptr_idx, (void*)obj); -#if defined(HAVE_SSL_CTX_SET_ECDH_AUTO) +#if !defined(OPENSSL_NO_EC) && defined(HAVE_SSL_CTX_SET_ECDH_AUTO) /* We use SSL_CTX_set1_curves_list() to specify the curve used in ECDH. It * allows to specify multiple curve names and OpenSSL will select * automatically from them. In OpenSSL 1.0.2, the automatic selection has to @@ -285,7 +285,7 @@ ossl_tmp_dh_callback(SSL *ssl, int is_export, int keylength) } #endif /* OPENSSL_NO_DH */ -#if !defined(OPENSSL_NO_EC) +#if !defined(OPENSSL_NO_EC) && defined(HAVE_SSL_CTX_SET_TMP_ECDH_CALLBACK) static VALUE ossl_call_tmp_ecdh_callback(VALUE args) { @@ -2300,7 +2300,7 @@ Init_ossl_ssl(void) */ rb_attr(cSSLContext, rb_intern("client_cert_cb"), 1, 1, Qfalse); -#if defined(HAVE_SSL_CTX_SET_TMP_ECDH_CALLBACK) +#if !defined(OPENSSL_NO_EC) && defined(HAVE_SSL_CTX_SET_TMP_ECDH_CALLBACK) /* * A callback invoked when ECDH parameters are required. * diff --git a/ext/openssl/ruby_missing.h b/ext/openssl/ruby_missing.h index d7384ec3..f076b175 100644 --- a/ext/openssl/ruby_missing.h +++ b/ext/openssl/ruby_missing.h @@ -24,4 +24,9 @@ #define rb_io_t OpenFile #endif +#ifndef RB_INTEGER_TYPE_P +/* for Ruby 2.3 compatibility */ +#define RB_INTEGER_TYPE_P(obj) (RB_FIXNUM_P(obj) || RB_TYPE_P(obj, T_BIGNUM)) +#endif + #endif /* _OSSL_RUBY_MISSING_H_ */ diff --git a/test/envutil.rb b/test/envutil.rb index ac2a3c0c..a4964c2c 100644 --- a/test/envutil.rb +++ b/test/envutil.rb @@ -365,7 +365,7 @@ module Test file ||= loc.path line ||= loc.lineno end - line -= 5 # lines until src + line -= 6 # lines until src src = <<eom # -*- coding: #{src.encoding}; -*- require 'test/unit';include Test::Unit::Assertions @@ -373,6 +373,7 @@ module Test puts [Marshal.dump($!)].pack('m')#, "assertions=\#{self._assertions}" exit } + def pend(msg = nil) $stdout.syswrite [Marshal.dump(msg.to_s)].pack("m"); exit! 0 end #{src} class Test::Unit::Runner @@stop_auto_run = true @@ -389,7 +390,9 @@ eom rescue => marshal_error ignore_stderr = nil end - if res + if res.is_a?(String) + pend res + elsif res if bt = res.backtrace bt.each do |l| l.sub!(/\A-:(\d+)/){"#{file}:#{line + $1.to_i}"} diff --git a/test/test_asn1.rb b/test/test_asn1.rb index 96c0859c..f226da5c 100644 --- a/test/test_asn1.rb +++ b/test/test_asn1.rb @@ -280,6 +280,12 @@ rEzBQ0F9dUyqQ9gyRg8KHhDfv9HzT1d/rnUZMkoombwYBRIUChGCYV0GnJcan2Zm assert_equal 2 ** 31, OpenSSL::ASN1.decode(encoded).value.to_i end + def test_decode_enumerated + encoded = OpenSSL::ASN1.Enumerated(0).to_der + assert_equal "\x0a\x01\x00".b, encoded + assert_equal encoded, OpenSSL::ASN1.decode(encoded).to_der + end + def test_create_inf_length_primitive expected = %w{ 24 80 04 01 61 00 00 } raw = [expected.join('')].pack('H*') diff --git a/test/test_engine.rb b/test/test_engine.rb index 77f6e1a9..1aa105f1 100644 --- a/test/test_engine.rb +++ b/test/test_engine.rb @@ -13,17 +13,21 @@ class OpenSSL::TestEngine < OpenSSL::TestCase def test_openssl_engine_builtin with_openssl <<-'end;' + orig = OpenSSL::Engine.engines + pend "'openssl' is already loaded" if orig.any? { |e| e.id == "openssl" } engine = OpenSSL::Engine.load("openssl") assert_equal(true, engine) - assert_equal(1, OpenSSL::Engine.engines.size) + assert_equal(1, OpenSSL::Engine.engines.size - orig.size) end; end def test_openssl_engine_by_id_string with_openssl <<-'end;' + orig = OpenSSL::Engine.engines + pend "'openssl' is already loaded" if orig.any? { |e| e.id == "openssl" } engine = get_engine assert_not_nil(engine) - assert_equal(1, OpenSSL::Engine.engines.size) + assert_equal(1, OpenSSL::Engine.engines.size - orig.size) end; end diff --git a/test/test_ocsp.rb b/test/test_ocsp.rb index d04b4216..f1e34982 100644 --- a/test/test_ocsp.rb +++ b/test/test_ocsp.rb @@ -8,6 +8,10 @@ class OpenSSL::TestOCSP < OpenSSL::TestCase 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,31 +21,218 @@ class OpenSSL::TestOCSP < OpenSSL::TestCase 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 cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert) assert_kind_of OpenSSL::OCSP::CertificateId, cid assert_equal @cert.serial, cid.serial - end - - def test_new_certificate_id_with_digest cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA256.new) assert_kind_of OpenSSL::OCSP::CertificateId, cid assert_equal @cert.serial, cid.serial - end if defined?(OpenSSL::Digest::SHA256) + end + + def test_certificate_id_issuer_name_hash + cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert) + assert_equal OpenSSL::Digest::SHA1.hexdigest(@cert.issuer.to_der), cid.issuer_name_hash + assert_equal "d91f736ac4dc3242f0fb9b77a3149bd83c5c43d0", cid.issuer_name_hash + end + + def test_certificate_id_issuer_key_hash + cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert) + assert_equal OpenSSL::Digest::SHA1.hexdigest(OpenSSL::ASN1.decode(@ca_cert.to_der).value[0].value[6].value[1].value), cid.issuer_key_hash + assert_equal "d1fef9fbf8ae1bc160cbfa03e2596dd873089213", cid.issuer_key_hash + end + + def test_certificate_id_hash_algorithm + cid_sha1 = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA1.new) + cid_sha256 = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA256.new) + assert_equal "sha1", cid_sha1.hash_algorithm + assert_equal "sha256", cid_sha256.hash_algorithm + end + + 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_new_ocsp_request + def test_certificate_id_dup + cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert) + assert_equal cid.to_der, cid.dup.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, [@cert]) - assert_kind_of OpenSSL::OCSP::Request, request - # in current implementation not same instance of certificate id, but should contain same data - assert_equal cid.serial, request.certid.first.serial + 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_request_sign_verify + 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, nil, 0, "SHA1") + assert_equal cid.to_der, request.certid.first.to_der + store1 = OpenSSL::X509::Store.new; store1.add_cert(@ca_cert) + assert_equal true, request.verify([@cert], store1) + assert_equal true, request.verify([], store1) + store2 = OpenSSL::X509::Store.new; store1.add_cert(@cert2) + assert_equal false, request.verify([], store2) + assert_equal true, request.verify([], store2, OpenSSL::OCSP::NOVERIFY) + end + + def test_request_nonce + req0 = OpenSSL::OCSP::Request.new + req1 = OpenSSL::OCSP::Request.new + req1.add_nonce("NONCE") + req2 = OpenSSL::OCSP::Request.new + req2.add_nonce("NONCF") + bres = OpenSSL::OCSP::BasicResponse.new + assert_equal 2, req0.check_nonce(bres) + bres.copy_nonce(req1) + assert_equal 1, req1.check_nonce(bres) + bres.add_nonce("NONCE") + assert_equal 1, req1.check_nonce(bres) + assert_equal 0, req2.check_nonce(bres) + assert_equal 3, req0.check_nonce(bres) + end + + def test_request_dup + 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, nil, 0, "SHA1") + assert_equal request.to_der, request.dup.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) + 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_basic_response_sign_verify + cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA256.new) + bres = OpenSSL::OCSP::BasicResponse.new + bres.add_status(cid, OpenSSL::OCSP::V_CERTSTATUS_REVOKED, OpenSSL::OCSP::REVOKED_STATUS_UNSPECIFIED, -400, -300, 500, []) + bres.sign(@cert2, @key2, [], 0, "SHA256") # how can I check the algorithm? + store1 = OpenSSL::X509::Store.new; store1.add_cert(@ca_cert) + assert_equal true, bres.verify([], store1) + store2 = OpenSSL::X509::Store.new; store2.add_cert(@cert) + assert_equal false, bres.verify([], store2) + assert_equal true, bres.verify([], store2, OpenSSL::OCSP::NOVERIFY) + end + + def test_basic_response_dup + 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) + assert_equal bres.to_der, bres.dup.to_der + end + + def test_basic_response_response_operations + bres = OpenSSL::OCSP::BasicResponse.new + now = Time.at(Time.now.to_i) + cid1 = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA1.new) + cid2 = OpenSSL::OCSP::CertificateId.new(@cert2, @ca_cert, OpenSSL::Digest::SHA1.new) + cid3 = OpenSSL::OCSP::CertificateId.new(@ca_cert, @ca_cert, OpenSSL::Digest::SHA1.new) + bres.add_status(cid1, OpenSSL::OCSP::V_CERTSTATUS_REVOKED, OpenSSL::OCSP::REVOKED_STATUS_UNSPECIFIED, now - 400, -300, nil, nil) + bres.add_status(cid2, OpenSSL::OCSP::V_CERTSTATUS_GOOD, nil, nil, -300, 500, []) + + assert_equal 2, bres.responses.size + single = bres.responses.first + assert_equal cid1.to_der, single.certid.to_der + assert_equal OpenSSL::OCSP::V_CERTSTATUS_REVOKED, single.cert_status + assert_equal OpenSSL::OCSP::REVOKED_STATUS_UNSPECIFIED, single.revocation_reason + assert_equal now - 400, single.revocation_time + assert_equal now - 300, single.this_update + assert_equal nil, single.next_update + assert_equal [], single.extensions + + assert_equal cid2.to_der, bres.find_response(cid2).certid.to_der + assert_equal nil, bres.find_response(cid3) + end + + def test_single_response_der + bres = OpenSSL::OCSP::BasicResponse.new + cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert) + bres.add_status(cid, OpenSSL::OCSP::V_CERTSTATUS_GOOD, nil, nil, -300, 500, nil) + single = bres.responses[0] + der = single.to_der + asn1 = OpenSSL::ASN1.decode(der) + assert_equal :CONTEXT_SPECIFIC, asn1.value[1].tag_class + assert_equal 0, asn1.value[1].tag # good + assert_equal der, OpenSSL::OCSP::SingleResponse.new(der).to_der + end + + def test_single_response_check_validity + bres = OpenSSL::OCSP::BasicResponse.new + cid1 = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA1.new) + cid2 = OpenSSL::OCSP::CertificateId.new(@cert2, @ca_cert, OpenSSL::Digest::SHA1.new) + bres.add_status(cid1, OpenSSL::OCSP::V_CERTSTATUS_REVOKED, OpenSSL::OCSP::REVOKED_STATUS_UNSPECIFIED, -400, -300, -50, []) + bres.add_status(cid2, OpenSSL::OCSP::V_CERTSTATUS_REVOKED, OpenSSL::OCSP::REVOKED_STATUS_UNSPECIFIED, -400, -300, nil, []) + + single1 = bres.responses[0] + assert_equal false, single1.check_validity + assert_equal false, single1.check_validity(30) + assert_equal true, single1.check_validity(60) + single2 = bres.responses[1] + assert_equal true, single2.check_validity + assert_equal true, single2.check_validity(0, 500) + assert_equal false, single2.check_validity(0, 200) + 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 + + def test_response_dup + bres = OpenSSL::OCSP::BasicResponse.new + bres.sign(@cert2, @key2, [@ca_cert], 0) + res = OpenSSL::OCSP::Response.create(OpenSSL::OCSP::RESPONSE_STATUS_SUCCESSFUL, bres) + assert_equal res.to_der, res.dup.to_der end end diff --git a/test/test_pair.rb b/test/test_pair.rb index 88e52a71..575cb6c1 100644 --- a/test/test_pair.rb +++ b/test/test_pair.rb @@ -433,7 +433,12 @@ module OpenSSL::TestPairM sock1, sock2 = tcp_pair ctx1 = OpenSSL::SSL::SSLContext.new - ctx1.ciphers = "ECDH" + begin + ctx1.ciphers = "ECDH" + rescue OpenSSL::SSL::SSLError + pend "ECDH is not enabled in this OpenSSL" if $!.message =~ /no cipher match/ + raise + end ctx1.ecdh_curves = "P-384:P-521" ctx1.security_level = 0 s1 = OpenSSL::SSL::SSLSocket.new(sock1, ctx1) diff --git a/test/test_pkey_dh.rb b/test/test_pkey_dh.rb index afd7a318..4d84f7ad 100644 --- a/test/test_pkey_dh.rb +++ b/test/test_pkey_dh.rb @@ -83,6 +83,16 @@ YoaOffgTf5qxiwkjnlVZQc3whgnEt9FpVMvQ9eknyeGB5KHfayAc3+hUAvI3/Cr3 assert_equal(dh.compute_key(dh2.pub_key), dh2.compute_key(dh.pub_key)) end + def test_dup + dh = OpenSSL::PKey::DH.new(NEW_KEYLEN) + dh2 = dh.dup + assert_equal dh.to_der, dh2.to_der # params + assert_equal_params dh, dh2 # keys + dh2.set_pqg(dh2.p + 1, nil, dh2.g) + assert_not_equal dh2.p, dh.p + assert_equal dh2.g, dh.g + end + private def assert_equal_params(dh1, dh2) diff --git a/test/test_pkey_dsa.rb b/test/test_pkey_dsa.rb index 211c0342..9c29c034 100644 --- a/test/test_pkey_dsa.rb +++ b/test/test_pkey_dsa.rb @@ -230,6 +230,14 @@ YNMbNw== assert(key3.private?) end + def test_dup + key = OpenSSL::PKey::DSA.new(256) + key2 = key.dup + assert_equal key.params, key2.params + key2.set_pqg(key2.p + 1, key2.q, key2.g) + assert_not_equal key.params, key2.params + end + private def check_sign_verify(digest) diff --git a/test/test_pkey_ec.rb b/test/test_pkey_ec.rb index e05b70ac..4498b2b8 100644 --- a/test/test_pkey_ec.rb +++ b/test/test_pkey_ec.rb @@ -14,17 +14,40 @@ class OpenSSL::TestEC < OpenSSL::TestCase OpenSSL::PKey::EC.builtin_curves.each do |curve, comment| group = OpenSSL::PKey::EC::Group.new(curve) - key = OpenSSL::PKey::EC.new(group) - key.generate_key! - # Oakley curves and X25519 are not suitable for signing next if ["Oakley", "X25519"].any? { |n| curve.start_with?(n) } + key = OpenSSL::PKey::EC.new(group) + key.generate_key! + @groups << group @keys << key end end + def test_dup + key = OpenSSL::PKey::EC.new("prime256v1") + key.generate_key! + key2 = key.dup + assert_equal key.to_der, key2.to_der + key_tmp = OpenSSL::PKey::EC.new("prime256v1").generate_key! + key2.private_key = key_tmp.private_key + key2.public_key = key_tmp.public_key + assert_not_equal key.to_der, key2.to_der + + group = key.group + group2 = group.dup + assert_equal group.to_der, group2.to_der + group2.asn1_flag ^= OpenSSL::PKey::EC::NAMED_CURVE + assert_not_equal group.to_der, group2.to_der + + point = key.public_key + point2 = point.dup + assert_equal point.to_bn, point2.to_bn + point2.invert! + assert_not_equal point.to_bn, point2.to_bn + end + def compare_keys(k1, k2) assert_equal(k1.to_pem, k2.to_pem) end diff --git a/test/test_pkey_rsa.rb b/test/test_pkey_rsa.rb index c3532f63..49e8ceac 100644 --- a/test/test_pkey_rsa.rb +++ b/test/test_pkey_rsa.rb @@ -294,6 +294,14 @@ AwEAAQ== assert(key3.private?) end + def test_dup + key = OpenSSL::PKey::RSA.generate(256, 17) + key2 = key.dup + assert_equal key.params, key2.params + key2.set_key(key2.n, 3, key2.d) + assert_not_equal key.params, key2.params + end + private def check_PUBKEY(asn1, key) diff --git a/test/utils.rb b/test/utils.rb index 88a02391..5cd772e7 100644 --- a/test/utils.rb +++ b/test/utils.rb @@ -299,7 +299,10 @@ AQjjxMXhwULlmuR/K+WwlaZPiLIBYalLAZQ7ZbOPeVkJ8ePao0eLAgEC ctx.cert = @svr_cert ctx.key = @svr_key ctx.tmp_dh_callback = proc { OpenSSL::TestUtils::TEST_KEY_DH1024 } - ctx.ecdh_curves = "P-256" + begin + ctx.ecdh_curves = "P-256" + rescue NotImplementedError + end ctx.verify_mode = verify_mode ctx_proc.call(ctx) if ctx_proc |