summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHiroshi SHIBATA <hsbt@ruby-lang.org>2020-02-16 15:21:29 +0900
committerGitHub <noreply@github.com>2020-02-16 15:21:29 +0900
commitb99775b163ce44079c1f8727ce9b4ed8bb03489d (patch)
tree4f9fd53f21c94dfeb05fefe1143bbe770228733a
parent0bfa479c52963b95a47ceab3d453f21b646366a2 (diff)
Import openssl-2.2.0 (#2693)
Import the master branch of ruby/openssl for preparing to release openssl-2.2.0
Notes
Notes: Merged-By: hsbt <hsbt@ruby-lang.org>
-rw-r--r--ext/openssl/History.md53
-rw-r--r--ext/openssl/depend100
-rw-r--r--ext/openssl/deprecation.rb8
-rw-r--r--ext/openssl/extconf.rb23
-rw-r--r--ext/openssl/lib/openssl.rb33
-rw-r--r--ext/openssl/lib/openssl/bn.rb2
-rw-r--r--ext/openssl/lib/openssl/buffering.rb33
-rw-r--r--ext/openssl/lib/openssl/cipher.rb2
-rw-r--r--ext/openssl/lib/openssl/config.rb7
-rw-r--r--ext/openssl/lib/openssl/digest.rb27
-rw-r--r--ext/openssl/lib/openssl/hmac.rb13
-rw-r--r--ext/openssl/lib/openssl/pkcs5.rb2
-rw-r--r--ext/openssl/lib/openssl/pkey.rb2
-rw-r--r--ext/openssl/lib/openssl/ssl.rb42
-rw-r--r--ext/openssl/lib/openssl/version.rb5
-rw-r--r--ext/openssl/lib/openssl/x509.rb170
-rw-r--r--ext/openssl/openssl.gemspec47
-rw-r--r--ext/openssl/openssl_missing.h37
-rw-r--r--ext/openssl/ossl.c54
-rw-r--r--ext/openssl/ossl.h7
-rw-r--r--ext/openssl/ossl_asn1.c25
-rw-r--r--ext/openssl/ossl_bn.c5
-rw-r--r--ext/openssl/ossl_cipher.c7
-rw-r--r--ext/openssl/ossl_digest.c2
-rw-r--r--ext/openssl/ossl_engine.c3
-rw-r--r--ext/openssl/ossl_hmac.c14
-rw-r--r--ext/openssl/ossl_kdf.c20
-rw-r--r--ext/openssl/ossl_ocsp.c15
-rw-r--r--ext/openssl/ossl_ocsp.h6
-rw-r--r--ext/openssl/ossl_pkcs7.c18
-rw-r--r--ext/openssl/ossl_pkcs7.h16
-rw-r--r--ext/openssl/ossl_pkey.c152
-rw-r--r--ext/openssl/ossl_pkey_ec.c29
-rw-r--r--ext/openssl/ossl_pkey_rsa.c26
-rw-r--r--ext/openssl/ossl_ssl.c113
-rw-r--r--ext/openssl/ossl_ts.c1519
-rw-r--r--ext/openssl/ossl_ts.h16
-rw-r--r--ext/openssl/ossl_version.h15
-rw-r--r--ext/openssl/ossl_x509ext.c14
-rw-r--r--ext/openssl/ossl_x509name.c10
-rw-r--r--test/openssl/fixtures/chain/dh512.pem4
-rw-r--r--test/openssl/fixtures/chain/server.crt13
-rw-r--r--test/openssl/fixtures/chain/server.csr11
-rw-r--r--test/openssl/fixtures/chain/server.key15
-rw-r--r--test/openssl/test_asn1.rb38
-rw-r--r--test/openssl/test_bn.rb10
-rw-r--r--test/openssl/test_buffering.rb11
-rw-r--r--test/openssl/test_cipher.rb17
-rw-r--r--test/openssl/test_config.rb18
-rw-r--r--test/openssl/test_digest.rb35
-rw-r--r--test/openssl/test_engine.rb2
-rw-r--r--test/openssl/test_fips.rb2
-rw-r--r--test/openssl/test_hmac.rb12
-rw-r--r--test/openssl/test_kdf.rb2
-rw-r--r--test/openssl/test_ns_spki.rb4
-rw-r--r--test/openssl/test_ocsp.rb3
-rw-r--r--test/openssl/test_ossl.rb62
-rw-r--r--test/openssl/test_pair.rb14
-rw-r--r--test/openssl/test_pkcs12.rb2
-rw-r--r--test/openssl/test_pkcs7.rb24
-rw-r--r--test/openssl/test_pkey_dh.rb2
-rw-r--r--test/openssl/test_pkey_dsa.rb2
-rw-r--r--test/openssl/test_pkey_ec.rb18
-rw-r--r--test/openssl/test_pkey_rsa.rb142
-rw-r--r--test/openssl/test_random.rb2
-rw-r--r--test/openssl/test_ssl.rb128
-rw-r--r--test/openssl/test_ssl_session.rb2
-rw-r--r--test/openssl/test_ts.rb667
-rw-r--r--test/openssl/test_x509attr.rb12
-rw-r--r--test/openssl/test_x509cert.rb93
-rw-r--r--test/openssl/test_x509crl.rb27
-rw-r--r--test/openssl/test_x509ext.rb15
-rw-r--r--test/openssl/test_x509name.rb32
-rw-r--r--test/openssl/test_x509req.rb9
-rw-r--r--test/openssl/test_x509store.rb2
-rw-r--r--test/openssl/ut_eof.rb10
-rw-r--r--test/openssl/utils.rb92
77 files changed, 3905 insertions, 341 deletions
diff --git a/ext/openssl/History.md b/ext/openssl/History.md
index db5050014e2..cdb44b1293a 100644
--- a/ext/openssl/History.md
+++ b/ext/openssl/History.md
@@ -1,3 +1,56 @@
+Version 2.2.0 (not yet released)
+=============
+
+* Change default `OpenSSL::SSL::SSLServer#listen` backlog argument from
+ 5 to `Socket::SOMAXCONN`.
+* Make `OpenSSL::HMAC#==` use a timing safe string comparison.
+* Remove unsupported MDC2, DSS, DSS1, and SHA algorithms.
+* Add support for SHA3 and BLAKE digests.
+* Add `OpenSSL::SSL::SSLSocket.open` for opening a `TCPSocket` and
+ returning an `OpenSSL::SSL::SSLSocket` for it.
+* Support marshalling of `OpenSSL::X509` objects.
+* Add `OpenSSL.secure_compare` for timing safe string comparison for
+ strings of possibly unequal length.
+* Add `OpenSSL.fixed_length_secure_compare` for timing safe string
+ comparison for strings of equal length.
+* Add `OpenSSL::SSL::SSLSocket#{finished_message,peer_finished_message}`
+ for last finished message sent and received.
+* Add `OpenSSL::Timestamp` module for handing timestamp requests and
+ responses.
+* Add helper methods for `OpenSSL::X509::Certificate`:
+ `find_extension`, `subject_key_identifier`,
+ `authority_key_identifier`, `crl_uris`, `ca_issuer_uris` and
+ `ocsp_uris`.
+* Add helper methods for `OpenSSL::X509::CRL`:
+ `find_extension` and `subject_key_identifier`.
+* Remove `OpenSSL::PKCS7::SignerInfo#name` alias for `#issuer`.
+* Add `OpenSSL::ECPoint#add` for adding points to an elliptic curve
+ group.
+ [[GitHub #261]](https://github.com/ruby/openssl/pull/261)
+* Make `OpenSSL::PKey::RSA#{export,to_der}` correctly check `key`,
+ `factors`, and `crt_params`.
+ [[GitHub #258]](https://github.com/ruby/openssl/pull/258)
+* Add `OpenSSL::SSL::{SSLSocket,SSLServer}#fileno`, returning the
+ underlying socket file descriptor number.
+ [[GitHub #247]](https://github.com/ruby/openssl/pull/247)
+* Support client certificates with TLS 1.3, and support post-handshake
+ authentication with OpenSSL 1.1.1+.
+ [[GitHub #239]](https://github.com/ruby/openssl/pull/239)
+* Add `OpenSSL::ASN1::ObjectId#==` for equality testing.
+* Add `OpenSSL::X509::Extension#value_der` for the raw value of
+ the extension.
+ [[GitHub #234]](https://github.com/ruby/openssl/pull/234)
+* Signficantly reduce allocated memory in `OpenSSL::Buffering#do_write`.
+ [[GitHub #212]](https://github.com/ruby/openssl/pull/212)
+* Ensure all valid IPv6 addresses are considered valid as elements
+ of subjectAlternativeName in certificates.
+ [[GitHub #185]](https://github.com/ruby/openssl/pull/185)
+* Allow recipient's certificate to be omitted in PCKS7#decrypt.
+ [[GitHub #183]](https://github.com/ruby/openssl/pull/183)
+* Add support for reading keys in PKCS8 format and export via instance methods
+ added to `OpenSSL::PKey` classes: `private_to_der`, `private_to_pem`,
+ `public_to_der` and `public_to_pem`.
+
Version 2.1.2
=============
diff --git a/ext/openssl/depend b/ext/openssl/depend
index 68cf357294f..943f4f4e47f 100644
--- a/ext/openssl/depend
+++ b/ext/openssl/depend
@@ -39,7 +39,7 @@ ossl.o: ossl_pkcs7.h
ossl.o: ossl_pkey.h
ossl.o: ossl_rand.h
ossl.o: ossl_ssl.h
-ossl.o: ossl_version.h
+ossl.o: ossl_ts.h
ossl.o: ossl_x509.h
ossl.o: ruby_missing.h
ossl_asn1.o: $(RUBY_EXTCONF_H)
@@ -77,7 +77,7 @@ ossl_asn1.o: ossl_pkcs7.h
ossl_asn1.o: ossl_pkey.h
ossl_asn1.o: ossl_rand.h
ossl_asn1.o: ossl_ssl.h
-ossl_asn1.o: ossl_version.h
+ossl_asn1.o: ossl_ts.h
ossl_asn1.o: ossl_x509.h
ossl_asn1.o: ruby_missing.h
ossl_bio.o: $(RUBY_EXTCONF_H)
@@ -115,7 +115,7 @@ ossl_bio.o: ossl_pkcs7.h
ossl_bio.o: ossl_pkey.h
ossl_bio.o: ossl_rand.h
ossl_bio.o: ossl_ssl.h
-ossl_bio.o: ossl_version.h
+ossl_bio.o: ossl_ts.h
ossl_bio.o: ossl_x509.h
ossl_bio.o: ruby_missing.h
ossl_bn.o: $(RUBY_EXTCONF_H)
@@ -153,7 +153,7 @@ ossl_bn.o: ossl_pkcs7.h
ossl_bn.o: ossl_pkey.h
ossl_bn.o: ossl_rand.h
ossl_bn.o: ossl_ssl.h
-ossl_bn.o: ossl_version.h
+ossl_bn.o: ossl_ts.h
ossl_bn.o: ossl_x509.h
ossl_bn.o: ruby_missing.h
ossl_cipher.o: $(RUBY_EXTCONF_H)
@@ -191,7 +191,7 @@ ossl_cipher.o: ossl_pkcs7.h
ossl_cipher.o: ossl_pkey.h
ossl_cipher.o: ossl_rand.h
ossl_cipher.o: ossl_ssl.h
-ossl_cipher.o: ossl_version.h
+ossl_cipher.o: ossl_ts.h
ossl_cipher.o: ossl_x509.h
ossl_cipher.o: ruby_missing.h
ossl_config.o: $(RUBY_EXTCONF_H)
@@ -229,7 +229,7 @@ ossl_config.o: ossl_pkcs7.h
ossl_config.o: ossl_pkey.h
ossl_config.o: ossl_rand.h
ossl_config.o: ossl_ssl.h
-ossl_config.o: ossl_version.h
+ossl_config.o: ossl_ts.h
ossl_config.o: ossl_x509.h
ossl_config.o: ruby_missing.h
ossl_digest.o: $(RUBY_EXTCONF_H)
@@ -267,7 +267,7 @@ ossl_digest.o: ossl_pkcs7.h
ossl_digest.o: ossl_pkey.h
ossl_digest.o: ossl_rand.h
ossl_digest.o: ossl_ssl.h
-ossl_digest.o: ossl_version.h
+ossl_digest.o: ossl_ts.h
ossl_digest.o: ossl_x509.h
ossl_digest.o: ruby_missing.h
ossl_engine.o: $(RUBY_EXTCONF_H)
@@ -305,7 +305,7 @@ ossl_engine.o: ossl_pkcs7.h
ossl_engine.o: ossl_pkey.h
ossl_engine.o: ossl_rand.h
ossl_engine.o: ossl_ssl.h
-ossl_engine.o: ossl_version.h
+ossl_engine.o: ossl_ts.h
ossl_engine.o: ossl_x509.h
ossl_engine.o: ruby_missing.h
ossl_hmac.o: $(RUBY_EXTCONF_H)
@@ -343,7 +343,7 @@ ossl_hmac.o: ossl_pkcs7.h
ossl_hmac.o: ossl_pkey.h
ossl_hmac.o: ossl_rand.h
ossl_hmac.o: ossl_ssl.h
-ossl_hmac.o: ossl_version.h
+ossl_hmac.o: ossl_ts.h
ossl_hmac.o: ossl_x509.h
ossl_hmac.o: ruby_missing.h
ossl_kdf.o: $(RUBY_EXTCONF_H)
@@ -381,7 +381,7 @@ ossl_kdf.o: ossl_pkcs7.h
ossl_kdf.o: ossl_pkey.h
ossl_kdf.o: ossl_rand.h
ossl_kdf.o: ossl_ssl.h
-ossl_kdf.o: ossl_version.h
+ossl_kdf.o: ossl_ts.h
ossl_kdf.o: ossl_x509.h
ossl_kdf.o: ruby_missing.h
ossl_ns_spki.o: $(RUBY_EXTCONF_H)
@@ -419,7 +419,7 @@ ossl_ns_spki.o: ossl_pkcs7.h
ossl_ns_spki.o: ossl_pkey.h
ossl_ns_spki.o: ossl_rand.h
ossl_ns_spki.o: ossl_ssl.h
-ossl_ns_spki.o: ossl_version.h
+ossl_ns_spki.o: ossl_ts.h
ossl_ns_spki.o: ossl_x509.h
ossl_ns_spki.o: ruby_missing.h
ossl_ocsp.o: $(RUBY_EXTCONF_H)
@@ -457,7 +457,7 @@ ossl_ocsp.o: ossl_pkcs7.h
ossl_ocsp.o: ossl_pkey.h
ossl_ocsp.o: ossl_rand.h
ossl_ocsp.o: ossl_ssl.h
-ossl_ocsp.o: ossl_version.h
+ossl_ocsp.o: ossl_ts.h
ossl_ocsp.o: ossl_x509.h
ossl_ocsp.o: ruby_missing.h
ossl_pkcs12.o: $(RUBY_EXTCONF_H)
@@ -495,7 +495,7 @@ ossl_pkcs12.o: ossl_pkcs7.h
ossl_pkcs12.o: ossl_pkey.h
ossl_pkcs12.o: ossl_rand.h
ossl_pkcs12.o: ossl_ssl.h
-ossl_pkcs12.o: ossl_version.h
+ossl_pkcs12.o: ossl_ts.h
ossl_pkcs12.o: ossl_x509.h
ossl_pkcs12.o: ruby_missing.h
ossl_pkcs7.o: $(RUBY_EXTCONF_H)
@@ -533,7 +533,7 @@ ossl_pkcs7.o: ossl_pkcs7.h
ossl_pkcs7.o: ossl_pkey.h
ossl_pkcs7.o: ossl_rand.h
ossl_pkcs7.o: ossl_ssl.h
-ossl_pkcs7.o: ossl_version.h
+ossl_pkcs7.o: ossl_ts.h
ossl_pkcs7.o: ossl_x509.h
ossl_pkcs7.o: ruby_missing.h
ossl_pkey.o: $(RUBY_EXTCONF_H)
@@ -571,7 +571,7 @@ ossl_pkey.o: ossl_pkey.c
ossl_pkey.o: ossl_pkey.h
ossl_pkey.o: ossl_rand.h
ossl_pkey.o: ossl_ssl.h
-ossl_pkey.o: ossl_version.h
+ossl_pkey.o: ossl_ts.h
ossl_pkey.o: ossl_x509.h
ossl_pkey.o: ruby_missing.h
ossl_pkey_dh.o: $(RUBY_EXTCONF_H)
@@ -609,7 +609,7 @@ ossl_pkey_dh.o: ossl_pkey.h
ossl_pkey_dh.o: ossl_pkey_dh.c
ossl_pkey_dh.o: ossl_rand.h
ossl_pkey_dh.o: ossl_ssl.h
-ossl_pkey_dh.o: ossl_version.h
+ossl_pkey_dh.o: ossl_ts.h
ossl_pkey_dh.o: ossl_x509.h
ossl_pkey_dh.o: ruby_missing.h
ossl_pkey_dsa.o: $(RUBY_EXTCONF_H)
@@ -647,7 +647,7 @@ ossl_pkey_dsa.o: ossl_pkey.h
ossl_pkey_dsa.o: ossl_pkey_dsa.c
ossl_pkey_dsa.o: ossl_rand.h
ossl_pkey_dsa.o: ossl_ssl.h
-ossl_pkey_dsa.o: ossl_version.h
+ossl_pkey_dsa.o: ossl_ts.h
ossl_pkey_dsa.o: ossl_x509.h
ossl_pkey_dsa.o: ruby_missing.h
ossl_pkey_ec.o: $(RUBY_EXTCONF_H)
@@ -685,7 +685,7 @@ ossl_pkey_ec.o: ossl_pkey.h
ossl_pkey_ec.o: ossl_pkey_ec.c
ossl_pkey_ec.o: ossl_rand.h
ossl_pkey_ec.o: ossl_ssl.h
-ossl_pkey_ec.o: ossl_version.h
+ossl_pkey_ec.o: ossl_ts.h
ossl_pkey_ec.o: ossl_x509.h
ossl_pkey_ec.o: ruby_missing.h
ossl_pkey_rsa.o: $(RUBY_EXTCONF_H)
@@ -723,7 +723,7 @@ ossl_pkey_rsa.o: ossl_pkey.h
ossl_pkey_rsa.o: ossl_pkey_rsa.c
ossl_pkey_rsa.o: ossl_rand.h
ossl_pkey_rsa.o: ossl_ssl.h
-ossl_pkey_rsa.o: ossl_version.h
+ossl_pkey_rsa.o: ossl_ts.h
ossl_pkey_rsa.o: ossl_x509.h
ossl_pkey_rsa.o: ruby_missing.h
ossl_rand.o: $(RUBY_EXTCONF_H)
@@ -761,7 +761,7 @@ ossl_rand.o: ossl_pkey.h
ossl_rand.o: ossl_rand.c
ossl_rand.o: ossl_rand.h
ossl_rand.o: ossl_ssl.h
-ossl_rand.o: ossl_version.h
+ossl_rand.o: ossl_ts.h
ossl_rand.o: ossl_x509.h
ossl_rand.o: ruby_missing.h
ossl_ssl.o: $(RUBY_EXTCONF_H)
@@ -799,7 +799,7 @@ ossl_ssl.o: ossl_pkey.h
ossl_ssl.o: ossl_rand.h
ossl_ssl.o: ossl_ssl.c
ossl_ssl.o: ossl_ssl.h
-ossl_ssl.o: ossl_version.h
+ossl_ssl.o: ossl_ts.h
ossl_ssl.o: ossl_x509.h
ossl_ssl.o: ruby_missing.h
ossl_ssl_session.o: $(RUBY_EXTCONF_H)
@@ -837,9 +837,47 @@ ossl_ssl_session.o: ossl_pkey.h
ossl_ssl_session.o: ossl_rand.h
ossl_ssl_session.o: ossl_ssl.h
ossl_ssl_session.o: ossl_ssl_session.c
-ossl_ssl_session.o: ossl_version.h
+ossl_ssl_session.o: ossl_ts.h
ossl_ssl_session.o: ossl_x509.h
ossl_ssl_session.o: ruby_missing.h
+ossl_ts.o: $(RUBY_EXTCONF_H)
+ossl_ts.o: $(arch_hdrdir)/ruby/config.h
+ossl_ts.o: $(hdrdir)/ruby.h
+ossl_ts.o: $(hdrdir)/ruby/assert.h
+ossl_ts.o: $(hdrdir)/ruby/backward.h
+ossl_ts.o: $(hdrdir)/ruby/defines.h
+ossl_ts.o: $(hdrdir)/ruby/encoding.h
+ossl_ts.o: $(hdrdir)/ruby/intern.h
+ossl_ts.o: $(hdrdir)/ruby/io.h
+ossl_ts.o: $(hdrdir)/ruby/missing.h
+ossl_ts.o: $(hdrdir)/ruby/onigmo.h
+ossl_ts.o: $(hdrdir)/ruby/oniguruma.h
+ossl_ts.o: $(hdrdir)/ruby/ruby.h
+ossl_ts.o: $(hdrdir)/ruby/st.h
+ossl_ts.o: $(hdrdir)/ruby/subst.h
+ossl_ts.o: $(hdrdir)/ruby/thread.h
+ossl_ts.o: openssl_missing.h
+ossl_ts.o: ossl.h
+ossl_ts.o: ossl_asn1.h
+ossl_ts.o: ossl_bio.h
+ossl_ts.o: ossl_bn.h
+ossl_ts.o: ossl_cipher.h
+ossl_ts.o: ossl_config.h
+ossl_ts.o: ossl_digest.h
+ossl_ts.o: ossl_engine.h
+ossl_ts.o: ossl_hmac.h
+ossl_ts.o: ossl_kdf.h
+ossl_ts.o: ossl_ns_spki.h
+ossl_ts.o: ossl_ocsp.h
+ossl_ts.o: ossl_pkcs12.h
+ossl_ts.o: ossl_pkcs7.h
+ossl_ts.o: ossl_pkey.h
+ossl_ts.o: ossl_rand.h
+ossl_ts.o: ossl_ssl.h
+ossl_ts.o: ossl_ts.c
+ossl_ts.o: ossl_ts.h
+ossl_ts.o: ossl_x509.h
+ossl_ts.o: ruby_missing.h
ossl_x509.o: $(RUBY_EXTCONF_H)
ossl_x509.o: $(arch_hdrdir)/ruby/config.h
ossl_x509.o: $(hdrdir)/ruby.h
@@ -874,7 +912,7 @@ ossl_x509.o: ossl_pkcs7.h
ossl_x509.o: ossl_pkey.h
ossl_x509.o: ossl_rand.h
ossl_x509.o: ossl_ssl.h
-ossl_x509.o: ossl_version.h
+ossl_x509.o: ossl_ts.h
ossl_x509.o: ossl_x509.c
ossl_x509.o: ossl_x509.h
ossl_x509.o: ruby_missing.h
@@ -912,7 +950,7 @@ ossl_x509attr.o: ossl_pkcs7.h
ossl_x509attr.o: ossl_pkey.h
ossl_x509attr.o: ossl_rand.h
ossl_x509attr.o: ossl_ssl.h
-ossl_x509attr.o: ossl_version.h
+ossl_x509attr.o: ossl_ts.h
ossl_x509attr.o: ossl_x509.h
ossl_x509attr.o: ossl_x509attr.c
ossl_x509attr.o: ruby_missing.h
@@ -950,7 +988,7 @@ ossl_x509cert.o: ossl_pkcs7.h
ossl_x509cert.o: ossl_pkey.h
ossl_x509cert.o: ossl_rand.h
ossl_x509cert.o: ossl_ssl.h
-ossl_x509cert.o: ossl_version.h
+ossl_x509cert.o: ossl_ts.h
ossl_x509cert.o: ossl_x509.h
ossl_x509cert.o: ossl_x509cert.c
ossl_x509cert.o: ruby_missing.h
@@ -988,7 +1026,7 @@ ossl_x509crl.o: ossl_pkcs7.h
ossl_x509crl.o: ossl_pkey.h
ossl_x509crl.o: ossl_rand.h
ossl_x509crl.o: ossl_ssl.h
-ossl_x509crl.o: ossl_version.h
+ossl_x509crl.o: ossl_ts.h
ossl_x509crl.o: ossl_x509.h
ossl_x509crl.o: ossl_x509crl.c
ossl_x509crl.o: ruby_missing.h
@@ -1026,7 +1064,7 @@ ossl_x509ext.o: ossl_pkcs7.h
ossl_x509ext.o: ossl_pkey.h
ossl_x509ext.o: ossl_rand.h
ossl_x509ext.o: ossl_ssl.h
-ossl_x509ext.o: ossl_version.h
+ossl_x509ext.o: ossl_ts.h
ossl_x509ext.o: ossl_x509.h
ossl_x509ext.o: ossl_x509ext.c
ossl_x509ext.o: ruby_missing.h
@@ -1064,7 +1102,7 @@ ossl_x509name.o: ossl_pkcs7.h
ossl_x509name.o: ossl_pkey.h
ossl_x509name.o: ossl_rand.h
ossl_x509name.o: ossl_ssl.h
-ossl_x509name.o: ossl_version.h
+ossl_x509name.o: ossl_ts.h
ossl_x509name.o: ossl_x509.h
ossl_x509name.o: ossl_x509name.c
ossl_x509name.o: ruby_missing.h
@@ -1102,7 +1140,7 @@ ossl_x509req.o: ossl_pkcs7.h
ossl_x509req.o: ossl_pkey.h
ossl_x509req.o: ossl_rand.h
ossl_x509req.o: ossl_ssl.h
-ossl_x509req.o: ossl_version.h
+ossl_x509req.o: ossl_ts.h
ossl_x509req.o: ossl_x509.h
ossl_x509req.o: ossl_x509req.c
ossl_x509req.o: ruby_missing.h
@@ -1140,7 +1178,7 @@ ossl_x509revoked.o: ossl_pkcs7.h
ossl_x509revoked.o: ossl_pkey.h
ossl_x509revoked.o: ossl_rand.h
ossl_x509revoked.o: ossl_ssl.h
-ossl_x509revoked.o: ossl_version.h
+ossl_x509revoked.o: ossl_ts.h
ossl_x509revoked.o: ossl_x509.h
ossl_x509revoked.o: ossl_x509revoked.c
ossl_x509revoked.o: ruby_missing.h
@@ -1178,7 +1216,7 @@ ossl_x509store.o: ossl_pkcs7.h
ossl_x509store.o: ossl_pkey.h
ossl_x509store.o: ossl_rand.h
ossl_x509store.o: ossl_ssl.h
-ossl_x509store.o: ossl_version.h
+ossl_x509store.o: ossl_ts.h
ossl_x509store.o: ossl_x509.h
ossl_x509store.o: ossl_x509store.c
ossl_x509store.o: ruby_missing.h
diff --git a/ext/openssl/deprecation.rb b/ext/openssl/deprecation.rb
index afe989ead1f..6af7d562fbd 100644
--- a/ext/openssl/deprecation.rb
+++ b/ext/openssl/deprecation.rb
@@ -1,9 +1,9 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
module OpenSSL
def self.deprecated_warning_flag
unless flag = (@deprecated_warning_flag ||= nil)
if try_compile("", flag = "-Werror=deprecated-declarations")
- $warnflags = "#{@warnflags = $warnflags}" #{flag}"
+ $warnflags << " #{flag}"
else
flag = ""
end
@@ -12,10 +12,6 @@ module OpenSSL
flag
end
- def self.restore_warning_flag
- $warnflags = @warnflags
- end
-
def self.check_func(func, header)
have_func(func, header, deprecated_warning_flag)
end
diff --git a/ext/openssl/extconf.rb b/ext/openssl/extconf.rb
index 264130bb51d..344d7596520 100644
--- a/ext/openssl/extconf.rb
+++ b/ext/openssl/extconf.rb
@@ -1,5 +1,5 @@
# -*- coding: us-ascii -*-
-# frozen_string_literal: false
+# frozen_string_literal: true
=begin
= Info
'OpenSSL for Ruby 2' project
@@ -19,7 +19,7 @@ dir_config("kerberos")
Logging::message "=== OpenSSL for Ruby configurator ===\n"
-# Check with -Werror=deprecated-declarations if available
+# Add -Werror=deprecated-declarations to $warnflags if available
OpenSSL.deprecated_warning_flag
##
@@ -40,6 +40,12 @@ end
Logging::message "=== Checking for required stuff... ===\n"
result = pkg_config("openssl") && have_header("openssl/ssl.h")
+if $mingw
+ append_cflags '-D_FORTIFY_SOURCE=2'
+ append_ldflags '-fstack-protector'
+ have_library 'ssp'
+end
+
def find_openssl_library
if $mswin || $mingw
# required for static OpenSSL libraries
@@ -109,7 +115,8 @@ Logging::message "=== Checking for OpenSSL features... ===\n"
# compile options
have_func("RAND_egd")
engines = %w{builtin_engines openbsd_dev_crypto dynamic 4758cca aep atalla chil
- cswift nuron sureware ubsec padlock capi gmp gost cryptodev aesni}
+ cswift nuron sureware ubsec padlock capi gmp gost cryptodev aesni
+ cloudhsm}
engines.each { |name|
OpenSSL.check_func_or_macro("ENGINE_load_#{name}", "openssl/engine.h")
}
@@ -144,6 +151,7 @@ have_func("HMAC_CTX_free")
OpenSSL.check_func("RAND_pseudo_bytes", "openssl/rand.h") # deprecated
have_func("X509_STORE_get_ex_data")
have_func("X509_STORE_set_ex_data")
+have_func("X509_STORE_get_ex_new_index")
have_func("X509_CRL_get0_signature")
have_func("X509_REQ_get0_signature")
have_func("X509_REVOKED_get0_serialNumber")
@@ -164,11 +172,18 @@ OpenSSL.check_func_or_macro("SSL_CTX_set_min_proto_version", "openssl/ssl.h")
have_func("SSL_CTX_get_security_level")
have_func("X509_get0_notBefore")
have_func("SSL_SESSION_get_protocol_version")
+have_func("TS_STATUS_INFO_get0_status")
+have_func("TS_STATUS_INFO_get0_text")
+have_func("TS_STATUS_INFO_get0_failure_info")
+have_func("TS_VERIFY_CTS_set_certs")
+have_func("TS_VERIFY_CTX_set_store")
+have_func("TS_VERIFY_CTX_add_flags")
+have_func("TS_RESP_CTX_set_time_cb")
have_func("EVP_PBE_scrypt")
+have_func("SSL_CTX_set_post_handshake_auth")
Logging::message "=== Checking done. ===\n"
create_header
-OpenSSL.restore_warning_flag
create_makefile("openssl")
Logging::message "Done.\n"
diff --git a/ext/openssl/lib/openssl.rb b/ext/openssl/lib/openssl.rb
index 09142829205..7ba2229a1bc 100644
--- a/ext/openssl/lib/openssl.rb
+++ b/ext/openssl/lib/openssl.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
=begin
= Info
'OpenSSL for Ruby 2' project
@@ -12,11 +12,26 @@
require 'openssl.so'
-require 'openssl/bn'
-require 'openssl/pkey'
-require 'openssl/cipher'
-require 'openssl/config'
-require 'openssl/digest'
-require 'openssl/x509'
-require 'openssl/ssl'
-require 'openssl/pkcs5'
+require_relative 'openssl/bn'
+require_relative 'openssl/pkey'
+require_relative 'openssl/cipher'
+require_relative 'openssl/config'
+require_relative 'openssl/digest'
+require_relative 'openssl/hmac'
+require_relative 'openssl/x509'
+require_relative 'openssl/ssl'
+require_relative 'openssl/pkcs5'
+
+module OpenSSL
+ # call-seq:
+ # OpenSSL.secure_compare(string, string) -> boolean
+ #
+ # Constant time memory comparison. Inputs are hashed using SHA-256 to mask
+ # the length of the secret. Returns +true+ if the strings are identical,
+ # +false+ otherwise.
+ def self.secure_compare(a, b)
+ hashed_a = OpenSSL::Digest::SHA256.digest(a)
+ hashed_b = OpenSSL::Digest::SHA256.digest(b)
+ OpenSSL.fixed_length_secure_compare(hashed_a, hashed_b) && a == b
+ end
+end
diff --git a/ext/openssl/lib/openssl/bn.rb b/ext/openssl/lib/openssl/bn.rb
index 8d1ebefb6e4..0a5e11b4c2e 100644
--- a/ext/openssl/lib/openssl/bn.rb
+++ b/ext/openssl/lib/openssl/bn.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#--
#
# = Ruby-space definitions that completes C-space funcs for BN
diff --git a/ext/openssl/lib/openssl/buffering.rb b/ext/openssl/lib/openssl/buffering.rb
index 5d1586e594d..a5f4241bf45 100644
--- a/ext/openssl/lib/openssl/buffering.rb
+++ b/ext/openssl/lib/openssl/buffering.rb
@@ -1,5 +1,5 @@
# coding: binary
-# frozen_string_literal: false
+# frozen_string_literal: true
#--
#= Info
# 'OpenSSL for Ruby 2' project
@@ -22,6 +22,29 @@
module OpenSSL::Buffering
include Enumerable
+ # A buffer which will retain binary encoding.
+ class Buffer < String
+ BINARY = Encoding::BINARY
+
+ def initialize
+ super
+
+ force_encoding(BINARY)
+ end
+
+ def << string
+ if string.encoding == BINARY
+ super(string)
+ else
+ super(string.b)
+ end
+
+ return self
+ end
+
+ alias concat <<
+ end
+
##
# The "sync mode" of the SSLSocket.
#
@@ -40,7 +63,7 @@ module OpenSSL::Buffering
def initialize(*)
super
@eof = false
- @rbuffer = ""
+ @rbuffer = Buffer.new
@sync = @io.sync
end
@@ -312,7 +335,7 @@ module OpenSSL::Buffering
# buffer is flushed to the underlying socket.
def do_write(s)
- @wbuffer = "" unless defined? @wbuffer
+ @wbuffer = Buffer.new unless defined? @wbuffer
@wbuffer << s
@wbuffer.force_encoding(Encoding::BINARY)
@sync ||= false
@@ -398,7 +421,7 @@ module OpenSSL::Buffering
# See IO#puts for full details.
def puts(*args)
- s = ""
+ s = Buffer.new
if args.empty?
s << "\n"
end
@@ -416,7 +439,7 @@ module OpenSSL::Buffering
# See IO#print for full details.
def print(*args)
- s = ""
+ s = Buffer.new
args.each{ |arg| s << arg.to_s }
do_write(s)
nil
diff --git a/ext/openssl/lib/openssl/cipher.rb b/ext/openssl/lib/openssl/cipher.rb
index af721b3a803..8ad8c35dd30 100644
--- a/ext/openssl/lib/openssl/cipher.rb
+++ b/ext/openssl/lib/openssl/cipher.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#--
# = Ruby-space predefined Cipher subclasses
#
diff --git a/ext/openssl/lib/openssl/config.rb b/ext/openssl/lib/openssl/config.rb
index 48d8be00694..ef83c57b204 100644
--- a/ext/openssl/lib/openssl/config.rb
+++ b/ext/openssl/lib/openssl/config.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
=begin
= Ruby-space definitions that completes C-space funcs for Config
@@ -53,9 +53,8 @@ module OpenSSL
def parse_config(io)
begin
parse_config_lines(io)
- rescue ConfigError => e
- e.message.replace("error in line #{io.lineno}: " + e.message)
- raise
+ rescue => error
+ raise ConfigError, "error in line #{io.lineno}: " + error.message
end
end
diff --git a/ext/openssl/lib/openssl/digest.rb b/ext/openssl/lib/openssl/digest.rb
index b6744de6bd9..c9539119543 100644
--- a/ext/openssl/lib/openssl/digest.rb
+++ b/ext/openssl/lib/openssl/digest.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#--
# = Ruby-space predefined Digest subclasses
#
@@ -15,11 +15,17 @@
module OpenSSL
class Digest
- alg = %w(MD2 MD4 MD5 MDC2 RIPEMD160 SHA1 SHA224 SHA256 SHA384 SHA512)
- if OPENSSL_VERSION_NUMBER < 0x10100000
- alg += %w(DSS DSS1 SHA)
+ # You can get a list of all algorithms:
+ # openssl list -digest-algorithms
+
+ ALGORITHMS = %w(MD4 MD5 RIPEMD160 SHA1 SHA224 SHA256 SHA384 SHA512)
+
+ if !OPENSSL_VERSION.include?("LibreSSL") && OPENSSL_VERSION_NUMBER > 0x10101000
+ ALGORITHMS.concat %w(BLAKE2b512 BLAKE2s256 SHA3-224 SHA3-256 SHA3-384 SHA3-512)
end
+ ALGORITHMS.freeze
+
# Return the hash value computed with _name_ Digest. _name_ is either the
# long name or short name of a supported digest algorithm.
#
@@ -35,17 +41,20 @@ module OpenSSL
super(data, name)
end
- alg.each{|name|
+ ALGORITHMS.each do |name|
klass = Class.new(self) {
define_method(:initialize, ->(data = nil) {super(name, data)})
}
+
singleton = (class << klass; self; end)
+
singleton.class_eval{
- define_method(:digest){|data| new.digest(data) }
- define_method(:hexdigest){|data| new.hexdigest(data) }
+ define_method(:digest) {|data| new.digest(data)}
+ define_method(:hexdigest) {|data| new.hexdigest(data)}
}
- const_set(name, klass)
- }
+
+ const_set(name.tr('-', '_'), klass)
+ end
# Deprecated.
#
diff --git a/ext/openssl/lib/openssl/hmac.rb b/ext/openssl/lib/openssl/hmac.rb
new file mode 100644
index 00000000000..3d4427611d5
--- /dev/null
+++ b/ext/openssl/lib/openssl/hmac.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module OpenSSL
+ class HMAC
+ # Securely compare with another HMAC instance in constant time.
+ def ==(other)
+ return false unless HMAC === other
+ return false unless self.digest.bytesize == other.digest.bytesize
+
+ OpenSSL.fixed_length_secure_compare(self.digest, other.digest)
+ end
+ end
+end
diff --git a/ext/openssl/lib/openssl/pkcs5.rb b/ext/openssl/lib/openssl/pkcs5.rb
index 959447df5e5..8dedc4beef5 100644
--- a/ext/openssl/lib/openssl/pkcs5.rb
+++ b/ext/openssl/lib/openssl/pkcs5.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#--
# Ruby/OpenSSL Project
# Copyright (C) 2017 Ruby/OpenSSL Project Authors
diff --git a/ext/openssl/lib/openssl/pkey.rb b/ext/openssl/lib/openssl/pkey.rb
index 8a547c340d4..ecb112f7922 100644
--- a/ext/openssl/lib/openssl/pkey.rb
+++ b/ext/openssl/lib/openssl/pkey.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#--
# Ruby/OpenSSL Project
# Copyright (C) 2017 Ruby/OpenSSL Project Authors
diff --git a/ext/openssl/lib/openssl/ssl.rb b/ext/openssl/lib/openssl/ssl.rb
index 355eb2ebbbc..8554ada0bb3 100644
--- a/ext/openssl/lib/openssl/ssl.rb
+++ b/ext/openssl/lib/openssl/ssl.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
=begin
= Info
'OpenSSL for Ruby 2' project
@@ -13,6 +13,7 @@
require "openssl/buffering"
require "io/nonblock"
require "ipaddr"
+require "socket"
module OpenSSL
module SSL
@@ -231,6 +232,11 @@ YoaOffgTf5qxiwkjnlVZQc3whgnEt9FpVMvQ9eknyeGB5KHfayAc3+hUAvI3/Cr3
end
module SocketForwarder
+ # The file descriptor for the socket.
+ def fileno
+ to_io.fileno
+ end
+
def addr
to_io.addr
end
@@ -435,6 +441,38 @@ YoaOffgTf5qxiwkjnlVZQc3whgnEt9FpVMvQ9eknyeGB5KHfayAc3+hUAvI3/Cr3
def session_get_cb
@context.session_get_cb
end
+
+ class << self
+
+ # call-seq:
+ # open(remote_host, remote_port, local_host=nil, local_port=nil, context: nil)
+ #
+ # Creates a new instance of SSLSocket.
+ # _remote\_host_ and _remote\_port_ are used to open TCPSocket.
+ # If _local\_host_ and _local\_port_ are specified,
+ # then those parameters are used on the local end to establish the connection.
+ # If _context_ is provided,
+ # the SSL Sockets initial params will be taken from the context.
+ #
+ # === Examples
+ #
+ # sock = OpenSSL::SSL::SSLSocket.open('localhost', 443)
+ # sock.connect # Initiates a connection to localhost:443
+ #
+ # with SSLContext:
+ #
+ # ctx = OpenSSL::SSL::SSLContext.new
+ # sock = OpenSSL::SSL::SSLSocket.open('localhost', 443, context: ctx)
+ # sock.connect # Initiates a connection to localhost:443 with SSLContext
+ def open(remote_host, remote_port, local_host=nil, local_port=nil, context: nil)
+ sock = ::TCPSocket.open(remote_host, remote_port, local_host, local_port)
+ if context.nil?
+ return OpenSSL::SSL::SSLSocket.new(sock)
+ else
+ return OpenSSL::SSL::SSLSocket.new(sock, context)
+ end
+ end
+ end
end
##
@@ -465,7 +503,7 @@ YoaOffgTf5qxiwkjnlVZQc3whgnEt9FpVMvQ9eknyeGB5KHfayAc3+hUAvI3/Cr3
end
# See TCPServer#listen for details.
- def listen(backlog=5)
+ def listen(backlog=Socket::SOMAXCONN)
@svr.listen(backlog)
end
diff --git a/ext/openssl/lib/openssl/version.rb b/ext/openssl/lib/openssl/version.rb
new file mode 100644
index 00000000000..b07bb330097
--- /dev/null
+++ b/ext/openssl/lib/openssl/version.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+module OpenSSL
+ VERSION = "2.2.0" unless defined?(VERSION)
+end
diff --git a/ext/openssl/lib/openssl/x509.rb b/ext/openssl/lib/openssl/x509.rb
index 98358f90da8..1d2a5aaca83 100644
--- a/ext/openssl/lib/openssl/x509.rb
+++ b/ext/openssl/lib/openssl/x509.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#--
# = Ruby-space definitions that completes C-space funcs for X509 and subclasses
#
@@ -14,6 +14,22 @@
module OpenSSL
module X509
+ module Marshal
+ def self.included(base)
+ base.extend(ClassMethods)
+ end
+
+ module ClassMethods
+ def _load(string)
+ new(string)
+ end
+ end
+
+ def _dump(_level)
+ to_der
+ end
+ end
+
class ExtensionFactory
def create_extension(*arg)
if arg.size > 1
@@ -41,6 +57,8 @@ module OpenSSL
end
class Extension
+ include Marshal
+
def ==(other)
return false unless Extension === other
to_der == other.to_der
@@ -60,9 +78,146 @@ module OpenSSL
def to_a
[ self.oid, self.value, self.critical? ]
end
+
+ module Helpers
+ def find_extension(oid)
+ extensions.find { |e| e.oid == oid }
+ end
+ end
+
+ module SubjectKeyIdentifier
+ include Helpers
+
+ # Get the subject's key identifier from the subjectKeyIdentifier
+ # exteension, as described in RFC5280 Section 4.2.1.2.
+ #
+ # Returns the binary String key identifier or nil or raises
+ # ASN1::ASN1Error.
+ def subject_key_identifier
+ ext = find_extension("subjectKeyIdentifier")
+ return nil if ext.nil?
+
+ ski_asn1 = ASN1.decode(ext.value_der)
+ if ext.critical? || ski_asn1.tag_class != :UNIVERSAL || ski_asn1.tag != ASN1::OCTET_STRING
+ raise ASN1::ASN1Error, "invalid extension"
+ end
+
+ ski_asn1.value
+ end
+ end
+
+ module AuthorityKeyIdentifier
+ include Helpers
+
+ # Get the issuing certificate's key identifier from the
+ # authorityKeyIdentifier extension, as described in RFC5280
+ # Section 4.2.1.1
+ #
+ # Returns the binary String keyIdentifier or nil or raises
+ # ASN1::ASN1Error.
+ def authority_key_identifier
+ ext = find_extension("authorityKeyIdentifier")
+ return nil if ext.nil?
+
+ aki_asn1 = ASN1.decode(ext.value_der)
+ if ext.critical? || aki_asn1.tag_class != :UNIVERSAL || aki_asn1.tag != ASN1::SEQUENCE
+ raise ASN1::ASN1Error, "invalid extension"
+ end
+
+ key_id = aki_asn1.value.find do |v|
+ v.tag_class == :CONTEXT_SPECIFIC && v.tag == 0
+ end
+
+ key_id.nil? ? nil : key_id.value
+ end
+ end
+
+ module CRLDistributionPoints
+ include Helpers
+
+ # Get the distributionPoint fullName URI from the certificate's CRL
+ # distribution points extension, as described in RFC5280 Section
+ # 4.2.1.13
+ #
+ # Returns an array of strings or nil or raises ASN1::ASN1Error.
+ def crl_uris
+ ext = find_extension("crlDistributionPoints")
+ return nil if ext.nil?
+
+ cdp_asn1 = ASN1.decode(ext.value_der)
+ if cdp_asn1.tag_class != :UNIVERSAL || cdp_asn1.tag != ASN1::SEQUENCE
+ raise ASN1::ASN1Error, "invalid extension"
+ end
+
+ crl_uris = cdp_asn1.map do |crl_distribution_point|
+ distribution_point = crl_distribution_point.value.find do |v|
+ v.tag_class == :CONTEXT_SPECIFIC && v.tag == 0
+ end
+ full_name = distribution_point&.value&.find do |v|
+ v.tag_class == :CONTEXT_SPECIFIC && v.tag == 0
+ end
+ full_name&.value&.find do |v|
+ v.tag_class == :CONTEXT_SPECIFIC && v.tag == 6 # uniformResourceIdentifier
+ end
+ end
+
+ crl_uris&.map(&:value)
+ end
+ end
+
+ module AuthorityInfoAccess
+ include Helpers
+
+ # Get the information and services for the issuer from the certificate's
+ # authority information access extension exteension, as described in RFC5280
+ # Section 4.2.2.1.
+ #
+ # Returns an array of strings or nil or raises ASN1::ASN1Error.
+ def ca_issuer_uris
+ aia_asn1 = parse_aia_asn1
+ return nil if aia_asn1.nil?
+
+ ca_issuer = aia_asn1.value.select do |authority_info_access|
+ authority_info_access.value.first.value == "caIssuers"
+ end
+
+ ca_issuer&.map(&:value)&.map(&:last)&.map(&:value)
+ end
+
+ # Get the URIs for OCSP from the certificate's authority information access
+ # extension exteension, as described in RFC5280 Section 4.2.2.1.
+ #
+ # Returns an array of strings or nil or raises ASN1::ASN1Error.
+ def ocsp_uris
+ aia_asn1 = parse_aia_asn1
+ return nil if aia_asn1.nil?
+
+ ocsp = aia_asn1.value.select do |authority_info_access|
+ authority_info_access.value.first.value == "OCSP"
+ end
+
+ ocsp&.map(&:value)&.map(&:last)&.map(&:value)
+ end
+
+ private
+
+ def parse_aia_asn1
+ ext = find_extension("authorityInfoAccess")
+ return nil if ext.nil?
+
+ aia_asn1 = ASN1.decode(ext.value_der)
+ if ext.critical? || aia_asn1.tag_class != :UNIVERSAL || aia_asn1.tag != ASN1::SEQUENCE
+ raise ASN1::ASN1Error, "invalid extension"
+ end
+
+ aia_asn1
+ end
+ end
end
class Name
+ include Marshal
+
module RFC2253DN
Special = ',=+<>#;'
HexChar = /[0-9a-fA-F]/
@@ -166,6 +321,8 @@ module OpenSSL
end
class Attribute
+ include Marshal
+
def ==(other)
return false unless Attribute === other
to_der == other.to_der
@@ -179,6 +336,12 @@ module OpenSSL
end
class Certificate
+ include Marshal
+ include Extension::SubjectKeyIdentifier
+ include Extension::AuthorityKeyIdentifier
+ include Extension::CRLDistributionPoints
+ include Extension::AuthorityInfoAccess
+
def pretty_print(q)
q.object_group(self) {
q.breakable
@@ -192,6 +355,9 @@ module OpenSSL
end
class CRL
+ include Marshal
+ include Extension::AuthorityKeyIdentifier
+
def ==(other)
return false unless CRL === other
to_der == other.to_der
@@ -206,6 +372,8 @@ module OpenSSL
end
class Request
+ include Marshal
+
def ==(other)
return false unless Request === other
to_der == other.to_der
diff --git a/ext/openssl/openssl.gemspec b/ext/openssl/openssl.gemspec
index 295379fb6c3..471a3c42654 100644
--- a/ext/openssl/openssl.gemspec
+++ b/ext/openssl/openssl.gemspec
@@ -1,29 +1,26 @@
-# -*- encoding: utf-8 -*-
+Gem::Specification.new do |spec|
+ spec.name = "openssl"
+ spec.version = "2.2.0"
+ spec.authors = ["Martin Bosslet", "SHIBATA Hiroshi", "Zachary Scott", "Kazuki Yamaguchi"]
+ spec.email = ["ruby-core@ruby-lang.org"]
+ spec.summary = %q{OpenSSL provides SSL, TLS and general purpose cryptography.}
+ spec.description = %q{It wraps the OpenSSL library.}
+ spec.homepage = "https://github.com/ruby/openssl"
+ spec.license = "Ruby"
-Gem::Specification.new do |s|
- s.name = "openssl"
- s.version = "2.1.2"
+ spec.files = Dir["lib/**/*.rb", "ext/**/*.{c,h,rb}", "*.md", "BSDL", "LICENSE.txt"]
+ spec.require_paths = ["lib"]
+ spec.extensions = ["ext/openssl/extconf.rb"]
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
- s.metadata = { "msys2_mingw_dependencies" => "openssl" } if s.respond_to? :metadata=
- s.require_paths = ["lib"]
- s.authors = ["Martin Bosslet", "SHIBATA Hiroshi", "Zachary Scott", "Kazuki Yamaguchi"]
- s.date = "2018-10-17"
- s.description = "It wraps the OpenSSL library."
- s.email = ["ruby-core@ruby-lang.org"]
- s.extensions = ["ext/openssl/extconf.rb"]
- s.extra_rdoc_files = ["README.md", "CONTRIBUTING.md", "History.md"]
- s.files = ["BSDL", "CONTRIBUTING.md", "History.md", "LICENSE.txt", "README.md", "ext/openssl/deprecation.rb", "ext/openssl/extconf.rb", "ext/openssl/openssl_missing.c", "ext/openssl/openssl_missing.h", "ext/openssl/ossl.c", "ext/openssl/ossl.h", "ext/openssl/ossl_asn1.c", "ext/openssl/ossl_asn1.h", "ext/openssl/ossl_bio.c", "ext/openssl/ossl_bio.h", "ext/openssl/ossl_bn.c", "ext/openssl/ossl_bn.h", "ext/openssl/ossl_cipher.c", "ext/openssl/ossl_cipher.h", "ext/openssl/ossl_config.c", "ext/openssl/ossl_config.h", "ext/openssl/ossl_digest.c", "ext/openssl/ossl_digest.h", "ext/openssl/ossl_engine.c", "ext/openssl/ossl_engine.h", "ext/openssl/ossl_hmac.c", "ext/openssl/ossl_hmac.h", "ext/openssl/ossl_kdf.c", "ext/openssl/ossl_kdf.h", "ext/openssl/ossl_ns_spki.c", "ext/openssl/ossl_ns_spki.h", "ext/openssl/ossl_ocsp.c", "ext/openssl/ossl_ocsp.h", "ext/openssl/ossl_pkcs12.c", "ext/openssl/ossl_pkcs12.h", "ext/openssl/ossl_pkcs7.c", "ext/openssl/ossl_pkcs7.h", "ext/openssl/ossl_pkey.c", "ext/openssl/ossl_pkey.h", "ext/openssl/ossl_pkey_dh.c", "ext/openssl/ossl_pkey_dsa.c", "ext/openssl/ossl_pkey_ec.c", "ext/openssl/ossl_pkey_rsa.c", "ext/openssl/ossl_rand.c", "ext/openssl/ossl_rand.h", "ext/openssl/ossl_ssl.c", "ext/openssl/ossl_ssl.h", "ext/openssl/ossl_ssl_session.c", "ext/openssl/ossl_version.h", "ext/openssl/ossl_x509.c", "ext/openssl/ossl_x509.h", "ext/openssl/ossl_x509attr.c", "ext/openssl/ossl_x509cert.c", "ext/openssl/ossl_x509crl.c", "ext/openssl/ossl_x509ext.c", "ext/openssl/ossl_x509name.c", "ext/openssl/ossl_x509req.c", "ext/openssl/ossl_x509revoked.c", "ext/openssl/ossl_x509store.c", "ext/openssl/ruby_missing.h", "lib/openssl.rb", "lib/openssl/bn.rb", "lib/openssl/buffering.rb", "lib/openssl/cipher.rb", "lib/openssl/config.rb", "lib/openssl/digest.rb", "lib/openssl/pkcs5.rb", "lib/openssl/pkey.rb", "lib/openssl/ssl.rb", "lib/openssl/x509.rb"]
- s.homepage = "https://github.com/ruby/openssl"
- s.licenses = ["Ruby"]
- s.rdoc_options = ["--main", "README.md"]
- s.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
- s.rubygems_version = "3.0.0.beta1"
- s.summary = "OpenSSL provides SSL, TLS and general purpose cryptography."
+ spec.extra_rdoc_files = Dir["*.md"]
+ spec.rdoc_options = ["--main", "README.md"]
- s.add_runtime_dependency("ipaddr", [">= 0"])
- s.add_development_dependency("rake", [">= 0"])
- s.add_development_dependency("rake-compiler", [">= 0"])
- s.add_development_dependency("test-unit", ["~> 3.0"])
- s.add_development_dependency("rdoc", [">= 0"])
+ spec.required_ruby_version = ">= 2.3.0"
+
+ spec.add_development_dependency "rake"
+ spec.add_development_dependency "rake-compiler"
+ spec.add_development_dependency "test-unit", "~> 3.0"
+ spec.add_development_dependency "rdoc"
+
+ spec.metadata["msys2_mingw_dependencies"] = "openssl"
end
diff --git a/ext/openssl/openssl_missing.h b/ext/openssl/openssl_missing.h
index 09998214e1d..7d218f86f5e 100644
--- a/ext/openssl/openssl_missing.h
+++ b/ext/openssl/openssl_missing.h
@@ -72,6 +72,9 @@ void ossl_HMAC_CTX_free(HMAC_CTX *);
#if !defined(HAVE_X509_STORE_SET_EX_DATA)
# define X509_STORE_set_ex_data(x, idx, data) \
CRYPTO_set_ex_data(&(x)->ex_data, (idx), (data))
+#endif
+
+#if !defined(HAVE_X509_STORE_GET_EX_NEW_INDEX) && !defined(X509_STORE_get_ex_new_index)
# define X509_STORE_get_ex_new_index(l, p, newf, dupf, freef) \
CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_X509_STORE, (l), (p), \
(newf), (dupf), (freef))
@@ -144,7 +147,8 @@ void ossl_X509_REQ_get0_signature(const X509_REQ *, const ASN1_BIT_STRING **, co
CRYPTO_add(&(x)->references, 1, CRYPTO_LOCK_EVP_PKEY);
#endif
-#if !defined(HAVE_OPAQUE_OPENSSL)
+#if !defined(HAVE_OPAQUE_OPENSSL) && \
+ (!defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER < 0x2070000fL)
#define IMPL_PKEY_GETTER(_type, _name) \
static inline _type *EVP_PKEY_get0_##_type(EVP_PKEY *pkey) { \
return pkey->pkey._name; }
@@ -219,4 +223,35 @@ IMPL_PKEY_GETTER(EC_KEY, ec)
# define SSL_SESSION_get_protocol_version(s) ((s)->ssl_version)
#endif
+#if !defined(HAVE_TS_STATUS_INFO_GET0_STATUS)
+# define TS_STATUS_INFO_get0_status(a) ((a)->status)
+#endif
+
+#if !defined(HAVE_TS_STATUS_INFO_GET0_TEXT)
+# define TS_STATUS_INFO_get0_text(a) ((a)->text)
+#endif
+
+#if !defined(HAVE_TS_STATUS_INFO_GET0_FAILURE_INFO)
+# define TS_STATUS_INFO_get0_failure_info(a) ((a)->failure_info)
+#endif
+
+#if !defined(HAVE_TS_VERIFY_CTS_SET_CERTS)
+# define TS_VERIFY_CTS_set_certs(ctx, crts) ((ctx)->certs=(crts))
+#endif
+
+#if !defined(HAVE_TS_VERIFY_CTX_SET_STORE)
+# define TS_VERIFY_CTX_set_store(ctx, str) ((ctx)->store=(str))
+#endif
+
+#if !defined(HAVE_TS_VERIFY_CTX_ADD_FLAGS)
+# define TS_VERIFY_CTX_add_flags(ctx, f) ((ctx)->flags |= (f))
+#endif
+
+#if !defined(HAVE_TS_RESP_CTX_SET_TIME_CB)
+# define TS_RESP_CTX_set_time_cb(ctx, callback, dta) do { \
+ (ctx)->time_cb = (callback); \
+ (ctx)->time_cb_data = (dta); \
+ } while (0)
+#endif
+
#endif /* _OSSL_OPENSSL_MISSING_H_ */
diff --git a/ext/openssl/ossl.c b/ext/openssl/ossl.c
index e4196f07547..14a7919a93f 100644
--- a/ext/openssl/ossl.c
+++ b/ext/openssl/ossl.c
@@ -605,6 +605,35 @@ static void Init_ossl_locks(void)
#endif /* !HAVE_OPENSSL_110_THREADING_API */
/*
+ * call-seq:
+ * OpenSSL.fixed_length_secure_compare(string, string) -> boolean
+ *
+ * Constant time memory comparison for fixed length strings, such as results
+ * of HMAC calculations.
+ *
+ * Returns +true+ if the strings are identical, +false+ if they are of the same
+ * length but not identical. If the length is different, +ArgumentError+ is
+ * raised.
+ */
+static VALUE
+ossl_crypto_fixed_length_secure_compare(VALUE dummy, VALUE str1, VALUE str2)
+{
+ const unsigned char *p1 = (const unsigned char *)StringValuePtr(str1);
+ const unsigned char *p2 = (const unsigned char *)StringValuePtr(str2);
+ long len1 = RSTRING_LEN(str1);
+ long len2 = RSTRING_LEN(str2);
+
+ if (len1 != len2) {
+ ossl_raise(rb_eArgError, "inputs must be of equal length");
+ }
+
+ switch (CRYPTO_memcmp(p1, p2, len1)) {
+ case 0: return Qtrue;
+ default: return Qfalse;
+ }
+}
+
+/*
* OpenSSL provides SSL, TLS and general purpose cryptography. It wraps the
* OpenSSL[https://www.openssl.org/] library.
*
@@ -635,7 +664,7 @@ static void Init_ossl_locks(void)
* ahold of the key may use it unless it is encrypted. In order to securely
* export a key you may export it with a pass phrase.
*
- * cipher = OpenSSL::Cipher.new 'AES-128-CBC'
+ * cipher = OpenSSL::Cipher.new 'AES-256-CBC'
* pass_phrase = 'my secure pass phrase goes here'
*
* key_secure = key.export cipher, pass_phrase
@@ -745,7 +774,7 @@ static void Init_ossl_locks(void)
* using PBKDF2. PKCS #5 v2.0 recommends at least 8 bytes for the salt,
* the number of iterations largely depends on the hardware being used.
*
- * cipher = OpenSSL::Cipher.new 'AES-128-CBC'
+ * cipher = OpenSSL::Cipher.new 'AES-256-CBC'
* cipher.encrypt
* iv = cipher.random_iv
*
@@ -768,7 +797,7 @@ static void Init_ossl_locks(void)
* Use the same steps as before to derive the symmetric AES key, this time
* setting the Cipher up for decryption.
*
- * cipher = OpenSSL::Cipher.new 'AES-128-CBC'
+ * cipher = OpenSSL::Cipher.new 'AES-256-CBC'
* cipher.decrypt
* cipher.iv = iv # the one generated with #random_iv
*
@@ -803,7 +832,7 @@ static void Init_ossl_locks(void)
*
* First set up the cipher for encryption
*
- * encryptor = OpenSSL::Cipher.new 'AES-128-CBC'
+ * encryptor = OpenSSL::Cipher.new 'AES-256-CBC'
* encryptor.encrypt
* encryptor.pkcs5_keyivgen pass_phrase, salt
*
@@ -816,7 +845,7 @@ static void Init_ossl_locks(void)
*
* Use a new Cipher instance set up for decryption
*
- * decryptor = OpenSSL::Cipher.new 'AES-128-CBC'
+ * decryptor = OpenSSL::Cipher.new 'AES-256-CBC'
* decryptor.decrypt
* decryptor.pkcs5_keyivgen pass_phrase, salt
*
@@ -833,7 +862,7 @@ static void Init_ossl_locks(void)
* signature.
*
* key = OpenSSL::PKey::RSA.new 2048
- * name = OpenSSL::X509::Name.parse 'CN=nobody/DC=example'
+ * name = OpenSSL::X509::Name.parse '/CN=nobody/DC=example'
*
* cert = OpenSSL::X509::Certificate.new
* cert.version = 2
@@ -904,7 +933,7 @@ static void Init_ossl_locks(void)
* ca_key = OpenSSL::PKey::RSA.new 2048
* pass_phrase = 'my secure pass phrase goes here'
*
- * cipher = OpenSSL::Cipher.new 'AES-128-CBC'
+ * cipher = OpenSSL::Cipher.new 'AES-256-CBC'
*
* open 'ca_key.pem', 'w', 0400 do |io|
* io.write ca_key.export(cipher, pass_phrase)
@@ -915,7 +944,7 @@ static void Init_ossl_locks(void)
* A CA certificate is created the same way we created a certificate above, but
* with different extensions.
*
- * ca_name = OpenSSL::X509::Name.parse 'CN=ca/DC=example'
+ * ca_name = OpenSSL::X509::Name.parse '/CN=ca/DC=example'
*
* ca_cert = OpenSSL::X509::Certificate.new
* ca_cert.serial = 0
@@ -1125,11 +1154,7 @@ Init_openssl(void)
*/
mOSSL = rb_define_module("OpenSSL");
rb_global_variable(&mOSSL);
-
- /*
- * OpenSSL ruby extension version
- */
- rb_define_const(mOSSL, "VERSION", rb_str_new2(OSSL_VERSION));
+ rb_define_singleton_method(mOSSL, "fixed_length_secure_compare", ossl_crypto_fixed_length_secure_compare, 2);
/*
* Version of OpenSSL the ruby OpenSSL extension was built with
@@ -1205,6 +1230,9 @@ Init_openssl(void)
Init_ossl_pkey();
Init_ossl_rand();
Init_ossl_ssl();
+#ifndef OPENSSL_NO_TS
+ Init_ossl_ts();
+#endif
Init_ossl_x509();
Init_ossl_ocsp();
Init_ossl_engine();
diff --git a/ext/openssl/ossl.h b/ext/openssl/ossl.h
index 6af7ddd7d07..8074afcd794 100644
--- a/ext/openssl/ossl.h
+++ b/ext/openssl/ossl.h
@@ -27,6 +27,9 @@
#include <openssl/hmac.h>
#include <openssl/rand.h>
#include <openssl/conf.h>
+#ifndef OPENSSL_NO_TS
+ #include <openssl/ts.h>
+#endif
#include <openssl/crypto.h>
#if !defined(OPENSSL_NO_ENGINE)
# include <openssl/engine.h>
@@ -167,7 +170,9 @@ void ossl_debug(const char *, ...);
#include "ossl_pkey.h"
#include "ossl_rand.h"
#include "ossl_ssl.h"
-#include "ossl_version.h"
+#ifndef OPENSSL_NO_TS
+ #include "ossl_ts.h"
+#endif
#include "ossl_x509.h"
#include "ossl_engine.h"
#include "ossl_kdf.h"
diff --git a/ext/openssl/ossl_asn1.c b/ext/openssl/ossl_asn1.c
index 0085d4beaba..9eb1826f02c 100644
--- a/ext/openssl/ossl_asn1.c
+++ b/ext/openssl/ossl_asn1.c
@@ -1285,6 +1285,30 @@ ossl_asn1obj_get_ln(VALUE self)
return ret;
}
+/*
+ * call-seq:
+ * oid == other_oid => true or false
+ *
+ * Returns +true+ if _other_oid_ is the same as _oid_
+ */
+static VALUE
+ossl_asn1obj_eq(VALUE self, VALUE other)
+{
+ VALUE valSelf, valOther;
+ int nidSelf, nidOther;
+
+ valSelf = ossl_asn1_get_value(self);
+ valOther = ossl_asn1_get_value(other);
+
+ if ((nidSelf = OBJ_txt2nid(StringValueCStr(valSelf))) == NID_undef)
+ ossl_raise(eASN1Error, "OBJ_txt2nid");
+
+ if ((nidOther = OBJ_txt2nid(StringValueCStr(valOther))) == NID_undef)
+ ossl_raise(eASN1Error, "OBJ_txt2nid");
+
+ return nidSelf == nidOther ? Qtrue : Qfalse;
+}
+
static VALUE
asn1obj_get_oid_i(VALUE vobj)
{
@@ -1818,6 +1842,7 @@ do{\
rb_define_method(cASN1ObjectId, "oid", ossl_asn1obj_get_oid, 0);
rb_define_alias(cASN1ObjectId, "short_name", "sn");
rb_define_alias(cASN1ObjectId, "long_name", "ln");
+ rb_define_method(cASN1ObjectId, "==", ossl_asn1obj_eq, 1);
rb_attr(cASN1BitString, rb_intern("unused_bits"), 1, 1, 0);
rb_define_method(cASN1EndOfContent, "initialize", ossl_asn1eoc_initialize, 0);
diff --git a/ext/openssl/ossl_bn.c b/ext/openssl/ossl_bn.c
index 6f0064e9661..6493e051e9e 100644
--- a/ext/openssl/ossl_bn.c
+++ b/ext/openssl/ossl_bn.c
@@ -173,7 +173,6 @@ ossl_bn_alloc(VALUE klass)
/*
* call-seq:
- * OpenSSL::BN.new => aBN
* OpenSSL::BN.new(bn) => aBN
* OpenSSL::BN.new(integer) => aBN
* OpenSSL::BN.new(string) => aBN
@@ -193,6 +192,10 @@ ossl_bn_initialize(int argc, VALUE *argv, VALUE self)
base = NUM2INT(bs);
}
+ if (NIL_P(str)) {
+ ossl_raise(rb_eArgError, "invalid argument");
+ }
+
if (RB_INTEGER_TYPE_P(str)) {
GetBN(self, bn);
integer_to_bnptr(str, bn);
diff --git a/ext/openssl/ossl_cipher.c b/ext/openssl/ossl_cipher.c
index 0840c84a712..66bf0beb111 100644
--- a/ext/openssl/ossl_cipher.c
+++ b/ext/openssl/ossl_cipher.c
@@ -104,7 +104,7 @@ ossl_cipher_alloc(VALUE klass)
* call-seq:
* Cipher.new(string) -> cipher
*
- * The string must be a valid cipher name like "AES-128-CBC" or "3DES".
+ * The string must contain a valid cipher name like "AES-256-CBC".
*
* A list of cipher names is available by calling OpenSSL::Cipher.ciphers.
*/
@@ -237,8 +237,7 @@ ossl_cipher_init(int argc, VALUE *argv, VALUE self, int mode)
ossl_raise(eCipherError, NULL);
}
- if (p_key)
- rb_ivar_set(self, id_key_set, Qtrue);
+ rb_ivar_set(self, id_key_set, p_key ? Qtrue : Qfalse);
return self;
}
@@ -896,7 +895,7 @@ Init_ossl_cipher(void)
* without processing the password further. A simple and secure way to
* create a key for a particular Cipher is
*
- * cipher = OpenSSL::AES256.new(:CFB)
+ * cipher = OpenSSL::Cipher::AES256.new(:CFB)
* cipher.encrypt
* key = cipher.random_key # also sets the generated key on the Cipher
*
diff --git a/ext/openssl/ossl_digest.c b/ext/openssl/ossl_digest.c
index 112ce336477..661b230fb7e 100644
--- a/ext/openssl/ossl_digest.c
+++ b/ext/openssl/ossl_digest.c
@@ -352,8 +352,6 @@ Init_ossl_digest(void)
* * SHA, SHA1, SHA224, SHA256, SHA384 and SHA512
* * MD2, MD4, MDC2 and MD5
* * RIPEMD160
- * * DSS, DSS1 (Pseudo algorithms to be used for DSA signatures. DSS is
- * equal to SHA and DSS1 is equal to SHA1)
*
* For each of these algorithms, there is a sub-class of Digest that
* can be instantiated as simply as e.g.
diff --git a/ext/openssl/ossl_engine.c b/ext/openssl/ossl_engine.c
index 5ca0d4ca3f1..f90bf061df8 100644
--- a/ext/openssl/ossl_engine.c
+++ b/ext/openssl/ossl_engine.c
@@ -150,6 +150,9 @@ ossl_engine_s_load(int argc, VALUE *argv, VALUE klass)
#if HAVE_ENGINE_LOAD_AESNI
OSSL_ENGINE_LOAD_IF_MATCH(aesni, AESNI);
#endif
+#if HAVE_ENGINE_LOAD_CLOUDHSM
+ OSSL_ENGINE_LOAD_IF_MATCH(cloudhsm, CLOUDHSM);
+#endif
#endif
#ifdef HAVE_ENGINE_LOAD_OPENBSD_DEV_CRYPTO
OSSL_ENGINE_LOAD_IF_MATCH(openbsd_dev_crypto, OPENBSD_DEV_CRYPTO);
diff --git a/ext/openssl/ossl_hmac.c b/ext/openssl/ossl_hmac.c
index 564dcab5223..2ac2e5c6cea 100644
--- a/ext/openssl/ossl_hmac.c
+++ b/ext/openssl/ossl_hmac.c
@@ -84,18 +84,12 @@ ossl_hmac_alloc(VALUE klass)
*
* === A note about comparisons
*
- * Two instances won't be equal when they're compared, even if they have the
- * same value. Use #to_s or #hexdigest to return the authentication code that
- * the instance represents. For example:
+ * Two instances can be securely compared with #== in constant time:
*
* other_instance = OpenSSL::HMAC.new('key', OpenSSL::Digest.new('sha1'))
- * #=> f42bb0eeb018ebbd4597ae7213711ec60760843f
- * instance
- * #=> f42bb0eeb018ebbd4597ae7213711ec60760843f
- * instance == other_instance
- * #=> false
- * instance.to_s == other_instance.to_s
- * #=> true
+ * #=> f42bb0eeb018ebbd4597ae7213711ec60760843f
+ * instance == other_instance
+ * #=> true
*
*/
static VALUE
diff --git a/ext/openssl/ossl_kdf.c b/ext/openssl/ossl_kdf.c
index ee124718b51..3d0e66b5f52 100644
--- a/ext/openssl/ossl_kdf.c
+++ b/ext/openssl/ossl_kdf.c
@@ -284,24 +284,8 @@ Init_ossl_kdf(void)
* Typically, "==" short-circuits on evaluation, and is therefore
* vulnerable to timing attacks. The proper way is to use a method that
* always takes the same amount of time when comparing two values, thus
- * not leaking any information to potential attackers. To compare two
- * values, the following could be used:
- *
- * def eql_time_cmp(a, b)
- * unless a.length == b.length
- * return false
- * end
- * cmp = b.bytes
- * result = 0
- * a.bytes.each_with_index {|c,i|
- * result |= c ^ cmp[i]
- * }
- * result == 0
- * end
- *
- * Please note that the premature return in case of differing lengths
- * typically does not leak valuable information - when using PBKDF2, the
- * length of the values to be compared is of fixed size.
+ * not leaking any information to potential attackers. To do this, use
+ * +OpenSSL.fixed_length_secure_compare+.
*/
mKDF = rb_define_module_under(mOSSL, "KDF");
/*
diff --git a/ext/openssl/ossl_ocsp.c b/ext/openssl/ossl_ocsp.c
index c0237791da9..2ca4f62f70f 100644
--- a/ext/openssl/ossl_ocsp.c
+++ b/ext/openssl/ossl_ocsp.c
@@ -1489,13 +1489,15 @@ ossl_ocspcid_initialize_copy(VALUE self, VALUE other)
* call-seq:
* OpenSSL::OCSP::CertificateId.new(subject, issuer, digest = nil) -> certificate_id
* OpenSSL::OCSP::CertificateId.new(der_string) -> certificate_id
+ * OpenSSL::OCSP::CertificateId.new(obj) -> certificate_id
*
* Creates a new OpenSSL::OCSP::CertificateId for the given _subject_ and
* _issuer_ X509 certificates. The _digest_ is a digest algorithm that is used
* to compute the hash values. This defaults to SHA-1.
*
* If only one argument is given, decodes it as DER representation of a
- * certificate ID.
+ * certificate ID or generates certificate ID from the object that responds to
+ * the to_der method.
*/
static VALUE
ossl_ocspcid_initialize(int argc, VALUE *argv, VALUE self)
@@ -1734,18 +1736,11 @@ Init_ossl_ocsp(void)
* To submit the request to the CA for verification we need to extract the
* OCSP URI from the subject certificate:
*
- * authority_info_access = subject.extensions.find do |extension|
- * extension.oid == 'authorityInfoAccess'
- * end
- *
- * descriptions = authority_info_access.value.split "\n"
- * ocsp = descriptions.find do |description|
- * description.start_with? 'OCSP'
- * end
+ * ocsp_uris = subject.ocsp_uris
*
* require 'uri'
*
- * ocsp_uri = URI ocsp[/URI:(.*)/, 1]
+ * ocsp_uri = URI ocsp_uris[0]
*
* To submit the request we'll POST the request to the OCSP URI (per RFC
* 2560). Note that we only handle HTTP requests and don't handle any
diff --git a/ext/openssl/ossl_ocsp.h b/ext/openssl/ossl_ocsp.h
index 21e2c99a2e3..6d2aac8657c 100644
--- a/ext/openssl/ossl_ocsp.h
+++ b/ext/openssl/ossl_ocsp.h
@@ -13,9 +13,9 @@
#if !defined(OPENSSL_NO_OCSP)
extern VALUE mOCSP;
-extern VALUE cOPCSReq;
-extern VALUE cOPCSRes;
-extern VALUE cOPCSBasicRes;
+extern VALUE cOCSPReq;
+extern VALUE cOCSPRes;
+extern VALUE cOCSPBasicRes;
#endif
void Init_ossl_ocsp(void);
diff --git a/ext/openssl/ossl_pkcs7.c b/ext/openssl/ossl_pkcs7.c
index 28010c81fb7..ea8e92d1bcb 100644
--- a/ext/openssl/ossl_pkcs7.c
+++ b/ext/openssl/ossl_pkcs7.c
@@ -9,21 +9,6 @@
*/
#include "ossl.h"
-#define NewPKCS7(klass) \
- TypedData_Wrap_Struct((klass), &ossl_pkcs7_type, 0)
-#define SetPKCS7(obj, pkcs7) do { \
- if (!(pkcs7)) { \
- ossl_raise(rb_eRuntimeError, "PKCS7 wasn't initialized."); \
- } \
- RTYPEDDATA_DATA(obj) = (pkcs7); \
-} while (0)
-#define GetPKCS7(obj, pkcs7) do { \
- TypedData_Get_Struct((obj), PKCS7, &ossl_pkcs7_type, (pkcs7)); \
- if (!(pkcs7)) { \
- ossl_raise(rb_eRuntimeError, "PKCS7 wasn't initialized."); \
- } \
-} while (0)
-
#define NewPKCS7si(klass) \
TypedData_Wrap_Struct((klass), &ossl_pkcs7_signer_info_type, 0)
#define SetPKCS7si(obj, p7si) do { \
@@ -75,7 +60,7 @@ ossl_pkcs7_free(void *ptr)
PKCS7_free(ptr);
}
-static const rb_data_type_t ossl_pkcs7_type = {
+const rb_data_type_t ossl_pkcs7_type = {
"OpenSSL/PKCS7",
{
0, ossl_pkcs7_free,
@@ -1088,7 +1073,6 @@ Init_ossl_pkcs7(void)
rb_define_alloc_func(cPKCS7Signer, ossl_pkcs7si_alloc);
rb_define_method(cPKCS7Signer, "initialize", ossl_pkcs7si_initialize,3);
rb_define_method(cPKCS7Signer, "issuer", ossl_pkcs7si_get_issuer, 0);
- rb_define_alias(cPKCS7Signer, "name", "issuer");
rb_define_method(cPKCS7Signer, "serial", ossl_pkcs7si_get_serial,0);
rb_define_method(cPKCS7Signer,"signed_time",ossl_pkcs7si_get_signed_time,0);
diff --git a/ext/openssl/ossl_pkcs7.h b/ext/openssl/ossl_pkcs7.h
index 139e00d640a..3e1b0946707 100644
--- a/ext/openssl/ossl_pkcs7.h
+++ b/ext/openssl/ossl_pkcs7.h
@@ -10,6 +10,22 @@
#if !defined(_OSSL_PKCS7_H_)
#define _OSSL_PKCS7_H_
+#define NewPKCS7(klass) \
+ TypedData_Wrap_Struct((klass), &ossl_pkcs7_type, 0)
+#define SetPKCS7(obj, pkcs7) do { \
+ if (!(pkcs7)) { \
+ ossl_raise(rb_eRuntimeError, "PKCS7 wasn't initialized."); \
+ } \
+ RTYPEDDATA_DATA(obj) = (pkcs7); \
+} while (0)
+#define GetPKCS7(obj, pkcs7) do { \
+ TypedData_Get_Struct((obj), PKCS7, &ossl_pkcs7_type, (pkcs7)); \
+ if (!(pkcs7)) { \
+ ossl_raise(rb_eRuntimeError, "PKCS7 wasn't initialized."); \
+ } \
+} while (0)
+
+extern const rb_data_type_t ossl_pkcs7_type;
extern VALUE cPKCS7;
extern VALUE cPKCS7Signer;
extern VALUE cPKCS7Recipient;
diff --git a/ext/openssl/ossl_pkey.c b/ext/openssl/ossl_pkey.c
index e1fffb2446f..fc4cac3bc40 100644
--- a/ext/openssl/ossl_pkey.c
+++ b/ext/openssl/ossl_pkey.c
@@ -167,21 +167,27 @@ ossl_pkey_new_from_data(int argc, VALUE *argv, VALUE self)
pass = ossl_pem_passwd_value(pass);
bio = ossl_obj2bio(&data);
- if (!(pkey = d2i_PrivateKey_bio(bio, NULL))) {
- OSSL_BIO_reset(bio);
- if (!(pkey = PEM_read_bio_PrivateKey(bio, NULL, ossl_pem_passwd_cb, (void *)pass))) {
- OSSL_BIO_reset(bio);
- if (!(pkey = d2i_PUBKEY_bio(bio, NULL))) {
- OSSL_BIO_reset(bio);
- pkey = PEM_read_bio_PUBKEY(bio, NULL, ossl_pem_passwd_cb, (void *)pass);
- }
- }
- }
+ if ((pkey = d2i_PrivateKey_bio(bio, NULL)))
+ goto ok;
+ OSSL_BIO_reset(bio);
+ if ((pkey = d2i_PKCS8PrivateKey_bio(bio, NULL, ossl_pem_passwd_cb, (void *)pass)))
+ goto ok;
+ OSSL_BIO_reset(bio);
+ if ((pkey = d2i_PUBKEY_bio(bio, NULL)))
+ goto ok;
+ OSSL_BIO_reset(bio);
+ /* PEM_read_bio_PrivateKey() also parses PKCS #8 formats */
+ if ((pkey = PEM_read_bio_PrivateKey(bio, NULL, ossl_pem_passwd_cb, (void *)pass)))
+ goto ok;
+ OSSL_BIO_reset(bio);
+ if ((pkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL)))
+ goto ok;
BIO_free(bio);
- if (!pkey)
- ossl_raise(ePKeyError, "Could not parse PKey");
+ ossl_raise(ePKeyError, "Could not parse PKey");
+ok:
+ BIO_free(bio);
return ossl_pkey_new(pkey);
}
@@ -293,6 +299,124 @@ ossl_pkey_initialize(VALUE self)
return self;
}
+static VALUE
+do_pkcs8_export(int argc, VALUE *argv, VALUE self, int to_der)
+{
+ EVP_PKEY *pkey;
+ VALUE cipher, pass;
+ const EVP_CIPHER *enc = NULL;
+ BIO *bio;
+
+ GetPKey(self, pkey);
+ rb_scan_args(argc, argv, "02", &cipher, &pass);
+ if (argc > 0) {
+ /*
+ * TODO: EncryptedPrivateKeyInfo actually has more options.
+ * Should they be exposed?
+ */
+ enc = ossl_evp_get_cipherbyname(cipher);
+ pass = ossl_pem_passwd_value(pass);
+ }
+
+ bio = BIO_new(BIO_s_mem());
+ if (!bio)
+ ossl_raise(ePKeyError, "BIO_new");
+ if (to_der) {
+ if (!i2d_PKCS8PrivateKey_bio(bio, pkey, enc, NULL, 0,
+ ossl_pem_passwd_cb, (void *)pass)) {
+ BIO_free(bio);
+ ossl_raise(ePKeyError, "i2d_PKCS8PrivateKey_bio");
+ }
+ }
+ else {
+ if (!PEM_write_bio_PKCS8PrivateKey(bio, pkey, enc, NULL, 0,
+ ossl_pem_passwd_cb, (void *)pass)) {
+ BIO_free(bio);
+ ossl_raise(ePKeyError, "PEM_write_bio_PKCS8PrivateKey");
+ }
+ }
+ return ossl_membio2str(bio);
+}
+
+/*
+ * call-seq:
+ * pkey.private_to_der -> string
+ * pkey.private_to_der(cipher, password) -> string
+ *
+ * Serializes the private key to DER-encoded PKCS #8 format. If called without
+ * arguments, unencrypted PKCS #8 PrivateKeyInfo format is used. If called with
+ * a cipher name and a password, PKCS #8 EncryptedPrivateKeyInfo format with
+ * PBES2 encryption scheme is used.
+ */
+static VALUE
+ossl_pkey_private_to_der(int argc, VALUE *argv, VALUE self)
+{
+ return do_pkcs8_export(argc, argv, self, 1);
+}
+
+/*
+ * call-seq:
+ * pkey.private_to_pem -> string
+ * pkey.private_to_pem(cipher, password) -> string
+ *
+ * Serializes the private key to PEM-encoded PKCS #8 format. See #private_to_der
+ * for more details.
+ */
+static VALUE
+ossl_pkey_private_to_pem(int argc, VALUE *argv, VALUE self)
+{
+ return do_pkcs8_export(argc, argv, self, 0);
+}
+
+static VALUE
+do_spki_export(VALUE self, int to_der)
+{
+ EVP_PKEY *pkey;
+ BIO *bio;
+
+ GetPKey(self, pkey);
+ bio = BIO_new(BIO_s_mem());
+ if (!bio)
+ ossl_raise(ePKeyError, "BIO_new");
+ if (to_der) {
+ if (!i2d_PUBKEY_bio(bio, pkey)) {
+ BIO_free(bio);
+ ossl_raise(ePKeyError, "i2d_PUBKEY_bio");
+ }
+ }
+ else {
+ if (!PEM_write_bio_PUBKEY(bio, pkey)) {
+ BIO_free(bio);
+ ossl_raise(ePKeyError, "PEM_write_bio_PUBKEY");
+ }
+ }
+ return ossl_membio2str(bio);
+}
+
+/*
+ * call-seq:
+ * pkey.public_to_der -> string
+ *
+ * Serializes the public key to DER-encoded X.509 SubjectPublicKeyInfo format.
+ */
+static VALUE
+ossl_pkey_public_to_der(VALUE self)
+{
+ return do_spki_export(self, 1);
+}
+
+/*
+ * call-seq:
+ * pkey.public_to_pem -> string
+ *
+ * Serializes the public key to PEM-encoded X.509 SubjectPublicKeyInfo format.
+ */
+static VALUE
+ossl_pkey_public_to_pem(VALUE self)
+{
+ return do_spki_export(self, 0);
+}
+
/*
* call-seq:
* pkey.sign(digest, data) -> String
@@ -491,6 +615,10 @@ Init_ossl_pkey(void)
rb_define_alloc_func(cPKey, ossl_pkey_alloc);
rb_define_method(cPKey, "initialize", ossl_pkey_initialize, 0);
+ rb_define_method(cPKey, "private_to_der", ossl_pkey_private_to_der, -1);
+ rb_define_method(cPKey, "private_to_pem", ossl_pkey_private_to_pem, -1);
+ rb_define_method(cPKey, "public_to_der", ossl_pkey_public_to_der, 0);
+ rb_define_method(cPKey, "public_to_pem", ossl_pkey_public_to_pem, 0);
rb_define_method(cPKey, "sign", ossl_pkey_sign, 2);
rb_define_method(cPKey, "verify", ossl_pkey_verify, 3);
diff --git a/ext/openssl/ossl_pkey_ec.c b/ext/openssl/ossl_pkey_ec.c
index 8bb611248b7..fc2bc6c8151 100644
--- a/ext/openssl/ossl_pkey_ec.c
+++ b/ext/openssl/ossl_pkey_ec.c
@@ -1564,6 +1564,34 @@ ossl_ec_point_to_octet_string(VALUE self, VALUE conversion_form)
/*
* call-seq:
+ * point.add(point) => point
+ *
+ * Performs elliptic curve point addition.
+ */
+static VALUE ossl_ec_point_add(VALUE self, VALUE other)
+{
+ EC_POINT *point_self, *point_other, *point_result;
+ const EC_GROUP *group;
+ VALUE group_v = rb_attr_get(self, id_i_group);
+ VALUE result;
+
+ GetECPoint(self, point_self);
+ GetECPoint(other, point_other);
+ GetECGroup(group_v, group);
+
+ result = rb_obj_alloc(cEC_POINT);
+ ossl_ec_point_initialize(1, &group_v, result);
+ GetECPoint(result, point_result);
+
+ if (EC_POINT_add(group, point_result, point_self, point_other, ossl_bn_ctx) != 1) {
+ ossl_raise(eEC_POINT, "EC_POINT_add");
+ }
+
+ return result;
+}
+
+/*
+ * call-seq:
* point.mul(bn1 [, bn2]) => point
* point.mul(bns, points [, bn2]) => point
*
@@ -1786,6 +1814,7 @@ void Init_ossl_ec(void)
/* all the other methods */
rb_define_method(cEC_POINT, "to_octet_string", ossl_ec_point_to_octet_string, 1);
+ rb_define_method(cEC_POINT, "add", ossl_ec_point_add, 1);
rb_define_method(cEC_POINT, "mul", ossl_ec_point_mul, -1);
id_i_group = rb_intern("@group");
diff --git a/ext/openssl/ossl_pkey_rsa.c b/ext/openssl/ossl_pkey_rsa.c
index 4800fb27108..761866c66a1 100644
--- a/ext/openssl/ossl_pkey_rsa.c
+++ b/ext/openssl/ossl_pkey_rsa.c
@@ -26,10 +26,10 @@
static inline int
RSA_HAS_PRIVATE(RSA *rsa)
{
- const BIGNUM *p, *q;
+ const BIGNUM *e, *d;
- RSA_get0_factors(rsa, &p, &q);
- return p && q; /* d? why? */
+ RSA_get0_key(rsa, NULL, &e, &d);
+ return e && d;
}
static inline int
@@ -341,6 +341,7 @@ static VALUE
ossl_rsa_export(int argc, VALUE *argv, VALUE self)
{
RSA *rsa;
+ const BIGNUM *n, *e, *d, *p, *q, *dmp1, *dmq1, *iqmp;
BIO *out;
const EVP_CIPHER *ciph = NULL;
VALUE cipher, pass, str;
@@ -356,7 +357,10 @@ ossl_rsa_export(int argc, VALUE *argv, VALUE self)
if (!(out = BIO_new(BIO_s_mem()))) {
ossl_raise(eRSAError, NULL);
}
- if (RSA_HAS_PRIVATE(rsa)) {
+ RSA_get0_key(rsa, &n, &e, &d);
+ RSA_get0_factors(rsa, &p, &q);
+ RSA_get0_crt_params(rsa, &dmp1, &dmq1, &iqmp);
+ if (n && e && d && p && q && dmp1 && dmq1 && iqmp) {
if (!PEM_write_bio_RSAPrivateKey(out, rsa, ciph, NULL, 0,
ossl_pem_passwd_cb, (void *)pass)) {
BIO_free(out);
@@ -383,23 +387,27 @@ static VALUE
ossl_rsa_to_der(VALUE self)
{
RSA *rsa;
+ const BIGNUM *n, *e, *d, *p, *q, *dmp1, *dmq1, *iqmp;
int (*i2d_func)(const RSA *, unsigned char **);
- unsigned char *p;
+ unsigned char *ptr;
long len;
VALUE str;
GetRSA(self, rsa);
- if (RSA_HAS_PRIVATE(rsa))
+ RSA_get0_key(rsa, &n, &e, &d);
+ RSA_get0_factors(rsa, &p, &q);
+ RSA_get0_crt_params(rsa, &dmp1, &dmq1, &iqmp);
+ if (n && e && d && p && q && dmp1 && dmq1 && iqmp)
i2d_func = i2d_RSAPrivateKey;
else
i2d_func = (int (*)(const RSA *, unsigned char **))i2d_RSA_PUBKEY;
if((len = i2d_func(rsa, NULL)) <= 0)
ossl_raise(eRSAError, NULL);
str = rb_str_new(0, len);
- p = (unsigned char *)RSTRING_PTR(str);
- if(i2d_func(rsa, &p) < 0)
+ ptr = (unsigned char *)RSTRING_PTR(str);
+ if(i2d_func(rsa, &ptr) < 0)
ossl_raise(eRSAError, NULL);
- ossl_str_adjust(str, p);
+ ossl_str_adjust(str, ptr);
return str;
}
diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c
index 80a42383f70..dfbfbb22ee7 100644
--- a/ext/openssl/ossl_ssl.c
+++ b/ext/openssl/ossl_ssl.c
@@ -810,6 +810,10 @@ ossl_sslctx_setup(VALUE self)
}
#endif /* OPENSSL_NO_EC */
+#ifdef HAVE_SSL_CTX_SET_POST_HANDSHAKE_AUTH
+ SSL_CTX_set_post_handshake_auth(ctx, 1);
+#endif
+
val = rb_attr_get(self, id_i_cert_store);
if (!NIL_P(val)) {
X509_STORE *store = GetX509StorePtr(val); /* NO NEED TO DUP */
@@ -1318,6 +1322,17 @@ ossl_sslctx_add_certificate(int argc, VALUE *argv, VALUE self)
return self;
}
+static VALUE
+ossl_sslctx_add_certificate_chain_file(VALUE self, VALUE path)
+{
+ StringValue(path);
+ SSL_CTX *ctx = NULL;
+
+ GetSSLCTX(self, ctx);
+
+ return SSL_CTX_use_certificate_chain_file(ctx, RSTRING_PTR(path)) == 1 ? Qtrue : Qfalse;
+}
+
/*
* call-seq:
* ctx.session_add(session) -> true | false
@@ -1877,18 +1892,24 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock)
}
}
else {
- ID meth = nonblock ? rb_intern("read_nonblock") : rb_intern("sysread");
+ ID meth = nonblock ? rb_intern("read_nonblock") : rb_intern("sysread");
- rb_warning("SSL session is not started yet.");
- if (nonblock) {
+ rb_warning("SSL session is not started yet.");
+#if defined(RB_PASS_KEYWORDS)
+ if (nonblock) {
VALUE argv[3];
argv[0] = len;
argv[1] = str;
argv[2] = opts;
return rb_funcallv_kw(io, meth, 3, argv, RB_PASS_KEYWORDS);
}
- else
- return rb_funcall(io, meth, 2, len, str);
+#else
+ if (nonblock) {
+ return rb_funcall(io, meth, 3, len, str, opts);
+ }
+#endif
+ else
+ return rb_funcall(io, meth, 2, len, str);
}
end:
@@ -1911,7 +1932,6 @@ ossl_ssl_read(int argc, VALUE *argv, VALUE self)
}
/*
- * :nodoc:
* call-seq:
* ssl.sysread_nonblock(length) => string
* ssl.sysread_nonblock(length, buffer) => buffer
@@ -1976,15 +1996,21 @@ ossl_ssl_write_internal(VALUE self, VALUE str, VALUE opts)
ID meth = nonblock ?
rb_intern("write_nonblock") : rb_intern("syswrite");
- rb_warning("SSL session is not started yet.");
- if (nonblock) {
+ rb_warning("SSL session is not started yet.");
+#if defined(RB_PASS_KEYWORDS)
+ if (nonblock) {
VALUE argv[2];
argv[0] = str;
argv[1] = opts;
return rb_funcallv_kw(io, meth, 2, argv, RB_PASS_KEYWORDS);
}
- else
- return rb_funcall(io, meth, 1, str);
+#else
+ if (nonblock) {
+ return rb_funcall(io, meth, 2, str, opts);
+ }
+#endif
+ else
+ return rb_funcall(io, meth, 1, str);
}
end:
@@ -2004,7 +2030,6 @@ ossl_ssl_write(VALUE self, VALUE str)
}
/*
- * :nodoc:
* call-seq:
* ssl.syswrite_nonblock(string) => Integer
*
@@ -2022,7 +2047,6 @@ ossl_ssl_write_nonblock(int argc, VALUE *argv, VALUE self)
}
/*
- * :nodoc:
* call-seq:
* ssl.stop => nil
*
@@ -2294,6 +2318,56 @@ ossl_ssl_get_verify_result(VALUE self)
/*
* call-seq:
+ * ssl.finished_message => "finished message"
+ *
+ * Returns the last *Finished* message sent
+ *
+ */
+static VALUE
+ossl_ssl_get_finished(VALUE self)
+{
+ SSL *ssl;
+
+ GetSSL(self, ssl);
+
+ char sizer[1];
+ size_t len = SSL_get_finished(ssl, sizer, 0);
+
+ if(len == 0)
+ return Qnil;
+
+ char* buf = ALLOCA_N(char, len);
+ SSL_get_finished(ssl, buf, len);
+ return rb_str_new(buf, len);
+}
+
+/*
+ * call-seq:
+ * ssl.peer_finished_message => "peer finished message"
+ *
+ * Returns the last *Finished* message received
+ *
+ */
+static VALUE
+ossl_ssl_get_peer_finished(VALUE self)
+{
+ SSL *ssl;
+
+ GetSSL(self, ssl);
+
+ char sizer[1];
+ size_t len = SSL_get_peer_finished(ssl, sizer, 0);
+
+ if(len == 0)
+ return Qnil;
+
+ char* buf = ALLOCA_N(char, len);
+ SSL_get_peer_finished(ssl, buf, len);
+ return rb_str_new(buf, len);
+}
+
+/*
+ * call-seq:
* ssl.client_ca => [x509name, ...]
*
* Returns the list of client CAs. Please note that in contrast to
@@ -2614,13 +2688,13 @@ Init_ossl_ssl(void)
rb_define_const(mSSLExtConfig, "HAVE_TLSEXT_HOST_NAME", Qtrue);
/*
- * A callback invoked whenever a new handshake is initiated. May be used
- * to disable renegotiation entirely.
+ * A callback invoked whenever a new handshake is initiated on an
+ * established connection. May be used to disable renegotiation entirely.
*
* The callback is invoked with the active SSLSocket. The callback's
- * return value is irrelevant, normal return indicates "approval" of the
+ * return value is ignored. A normal return indicates "approval" of the
* renegotiation and will continue the process. To forbid renegotiation
- * and to cancel the process, an Error may be raised within the callback.
+ * and to cancel the process, raise an exception within the callback.
*
* === Disable client renegotiation
*
@@ -2628,10 +2702,8 @@ Init_ossl_ssl(void)
* renegotiation entirely. You may use a callback as follows to implement
* this feature:
*
- * num_handshakes = 0
* ctx.renegotiation_cb = lambda do |ssl|
- * num_handshakes += 1
- * raise RuntimeError.new("Client renegotiation disabled") if num_handshakes > 1
+ * raise RuntimeError, "Client renegotiation disabled"
* end
*/
rb_attr(cSSLContext, rb_intern("renegotiation_cb"), 1, 1, Qfalse);
@@ -2712,6 +2784,7 @@ Init_ossl_ssl(void)
rb_define_method(cSSLContext, "enable_fallback_scsv", ossl_sslctx_enable_fallback_scsv, 0);
#endif
rb_define_method(cSSLContext, "add_certificate", ossl_sslctx_add_certificate, -1);
+ rb_define_method(cSSLContext, "add_certificate_chain_file", ossl_sslctx_add_certificate_chain_file, 1);
rb_define_method(cSSLContext, "setup", ossl_sslctx_setup, 0);
rb_define_alias(cSSLContext, "freeze", "setup");
@@ -2809,6 +2882,8 @@ Init_ossl_ssl(void)
rb_define_method(cSSLSocket, "client_ca", ossl_ssl_get_client_ca_list, 0);
/* #hostname is defined in lib/openssl/ssl.rb */
rb_define_method(cSSLSocket, "hostname=", ossl_ssl_set_hostname, 1);
+ rb_define_method(cSSLSocket, "finished_message", ossl_ssl_get_finished, 0);
+ rb_define_method(cSSLSocket, "peer_finished_message", ossl_ssl_get_peer_finished, 0);
# ifdef HAVE_SSL_GET_SERVER_TMP_KEY
rb_define_method(cSSLSocket, "tmp_key", ossl_ssl_tmp_key, 0);
# endif
diff --git a/ext/openssl/ossl_ts.c b/ext/openssl/ossl_ts.c
new file mode 100644
index 00000000000..24f5289cdbc
--- /dev/null
+++ b/ext/openssl/ossl_ts.c
@@ -0,0 +1,1519 @@
+/*
+ *
+ * Copyright (C) 2010 Martin Bosslet <Martin.Bosslet@googlemail.com>
+ * All rights reserved.
+ */
+/*
+ * This program is licenced under the same licence as Ruby.
+ * (See the file 'LICENCE'.)
+ */
+#include "ossl.h"
+
+#ifndef OPENSSL_NO_TS
+
+#define NewTSRequest(klass) \
+ TypedData_Wrap_Struct((klass), &ossl_ts_req_type, 0)
+#define SetTSRequest(obj, req) do { \
+ if (!(req)) { \
+ ossl_raise(rb_eRuntimeError, "TS_REQ wasn't initialized."); \
+ } \
+ RTYPEDDATA_DATA(obj) = (req); \
+} while (0)
+#define GetTSRequest(obj, req) do { \
+ TypedData_Get_Struct((obj), TS_REQ, &ossl_ts_req_type, (req)); \
+ if (!(req)) { \
+ ossl_raise(rb_eRuntimeError, "TS_REQ wasn't initialized."); \
+ } \
+} while (0)
+
+#define NewTSResponse(klass) \
+ TypedData_Wrap_Struct((klass), &ossl_ts_resp_type, 0)
+#define SetTSResponse(obj, resp) do { \
+ if (!(resp)) { \
+ ossl_raise(rb_eRuntimeError, "TS_RESP wasn't initialized."); \
+ } \
+ RTYPEDDATA_DATA(obj) = (resp); \
+} while (0)
+#define GetTSResponse(obj, resp) do { \
+ TypedData_Get_Struct((obj), TS_RESP, &ossl_ts_resp_type, (resp)); \
+ if (!(resp)) { \
+ ossl_raise(rb_eRuntimeError, "TS_RESP wasn't initialized."); \
+ } \
+} while (0)
+
+#define NewTSTokenInfo(klass) \
+ TypedData_Wrap_Struct((klass), &ossl_ts_token_info_type, 0)
+#define SetTSTokenInfo(obj, info) do { \
+ if (!(info)) { \
+ ossl_raise(rb_eRuntimeError, "TS_TST_INFO wasn't initialized."); \
+ } \
+ RTYPEDDATA_DATA(obj) = (info); \
+} while (0)
+#define GetTSTokenInfo(obj, info) do { \
+ TypedData_Get_Struct((obj), TS_TST_INFO, &ossl_ts_token_info_type, (info)); \
+ if (!(info)) { \
+ ossl_raise(rb_eRuntimeError, "TS_TST_INFO wasn't initialized."); \
+ } \
+} while (0)
+
+#define ossl_tsfac_get_default_policy_id(o) rb_attr_get((o),rb_intern("@default_policy_id"))
+#define ossl_tsfac_get_serial_number(o) rb_attr_get((o),rb_intern("@serial_number"))
+#define ossl_tsfac_get_gen_time(o) rb_attr_get((o),rb_intern("@gen_time"))
+#define ossl_tsfac_get_additional_certs(o) rb_attr_get((o),rb_intern("@additional_certs"))
+#define ossl_tsfac_get_allowed_digests(o) rb_attr_get((o),rb_intern("@allowed_digests"))
+
+static VALUE mTimestamp;
+static VALUE eTimestampError;
+static VALUE cTimestampRequest;
+static VALUE cTimestampResponse;
+static VALUE cTimestampTokenInfo;
+static VALUE cTimestampFactory;
+static ID sBAD_ALG, sBAD_REQUEST, sBAD_DATA_FORMAT, sTIME_NOT_AVAILABLE;
+static ID sUNACCEPTED_POLICY, sUNACCEPTED_EXTENSION, sADD_INFO_NOT_AVAILABLE;
+static ID sSYSTEM_FAILURE;
+
+static void
+ossl_ts_req_free(void *ptr)
+{
+ TS_REQ_free(ptr);
+}
+
+static const rb_data_type_t ossl_ts_req_type = {
+ "OpenSSL/Timestamp/Request",
+ {
+ 0, ossl_ts_req_free,
+ },
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+};
+
+static void
+ossl_ts_resp_free(void *ptr)
+{
+ TS_RESP_free(ptr);
+}
+
+static const rb_data_type_t ossl_ts_resp_type = {
+ "OpenSSL/Timestamp/Response",
+ {
+ 0, ossl_ts_resp_free,
+ },
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+};
+
+static void
+ossl_ts_token_info_free(void *ptr)
+{
+ TS_TST_INFO_free(ptr);
+}
+
+static const rb_data_type_t ossl_ts_token_info_type = {
+ "OpenSSL/Timestamp/TokenInfo",
+ {
+ 0, ossl_ts_token_info_free,
+ },
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+};
+
+static VALUE
+asn1_to_der(void *template, int (*i2d)(void *template, unsigned char **pp))
+{
+ VALUE str;
+ int len;
+ unsigned char *p;
+
+ if((len = i2d(template, NULL)) <= 0)
+ ossl_raise(eTimestampError, "Error when encoding to DER");
+ str = rb_str_new(0, len);
+ p = (unsigned char *)RSTRING_PTR(str);
+ if(i2d(template, &p) <= 0)
+ ossl_raise(eTimestampError, "Error when encoding to DER");
+ rb_str_set_len(str, p - (unsigned char*)RSTRING_PTR(str));
+
+ return str;
+}
+
+static ASN1_OBJECT*
+obj_to_asn1obj(VALUE obj)
+{
+ ASN1_OBJECT *a1obj;
+
+ StringValue(obj);
+ a1obj = OBJ_txt2obj(RSTRING_PTR(obj), 0);
+ if(!a1obj) a1obj = OBJ_txt2obj(RSTRING_PTR(obj), 1);
+ if(!a1obj) ossl_raise(eASN1Error, "invalid OBJECT ID");
+
+ return a1obj;
+}
+
+static VALUE
+get_asn1obj(ASN1_OBJECT *obj)
+{
+ BIO *out;
+ VALUE ret;
+ int nid;
+ if ((nid = OBJ_obj2nid(obj)) != NID_undef)
+ ret = rb_str_new2(OBJ_nid2sn(nid));
+ else{
+ if (!(out = BIO_new(BIO_s_mem())))
+ ossl_raise(eX509AttrError, NULL);
+ i2a_ASN1_OBJECT(out, obj);
+ ret = ossl_membio2str(out);
+ }
+
+ return ret;
+}
+
+static VALUE
+ossl_ts_req_alloc(VALUE klass)
+{
+ TS_REQ *req;
+ VALUE obj;
+
+ obj = NewTSRequest(klass);
+ if (!(req = TS_REQ_new()))
+ ossl_raise(eTimestampError, NULL);
+ SetTSRequest(obj, req);
+
+ /* Defaults */
+ TS_REQ_set_version(req, 1);
+ TS_REQ_set_cert_req(req, 1);
+
+ return obj;
+}
+
+/*
+ * When creating a Request with the +File+ or +string+ parameter, the
+ * corresponding +File+ or +string+ must be DER-encoded.
+ *
+ * call-seq:
+ * OpenSSL::Timestamp::Request.new(file) -> request
+ * OpenSSL::Timestamp::Request.new(string) -> request
+ * OpenSSL::Timestamp::Request.new -> empty request
+ */
+static VALUE
+ossl_ts_req_initialize(int argc, VALUE *argv, VALUE self)
+{
+ TS_REQ *ts_req = DATA_PTR(self);
+ BIO *in;
+ VALUE arg;
+
+ if(rb_scan_args(argc, argv, "01", &arg) == 0) {
+ return self;
+ }
+
+ arg = ossl_to_der_if_possible(arg);
+ in = ossl_obj2bio(&arg);
+ ts_req = d2i_TS_REQ_bio(in, &ts_req);
+ BIO_free(in);
+ if (!ts_req)
+ ossl_raise(eTimestampError, "Error when decoding the timestamp request");
+ DATA_PTR(self) = ts_req;
+
+ return self;
+}
+
+/*
+ * Returns the 'short name' of the object identifier that represents the
+ * algorithm that was used to create the message imprint digest.
+ *
+ * call-seq:
+ * request.get_algorithm -> string or nil
+ */
+static VALUE
+ossl_ts_req_get_algorithm(VALUE self)
+{
+ TS_REQ *req;
+ TS_MSG_IMPRINT *mi;
+ X509_ALGOR *algor;
+ int algi;
+
+ GetTSRequest(self, req);
+ mi = TS_REQ_get_msg_imprint(req);
+ algor = TS_MSG_IMPRINT_get_algo(mi);
+ algi = OBJ_obj2nid(algor->algorithm);
+ if (algi == NID_undef || algi == NID_ccitt)
+ return Qnil;
+
+ return get_asn1obj(algor->algorithm);
+}
+
+/*
+ * Allows to set the object identifier or the 'short name' of the
+ * algorithm that was used to create the message imprint digest.
+ *
+ * ===Example:
+ * request.algorithm = "SHA1"
+ *
+ * call-seq:
+ * request.algorithm = "string" -> string
+ */
+static VALUE
+ossl_ts_req_set_algorithm(VALUE self, VALUE algo)
+{
+ TS_REQ *req;
+ TS_MSG_IMPRINT *mi;
+ ASN1_OBJECT *obj;
+ X509_ALGOR *algor;
+
+ GetTSRequest(self, req);
+ obj = obj_to_asn1obj(algo);
+ mi = TS_REQ_get_msg_imprint(req);
+ algor = TS_MSG_IMPRINT_get_algo(mi);
+ if (!X509_ALGOR_set0(algor, obj, V_ASN1_NULL, NULL)) {
+ ASN1_OBJECT_free(obj);
+ ossl_raise(eTimestampError, "X509_ALGOR_set0");
+ }
+
+ return algo;
+}
+
+/*
+ * Returns the message imprint (digest) of the data to be timestamped.
+ *
+ * call-seq:
+ * request.message_imprint -> string or nil
+ */
+static VALUE
+ossl_ts_req_get_msg_imprint(VALUE self)
+{
+ TS_REQ *req;
+ TS_MSG_IMPRINT *mi;
+ ASN1_OCTET_STRING *hashed_msg;
+ VALUE ret;
+
+ GetTSRequest(self, req);
+ mi = TS_REQ_get_msg_imprint(req);
+ hashed_msg = TS_MSG_IMPRINT_get_msg(mi);
+
+ ret = rb_str_new((const char *)hashed_msg->data, hashed_msg->length);
+
+ return ret;
+}
+
+/*
+ * Set the message imprint digest.
+ *
+ * call-seq:
+ * request.message_imprint = "string" -> string
+ */
+static VALUE
+ossl_ts_req_set_msg_imprint(VALUE self, VALUE hash)
+{
+ TS_REQ *req;
+ TS_MSG_IMPRINT *mi;
+ StringValue(hash);
+
+ GetTSRequest(self, req);
+ mi = TS_REQ_get_msg_imprint(req);
+ if (!TS_MSG_IMPRINT_set_msg(mi, (unsigned char *)RSTRING_PTR(hash), RSTRING_LENINT(hash)))
+ ossl_raise(eTimestampError, "TS_MSG_IMPRINT_set_msg");
+
+ return hash;
+}
+
+/*
+ * Returns the version of this request. +1+ is the default value.
+ *
+ * call-seq:
+ * request.version -> Integer
+ */
+static VALUE
+ossl_ts_req_get_version(VALUE self)
+{
+ TS_REQ *req;
+
+ GetTSRequest(self, req);
+ return LONG2NUM(TS_REQ_get_version(req));
+}
+
+/*
+ * Sets the version number for this Request. This should be +1+ for compliant
+ * servers.
+ *
+ * call-seq:
+ * request.version = number -> Integer
+ */
+static VALUE
+ossl_ts_req_set_version(VALUE self, VALUE version)
+{
+ TS_REQ *req;
+ long ver;
+
+ if ((ver = NUM2LONG(version)) < 0)
+ ossl_raise(eTimestampError, "version must be >= 0!");
+ GetTSRequest(self, req);
+ if (!TS_REQ_set_version(req, ver))
+ ossl_raise(eTimestampError, "TS_REQ_set_version");
+
+ return version;
+}
+
+/*
+ * Returns the 'short name' of the object identifier that represents the
+ * timestamp policy under which the server shall create the timestamp.
+ *
+ * call-seq:
+ * request.policy_id -> string or nil
+ */
+static VALUE
+ossl_ts_req_get_policy_id(VALUE self)
+{
+ TS_REQ *req;
+
+ GetTSRequest(self, req);
+ if (!TS_REQ_get_policy_id(req))
+ return Qnil;
+ return get_asn1obj(TS_REQ_get_policy_id(req));
+}
+
+/*
+ * Allows to set the object identifier that represents the
+ * timestamp policy under which the server shall create the timestamp. This
+ * may be left +nil+, implying that the timestamp server will issue the
+ * timestamp using some default policy.
+ *
+ * ===Example:
+ * request.policy_id = "1.2.3.4.5"
+ *
+ * call-seq:
+ * request.policy_id = "string" -> string
+ */
+static VALUE
+ossl_ts_req_set_policy_id(VALUE self, VALUE oid)
+{
+ TS_REQ *req;
+ ASN1_OBJECT *obj;
+ int ok;
+
+ GetTSRequest(self, req);
+ obj = obj_to_asn1obj(oid);
+ ok = TS_REQ_set_policy_id(req, obj);
+ ASN1_OBJECT_free(obj);
+ if (!ok)
+ ossl_raise(eTimestampError, "TS_REQ_set_policy_id");
+
+ return oid;
+}
+
+/*
+ * Returns the nonce (number used once) that the server shall include in its
+ * response.
+ *
+ * call-seq:
+ * request.nonce -> BN or nil
+ */
+static VALUE
+ossl_ts_req_get_nonce(VALUE self)
+{
+ TS_REQ *req;
+ const ASN1_INTEGER * nonce;
+
+ GetTSRequest(self, req);
+ if (!(nonce = TS_REQ_get_nonce(req)))
+ return Qnil;
+ return asn1integer_to_num(nonce);
+}
+
+/*
+ * Sets the nonce (number used once) that the server shall include in its
+ * response. If the nonce is set, the server must return the same nonce value in
+ * a valid Response.
+ *
+ * call-seq:
+ * request.nonce = number -> BN
+ */
+static VALUE
+ossl_ts_req_set_nonce(VALUE self, VALUE num)
+{
+ TS_REQ *req;
+ ASN1_INTEGER *nonce;
+ int ok;
+
+ GetTSRequest(self, req);
+ nonce = num_to_asn1integer(num, NULL);
+ ok = TS_REQ_set_nonce(req, nonce);
+ ASN1_INTEGER_free(nonce);
+ if (!ok)
+ ossl_raise(eTimestampError, NULL);
+ return num;
+}
+
+/*
+ * Indicates whether the response shall contain the timestamp authority's
+ * certificate or not.
+ *
+ * call-seq:
+ * request.cert_requested? -> true or false
+ */
+static VALUE
+ossl_ts_req_get_cert_requested(VALUE self)
+{
+ TS_REQ *req;
+
+ GetTSRequest(self, req);
+ return TS_REQ_get_cert_req(req) ? Qtrue: Qfalse;
+}
+
+/*
+ * Specify whether the response shall contain the timestamp authority's
+ * certificate or not. The default value is +true+.
+ *
+ * call-seq:
+ * request.cert_requested = boolean -> true or false
+ */
+static VALUE
+ossl_ts_req_set_cert_requested(VALUE self, VALUE requested)
+{
+ TS_REQ *req;
+
+ GetTSRequest(self, req);
+ TS_REQ_set_cert_req(req, RTEST(requested));
+
+ return requested;
+}
+
+/*
+ * DER-encodes this Request.
+ *
+ * call-seq:
+ * request.to_der -> DER-encoded string
+ */
+static VALUE
+ossl_ts_req_to_der(VALUE self)
+{
+ TS_REQ *req;
+ TS_MSG_IMPRINT *mi;
+ X509_ALGOR *algo;
+ ASN1_OCTET_STRING *hashed_msg;
+
+ GetTSRequest(self, req);
+ mi = TS_REQ_get_msg_imprint(req);
+
+ algo = TS_MSG_IMPRINT_get_algo(mi);
+ if (OBJ_obj2nid(algo->algorithm) == NID_undef)
+ ossl_raise(eTimestampError, "Message imprint missing algorithm");
+
+ hashed_msg = TS_MSG_IMPRINT_get_msg(mi);
+ if (!hashed_msg->length)
+ ossl_raise(eTimestampError, "Message imprint missing hashed message");
+
+ return asn1_to_der((void *)req, (int (*)(void *, unsigned char **))i2d_TS_REQ);
+}
+
+static VALUE
+ossl_ts_resp_alloc(VALUE klass)
+{
+ TS_RESP *resp;
+ VALUE obj;
+
+ obj = NewTSResponse(klass);
+ if (!(resp = TS_RESP_new()))
+ ossl_raise(eTimestampError, NULL);
+ SetTSResponse(obj, resp);
+
+ return obj;
+}
+
+/*
+ * Creates a Response from a +File+ or +string+ parameter, the
+ * corresponding +File+ or +string+ must be DER-encoded. Please note
+ * that Response is an immutable read-only class. If you'd like to create
+ * timestamps please refer to Factory instead.
+ *
+ * call-seq:
+ * OpenSSL::Timestamp::Response.new(file) -> response
+ * OpenSSL::Timestamp::Response.new(string) -> response
+ */
+static VALUE
+ossl_ts_resp_initialize(VALUE self, VALUE der)
+{
+ TS_RESP *ts_resp = DATA_PTR(self);
+ BIO *in;
+
+ der = ossl_to_der_if_possible(der);
+ in = ossl_obj2bio(&der);
+ ts_resp = d2i_TS_RESP_bio(in, &ts_resp);
+ BIO_free(in);
+ if (!ts_resp)
+ ossl_raise(eTimestampError, "Error when decoding the timestamp response");
+ DATA_PTR(self) = ts_resp;
+
+ return self;
+}
+
+/*
+ * Returns one of GRANTED, GRANTED_WITH_MODS, REJECTION, WAITING,
+ * REVOCATION_WARNING or REVOCATION_NOTIFICATION. A timestamp token has
+ * been created only in case +status+ is equal to GRANTED or GRANTED_WITH_MODS.
+ *
+ * call-seq:
+ * response.status -> BN (never nil)
+ */
+static VALUE
+ossl_ts_resp_get_status(VALUE self)
+{
+ TS_RESP *resp;
+ TS_STATUS_INFO *si;
+ const ASN1_INTEGER *st;
+
+ GetTSResponse(self, resp);
+ si = TS_RESP_get_status_info(resp);
+ st = TS_STATUS_INFO_get0_status(si);
+
+ return asn1integer_to_num(st);
+}
+
+/*
+ * In cases no timestamp token has been created, this field contains further
+ * info about the reason why response creation failed. The method returns either
+ * nil (the request was successful and a timestamp token was created) or one of
+ * the following:
+ * * :BAD_ALG - Indicates that the timestamp server rejects the message
+ * imprint algorithm used in the Request
+ * * :BAD_REQUEST - Indicates that the timestamp server was not able to process
+ * the Request properly
+ * * :BAD_DATA_FORMAT - Indicates that the timestamp server was not able to
+ * parse certain data in the Request
+ * * :TIME_NOT_AVAILABLE - Indicates that the server could not access its time
+ * source
+ * * :UNACCEPTED_POLICY - Indicates that the requested policy identifier is not
+ * recognized or supported by the timestamp server
+ * * :UNACCEPTED_EXTENSIION - Indicates that an extension in the Request is
+ * not supported by the timestamp server
+ * * :ADD_INFO_NOT_AVAILABLE -Indicates that additional information requested
+ * is either not understood or currently not available
+ * * :SYSTEM_FAILURE - Timestamp creation failed due to an internal error that
+ * occurred on the timestamp server
+ *
+ * call-seq:
+ * response.failure_info -> nil or symbol
+ */
+static VALUE
+ossl_ts_resp_get_failure_info(VALUE self)
+{
+ TS_RESP *resp;
+ TS_STATUS_INFO *si;
+
+ /* The ASN1_BIT_STRING_get_bit changed from 1.0.0. to 1.1.0, making this
+ * const. */
+ #if defined(HAVE_TS_STATUS_INFO_GET0_FAILURE_INFO)
+ const ASN1_BIT_STRING *fi;
+ #else
+ ASN1_BIT_STRING *fi;
+ #endif
+
+ GetTSResponse(self, resp);
+ si = TS_RESP_get_status_info(resp);
+ fi = TS_STATUS_INFO_get0_failure_info(si);
+ if (!fi)
+ return Qnil;
+ if (ASN1_BIT_STRING_get_bit(fi, TS_INFO_BAD_ALG))
+ return sBAD_ALG;
+ if (ASN1_BIT_STRING_get_bit(fi, TS_INFO_BAD_REQUEST))
+ return sBAD_REQUEST;
+ if (ASN1_BIT_STRING_get_bit(fi, TS_INFO_BAD_DATA_FORMAT))
+ return sBAD_DATA_FORMAT;
+ if (ASN1_BIT_STRING_get_bit(fi, TS_INFO_TIME_NOT_AVAILABLE))
+ return sTIME_NOT_AVAILABLE;
+ if (ASN1_BIT_STRING_get_bit(fi, TS_INFO_UNACCEPTED_POLICY))
+ return sUNACCEPTED_POLICY;
+ if (ASN1_BIT_STRING_get_bit(fi, TS_INFO_UNACCEPTED_EXTENSION))
+ return sUNACCEPTED_EXTENSION;
+ if (ASN1_BIT_STRING_get_bit(fi, TS_INFO_ADD_INFO_NOT_AVAILABLE))
+ return sADD_INFO_NOT_AVAILABLE;
+ if (ASN1_BIT_STRING_get_bit(fi, TS_INFO_SYSTEM_FAILURE))
+ return sSYSTEM_FAILURE;
+
+ ossl_raise(eTimestampError, "Unrecognized failure info.");
+}
+
+/*
+ * In cases of failure this field may contain an array of strings further
+ * describing the origin of the failure.
+ *
+ * call-seq:
+ * response.status_text -> Array of strings or nil
+ */
+static VALUE
+ossl_ts_resp_get_status_text(VALUE self)
+{
+ TS_RESP *resp;
+ TS_STATUS_INFO *si;
+ const STACK_OF(ASN1_UTF8STRING) *text;
+ ASN1_UTF8STRING *current;
+ int i;
+ VALUE ret = rb_ary_new();
+
+ GetTSResponse(self, resp);
+ si = TS_RESP_get_status_info(resp);
+ if ((text = TS_STATUS_INFO_get0_text(si))) {
+ for (i = 0; i < sk_ASN1_UTF8STRING_num(text); i++) {
+ current = sk_ASN1_UTF8STRING_value(text, i);
+ rb_ary_push(ret, asn1str_to_str(current));
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * If a timestamp token is present, this returns it in the form of a
+ * OpenSSL::PKCS7.
+ *
+ * call-seq:
+ * response.token -> nil or OpenSSL::PKCS7
+ */
+static VALUE
+ossl_ts_resp_get_token(VALUE self)
+{
+ TS_RESP *resp;
+ PKCS7 *p7, *copy;
+ VALUE obj;
+
+ GetTSResponse(self, resp);
+ if (!(p7 = TS_RESP_get_token(resp)))
+ return Qnil;
+
+ obj = NewPKCS7(cPKCS7);
+
+ if (!(copy = PKCS7_dup(p7)))
+ ossl_raise(eTimestampError, NULL);
+
+ SetPKCS7(obj, copy);
+
+ return obj;
+}
+
+/*
+ * Get the response's token info if present.
+ *
+ * call-seq:
+ * response.token_info -> nil or OpenSSL::Timestamp::TokenInfo
+ */
+static VALUE
+ossl_ts_resp_get_token_info(VALUE self)
+{
+ TS_RESP *resp;
+ TS_TST_INFO *info, *copy;
+ VALUE obj;
+
+ GetTSResponse(self, resp);
+ if (!(info = TS_RESP_get_tst_info(resp)))
+ return Qnil;
+
+ obj = NewTSTokenInfo(cTimestampTokenInfo);
+
+ if (!(copy = TS_TST_INFO_dup(info)))
+ ossl_raise(eTimestampError, NULL);
+
+ SetTSTokenInfo(obj, copy);
+
+ return obj;
+}
+
+/*
+ * If the Request specified to request the TSA certificate
+ * (Request#cert_requested = true), then this field contains the
+ * certificate of the timestamp authority.
+ *
+ * call-seq:
+ * response.tsa_certificate -> OpenSSL::X509::Certificate or nil
+ */
+static VALUE
+ossl_ts_resp_get_tsa_certificate(VALUE self)
+{
+ TS_RESP *resp;
+ PKCS7 *p7;
+ PKCS7_SIGNER_INFO *ts_info;
+ X509 *cert;
+
+ GetTSResponse(self, resp);
+ if (!(p7 = TS_RESP_get_token(resp)))
+ return Qnil;
+ ts_info = sk_PKCS7_SIGNER_INFO_value(p7->d.sign->signer_info, 0);
+ cert = PKCS7_cert_from_signer_info(p7, ts_info);
+ if (!cert)
+ return Qnil;
+ return ossl_x509_new(cert);
+}
+
+/*
+ * Returns the Response in DER-encoded form.
+ *
+ * call-seq:
+ * response.to_der -> string
+ */
+static VALUE
+ossl_ts_resp_to_der(VALUE self)
+{
+ TS_RESP *resp;
+
+ GetTSResponse(self, resp);
+ return asn1_to_der((void *)resp, (int (*)(void *, unsigned char **))i2d_TS_RESP);
+}
+
+/*
+ * Verifies a timestamp token by checking the signature, validating the
+ * certificate chain implied by tsa_certificate and by checking conformance to
+ * a given Request. Mandatory parameters are the Request associated to this
+ * Response, and an OpenSSL::X509::Store of trusted roots.
+ *
+ * Intermediate certificates can optionally be supplied for creating the
+ * certificate chain. These intermediate certificates must all be
+ * instances of OpenSSL::X509::Certificate.
+ *
+ * If validation fails, several kinds of exceptions can be raised:
+ * * TypeError if types don't fit
+ * * TimestampError if something is wrong with the timestamp token itself, if
+ * it is not conformant to the Request, or if validation of the timestamp
+ * certificate chain fails.
+ *
+ * call-seq:
+ * response.verify(Request, root_store) -> Response
+ * response.verify(Request, root_store, [intermediate_cert]) -> Response
+ */
+static VALUE
+ossl_ts_resp_verify(int argc, VALUE *argv, VALUE self)
+{
+ VALUE ts_req, store, intermediates;
+ TS_RESP *resp;
+ TS_REQ *req;
+ X509_STORE *x509st;
+ TS_VERIFY_CTX *ctx;
+ STACK_OF(X509) *x509inter = NULL;
+ PKCS7* p7;
+ X509 *cert;
+ int status, i, ok;
+
+ rb_scan_args(argc, argv, "21", &ts_req, &store, &intermediates);
+
+ GetTSResponse(self, resp);
+ GetTSRequest(ts_req, req);
+ x509st = GetX509StorePtr(store);
+
+ if (!(ctx = TS_REQ_to_TS_VERIFY_CTX(req, NULL))) {
+ ossl_raise(eTimestampError, "Error when creating the verification context.");
+ }
+
+ if (!NIL_P(intermediates)) {
+ x509inter = ossl_protect_x509_ary2sk(intermediates, &status);
+ if (status) {
+ TS_VERIFY_CTX_free(ctx);
+ rb_jump_tag(status);
+ }
+ } else if (!(x509inter = sk_X509_new_null())) {
+ TS_VERIFY_CTX_free(ctx);
+ ossl_raise(eTimestampError, "sk_X509_new_null");
+ }
+
+ if (!(p7 = TS_RESP_get_token(resp))) {
+ TS_VERIFY_CTX_free(ctx);
+ sk_X509_pop_free(x509inter, X509_free);
+ ossl_raise(eTimestampError, "TS_RESP_get_token");
+ }
+ for (i=0; i < sk_X509_num(p7->d.sign->cert); i++) {
+ cert = sk_X509_value(p7->d.sign->cert, i);
+ if (!sk_X509_push(x509inter, cert)) {
+ sk_X509_pop_free(x509inter, X509_free);
+ TS_VERIFY_CTX_free(ctx);
+ ossl_raise(eTimestampError, "sk_X509_push");
+ }
+ X509_up_ref(cert);
+ }
+
+ TS_VERIFY_CTS_set_certs(ctx, x509inter);
+ TS_VERIFY_CTX_add_flags(ctx, TS_VFY_SIGNATURE);
+ TS_VERIFY_CTX_set_store(ctx, x509st);
+
+ ok = TS_RESP_verify_response(ctx, resp);
+
+ /* WORKAROUND:
+ * X509_STORE can count references, but X509_STORE_free() doesn't check
+ * this. To prevent our X509_STORE from being freed with our
+ * TS_VERIFY_CTX we set the store to NULL first.
+ * Fixed in OpenSSL 1.0.2; bff9ce4db38b (master), 5b4b9ce976fc (1.0.2)
+ */
+ TS_VERIFY_CTX_set_store(ctx, NULL);
+ TS_VERIFY_CTX_free(ctx);
+
+ if (!ok)
+ ossl_raise(eTimestampError, "TS_RESP_verify_response");
+
+ return self;
+}
+
+static VALUE
+ossl_ts_token_info_alloc(VALUE klass)
+{
+ TS_TST_INFO *info;
+ VALUE obj;
+
+ obj = NewTSTokenInfo(klass);
+ if (!(info = TS_TST_INFO_new()))
+ ossl_raise(eTimestampError, NULL);
+ SetTSTokenInfo(obj, info);
+
+ return obj;
+}
+
+/*
+ * Creates a TokenInfo from a +File+ or +string+ parameter, the
+ * corresponding +File+ or +string+ must be DER-encoded. Please note
+ * that TokenInfo is an immutable read-only class. If you'd like to create
+ * timestamps please refer to Factory instead.
+ *
+ * call-seq:
+ * OpenSSL::Timestamp::TokenInfo.new(file) -> token-info
+ * OpenSSL::Timestamp::TokenInfo.new(string) -> token-info
+ */
+static VALUE
+ossl_ts_token_info_initialize(VALUE self, VALUE der)
+{
+ TS_TST_INFO *info = DATA_PTR(self);
+ BIO *in;
+
+ der = ossl_to_der_if_possible(der);
+ in = ossl_obj2bio(&der);
+ info = d2i_TS_TST_INFO_bio(in, &info);
+ BIO_free(in);
+ if (!info)
+ ossl_raise(eTimestampError, "Error when decoding the timestamp token info");
+ DATA_PTR(self) = info;
+
+ return self;
+}
+
+/*
+ * Returns the version number of the token info. With compliant servers,
+ * this value should be +1+ if present. If status is GRANTED or
+ * GRANTED_WITH_MODS.
+ *
+ * call-seq:
+ * token_info.version -> Integer or nil
+ */
+static VALUE
+ossl_ts_token_info_get_version(VALUE self)
+{
+ TS_TST_INFO *info;
+
+ GetTSTokenInfo(self, info);
+ return LONG2NUM(TS_TST_INFO_get_version(info));
+}
+
+/*
+ * Returns the timestamp policy object identifier of the policy this timestamp
+ * was created under. If status is GRANTED or GRANTED_WITH_MODS, this is never
+ * +nil+.
+ *
+ * ===Example:
+ * id = token_info.policy_id
+ * puts id -> "1.2.3.4.5"
+ *
+ * call-seq:
+ * token_info.policy_id -> string or nil
+ */
+static VALUE
+ossl_ts_token_info_get_policy_id(VALUE self)
+{
+ TS_TST_INFO *info;
+
+ GetTSTokenInfo(self, info);
+ return get_asn1obj(TS_TST_INFO_get_policy_id(info));
+}
+
+/*
+ * Returns the 'short name' of the object identifier representing the algorithm
+ * that was used to derive the message imprint digest. For valid timestamps,
+ * this is the same value that was already given in the Request. If status is
+ * GRANTED or GRANTED_WITH_MODS, this is never +nil+.
+ *
+ * ===Example:
+ * algo = token_info.algorithm
+ * puts algo -> "SHA1"
+ *
+ * call-seq:
+ * token_info.algorithm -> string or nil
+ */
+static VALUE
+ossl_ts_token_info_get_algorithm(VALUE self)
+{
+ TS_TST_INFO *info;
+ TS_MSG_IMPRINT *mi;
+ X509_ALGOR *algo;
+
+ GetTSTokenInfo(self, info);
+ mi = TS_TST_INFO_get_msg_imprint(info);
+ algo = TS_MSG_IMPRINT_get_algo(mi);
+ return get_asn1obj(algo->algorithm);
+}
+
+/*
+ * Returns the message imprint digest. For valid timestamps,
+ * this is the same value that was already given in the Request.
+ * If status is GRANTED or GRANTED_WITH_MODS, this is never +nil+.
+ *
+ * ===Example:
+ * mi = token_info.msg_imprint
+ * puts mi -> "DEADBEEF"
+ *
+ * call-seq:
+ * token_info.msg_imprint -> string.
+ */
+static VALUE
+ossl_ts_token_info_get_msg_imprint(VALUE self)
+{
+ TS_TST_INFO *info;
+ TS_MSG_IMPRINT *mi;
+ ASN1_OCTET_STRING *hashed_msg;
+ VALUE ret;
+
+ GetTSTokenInfo(self, info);
+ mi = TS_TST_INFO_get_msg_imprint(info);
+ hashed_msg = TS_MSG_IMPRINT_get_msg(mi);
+ ret = rb_str_new((const char *)hashed_msg->data, hashed_msg->length);
+
+ return ret;
+}
+
+/*
+ * Returns serial number of the timestamp token. This value shall never be the
+ * same for two timestamp tokens issued by a dedicated timestamp authority.
+ * If status is GRANTED or GRANTED_WITH_MODS, this is never +nil+.
+ *
+ * call-seq:
+ * token_info.serial_number -> BN or nil
+ */
+static VALUE
+ossl_ts_token_info_get_serial_number(VALUE self)
+{
+ TS_TST_INFO *info;
+
+ GetTSTokenInfo(self, info);
+ return asn1integer_to_num(TS_TST_INFO_get_serial(info));
+}
+
+/*
+ * Returns time when this timestamp token was created. If status is GRANTED or
+ * GRANTED_WITH_MODS, this is never +nil+.
+ *
+ * call-seq:
+ * token_info.gen_time -> Time
+ */
+static VALUE
+ossl_ts_token_info_get_gen_time(VALUE self)
+{
+ TS_TST_INFO *info;
+
+ GetTSTokenInfo(self, info);
+ return asn1time_to_time(TS_TST_INFO_get_time(info));
+}
+
+/*
+ * If the ordering field is missing, or if the ordering field is present
+ * and set to false, then the genTime field only indicates the time at
+ * which the time-stamp token has been created by the TSA. In such a
+ * case, the ordering of time-stamp tokens issued by the same TSA or
+ * different TSAs is only possible when the difference between the
+ * genTime of the first time-stamp token and the genTime of the second
+ * time-stamp token is greater than the sum of the accuracies of the
+ * genTime for each time-stamp token.
+ *
+ * If the ordering field is present and set to true, every time-stamp
+ * token from the same TSA can always be ordered based on the genTime
+ * field, regardless of the genTime accuracy.
+ *
+ * call-seq:
+ * token_info.ordering -> true, falses or nil
+ */
+static VALUE
+ossl_ts_token_info_get_ordering(VALUE self)
+{
+ TS_TST_INFO *info;
+
+ GetTSTokenInfo(self, info);
+ return TS_TST_INFO_get_ordering(info) ? Qtrue : Qfalse;
+}
+
+/*
+ * If the timestamp token is valid then this field contains the same nonce that
+ * was passed to the timestamp server in the initial Request.
+ *
+ * call-seq:
+ * token_info.nonce -> BN or nil
+ */
+static VALUE
+ossl_ts_token_info_get_nonce(VALUE self)
+{
+ TS_TST_INFO *info;
+ const ASN1_INTEGER *nonce;
+
+ GetTSTokenInfo(self, info);
+ if (!(nonce = TS_TST_INFO_get_nonce(info)))
+ return Qnil;
+
+ return asn1integer_to_num(nonce);
+}
+
+/*
+ * Returns the TokenInfo in DER-encoded form.
+ *
+ * call-seq:
+ * token_info.to_der -> string
+ */
+static VALUE
+ossl_ts_token_info_to_der(VALUE self)
+{
+ TS_TST_INFO *info;
+
+ GetTSTokenInfo(self, info);
+ return asn1_to_der((void *)info, (int (*)(void *, unsigned char **))i2d_TS_TST_INFO);
+}
+
+static ASN1_INTEGER *
+ossl_tsfac_serial_cb(struct TS_resp_ctx *ctx, void *data)
+{
+ ASN1_INTEGER **snptr = (ASN1_INTEGER **)data;
+ ASN1_INTEGER *sn = *snptr;
+ *snptr = NULL;
+ return sn;
+}
+
+static int
+ossl_tsfac_time_cb(struct TS_resp_ctx *ctx, void *data, long *sec, long *usec)
+{
+ *sec = *((long *)data);
+ *usec = 0;
+ return 1;
+}
+
+/*
+ * Creates a Response with the help of an OpenSSL::PKey, an
+ * OpenSSL::X509::Certificate and a Request.
+ *
+ * Mandatory parameters for timestamp creation that need to be set in the
+ * Request:
+ *
+ * * Request#algorithm
+ * * Request#message_imprint
+ *
+ * Mandatory parameters that need to be set in the Factory:
+ * * Factory#serial_number
+ * * Factory#gen_time
+ * * Factory#allowed_digests
+ *
+ * In addition one of either Request#policy_id or Factory#default_policy_id
+ * must be set.
+ *
+ * Raises a TimestampError if creation fails, though successfully created error
+ * responses may be returned.
+ *
+ * call-seq:
+ * factory.create_timestamp(key, certificate, request) -> Response
+ */
+static VALUE
+ossl_tsfac_create_ts(VALUE self, VALUE key, VALUE certificate, VALUE request)
+{
+ VALUE serial_number, def_policy_id, gen_time, additional_certs, allowed_digests;
+ VALUE str;
+ STACK_OF(X509) *inter_certs;
+ VALUE tsresp, ret = Qnil;
+ EVP_PKEY *sign_key;
+ X509 *tsa_cert;
+ TS_REQ *req;
+ TS_RESP *response = NULL;
+ TS_RESP_CTX *ctx = NULL;
+ BIO *req_bio;
+ ASN1_INTEGER *asn1_serial = NULL;
+ ASN1_OBJECT *def_policy_id_obj = NULL;
+ long lgen_time;
+ const char * err_msg = NULL;
+ int status = 0;
+
+ tsresp = NewTSResponse(cTimestampResponse);
+ tsa_cert = GetX509CertPtr(certificate);
+ sign_key = GetPrivPKeyPtr(key);
+ GetTSRequest(request, req);
+
+ gen_time = ossl_tsfac_get_gen_time(self);
+ if (!rb_obj_is_instance_of(gen_time, rb_cTime)) {
+ err_msg = "@gen_time must be a Time.";
+ goto end;
+ }
+ lgen_time = NUM2LONG(rb_funcall(gen_time, rb_intern("to_i"), 0));
+
+ serial_number = ossl_tsfac_get_serial_number(self);
+ if (NIL_P(serial_number)) {
+ err_msg = "@serial_number must be set.";
+ goto end;
+ }
+ asn1_serial = num_to_asn1integer(serial_number, NULL);
+
+ def_policy_id = ossl_tsfac_get_default_policy_id(self);
+ if (NIL_P(def_policy_id) && !TS_REQ_get_policy_id(req)) {
+ err_msg = "No policy id in the request and no default policy set";
+ goto end;
+ }
+ if (!NIL_P(def_policy_id) && !TS_REQ_get_policy_id(req)) {
+ def_policy_id_obj = (ASN1_OBJECT*)rb_protect((VALUE (*)(VALUE))obj_to_asn1obj, (VALUE)def_policy_id, &status);
+ if (status)
+ goto end;
+ }
+
+ if (!(ctx = TS_RESP_CTX_new())) {
+ err_msg = "Memory allocation failed.";
+ goto end;
+ }
+
+ TS_RESP_CTX_set_serial_cb(ctx, ossl_tsfac_serial_cb, &asn1_serial);
+ if (!TS_RESP_CTX_set_signer_cert(ctx, tsa_cert)) {
+ err_msg = "Certificate does not contain the timestamping extension";
+ goto end;
+ }
+
+ additional_certs = ossl_tsfac_get_additional_certs(self);
+ if (rb_obj_is_kind_of(additional_certs, rb_cArray)) {
+ inter_certs = ossl_protect_x509_ary2sk(additional_certs, &status);
+ if (status)
+ goto end;
+
+ /* this dups the sk_X509 and ups each cert's ref count */
+ TS_RESP_CTX_set_certs(ctx, inter_certs);
+ sk_X509_pop_free(inter_certs, X509_free);
+ }
+
+ TS_RESP_CTX_set_signer_key(ctx, sign_key);
+ if (!NIL_P(def_policy_id) && !TS_REQ_get_policy_id(req))
+ TS_RESP_CTX_set_def_policy(ctx, def_policy_id_obj);
+ if (TS_REQ_get_policy_id(req))
+ TS_RESP_CTX_set_def_policy(ctx, TS_REQ_get_policy_id(req));
+ TS_RESP_CTX_set_time_cb(ctx, ossl_tsfac_time_cb, &lgen_time);
+
+ allowed_digests = ossl_tsfac_get_allowed_digests(self);
+ if (rb_obj_is_kind_of(allowed_digests, rb_cArray)) {
+ int i;
+ VALUE rbmd;
+ const EVP_MD *md;
+
+ for (i = 0; i < RARRAY_LEN(allowed_digests); i++) {
+ rbmd = rb_ary_entry(allowed_digests, i);
+ md = (const EVP_MD *)rb_protect((VALUE (*)(VALUE))ossl_evp_get_digestbyname, rbmd, &status);
+ if (status)
+ goto end;
+ TS_RESP_CTX_add_md(ctx, md);
+ }
+ }
+
+ str = rb_protect(ossl_to_der, request, &status);
+ if (status)
+ goto end;
+
+ req_bio = (BIO*)rb_protect((VALUE (*)(VALUE))ossl_obj2bio, (VALUE)&str, &status);
+ if (status)
+ goto end;
+
+ response = TS_RESP_create_response(ctx, req_bio);
+ BIO_free(req_bio);
+
+ if (!response) {
+ err_msg = "Error during response generation";
+ goto end;
+ }
+
+ /* bad responses aren't exceptional, but openssl still sets error
+ * information. */
+ ossl_clear_error();
+
+ SetTSResponse(tsresp, response);
+ ret = tsresp;
+
+end:
+ ASN1_INTEGER_free(asn1_serial);
+ ASN1_OBJECT_free(def_policy_id_obj);
+ TS_RESP_CTX_free(ctx);
+ if (err_msg)
+ ossl_raise(eTimestampError, err_msg);
+ if (status)
+ rb_jump_tag(status);
+ return ret;
+}
+
+/*
+ * INIT
+ */
+void
+Init_ossl_ts(void)
+{
+ #if 0
+ mOSSL = rb_define_module("OpenSSL"); /* let rdoc know about mOSSL */
+ #endif
+
+ /*
+ * Possible return value for +Response#failure_info+. Indicates that the
+ * timestamp server rejects the message imprint algorithm used in the
+ * +Request+
+ */
+ sBAD_ALG = rb_intern("BAD_ALG");
+
+ /*
+ * Possible return value for +Response#failure_info+. Indicates that the
+ * timestamp server was not able to process the +Request+ properly.
+ */
+ sBAD_REQUEST = rb_intern("BAD_REQUEST");
+ /*
+ * Possible return value for +Response#failure_info+. Indicates that the
+ * timestamp server was not able to parse certain data in the +Request+.
+ */
+ sBAD_DATA_FORMAT = rb_intern("BAD_DATA_FORMAT");
+
+ sTIME_NOT_AVAILABLE = rb_intern("TIME_NOT_AVAILABLE");
+ sUNACCEPTED_POLICY = rb_intern("UNACCEPTED_POLICY");
+ sUNACCEPTED_EXTENSION = rb_intern("UNACCEPTED_EXTENSION");
+ sADD_INFO_NOT_AVAILABLE = rb_intern("ADD_INFO_NOT_AVAILABLE");
+ sSYSTEM_FAILURE = rb_intern("SYSTEM_FAILURE");
+
+ /* Document-class: OpenSSL::Timestamp
+ * Provides classes and methods to request, create and validate
+ * {RFC3161-compliant}[http://www.ietf.org/rfc/rfc3161.txt] timestamps.
+ * Request may be used to either create requests from scratch or to parse
+ * existing requests that again can be used to request timestamps from a
+ * timestamp server, e.g. via the net/http. The resulting timestamp
+ * response may be parsed using Response.
+ *
+ * Please note that Response is read-only and immutable. To create a
+ * Response, an instance of Factory as well as a valid Request are needed.
+ *
+ * ===Create a Response:
+ * #Assumes ts.p12 is a PKCS#12-compatible file with a private key
+ * #and a certificate that has an extended key usage of 'timeStamping'
+ * p12 = OpenSSL::PKCS12.new(File.open('ts.p12', 'rb'), 'pwd')
+ * md = OpenSSL::Digest::SHA1.new
+ * hash = md.digest(data) #some binary data to be timestamped
+ * req = OpenSSL::Timestamp::Request.new
+ * req.algorithm = 'SHA1'
+ * req.message_imprint = hash
+ * req.policy_id = "1.2.3.4.5"
+ * req.nonce = 42
+ * fac = OpenSSL::Timestamp::Factory.new
+ * fac.gen_time = Time.now
+ * fac.serial_number = 1
+ * timestamp = fac.create_timestamp(p12.key, p12.certificate, req)
+ *
+ * ===Verify a timestamp response:
+ * #Assume we have a timestamp token in a file called ts.der
+ * ts = OpenSSL::Timestamp::Response.new(File.open('ts.der', 'rb')
+ * #Assume we have the Request for this token in a file called req.der
+ * req = OpenSSL::Timestamp::Request.new(File.open('req.der', 'rb')
+ * # Assume the associated root CA certificate is contained in a
+ * # DER-encoded file named root.cer
+ * root = OpenSSL::X509::Certificate.new(File.open('root.cer', 'rb')
+ * # get the necessary intermediate certificates, available in
+ * # DER-encoded form in inter1.cer and inter2.cer
+ * inter1 = OpenSSL::X509::Certificate.new(File.open('inter1.cer', 'rb')
+ * inter2 = OpenSSL::X509::Certificate.new(File.open('inter2.cer', 'rb')
+ * ts.verify(req, root, inter1, inter2) -> ts or raises an exception if validation fails
+ *
+ */
+ mTimestamp = rb_define_module_under(mOSSL, "Timestamp");
+
+ /* Document-class: OpenSSL::Timestamp::TimestampError
+ * Generic exception class of the Timestamp module.
+ */
+ eTimestampError = rb_define_class_under(mTimestamp, "TimestampError", eOSSLError);
+
+ /* Document-class: OpenSSL::Timestamp::Response
+ * Immutable and read-only representation of a timestamp response returned
+ * from a timestamp server after receiving an associated Request. Allows
+ * access to specific information about the response but also allows to
+ * verify the Response.
+ */
+ cTimestampResponse = rb_define_class_under(mTimestamp, "Response", rb_cObject);
+ rb_define_alloc_func(cTimestampResponse, ossl_ts_resp_alloc);
+ rb_define_method(cTimestampResponse, "initialize", ossl_ts_resp_initialize, 1);
+ rb_define_method(cTimestampResponse, "status", ossl_ts_resp_get_status, 0);
+ rb_define_method(cTimestampResponse, "failure_info", ossl_ts_resp_get_failure_info, 0);
+ rb_define_method(cTimestampResponse, "status_text", ossl_ts_resp_get_status_text, 0);
+ rb_define_method(cTimestampResponse, "token", ossl_ts_resp_get_token, 0);
+ rb_define_method(cTimestampResponse, "token_info", ossl_ts_resp_get_token_info, 0);
+ rb_define_method(cTimestampResponse, "tsa_certificate", ossl_ts_resp_get_tsa_certificate, 0);
+ rb_define_method(cTimestampResponse, "to_der", ossl_ts_resp_to_der, 0);
+ rb_define_method(cTimestampResponse, "verify", ossl_ts_resp_verify, -1);
+
+ /* Document-class: OpenSSL::Timestamp::TokenInfo
+ * Immutable and read-only representation of a timestamp token info from a
+ * Response.
+ */
+ cTimestampTokenInfo = rb_define_class_under(mTimestamp, "TokenInfo", rb_cObject);
+ rb_define_alloc_func(cTimestampTokenInfo, ossl_ts_token_info_alloc);
+ rb_define_method(cTimestampTokenInfo, "initialize", ossl_ts_token_info_initialize, 1);
+ rb_define_method(cTimestampTokenInfo, "version", ossl_ts_token_info_get_version, 0);
+ rb_define_method(cTimestampTokenInfo, "policy_id", ossl_ts_token_info_get_policy_id, 0);
+ rb_define_method(cTimestampTokenInfo, "algorithm", ossl_ts_token_info_get_algorithm, 0);
+ rb_define_method(cTimestampTokenInfo, "message_imprint", ossl_ts_token_info_get_msg_imprint, 0);
+ rb_define_method(cTimestampTokenInfo, "serial_number", ossl_ts_token_info_get_serial_number, 0);
+ rb_define_method(cTimestampTokenInfo, "gen_time", ossl_ts_token_info_get_gen_time, 0);
+ rb_define_method(cTimestampTokenInfo, "ordering", ossl_ts_token_info_get_ordering, 0);
+ rb_define_method(cTimestampTokenInfo, "nonce", ossl_ts_token_info_get_nonce, 0);
+ rb_define_method(cTimestampTokenInfo, "to_der", ossl_ts_token_info_to_der, 0);
+
+ /* Document-class: OpenSSL::Timestamp::Request
+ * Allows to create timestamp requests or parse existing ones. A Request is
+ * also needed for creating timestamps from scratch with Factory. When
+ * created from scratch, some default values are set:
+ * * version is set to +1+
+ * * cert_requested is set to +true+
+ * * algorithm, message_imprint, policy_id, and nonce are set to +false+
+ */
+ cTimestampRequest = rb_define_class_under(mTimestamp, "Request", rb_cObject);
+ rb_define_alloc_func(cTimestampRequest, ossl_ts_req_alloc);
+ rb_define_method(cTimestampRequest, "initialize", ossl_ts_req_initialize, -1);
+ rb_define_method(cTimestampRequest, "version=", ossl_ts_req_set_version, 1);
+ rb_define_method(cTimestampRequest, "version", ossl_ts_req_get_version, 0);
+ rb_define_method(cTimestampRequest, "algorithm=", ossl_ts_req_set_algorithm, 1);
+ rb_define_method(cTimestampRequest, "algorithm", ossl_ts_req_get_algorithm, 0);
+ rb_define_method(cTimestampRequest, "message_imprint=", ossl_ts_req_set_msg_imprint, 1);
+ rb_define_method(cTimestampRequest, "message_imprint", ossl_ts_req_get_msg_imprint, 0);
+ rb_define_method(cTimestampRequest, "policy_id=", ossl_ts_req_set_policy_id, 1);
+ rb_define_method(cTimestampRequest, "policy_id", ossl_ts_req_get_policy_id, 0);
+ rb_define_method(cTimestampRequest, "nonce=", ossl_ts_req_set_nonce, 1);
+ rb_define_method(cTimestampRequest, "nonce", ossl_ts_req_get_nonce, 0);
+ rb_define_method(cTimestampRequest, "cert_requested=", ossl_ts_req_set_cert_requested, 1);
+ rb_define_method(cTimestampRequest, "cert_requested?", ossl_ts_req_get_cert_requested, 0);
+ rb_define_method(cTimestampRequest, "to_der", ossl_ts_req_to_der, 0);
+
+ /*
+ * Indicates a successful response. Equal to +0+.
+ */
+ rb_define_const(cTimestampResponse, "GRANTED", INT2NUM(TS_STATUS_GRANTED));
+ /*
+ * Indicates a successful response that probably contains modifications
+ * from the initial request. Equal to +1+.
+ */
+ rb_define_const(cTimestampResponse, "GRANTED_WITH_MODS", INT2NUM(TS_STATUS_GRANTED_WITH_MODS));
+ /*
+ * Indicates a failure. No timestamp token was created. Equal to +2+.
+ */
+ rb_define_const(cTimestampResponse, "REJECTION", INT2NUM(TS_STATUS_REJECTION));
+ /*
+ * Indicates a failure. No timestamp token was created. Equal to +3+.
+ */
+ rb_define_const(cTimestampResponse, "WAITING", INT2NUM(TS_STATUS_WAITING));
+ /*
+ * Indicates a failure. No timestamp token was created. Revocation of a
+ * certificate is imminent. Equal to +4+.
+ */
+ rb_define_const(cTimestampResponse, "REVOCATION_WARNING", INT2NUM(TS_STATUS_REVOCATION_WARNING));
+ /*
+ * Indicates a failure. No timestamp token was created. A certificate
+ * has been revoked. Equal to +5+.
+ */
+ rb_define_const(cTimestampResponse, "REVOCATION_NOTIFICATION", INT2NUM(TS_STATUS_REVOCATION_NOTIFICATION));
+
+ /* Document-class: OpenSSL::Timestamp::Factory
+ *
+ * Used to generate a Response from scratch.
+ *
+ * Please bear in mind that the implementation will always apply and prefer
+ * the policy object identifier given in the request over the default policy
+ * id specified in the Factory. As a consequence, +default_policy_id+ will
+ * only be applied if no Request#policy_id was given. But this also means
+ * that one needs to check the policy identifier in the request manually
+ * before creating the Response, e.g. to check whether it complies to a
+ * specific set of acceptable policies.
+ *
+ * There exists also the possibility to add certificates (instances of
+ * OpenSSL::X509::Certificate) besides the timestamping certificate
+ * that will be included in the resulting timestamp token if
+ * Request#cert_requested? is +true+. Ideally, one would also include any
+ * intermediate certificates (the root certificate can be left out - in
+ * order to trust it any verifying party will have to be in its possession
+ * anyway). This simplifies validation of the timestamp since these
+ * intermediate certificates are "already there" and need not be passed as
+ * external parameters to Response#verify anymore, thus minimizing external
+ * resources needed for verification.
+ *
+ * ===Example: Inclusion of (untrusted) intermediate certificates
+ *
+ * Assume we received a timestamp request that has set Request#policy_id to
+ * +nil+ and Request#cert_requested? to true. The raw request bytes are
+ * stored in a variable called +req_raw+. We'd still like to integrate
+ * the necessary intermediate certificates (in +inter1.cer+ and
+ * +inter2.cer+) to simplify validation of the resulting Response. +ts.p12+
+ * is a PKCS#12-compatible file including the private key and the
+ * timestamping certificate.
+ *
+ * req = OpenSSL::Timestamp::Request.new(raw_bytes)
+ * p12 = OpenSSL::PKCS12.new(File.open('ts.p12', 'rb'), 'pwd')
+ * inter1 = OpenSSL::X509::Certificate.new(File.open('inter1.cer', 'rb')
+ * inter2 = OpenSSL::X509::Certificate.new(File.open('inter2.cer', 'rb')
+ * fac = OpenSSL::Timestamp::Factory.new
+ * fac.gen_time = Time.now
+ * fac.serial_number = 1
+ * fac.allowed_digests = ["sha256", "sha384", "sha512"]
+ * #needed because the Request contained no policy identifier
+ * fac.default_policy_id = '1.2.3.4.5'
+ * fac.additional_certificates = [ inter1, inter2 ]
+ * timestamp = fac.create_timestamp(p12.key, p12.certificate, req)
+ *
+ * ==Attributes
+ *
+ * ===default_policy_id
+ *
+ * Request#policy_id will always be preferred over this if present in the
+ * Request, only if Request#policy_id is nil default_policy will be used.
+ * If none of both is present, a TimestampError will be raised when trying
+ * to create a Response.
+ *
+ * call-seq:
+ * factory.default_policy_id = "string" -> string
+ * factory.default_policy_id -> string or nil
+ *
+ * ===serial_number
+ *
+ * Sets or retrieves the serial number to be used for timestamp creation.
+ * Must be present for timestamp creation.
+ *
+ * call-seq:
+ * factory.serial_number = number -> number
+ * factory.serial_number -> number or nil
+ *
+ * ===gen_time
+ *
+ * Sets or retrieves the Time value to be used in the Response. Must be
+ * present for timestamp creation.
+ *
+ * call-seq:
+ * factory.gen_time = Time -> Time
+ * factory.gen_time -> Time or nil
+ *
+ * ===additional_certs
+ *
+ * Sets or retrieves additional certificates apart from the timestamp
+ * certificate (e.g. intermediate certificates) to be added to the Response.
+ * Must be an Array of OpenSSL::X509::Certificate.
+ *
+ * call-seq:
+ * factory.additional_certs = [cert1, cert2] -> [ cert1, cert2 ]
+ * factory.additional_certs -> array or nil
+ *
+ * ===allowed_digests
+ *
+ * Sets or retrieves the digest algorithms that the factory is allowed
+ * create timestamps for. Known vulnerable or weak algorithms should not be
+ * allowed where possible.
+ * Must be an Array of String or OpenSSL::Digest subclass instances.
+ *
+ * call-seq:
+ * factory.allowed_digests = ["sha1", OpenSSL::Digest::SHA256.new] -> [ "sha1", OpenSSL::Digest::SHA256.new ]
+ * factory.allowed_digests -> array or nil
+ *
+ */
+ cTimestampFactory = rb_define_class_under(mTimestamp, "Factory", rb_cObject);
+ rb_attr(cTimestampFactory, rb_intern("allowed_digests"), 1, 1, 0);
+ rb_attr(cTimestampFactory, rb_intern("default_policy_id"), 1, 1, 0);
+ rb_attr(cTimestampFactory, rb_intern("serial_number"), 1, 1, 0);
+ rb_attr(cTimestampFactory, rb_intern("gen_time"), 1, 1, 0);
+ rb_attr(cTimestampFactory, rb_intern("additional_certs"), 1, 1, 0);
+ rb_define_method(cTimestampFactory, "create_timestamp", ossl_tsfac_create_ts, 3);
+}
+
+#endif
diff --git a/ext/openssl/ossl_ts.h b/ext/openssl/ossl_ts.h
new file mode 100644
index 00000000000..25fb0e1d645
--- /dev/null
+++ b/ext/openssl/ossl_ts.h
@@ -0,0 +1,16 @@
+/*
+ *
+ * Copyright (C) 2010 Martin Bosslet <Martin.Bosslet@googlemail.com>
+ * All rights reserved.
+ */
+/*
+ * This program is licenced under the same licence as Ruby.
+ * (See the file 'LICENCE'.)
+ */
+
+#if !defined(_OSSL_TS_H_)
+#define _OSSL_TS_H_
+
+void Init_ossl_ts(void);
+
+#endif
diff --git a/ext/openssl/ossl_version.h b/ext/openssl/ossl_version.h
deleted file mode 100644
index c162f8c2a82..00000000000
--- a/ext/openssl/ossl_version.h
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
- * 'OpenSSL for Ruby' project
- * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz>
- * All rights reserved.
- */
-/*
- * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
- */
-#if !defined(_OSSL_VERSION_H_)
-#define _OSSL_VERSION_H_
-
-#define OSSL_VERSION "2.1.2"
-
-#endif /* _OSSL_VERSION_H_ */
diff --git a/ext/openssl/ossl_x509ext.c b/ext/openssl/ossl_x509ext.c
index 30ec09d7a31..5eb9bd759fc 100644
--- a/ext/openssl/ossl_x509ext.c
+++ b/ext/openssl/ossl_x509ext.c
@@ -403,6 +403,19 @@ ossl_x509ext_get_value(VALUE obj)
}
static VALUE
+ossl_x509ext_get_value_der(VALUE obj)
+{
+ X509_EXTENSION *ext;
+ ASN1_OCTET_STRING *value;
+
+ GetX509Ext(obj, ext);
+ if ((value = X509_EXTENSION_get_data(ext)) == NULL)
+ ossl_raise(eX509ExtError, NULL);
+
+ return rb_str_new((const char *)value->data, value->length);
+}
+
+static VALUE
ossl_x509ext_get_critical(VALUE obj)
{
X509_EXTENSION *ext;
@@ -472,6 +485,7 @@ Init_ossl_x509ext(void)
rb_define_method(cX509Ext, "critical=", ossl_x509ext_set_critical, 1);
rb_define_method(cX509Ext, "oid", ossl_x509ext_get_oid, 0);
rb_define_method(cX509Ext, "value", ossl_x509ext_get_value, 0);
+ rb_define_method(cX509Ext, "value_der", ossl_x509ext_get_value_der, 0);
rb_define_method(cX509Ext, "critical?", ossl_x509ext_get_critical, 0);
rb_define_method(cX509Ext, "to_der", ossl_x509ext_to_der, 0);
}
diff --git a/ext/openssl/ossl_x509name.c b/ext/openssl/ossl_x509name.c
index 1ea8400dbb9..1522c3d897c 100644
--- a/ext/openssl/ossl_x509name.c
+++ b/ext/openssl/ossl_x509name.c
@@ -387,17 +387,21 @@ ossl_x509name_cmp0(VALUE self, VALUE other)
/*
* call-seq:
- * name.cmp(other) -> -1 | 0 | 1
- * name <=> other -> -1 | 0 | 1
+ * name.cmp(other) -> -1 | 0 | 1 | nil
+ * name <=> other -> -1 | 0 | 1 | nil
*
* Compares this Name with _other_ and returns +0+ if they are the same and +-1+
* or ++1+ if they are greater or less than each other respectively.
+ * Returns +nil+ if they are not comparable (i.e. different types).
*/
static VALUE
ossl_x509name_cmp(VALUE self, VALUE other)
{
int result;
+ if (!rb_obj_is_kind_of(other, cX509Name))
+ return Qnil;
+
result = ossl_x509name_cmp0(self, other);
if (result < 0) return INT2FIX(-1);
if (result > 0) return INT2FIX(1);
@@ -494,7 +498,7 @@ ossl_x509name_to_der(VALUE self)
* You can create a Name by parsing a distinguished name String or by
* supplying the distinguished name as an Array.
*
- * name = OpenSSL::X509::Name.parse 'CN=nobody/DC=example'
+ * name = OpenSSL::X509::Name.parse '/CN=nobody/DC=example'
*
* name = OpenSSL::X509::Name.new [['CN', 'nobody'], ['DC', 'example']]
*/
diff --git a/test/openssl/fixtures/chain/dh512.pem b/test/openssl/fixtures/chain/dh512.pem
new file mode 100644
index 00000000000..fec138c7a96
--- /dev/null
+++ b/test/openssl/fixtures/chain/dh512.pem
@@ -0,0 +1,4 @@
+-----BEGIN DH PARAMETERS-----
+MEYCQQCjDVzTg9C4u43MV0TKDGsBuYdChrPMczr4IYjy+jHQvXm2DDadNNWBIDau
+4zNtwfLCg2gMwOc7t18m4Ten/NOLAgEC
+-----END DH PARAMETERS-----
diff --git a/test/openssl/fixtures/chain/server.crt b/test/openssl/fixtures/chain/server.crt
new file mode 100644
index 00000000000..d6b814f4500
--- /dev/null
+++ b/test/openssl/fixtures/chain/server.crt
@@ -0,0 +1,13 @@
+-----BEGIN CERTIFICATE-----
+MIICATCCAWoCCQDbxIRGgXeWaDANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJO
+WjETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
+cyBQdHkgTHRkMB4XDTE5MDYxMzA1MDU0MloXDTI5MDYxMDA1MDU0MlowRTELMAkG
+A1UEBhMCTloxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0
+IFdpZGdpdHMgUHR5IEx0ZDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA29Vu
+Y6m8pRrsXxUhlK2BX48CDChr8D53SqZozcQI26BCm+05TBnQxKAHOknR3y/ige2U
+2zftSwbSoK/zKUC8o5pKVL+l36anDEnZ6RWc9Z9CvmaCFjlcP4nXZO+yD1Is/jCy
+KqGGC8lQ920VXOCFflJj6AWg88+4C3GLjxJe6bMCAwEAATANBgkqhkiG9w0BAQsF
+AAOBgQCDaqKGBkYxNxnv37vEKp7zi/cov8LvEsZaAD1pcSU+ysBiBes/B7a/Qjcj
+PTZsH/hedn9mVynLkjc7LrztUWngTeW9gk5EB9YSwJdPhwLntV1TdaBlf/tu0n/c
+s7QxaZhFMUyo1Eof28zXVHhs1OEhlSjwJ8lxuC3vBE4F1BjSNQ==
+-----END CERTIFICATE-----
diff --git a/test/openssl/fixtures/chain/server.csr b/test/openssl/fixtures/chain/server.csr
new file mode 100644
index 00000000000..51b38e33f23
--- /dev/null
+++ b/test/openssl/fixtures/chain/server.csr
@@ -0,0 +1,11 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIIBhDCB7gIBADBFMQswCQYDVQQGEwJOWjETMBEGA1UECAwKU29tZS1TdGF0ZTEh
+MB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEB
+AQUAA4GNADCBiQKBgQDb1W5jqbylGuxfFSGUrYFfjwIMKGvwPndKpmjNxAjboEKb
+7TlMGdDEoAc6SdHfL+KB7ZTbN+1LBtKgr/MpQLyjmkpUv6XfpqcMSdnpFZz1n0K+
+ZoIWOVw/iddk77IPUiz+MLIqoYYLyVD3bRVc4IV+UmPoBaDzz7gLcYuPEl7pswID
+AQABoAAwDQYJKoZIhvcNAQELBQADgYEAONaTWYVfyMmd8irCtognRoM4tFF4xvDg
+PTcnHjVb/6oPPMU+mtQVD9qNf8SOdhNuYVTZ61mDLQGeq45CLM5qWjZkqFPHnngf
+ajfZRE7Y3vA8ZaWFvsTJYcU+R3/FRS0XnFYj99+q9Yi3JExSY+arElyAW3tFYlcs
+RWOCk1pT2Yc=
+-----END CERTIFICATE REQUEST-----
diff --git a/test/openssl/fixtures/chain/server.key b/test/openssl/fixtures/chain/server.key
new file mode 100644
index 00000000000..9590235db8f
--- /dev/null
+++ b/test/openssl/fixtures/chain/server.key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQDb1W5jqbylGuxfFSGUrYFfjwIMKGvwPndKpmjNxAjboEKb7TlM
+GdDEoAc6SdHfL+KB7ZTbN+1LBtKgr/MpQLyjmkpUv6XfpqcMSdnpFZz1n0K+ZoIW
+OVw/iddk77IPUiz+MLIqoYYLyVD3bRVc4IV+UmPoBaDzz7gLcYuPEl7pswIDAQAB
+AoGAGO+q5+83ENtu+JIjDwRnanmEV/C13biYO4WI2d5kytTw+VL9bt52yfcFGt2I
+yvJZlTdn7T340svhVIzg3ksTmp1xQk3zh6zR00zQy45kYwY8uyd8Xfh2IsnpByoc
+h2jWVX6LSqi1Iy3RxanHmMYPSMy15otsjwlwnnTAHLnnvzECQQDvw3TL90DucQSD
+S0h6DWAGakaiOMhY/PpFbTsjzw+uG+Up65tpz4QqPbsXfoReeK0CQIuyE/LlYoJl
+VOlIsL6HAkEA6rh4zsWi6KVTGa7qd5x70TEgxeMMAW1qUbak1THxeZTFYnyvucBz
+i+VQvHEVnCadhVpHIwbBNUeOyS5DXjj6dQJAA0Caf/3Noq5jykgmJomx6MReSusM
+RLDB0FlH+Rdg9hKozCXHCOtoto350LrFnuZyKlqnynWc0OHCNQ+uzm6fVwJAbtyW
+YsNCQLPlXhoZsEj+yj10B0NH5lyxfMrRa8jdDtnPqMbPkOJvMMIssfSPimNKvzN2
+qfqEww97R1ZMh3JOCQJBAIIwGHBN5rDGIb4CgR+PLsh8bve1X+gO8UnOYJXa/Uzx
+gAXE0uzHNH6rNSG0V/IQnFYlSHpNJGgcdSl+MZNLldQ=
+-----END RSA PRIVATE KEY-----
diff --git a/test/openssl/test_asn1.rb b/test/openssl/test_asn1.rb
index cc113018046..5f4575516a3 100644
--- a/test/openssl/test_asn1.rb
+++ b/test/openssl/test_asn1.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'utils'
if defined?(OpenSSL)
@@ -167,7 +167,7 @@ class OpenSSL::TestASN1 < OpenSSL::TestCase
assert_equal(OpenSSL::ASN1::OctetString, ext.value[2].class)
extv = OpenSSL::ASN1.decode(ext.value[2].value)
assert_equal(OpenSSL::ASN1::BitString, extv.class)
- str = "\000"; str[0] = 0b00000110.chr
+ str = +"\000"; str[0] = 0b00000110.chr
assert_equal(str, extv.value)
ext = extensions.value[0].value[2] # subjetKeyIdentifier
@@ -332,6 +332,32 @@ class OpenSSL::TestASN1 < OpenSSL::TestCase
pend "OBJ_obj2txt() not working (LibreSSL?)" if $!.message =~ /OBJ_obj2txt/
raise
end
+
+ aki = [
+ OpenSSL::ASN1::ObjectId.new("authorityKeyIdentifier"),
+ OpenSSL::ASN1::ObjectId.new("X509v3 Authority Key Identifier"),
+ OpenSSL::ASN1::ObjectId.new("2.5.29.35")
+ ]
+
+ ski = [
+ OpenSSL::ASN1::ObjectId.new("subjectKeyIdentifier"),
+ OpenSSL::ASN1::ObjectId.new("X509v3 Subject Key Identifier"),
+ OpenSSL::ASN1::ObjectId.new("2.5.29.14")
+ ]
+
+ aki.each do |a|
+ aki.each do |b|
+ assert a == b
+ end
+
+ ski.each do |b|
+ refute a == b
+ end
+ end
+
+ assert_raise(TypeError) {
+ OpenSSL::ASN1::ObjectId.new("authorityKeyIdentifier") == nil
+ }
end
def test_sequence
@@ -635,10 +661,10 @@ class OpenSSL::TestASN1 < OpenSSL::TestCase
assert_equal data, seq.entries
end
- def test_gc_stress
- skip "very time consuming test"
- assert_ruby_status(['--disable-gems', '-eGC.stress=true', '-erequire "openssl.so"'])
- end
+ # Very time consuming test.
+ # def test_gc_stress
+ # assert_ruby_status(['--disable-gems', '-eGC.stress=true', '-erequire "openssl.so"'])
+ # end
private
diff --git a/test/openssl/test_bn.rb b/test/openssl/test_bn.rb
index 0b5cd842418..547d334c649 100644
--- a/test/openssl/test_bn.rb
+++ b/test/openssl/test_bn.rb
@@ -1,5 +1,5 @@
# coding: us-ascii
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'utils'
require "prime"
@@ -15,6 +15,10 @@ class OpenSSL::TestBN < OpenSSL::TestCase
end
def test_new
+ assert_raise(ArgumentError) { OpenSSL::BN.new }
+ assert_raise(ArgumentError) { OpenSSL::BN.new(nil) }
+ assert_raise(ArgumentError) { OpenSSL::BN.new(nil, 2) }
+
assert_equal(@e1, OpenSSL::BN.new("999"))
assert_equal(@e1, OpenSSL::BN.new("999", 10))
assert_equal(@e1, OpenSSL::BN.new("\x03\xE7", 2))
@@ -273,9 +277,9 @@ class OpenSSL::TestBN < OpenSSL::TestCase
assert_instance_of(String, @e1.hash.to_s)
end
- def test_type_error
+ def test_argument_error
bug15760 = '[ruby-core:92231] [Bug #15760]'
- assert_raise(TypeError, bug15760) { OpenSSL::BN.new(nil, 2) }
+ assert_raise(ArgumentError, bug15760) { OpenSSL::BN.new(nil, 2) }
end
end
diff --git a/test/openssl/test_buffering.rb b/test/openssl/test_buffering.rb
index c85a6f020b3..7575c5b4fe4 100644
--- a/test/openssl/test_buffering.rb
+++ b/test/openssl/test_buffering.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'utils'
if defined?(OpenSSL)
@@ -10,7 +10,7 @@ class OpenSSL::TestBuffering < OpenSSL::TestCase
attr_accessor :sync
def initialize
- @io = ""
+ @io = Buffer.new
def @io.sync
true
end
@@ -41,6 +41,13 @@ class OpenSSL::TestBuffering < OpenSSL::TestCase
@io = IO.new
end
+ def test_encoding
+ @io.write '😊'
+ @io.flush
+
+ assert_equal @io.string.encoding, Encoding::BINARY
+ end
+
def test_flush
@io.write 'a'
diff --git a/test/openssl/test_cipher.rb b/test/openssl/test_cipher.rb
index d83fa4ec3d2..f6ec4980872 100644
--- a/test/openssl/test_cipher.rb
+++ b/test/openssl/test_cipher.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'utils'
if defined?(OpenSSL)
@@ -305,6 +305,21 @@ class OpenSSL::TestCipher < OpenSSL::TestCase
}
end
+ def test_crypt_after_key
+ key = ["2b7e151628aed2a6abf7158809cf4f3c"].pack("H*")
+ %w'ecb cbc cfb ctr gcm'.each do |c|
+ cipher = OpenSSL::Cipher.new("aes-128-#{c}")
+ cipher.key = key
+ cipher.encrypt
+ assert_raise(OpenSSL::Cipher::CipherError) { cipher.update("") }
+
+ cipher = OpenSSL::Cipher.new("aes-128-#{c}")
+ cipher.key = key
+ cipher.decrypt
+ assert_raise(OpenSSL::Cipher::CipherError) { cipher.update("") }
+ end
+ end
+
private
def new_encryptor(algo, **kwargs)
diff --git a/test/openssl/test_config.rb b/test/openssl/test_config.rb
index 3606c67d65c..dba66b08023 100644
--- a/test/openssl/test_config.rb
+++ b/test/openssl/test_config.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'utils'
if defined?(OpenSSL)
@@ -61,14 +61,14 @@ foo\\bar::foo\\bar = baz
[default1 default2]\t\t # space is allowed in section name
fo =b ar # space allowed in value
[emptysection]
- [dollar ]
+ [doller ]
foo=bar
bar = $(foo)
baz = 123$(default::bar)456${foo}798
qux = ${baz}
quxx = $qux.$qux
__EOC__
- assert_equal(['default', 'default1 default2', 'dollar', 'emptysection', 'foo', 'foo\\bar'], c.sections.sort)
+ assert_equal(['default', 'default1 default2', 'doller', 'emptysection', 'foo', 'foo\\bar'], c.sections.sort)
assert_equal(['', 'a', 'bar', 'baz', 'd', 'dq', 'dq2', 'esc', 'foo\\bar', 'sq'], c['default'].keys.sort)
assert_equal('c', c['default'][''])
assert_equal('', c['default']['a'])
@@ -84,12 +84,12 @@ __EOC__
assert_equal('baz', c['foo\\bar']['foo\\bar'])
assert_equal('b ar', c['default1 default2']['fo'])
- # dollar
- assert_equal('bar', c['dollar']['foo'])
- assert_equal('bar', c['dollar']['bar'])
- assert_equal('123baz456bar798', c['dollar']['baz'])
- assert_equal('123baz456bar798', c['dollar']['qux'])
- assert_equal('123baz456bar798.123baz456bar798', c['dollar']['quxx'])
+ # dolloer
+ assert_equal('bar', c['doller']['foo'])
+ assert_equal('bar', c['doller']['bar'])
+ assert_equal('123baz456bar798', c['doller']['baz'])
+ assert_equal('123baz456bar798', c['doller']['qux'])
+ assert_equal('123baz456bar798.123baz456bar798', c['doller']['quxx'])
excn = assert_raise(OpenSSL::ConfigError) do
OpenSSL::Config.parse("foo = $bar")
diff --git a/test/openssl/test_digest.rb b/test/openssl/test_digest.rb
index 2cb878b6fa1..e47fc0a3563 100644
--- a/test/openssl/test_digest.rb
+++ b/test/openssl/test_digest.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'utils'
if defined?(OpenSSL)
@@ -53,14 +53,21 @@ class OpenSSL::TestDigest < OpenSSL::TestCase
assert_equal(dig1, dig2, "reset")
end
- def test_digest_constants
- algs = %w(MD4 MD5 RIPEMD160 SHA1 SHA224 SHA256 SHA384 SHA512)
- if !libressl? && !openssl?(1, 1, 0)
- algs += %w(DSS1 SHA)
+ def test_required_digests
+ algorithms = OpenSSL::Digest::ALGORITHMS
+ required = %w{MD4 MD5 RIPEMD160 SHA1 SHA224 SHA256 SHA384 SHA512}
+
+ required.each do |name|
+ assert_include(algorithms, name)
end
- algs.each do |alg|
- assert_not_nil(OpenSSL::Digest.new(alg))
- klass = OpenSSL::Digest.const_get(alg)
+ end
+
+ def test_digest_constants
+ algorithms = OpenSSL::Digest::ALGORITHMS
+
+ algorithms.each do |name|
+ assert_not_nil(OpenSSL::Digest.new(name))
+ klass = OpenSSL::Digest.const_get(name.tr('-', '_'))
assert_not_nil(klass.new)
end
end
@@ -91,6 +98,18 @@ class OpenSSL::TestDigest < OpenSSL::TestCase
assert_equal(sha512_a, encode16(OpenSSL::Digest::SHA512.digest("a")))
end
+ def test_sha3
+ pend "SHA3 is not implemented" unless OpenSSL::Digest.const_defined?(:SHA3_224)
+ s224 = '6b4e03423667dbb73b6e15454f0eb1abd4597f9a1b078e3f5b5a6bc7'
+ s256 = 'a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a'
+ s384 = '0c63a75b845e4f7d01107d852e4c2485c51a50aaaa94fc61995e71bbee983a2ac3713831264adb47fb6bd1e058d5f004'
+ s512 = 'a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a615b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26'
+ assert_equal(OpenSSL::Digest::SHA3_224.hexdigest(""), s224)
+ assert_equal(OpenSSL::Digest::SHA3_256.hexdigest(""), s256)
+ assert_equal(OpenSSL::Digest::SHA3_384.hexdigest(""), s384)
+ assert_equal(OpenSSL::Digest::SHA3_512.hexdigest(""), s512)
+ end
+
def test_digest_by_oid_and_name_sha2
check_digest(OpenSSL::ASN1::ObjectId.new("SHA224"))
check_digest(OpenSSL::ASN1::ObjectId.new("SHA256"))
diff --git a/test/openssl/test_engine.rb b/test/openssl/test_engine.rb
index bb1123d5163..232ba866a5c 100644
--- a/test/openssl/test_engine.rb
+++ b/test/openssl/test_engine.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'utils'
if defined?(OpenSSL) && defined?(OpenSSL::Engine)
diff --git a/test/openssl/test_fips.rb b/test/openssl/test_fips.rb
index a577d7891ea..8cd474f9a32 100644
--- a/test/openssl/test_fips.rb
+++ b/test/openssl/test_fips.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'utils'
if defined?(OpenSSL)
diff --git a/test/openssl/test_hmac.rb b/test/openssl/test_hmac.rb
index 831a5b6b37d..9cb3c5a864b 100644
--- a/test/openssl/test_hmac.rb
+++ b/test/openssl/test_hmac.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'utils'
if defined?(OpenSSL)
@@ -39,6 +39,16 @@ class OpenSSL::TestHMAC < OpenSSL::TestCase
second = h1.update("test").hexdigest
assert_equal first, second
end
+
+ def test_eq
+ h1 = OpenSSL::HMAC.new("KEY", "MD5")
+ h2 = OpenSSL::HMAC.new("KEY", OpenSSL::Digest.new("MD5"))
+ h3 = OpenSSL::HMAC.new("FOO", "MD5")
+
+ assert_equal h1, h2
+ refute_equal h1, h2.digest
+ refute_equal h1, h3
+ end
end
end
diff --git a/test/openssl/test_kdf.rb b/test/openssl/test_kdf.rb
index 5e1db80c5f0..f4790c96afa 100644
--- a/test/openssl/test_kdf.rb
+++ b/test/openssl/test_kdf.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'utils'
if defined?(OpenSSL)
diff --git a/test/openssl/test_ns_spki.rb b/test/openssl/test_ns_spki.rb
index aa1e61824f7..052507de5ba 100644
--- a/test/openssl/test_ns_spki.rb
+++ b/test/openssl/test_ns_spki.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'utils'
if defined?(OpenSSL)
@@ -9,7 +9,7 @@ class OpenSSL::TestNSSPI < OpenSSL::TestCase
# This request data is adopt from the specification of
# "Netscape Extensions for User Key Generation".
# -- http://wp.netscape.com/eng/security/comm4-keygen.html
- @b64 = "MIHFMHEwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAnX0TILJrOMUue+PtwBRE6XfV"
+ @b64 = +"MIHFMHEwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAnX0TILJrOMUue+PtwBRE6XfV"
@b64 << "WtKQbsshxk5ZhcUwcwyvcnIq9b82QhJdoACdD34rqfCAIND46fXKQUnb0mvKzQID"
@b64 << "AQABFhFNb3ppbGxhSXNNeUZyaWVuZDANBgkqhkiG9w0BAQQFAANBAAKv2Eex2n/S"
@b64 << "r/7iJNroWlSzSMtTiQTEB+ADWHGj9u1xrUrOilq/o2cuQxIfZcNZkYAkWP4DubqW"
diff --git a/test/openssl/test_ocsp.rb b/test/openssl/test_ocsp.rb
index 50ad6c31f5d..a490a1539ef 100644
--- a/test/openssl/test_ocsp.rb
+++ b/test/openssl/test_ocsp.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative "utils"
if defined?(OpenSSL)
@@ -84,6 +84,7 @@ class OpenSSL::TestOCSP < OpenSSL::TestCase
assert_equal [cid.issuer_key_hash].pack("H*"), asn1.value[2].value
assert_equal @cert.serial, asn1.value[3].value
assert_equal der, OpenSSL::OCSP::CertificateId.new(der).to_der
+ assert_equal der, OpenSSL::OCSP::CertificateId.new(asn1).to_der
end
def test_certificate_id_dup
diff --git a/test/openssl/test_ossl.rb b/test/openssl/test_ossl.rb
new file mode 100644
index 00000000000..f517b1d83df
--- /dev/null
+++ b/test/openssl/test_ossl.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+require_relative "utils"
+
+require 'benchmark'
+
+if defined?(OpenSSL)
+
+class OpenSSL::OSSL < OpenSSL::SSLTestCase
+ def test_fixed_length_secure_compare
+ assert_raise(ArgumentError) { OpenSSL.fixed_length_secure_compare("aaa", "a") }
+ assert_raise(ArgumentError) { OpenSSL.fixed_length_secure_compare("aaa", "aa") }
+
+ assert OpenSSL.fixed_length_secure_compare("aaa", "aaa")
+ assert OpenSSL.fixed_length_secure_compare(
+ OpenSSL::Digest::SHA256.digest("aaa"), OpenSSL::Digest::SHA256.digest("aaa")
+ )
+
+ assert_raise(ArgumentError) { OpenSSL.fixed_length_secure_compare("aaa", "aaaa") }
+ refute OpenSSL.fixed_length_secure_compare("aaa", "baa")
+ refute OpenSSL.fixed_length_secure_compare("aaa", "aba")
+ refute OpenSSL.fixed_length_secure_compare("aaa", "aab")
+ assert_raise(ArgumentError) { OpenSSL.fixed_length_secure_compare("aaa", "aaab") }
+ assert_raise(ArgumentError) { OpenSSL.fixed_length_secure_compare("aaa", "b") }
+ assert_raise(ArgumentError) { OpenSSL.fixed_length_secure_compare("aaa", "bb") }
+ refute OpenSSL.fixed_length_secure_compare("aaa", "bbb")
+ assert_raise(ArgumentError) { OpenSSL.fixed_length_secure_compare("aaa", "bbbb") }
+ end
+
+ def test_secure_compare
+ refute OpenSSL.secure_compare("aaa", "a")
+ refute OpenSSL.secure_compare("aaa", "aa")
+
+ assert OpenSSL.secure_compare("aaa", "aaa")
+
+ refute OpenSSL.secure_compare("aaa", "aaaa")
+ refute OpenSSL.secure_compare("aaa", "baa")
+ refute OpenSSL.secure_compare("aaa", "aba")
+ refute OpenSSL.secure_compare("aaa", "aab")
+ refute OpenSSL.secure_compare("aaa", "aaab")
+ refute OpenSSL.secure_compare("aaa", "b")
+ refute OpenSSL.secure_compare("aaa", "bb")
+ refute OpenSSL.secure_compare("aaa", "bbb")
+ refute OpenSSL.secure_compare("aaa", "bbbb")
+ end
+
+ def test_memcmp_timing
+ # Ensure using fixed_length_secure_compare takes almost exactly the same amount of time to compare two different strings.
+ # Regular string comparison will short-circuit on the first non-matching character, failing this test.
+ # NOTE: this test may be susceptible to noise if the system running the tests is otherwise under load.
+ a = "x" * 512_000
+ b = "#{a}y"
+ c = "y#{a}"
+ a = "#{a}x"
+
+ n = 10_000
+ a_b_time = Benchmark.measure { n.times { OpenSSL.fixed_length_secure_compare(a, b) } }.real
+ a_c_time = Benchmark.measure { n.times { OpenSSL.fixed_length_secure_compare(a, c) } }.real
+ assert_in_delta(a_b_time, a_c_time, 1, "fixed_length_secure_compare timing test failed")
+ end
+end
+
+end
diff --git a/test/openssl/test_pair.rb b/test/openssl/test_pair.rb
index 08c15a0f750..e9cf98df153 100644
--- a/test/openssl/test_pair.rb
+++ b/test/openssl/test_pair.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'utils'
require_relative 'ut_eof'
@@ -128,11 +128,11 @@ module OpenSSL::TestPairM
ssl_pair {|s1, s2|
s2.write "a\nbcd"
assert_equal("a\n", s1.gets)
- result = ""
+ result = String.new
result << s1.readpartial(10) until result.length == 3
assert_equal("bcd", result)
s2.write "efg"
- result = ""
+ result = String.new
result << s1.readpartial(10) until result.length == 3
assert_equal("efg", result)
s2.close
@@ -242,22 +242,22 @@ module OpenSSL::TestPairM
def test_read_with_outbuf
ssl_pair { |s1, s2|
s1.write("abc\n")
- buf = ""
+ buf = String.new
ret = s2.read(2, buf)
assert_same ret, buf
assert_equal "ab", ret
- buf = "garbage"
+ buf = +"garbage"
ret = s2.read(2, buf)
assert_same ret, buf
assert_equal "c\n", ret
- buf = "garbage"
+ buf = +"garbage"
assert_equal :wait_readable, s2.read_nonblock(100, buf, exception: false)
assert_equal "", buf
s1.close
- buf = "garbage"
+ buf = +"garbage"
assert_equal nil, s2.read(100, buf)
assert_equal "", buf
}
diff --git a/test/openssl/test_pkcs12.rb b/test/openssl/test_pkcs12.rb
index de8e35ed79e..fdbe753b17e 100644
--- a/test/openssl/test_pkcs12.rb
+++ b/test/openssl/test_pkcs12.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative "utils"
if defined?(OpenSSL)
diff --git a/test/openssl/test_pkcs7.rb b/test/openssl/test_pkcs7.rb
index 6437112b74a..d0d9dcaf815 100644
--- a/test/openssl/test_pkcs7.rb
+++ b/test/openssl/test_pkcs7.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'utils'
if defined?(OpenSSL)
@@ -172,6 +172,28 @@ class OpenSSL::TestPKCS7 < OpenSSL::TestCase
assert_equal(:encrypted, p7.type)
end
+ def test_smime
+ store = OpenSSL::X509::Store.new
+ store.add_cert(@ca_cert)
+ ca_certs = [@ca_cert]
+
+ data = "aaaaa\r\nbbbbb\r\nccccc\r\n"
+ tmp = OpenSSL::PKCS7.sign(@ee1_cert, @rsa1024, data, ca_certs)
+ p7 = OpenSSL::PKCS7.new(tmp.to_der)
+ smime = OpenSSL::PKCS7.write_smime(p7)
+ assert_equal(true, smime.start_with?(<<END))
+MIME-Version: 1.0
+Content-Disposition: attachment; filename="smime.p7m"
+Content-Type: application/x-pkcs7-mime; smime-type=signed-data; name="smime.p7m"
+Content-Transfer-Encoding: base64
+
+END
+ assert_equal(p7.to_der, OpenSSL::PKCS7.read_smime(smime).to_der)
+
+ smime = OpenSSL::PKCS7.write_smime(p7, nil, 0)
+ assert_equal(p7.to_der, OpenSSL::PKCS7.read_smime(smime).to_der)
+ end
+
def test_degenerate_pkcs7
ca_cert_pem = <<END
-----BEGIN CERTIFICATE-----
diff --git a/test/openssl/test_pkey_dh.rb b/test/openssl/test_pkey_dh.rb
index 8c8fbaeefbe..6397e76d3f7 100644
--- a/test/openssl/test_pkey_dh.rb
+++ b/test/openssl/test_pkey_dh.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'utils'
if defined?(OpenSSL) && defined?(OpenSSL::PKey::DH)
diff --git a/test/openssl/test_pkey_dsa.rb b/test/openssl/test_pkey_dsa.rb
index d651949842c..2c839b7dfb7 100644
--- a/test/openssl/test_pkey_dsa.rb
+++ b/test/openssl/test_pkey_dsa.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'utils'
if defined?(OpenSSL) && defined?(OpenSSL::PKey::DSA)
diff --git a/test/openssl/test_pkey_ec.rb b/test/openssl/test_pkey_ec.rb
index 95b5a6426e3..1278a8b1baa 100644
--- a/test/openssl/test_pkey_ec.rb
+++ b/test/openssl/test_pkey_ec.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'utils'
if defined?(OpenSSL) && defined?(OpenSSL::PKey::EC)
@@ -289,6 +289,22 @@ class OpenSSL::TestEC < OpenSSL::PKeyTestCase
assert_equal true, point.on_curve?
end
+ def test_ec_point_add
+ group = OpenSSL::PKey::EC::Group.new(:GFp, 17, 2, 2)
+ group.point_conversion_form = :uncompressed
+ gen = OpenSSL::PKey::EC::Point.new(group, B(%w{ 04 05 01 }))
+ group.set_generator(gen, 19, 1)
+
+ point_a = OpenSSL::PKey::EC::Point.new(group, B(%w{ 04 06 03 }))
+ point_b = OpenSSL::PKey::EC::Point.new(group, B(%w{ 04 10 0D }))
+
+ result = point_a.add(point_b)
+ assert_equal B(%w{ 04 0D 07 }), result.to_octet_string(:uncompressed)
+
+ assert_raise(TypeError) { point_a.add(nil) }
+ assert_raise(ArgumentError) { point_a.add }
+ end
+
def test_ec_point_mul
begin
# y^2 = x^3 + 2x + 2 over F_17
diff --git a/test/openssl/test_pkey_rsa.rb b/test/openssl/test_pkey_rsa.rb
index ef02717d8b2..a9587aa1a44 100644
--- a/test/openssl/test_pkey_rsa.rb
+++ b/test/openssl/test_pkey_rsa.rb
@@ -1,9 +1,18 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative "utils"
if defined?(OpenSSL)
class OpenSSL::TestPKeyRSA < OpenSSL::PKeyTestCase
+ def test_no_private_exp
+ key = OpenSSL::PKey::RSA.new
+ rsa = Fixtures.pkey("rsa2048")
+ key.set_key(rsa.n, rsa.e, nil)
+ key.set_factors(rsa.p, rsa.q)
+ assert_raise(OpenSSL::PKey::RSAError){ key.private_encrypt("foo") }
+ assert_raise(OpenSSL::PKey::RSAError){ key.private_decrypt("foo") }
+ end
+
def test_padding
key = OpenSSL::PKey::RSA.new(512, 3)
@@ -31,14 +40,32 @@ class OpenSSL::TestPKeyRSA < OpenSSL::PKeyTestCase
end
def test_private
+ # Generated by key size and public exponent
key = OpenSSL::PKey::RSA.new(512, 3)
assert(key.private?)
+
+ # Generated by DER
key2 = OpenSSL::PKey::RSA.new(key.to_der)
assert(key2.private?)
+
+ # public key
key3 = key.public_key
assert(!key3.private?)
+
+ # Generated by public key DER
key4 = OpenSSL::PKey::RSA.new(key3.to_der)
assert(!key4.private?)
+ rsa1024 = Fixtures.pkey("rsa1024")
+
+ # Generated by RSA#set_key
+ key5 = OpenSSL::PKey::RSA.new
+ key5.set_key(rsa1024.n, rsa1024.e, rsa1024.d)
+ assert(key5.private?)
+
+ # Generated by RSA#set_key, without d
+ key6 = OpenSSL::PKey::RSA.new
+ key6.set_key(rsa1024.n, rsa1024.e, nil)
+ assert(!key6.private?)
end
def test_new
@@ -153,6 +180,40 @@ class OpenSSL::TestPKeyRSA < OpenSSL::PKeyTestCase
}
end
+ def test_export
+ rsa1024 = Fixtures.pkey("rsa1024")
+ key = OpenSSL::PKey::RSA.new
+
+ # key has only n, e and d
+ key.set_key(rsa1024.n, rsa1024.e, rsa1024.d)
+ assert_equal rsa1024.public_key.export, key.export
+
+ # key has only n, e, d, p and q
+ key.set_factors(rsa1024.p, rsa1024.q)
+ assert_equal rsa1024.public_key.export, key.export
+
+ # key has n, e, d, p, q, dmp1, dmq1 and iqmp
+ key.set_crt_params(rsa1024.dmp1, rsa1024.dmq1, rsa1024.iqmp)
+ assert_equal rsa1024.export, key.export
+ end
+
+ def test_to_der
+ rsa1024 = Fixtures.pkey("rsa1024")
+ key = OpenSSL::PKey::RSA.new
+
+ # key has only n, e and d
+ key.set_key(rsa1024.n, rsa1024.e, rsa1024.d)
+ assert_equal rsa1024.public_key.to_der, key.to_der
+
+ # key has only n, e, d, p and q
+ key.set_factors(rsa1024.p, rsa1024.q)
+ assert_equal rsa1024.public_key.to_der, key.to_der
+
+ # key has n, e, d, p, q, dmp1, dmq1 and iqmp
+ key.set_crt_params(rsa1024.dmp1, rsa1024.dmq1, rsa1024.iqmp)
+ assert_equal rsa1024.to_der, key.to_der
+ end
+
def test_RSAPrivateKey
rsa1024 = Fixtures.pkey("rsa1024")
asn1 = OpenSSL::ASN1::Sequence([
@@ -295,6 +356,85 @@ class OpenSSL::TestPKeyRSA < OpenSSL::PKeyTestCase
}
end
+ def test_private_encoding
+ rsa1024 = Fixtures.pkey("rsa1024")
+ asn1 = OpenSSL::ASN1::Sequence([
+ OpenSSL::ASN1::Integer(0),
+ OpenSSL::ASN1::Sequence([
+ OpenSSL::ASN1::ObjectId("rsaEncryption"),
+ OpenSSL::ASN1::Null(nil)
+ ]),
+ OpenSSL::ASN1::OctetString(rsa1024.to_der)
+ ])
+ assert_equal asn1.to_der, rsa1024.private_to_der
+ assert_same_rsa rsa1024, OpenSSL::PKey.read(asn1.to_der)
+
+ pem = <<~EOF
+ -----BEGIN PRIVATE KEY-----
+ MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAMvCxLDUQKc+1P4+
+ Q6AeFwYDvWfALb+cvzlUEadGoPE6qNWHsLFoo8RFgeyTgE8KQTduu1OE9Zz2SMcR
+ BDu5/1jWtsLPSVrI2ofLLBARUsWanVyki39DeB4u/xkP2mKGjAokPIwOI3oCthSZ
+ lzO9bj3voxTf6XngTqUX8l8URTmHAgMBAAECgYEApKX8xBqvJ7XI7Kypfo/x8MVC
+ 3rxW+1eQ2aVKIo4a7PKGjQz5RVIVyzqTUvSZoMTbkAxlSIbO5YfJpTnl3tFcOB6y
+ QMxqQPW/pl6Ni3EmRJdsRM5MsPBRZOfrXxOCdvXu1TWOS1S1TrvEr/TyL9eh2WCd
+ CGzpWgdO4KHce7vs7pECQQDv6DGoG5lHnvbvj9qSJb9K5ebRJc8S+LI7Uy5JHC0j
+ zsHTYPSqBXwPVQdGbgCEycnwwKzXzT2QxAQmJBQKun2ZAkEA2W3aeAE7Xi6zo2eG
+ 4Cx4UNMHMIdfBRS7VgoekwybGmcapqV0aBew5kHeWAmxP1WUZ/dgZh2QtM1VuiBA
+ qUqkHwJBAOJLCRvi/JB8N7z82lTk2i3R8gjyOwNQJv6ilZRMyZ9vFZFHcUE27zCf
+ Kb+bX03h8WPwupjMdfgpjShU+7qq8nECQQDBrmyc16QVyo40sgTgblyiysitvviy
+ ovwZsZv4q5MCmvOPnPUrwGbRRb2VONUOMOKpFiBl9lIv7HU//nj7FMVLAkBjUXED
+ 83dA8JcKM+HlioXEAxCzZVVhN+D63QwRwkN08xAPklfqDkcqccWDaZm2hdCtaYlK
+ funwYkrzI1OikQSs
+ -----END PRIVATE KEY-----
+ EOF
+ assert_equal pem, rsa1024.private_to_pem
+ assert_same_rsa rsa1024, OpenSSL::PKey.read(pem)
+ end
+
+ def test_private_encoding_encrypted
+ rsa1024 = Fixtures.pkey("rsa1024")
+ encoded = rsa1024.private_to_der("aes-128-cbc", "abcdef")
+ asn1 = OpenSSL::ASN1.decode(encoded) # PKCS #8 EncryptedPrivateKeyInfo
+ assert_kind_of OpenSSL::ASN1::Sequence, asn1
+ assert_equal 2, asn1.value.size
+ assert_not_equal rsa1024.private_to_der, encoded
+ assert_same_rsa rsa1024, OpenSSL::PKey.read(encoded, "abcdef")
+ assert_same_rsa rsa1024, OpenSSL::PKey.read(encoded) { "abcdef" }
+ assert_raise(OpenSSL::PKey::PKeyError) { OpenSSL::PKey.read(encoded, "abcxyz") }
+
+ encoded = rsa1024.private_to_pem("aes-128-cbc", "abcdef")
+ assert_match (/BEGIN ENCRYPTED PRIVATE KEY/), encoded.lines[0]
+ assert_same_rsa rsa1024, OpenSSL::PKey.read(encoded, "abcdef")
+
+ # certtool --load-privkey=test/fixtures/pkey/rsa1024.pem --to-p8 --password=abcdef
+ pem = <<~EOF
+ -----BEGIN ENCRYPTED PRIVATE KEY-----
+ MIICojAcBgoqhkiG9w0BDAEDMA4ECLqajUdSNfzwAgIEkQSCAoCDWhxr1HUrKLXA
+ FsFGGQfPT0aKH4gZipaSXXQRl0KwifHwHoDtfo/mAkJVZMnUVOm1AQ4LTFS3EdTy
+ JUwICGEQHb7QAiokIRoi0K2yHhOxVO8qgbnWuisWpiT6Ru1jCqTs/wcqlqF7z2jM
+ oXDk/vuekKst1DDXDcHrzhDkwhCQWj6jt1r2Vwaryy0FyeqsWAgBDiK2LsnCgkGD
+ 21uhNZ/iWMG6tvY9hB8MDdiBJ41YdSG/AKLulAxQ1ibJz0Tasu66TmwFvWhBlME+
+ QbqfgmkgWg5buu53SvDfCA47zXihclbtdfW+U3CJ9OJkx0535TVdZbuC1QgKXvG7
+ 4iKGFRMWYJqZvZM3GL4xbC75AxjXZsdCfV81VjZxjeU6ung/NRzCuCUcmBOQzo1D
+ Vv6COwAa6ttQWM0Ti8oIQHdu5Qi+nuOEHDLxCxD962M37H99sEO5cESjmrGVxhEo
+ 373L4+11geGSCajdp0yiAGnXQfwaKta8cL693bRObN+b1Y+vqtDKH26N9a4R3qgg
+ 2XwgQ5GH5CODoXZpi0wxncXO+3YuuhGeArtzKSXLNxHzIMlY7wZX+0e9UU03zfV/
+ aOe4/q5DpkNxgHePt0oEpamSKY5W3jzVi1dlFWsRjud1p/Grt2zjSWTYClBlJqG1
+ A/3IeDZCu+acaePJjFyv5dFffIj2l4bAYB+LFrZlSu3F/EimO/dCDWJ9JGlMK0aF
+ l9brh7786Mo+YfyklaqMMEHBbbR2Es7PR6Gt7lrcIXmzy9XSsxT6IiD1rG9KKR3i
+ CQxTup6JAx9w1q+adL+Ypikoy3gGD/ccUY6TtPoCmkQwSCS+JqQnFlCiThDJbu+V
+ eqqUNkZq
+ -----END ENCRYPTED PRIVATE KEY-----
+ EOF
+ assert_same_rsa rsa1024, OpenSSL::PKey.read(pem, "abcdef")
+ end
+
+ def test_public_encoding
+ rsa1024 = Fixtures.pkey("rsa1024")
+ assert_equal dup_public(rsa1024).to_der, rsa1024.public_to_der
+ assert_equal dup_public(rsa1024).to_pem, rsa1024.public_to_pem
+ end
+
def test_dup
key = Fixtures.pkey("rsa1024")
key2 = key.dup
diff --git a/test/openssl/test_random.rb b/test/openssl/test_random.rb
index d5a374540d7..33af375720f 100644
--- a/test/openssl/test_random.rb
+++ b/test/openssl/test_random.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative "utils"
if defined?(OpenSSL)
diff --git a/test/openssl/test_ssl.rb b/test/openssl/test_ssl.rb
index 940bc135edd..eb5b77be022 100644
--- a/test/openssl/test_ssl.rb
+++ b/test/openssl/test_ssl.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative "utils"
if defined?(OpenSSL)
@@ -56,6 +56,52 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
}
end
+ def test_socket_open
+ start_server { |port|
+ begin
+ ssl = OpenSSL::SSL::SSLSocket.open("127.0.0.1", port)
+ ssl.sync_close = true
+ ssl.connect
+
+ ssl.puts "abc"; assert_equal "abc\n", ssl.gets
+ ensure
+ ssl&.close
+ end
+ }
+ end
+
+ def test_socket_open_with_context
+ start_server { |port|
+ begin
+ ctx = OpenSSL::SSL::SSLContext.new
+ ssl = OpenSSL::SSL::SSLSocket.open("127.0.0.1", port, context: ctx)
+ ssl.sync_close = true
+ ssl.connect
+
+ assert_equal ssl.context, ctx
+ ssl.puts "abc"; assert_equal "abc\n", ssl.gets
+ ensure
+ ssl&.close
+ end
+ }
+ end
+
+ def test_socket_open_with_local_address_port_context
+ start_server { |port|
+ begin
+ ctx = OpenSSL::SSL::SSLContext.new
+ ssl = OpenSSL::SSL::SSLSocket.open("127.0.0.1", port, "127.0.0.1", 8000, context: ctx)
+ ssl.sync_close = true
+ ssl.connect
+
+ assert_equal ssl.context, ctx
+ ssl.puts "abc"; assert_equal "abc\n", ssl.gets
+ ensure
+ ssl&.close
+ end
+ }
+ end
+
def test_add_certificate
ctx_proc = -> ctx {
# Unset values set by start_server
@@ -139,15 +185,20 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
end
end
+ def test_add_certificate_chain_file
+ ctx = OpenSSL::SSL::SSLContext.new
+ assert ctx.add_certificate_chain_file(Fixtures.file_path("chain", "server.crt"))
+ end
+
def test_sysread_and_syswrite
start_server { |port|
server_connect(port) { |ssl|
- str = "x" * 100 + "\n"
+ str = +("x" * 100 + "\n")
ssl.syswrite(str)
newstr = ssl.sysread(str.bytesize)
assert_equal(str, newstr)
- buf = ""
+ buf = String.new
ssl.syswrite(str)
assert_same buf, ssl.sysread(str.size, buf)
assert_equal(str, buf)
@@ -155,23 +206,21 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
}
end
- def test_sysread_nonblock_and_syswrite_nonblock_keywords
- start_server(ignore_listener_error: true) do |port|
- sock = TCPSocket.new("127.0.0.1", port)
- ssl = OpenSSL::SSL::SSLSocket.new(sock)
-
- assert_warn ("") do
- ssl.send(:syswrite_nonblock, "1", exception: false)
- ssl.send(:sysread_nonblock, 1, exception: false) rescue nil
- ssl.send(:sysread_nonblock, 1, String.new, exception: false) rescue nil
- end
- ensure
- sock&.close
- end
- end
+ # TODO fix this test
+ # def test_sysread_nonblock_and_syswrite_nonblock_keywords
+ # start_server do |port|
+ # server_connect(port) do |ssl|
+ # assert_warning("") do
+ # ssl.send(:syswrite_nonblock, "12", exception: false)
+ # ssl.send(:sysread_nonblock, 1, exception: false) rescue nil
+ # ssl.send(:sysread_nonblock, 1, String.new, exception: false) rescue nil
+ # end
+ # end
+ # end
+ # end
def test_sync_close
- start_server { |port|
+ start_server do |port|
begin
sock = TCPSocket.new("127.0.0.1", port)
ssl = OpenSSL::SSL::SSLSocket.new(sock)
@@ -194,7 +243,7 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
ensure
sock&.close
end
- }
+ end
end
def test_copy_stream
@@ -434,6 +483,29 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
}
end
+ def test_finished_messages
+ server_finished = nil
+ server_peer_finished = nil
+ client_finished = nil
+ client_peer_finished = nil
+
+ start_server(accept_proc: proc { |server|
+ server_finished = server.finished_message
+ server_peer_finished = server.peer_finished_message
+ }) { |port|
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE
+ server_connect(port, ctx) { |ssl|
+ client_finished = ssl.finished_message
+ client_peer_finished = ssl.peer_finished_message
+ sleep 0.05
+ ssl.send :stop
+ }
+ }
+ assert_equal(server_finished, client_peer_finished)
+ assert_equal(server_peer_finished, client_finished)
+ end
+
def test_sslctx_set_params
ctx = OpenSSL::SSL::SSLContext.new
ctx.set_params
@@ -1565,6 +1637,20 @@ end
}
end
+ def test_fileno
+ ctx = OpenSSL::SSL::SSLContext.new
+ sock1, sock2 = socketpair
+
+ socket = OpenSSL::SSL::SSLSocket.new(sock1)
+ server = OpenSSL::SSL::SSLServer.new(sock2, ctx)
+
+ assert_equal socket.fileno, socket.to_io.fileno
+ assert_equal server.fileno, server.to_io.fileno
+ ensure
+ sock1.close
+ sock2.close
+ end
+
private
def start_server_version(version, ctx_proc = nil,
@@ -1597,8 +1683,8 @@ end
def assert_handshake_error
# different OpenSSL versions react differently when facing a SSL/TLS version
- # that has been marked as forbidden, therefore either of these may be raised
- assert_raise(OpenSSL::SSL::SSLError, Errno::ECONNRESET) {
+ # that has been marked as forbidden, therefore any of these may be raised
+ assert_raise(OpenSSL::SSL::SSLError, Errno::ECONNRESET, Errno::EPIPE) {
yield
}
end
diff --git a/test/openssl/test_ssl_session.rb b/test/openssl/test_ssl_session.rb
index e199f86d2b9..89726d4463d 100644
--- a/test/openssl/test_ssl_session.rb
+++ b/test/openssl/test_ssl_session.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative "utils"
if defined?(OpenSSL)
diff --git a/test/openssl/test_ts.rb b/test/openssl/test_ts.rb
new file mode 100644
index 00000000000..c57f08cf7ac
--- /dev/null
+++ b/test/openssl/test_ts.rb
@@ -0,0 +1,667 @@
+require_relative "utils"
+
+if defined?(OpenSSL) && defined?(OpenSSL::Timestamp)
+
+class OpenSSL::TestTimestamp < OpenSSL::TestCase
+ def intermediate_key
+ @intermediate_key ||= OpenSSL::PKey::RSA.new <<-_end_of_pem_
+-----BEGIN RSA PRIVATE KEY-----
+MIICWwIBAAKBgQCcyODxH+oTrr7l7MITWcGaYnnBma6vidCCJjuSzZpaRmXZHAyH
+0YcY4ttC0BdJ4uV+cE05IySVC7tyvVfFb8gFQ6XJV+AEktP+XkLbcxZgj9d2NVu1
+ziXdI+ldXkPnMhyWpMS5E7SD6gflv9NhUYEsmAGsUgdK6LDmm2W2/4TlewIDAQAB
+AoGAYgx6KDFWONLqjW3f/Sv/mGYHUNykUyDzpcD1Npyf797gqMMSzwlo3FZa2tC6
+D7n23XirwpTItvEsW9gvgMikJDPlThAeGLZ+L0UbVNNBHVxGP998Nda1kxqKvhRE
+pfZCKc7PLM9ZXc6jBTmgxdcAYfVCCVUoa2mEf9Ktr3BlI4kCQQDQAM09+wHDXGKP
+o2UnCwCazGtyGU2r0QCzHlh9BVY+KD2KjjhuWh86rEbdWN7hEW23Je1vXIhuM6Pa
+/Ccd+XYnAkEAwPZ91PK6idEONeGQ4I3dyMKV2SbaUjfq3MDL4iIQPQPuj7QsBO/5
+3Nf9ReSUUTRFCUVwoC8k4Z1KAJhR/K/ejQJANE7PTnPuGJQGETs09+GTcFpR9uqY
+FspDk8fg1ufdrVnvSAXF+TJewiGK3KU5v33jinhWQngRsyz3Wt2odKhEZwJACbjh
+oicQqvzzgFd7GzVKpWDYd/ZzLY1PsgusuhoJQ2m9TVRAm4cTycLAKhNYPbcqe0sa
+X5fAffWU0u7ZwqeByQJAOUAbYET4RU3iymAvAIDFj8LiQnizG9t5Ty3HXlijKQYv
+y8gsvWd4CdxwOPatWpBUX9L7IXcMJmD44xXTUvpbfQ==
+-----END RSA PRIVATE KEY-----
+_end_of_pem_
+ end
+
+ def ee_key
+ @ee_key ||= OpenSSL::PKey::RSA.new <<-_end_of_pem_
+-----BEGIN RSA PRIVATE KEY-----
+MIICWwIBAAKBgQDA6eB5r2O5KOKNbKMBhzadl43lgpwqq28m+G0gH38kKCL1f3o9
+P8xUZm7sZqcWEervZMSSXMGBV9DgeoSR+U6FMJywgQGx/JNRx7wZTMNym3PvgLkl
+xCXh6ZA0/xbtJtcNI+UUv0ENBkTIuUWBhkAf3jQclAr9aQ0ktYBuHAcRcQIDAQAB
+AoGAKNhcAuezwZx6e18pFEXAtpVEIfgJgK9TlXi8AjUpAkrNPBWFmDpN1QDrM3p4
+nh+lEpLPW/3vqqchPqYyM4YJraMLpS3KUG+s7+m9QIia0ri2WV5Cig7WL+Tl9p7K
+b3oi2Aj/wti8GfOLFQXOQQ4Ea4GoCv2Sxe0GZR39UBxzTsECQQD1zuVIwBvqU2YR
+8innsoa+j4u2hulRmQO6Zgpzj5vyRYfA9uZxQ9nKbfJvzuWwUv+UzyS9RqxarqrP
+5nQw5EmVAkEAyOmJg6+AfGrgvSWfSpXEds/WA/sHziCO3rE4/sd6cnDc6XcTgeMs
+mT8Z3kAYGpqFDew5orUylPfJJa+PUueJbQJAY+gkvw3+Cp69FLw1lgu0wo07fwOU
+n2qu3jsNMm0DOFRUWfTAMvcd9S385L7WEnWZldUfnKK1+OGXYYrMXPbchQJAChU2
+UoaHQzc16iguM1cK0g+iJPb/MEgQA3sPajHmokGpxIm2T+lvvo0dJjs/Om6QyN8X
+EWRYkoNQ8/Q4lCeMjQJAfvDIGtyqF4PieFHYgluQAv5pGgYpakdc8SYyeRH9NKey
+GaL27FRs4fRWf9OmxPhUVgIyGzLGXrueemvQUDHObA==
+-----END RSA PRIVATE KEY-----
+_end_of_pem_
+ end
+
+ def ca_cert
+ @ca_cert ||= OpenSSL::Certs.ca_cert
+ end
+
+ def ca_store
+ @ca_store ||= OpenSSL::X509::Store.new.tap { |s| s.add_cert(ca_cert) }
+ end
+
+ def ts_cert_direct
+ @ts_cert_direct ||= OpenSSL::Certs.ts_cert_direct(ee_key, ca_cert)
+ end
+
+ def intermediate_cert
+ @intermediate_cert ||= OpenSSL::Certs.intermediate_cert(intermediate_key, ca_cert)
+ end
+
+ def intermediate_store
+ @intermediate_store ||= OpenSSL::X509::Store.new.tap { |s| s.add_cert(intermediate_cert) }
+ end
+
+ def ts_cert_ee
+ @ts_cert_ee ||= OpenSSL::Certs.ts_cert_ee(ee_key, intermediate_cert, intermediate_key)
+ end
+
+ def test_create_request
+ req = OpenSSL::Timestamp::Request.new
+ assert_equal(true, req.cert_requested?)
+ assert_equal(1, req.version)
+ assert_nil(req.algorithm)
+ assert_equal("", req.message_imprint)
+ assert_nil(req.policy_id)
+ assert_nil(req.nonce)
+ end
+
+ def test_request_mandatory_fields
+ req = OpenSSL::Timestamp::Request.new
+ assert_raise(OpenSSL::Timestamp::TimestampError) do
+ tmp = req.to_der
+ pp OpenSSL::ASN1.decode(tmp)
+ end
+ req.algorithm = "sha1"
+ assert_raise(OpenSSL::Timestamp::TimestampError) do
+ req.to_der
+ end
+ req.message_imprint = OpenSSL::Digest::SHA1.new.digest("data")
+ req.to_der
+ end
+
+ def test_request_assignment
+ req = OpenSSL::Timestamp::Request.new
+
+ req.version = 2
+ assert_equal(2, req.version)
+ assert_raise(TypeError) { req.version = nil }
+ assert_raise(TypeError) { req.version = "foo" }
+
+ req.algorithm = "SHA1"
+ assert_equal("SHA1", req.algorithm)
+ assert_raise(TypeError) { req.algorithm = nil }
+ assert_raise(OpenSSL::ASN1::ASN1Error) { req.algorithm = "xxx" }
+
+ req.message_imprint = "test"
+ assert_equal("test", req.message_imprint)
+ assert_raise(TypeError) { req.message_imprint = nil }
+
+ req.policy_id = "1.2.3.4.5"
+ assert_equal("1.2.3.4.5", req.policy_id)
+ assert_raise(TypeError) { req.policy_id = 123 }
+ assert_raise(TypeError) { req.policy_id = nil }
+
+ req.nonce = 42
+ assert_equal(42, req.nonce)
+ assert_raise(TypeError) { req.nonce = "foo" }
+ assert_raise(TypeError) { req.nonce = nil }
+
+ req.cert_requested = false
+ assert_equal(false, req.cert_requested?)
+ req.cert_requested = nil
+ assert_equal(false, req.cert_requested?)
+ req.cert_requested = 123
+ assert_equal(true, req.cert_requested?)
+ req.cert_requested = "asdf"
+ assert_equal(true, req.cert_requested?)
+ end
+
+ def test_request_serialization
+ req = OpenSSL::Timestamp::Request.new
+
+ req.version = 2
+ req.algorithm = "SHA1"
+ req.message_imprint = "test"
+ req.policy_id = "1.2.3.4.5"
+ req.nonce = 42
+ req.cert_requested = true
+
+ req = OpenSSL::Timestamp::Request.new(req.to_der)
+
+ assert_equal(2, req.version)
+ assert_equal("SHA1", req.algorithm)
+ assert_equal("test", req.message_imprint)
+ assert_equal("1.2.3.4.5", req.policy_id)
+ assert_equal(42, req.nonce)
+ assert_equal(true, req.cert_requested?)
+
+ end
+
+ def test_request_re_assignment
+ #tests whether the potential 'freeing' of previous values in C works properly
+ req = OpenSSL::Timestamp::Request.new
+ req.version = 2
+ req.version = 3
+ req.algorithm = "SHA1"
+ req.algorithm = "SHA256"
+ req.message_imprint = "test"
+ req.message_imprint = "test2"
+ req.policy_id = "1.2.3.4.5"
+ req.policy_id = "1.2.3.4.6"
+ req.nonce = 42
+ req.nonce = 24
+ req.cert_requested = false
+ req.cert_requested = true
+ req.to_der
+ end
+
+ def test_request_encode_decode
+ req = OpenSSL::Timestamp::Request.new
+ req.algorithm = "SHA1"
+ digest = OpenSSL::Digest::SHA1.new.digest("test")
+ req.message_imprint = digest
+ req.policy_id = "1.2.3.4.5"
+ req.nonce = 42
+
+ qer = OpenSSL::Timestamp::Request.new(req.to_der)
+ assert_equal(1, qer.version)
+ assert_equal("SHA1", qer.algorithm)
+ assert_equal(digest, qer.message_imprint)
+ assert_equal("1.2.3.4.5", qer.policy_id)
+ assert_equal(42, qer.nonce)
+
+ #put OpenSSL::ASN1.decode inbetween
+ qer2 = OpenSSL::Timestamp::Request.new(OpenSSL::ASN1.decode(req.to_der))
+ assert_equal(1, qer2.version)
+ assert_equal("SHA1", qer2.algorithm)
+ assert_equal(digest, qer2.message_imprint)
+ assert_equal("1.2.3.4.5", qer2.policy_id)
+ assert_equal(42, qer2.nonce)
+ end
+
+ def test_response_constants
+ assert_equal(0, OpenSSL::Timestamp::Response::GRANTED)
+ assert_equal(1, OpenSSL::Timestamp::Response::GRANTED_WITH_MODS)
+ assert_equal(2, OpenSSL::Timestamp::Response::REJECTION)
+ assert_equal(3, OpenSSL::Timestamp::Response::WAITING)
+ assert_equal(4, OpenSSL::Timestamp::Response::REVOCATION_WARNING)
+ assert_equal(5, OpenSSL::Timestamp::Response::REVOCATION_NOTIFICATION)
+ end
+
+ def test_response_creation
+ req = OpenSSL::Timestamp::Request.new
+ req.algorithm = "SHA1"
+ digest = OpenSSL::Digest::SHA1.new.digest("test")
+ req.message_imprint = digest
+ req.policy_id = "1.2.3.4.5"
+
+ fac = OpenSSL::Timestamp::Factory.new
+ time = Time.now
+ fac.gen_time = time
+ fac.serial_number = 1
+ fac.allowed_digests = ["sha1"]
+
+ resp = fac.create_timestamp(ee_key, ts_cert_ee, req)
+ resp = OpenSSL::Timestamp::Response.new(resp)
+ assert_equal(OpenSSL::Timestamp::Response::GRANTED, resp.status)
+ assert_nil(resp.failure_info)
+ assert_equal([], resp.status_text)
+ assert_equal(1, resp.token_info.version)
+ assert_equal("1.2.3.4.5", resp.token_info.policy_id)
+ assert_equal("SHA1", resp.token_info.algorithm)
+ assert_equal(digest, resp.token_info.message_imprint)
+ assert_equal(1, resp.token_info.serial_number)
+ assert_equal(time.to_i, resp.token_info.gen_time.to_i)
+ assert_equal(false, resp.token_info.ordering)
+ assert_nil(resp.token_info.nonce)
+ assert_cert(ts_cert_ee, resp.tsa_certificate)
+ #compare PKCS7
+ token = OpenSSL::ASN1.decode(resp.to_der).value[1]
+ assert_equal(token.to_der, resp.token.to_der)
+ end
+
+ def test_response_mandatory_fields
+ fac = OpenSSL::Timestamp::Factory.new
+ req = OpenSSL::Timestamp::Request.new
+ assert_raise(OpenSSL::Timestamp::TimestampError) do
+ fac.create_timestamp(ee_key, ts_cert_ee, req)
+ end
+ req.algorithm = "sha1"
+ assert_raise(OpenSSL::Timestamp::TimestampError) do
+ fac.create_timestamp(ee_key, ts_cert_ee, req)
+ end
+ req.message_imprint = OpenSSL::Digest::SHA1.new.digest("data")
+ assert_raise(OpenSSL::Timestamp::TimestampError) do
+ fac.create_timestamp(ee_key, ts_cert_ee, req)
+ end
+ fac.gen_time = Time.now
+ assert_raise(OpenSSL::Timestamp::TimestampError) do
+ fac.create_timestamp(ee_key, ts_cert_ee, req)
+ end
+ fac.serial_number = 1
+ fac.allowed_digests = ["sha1"]
+ assert_raise(OpenSSL::Timestamp::TimestampError) do
+ fac.create_timestamp(ee_key, ts_cert_ee, req)
+ end
+ fac.default_policy_id = "1.2.3.4.5"
+ assert_equal OpenSSL::Timestamp::Response::GRANTED, fac.create_timestamp(ee_key, ts_cert_ee, req).status
+ fac.default_policy_id = nil
+ assert_raise(OpenSSL::Timestamp::TimestampError) do
+ fac.create_timestamp(ee_key, ts_cert_ee, req)
+ end
+ req.policy_id = "1.2.3.4.5"
+ assert_equal OpenSSL::Timestamp::Response::GRANTED, fac.create_timestamp(ee_key, ts_cert_ee, req).status
+ end
+
+ def test_response_allowed_digests
+ req = OpenSSL::Timestamp::Request.new
+ req.algorithm = "SHA1"
+ req.message_imprint = OpenSSL::Digest::SHA1.digest("test")
+
+ fac = OpenSSL::Timestamp::Factory.new
+ fac.gen_time = Time.now
+ fac.serial_number = 1
+ fac.default_policy_id = "1.2.3.4.6"
+
+ # None allowed by default
+ resp = fac.create_timestamp(ee_key, ts_cert_ee, req)
+ assert_equal OpenSSL::Timestamp::Response::REJECTION, resp.status
+
+ # Explicitly allow SHA1 (string)
+ fac.allowed_digests = ["sha1"]
+ resp = fac.create_timestamp(ee_key, ts_cert_ee, req)
+ assert_equal OpenSSL::Timestamp::Response::GRANTED, resp.status
+
+ # Explicitly allow SHA1 (object)
+ fac.allowed_digests = [OpenSSL::Digest::SHA1.new]
+ resp = fac.create_timestamp(ee_key, ts_cert_ee, req)
+ assert_equal OpenSSL::Timestamp::Response::GRANTED, resp.status
+
+ # Others not allowed
+ req.algorithm = "SHA256"
+ req.message_imprint = OpenSSL::Digest::SHA256.digest("test")
+ resp = fac.create_timestamp(ee_key, ts_cert_ee, req)
+ assert_equal OpenSSL::Timestamp::Response::REJECTION, resp.status
+
+ # Non-Array
+ fac.allowed_digests = 123
+ resp = fac.create_timestamp(ee_key, ts_cert_ee, req)
+ assert_equal OpenSSL::Timestamp::Response::REJECTION, resp.status
+
+ # Non-String, non-Digest Array element
+ fac.allowed_digests = ["sha1", OpenSSL::Digest::SHA1.new, 123]
+ assert_raise(TypeError) do
+ fac.create_timestamp(ee_key, ts_cert_ee, req)
+ end
+ end
+
+ def test_response_default_policy
+ req = OpenSSL::Timestamp::Request.new
+ req.algorithm = "SHA1"
+ digest = OpenSSL::Digest::SHA1.new.digest("test")
+ req.message_imprint = digest
+
+ fac = OpenSSL::Timestamp::Factory.new
+ fac.gen_time = Time.now
+ fac.serial_number = 1
+ fac.allowed_digests = ["sha1"]
+ fac.default_policy_id = "1.2.3.4.6"
+
+ resp = fac.create_timestamp(ee_key, ts_cert_ee, req)
+ assert_equal(OpenSSL::Timestamp::Response::GRANTED, resp.status)
+ assert_equal("1.2.3.4.6", resp.token_info.policy_id)
+ end
+
+ def test_response_bad_purpose
+ req = OpenSSL::Timestamp::Request.new
+ req.algorithm = "SHA1"
+ digest = OpenSSL::Digest::SHA1.new.digest("test")
+ req.message_imprint = digest
+ req.policy_id = "1.2.3.4.5"
+ req.nonce = 42
+
+ fac = OpenSSL::Timestamp::Factory.new
+ fac.gen_time = Time.now
+ fac.serial_number = 1
+ fac.allowed_digests = ["sha1"]
+
+
+ assert_raise(OpenSSL::Timestamp::TimestampError) do
+ fac.create_timestamp(ee_key, intermediate_cert, req)
+ end
+ end
+
+ def test_no_cert_requested
+ req = OpenSSL::Timestamp::Request.new
+ req.algorithm = "SHA1"
+ digest = OpenSSL::Digest::SHA1.new.digest("test")
+ req.message_imprint = digest
+ req.cert_requested = false
+
+ fac = OpenSSL::Timestamp::Factory.new
+ fac.gen_time = Time.now
+ fac.serial_number = 1
+ fac.allowed_digests = ["sha1"]
+ fac.default_policy_id = "1.2.3.4.5"
+
+ resp = fac.create_timestamp(ee_key, ts_cert_ee, req)
+ assert_equal(OpenSSL::Timestamp::Response::GRANTED, resp.status)
+ assert_nil(resp.tsa_certificate)
+ end
+
+ def test_response_no_policy_defined
+ assert_raise(OpenSSL::Timestamp::TimestampError) do
+ req = OpenSSL::Timestamp::Request.new
+ req.algorithm = "SHA1"
+ digest = OpenSSL::Digest::SHA1.new.digest("test")
+ req.message_imprint = digest
+
+ fac = OpenSSL::Timestamp::Factory.new
+ fac.gen_time = Time.now
+ fac.serial_number = 1
+ fac.allowed_digests = ["sha1"]
+
+ fac.create_timestamp(ee_key, ts_cert_ee, req)
+ end
+ end
+
+ def test_verify_ee_no_req
+ assert_raise(TypeError) do
+ ts, _ = timestamp_ee
+ ts.verify(nil, ca_cert)
+ end
+ end
+
+ def test_verify_ee_no_store
+ assert_raise(TypeError) do
+ ts, req = timestamp_ee
+ ts.verify(req, nil)
+ end
+ end
+
+ def test_verify_ee_wrong_root_no_intermediate
+ assert_raise(OpenSSL::Timestamp::TimestampError) do
+ ts, req = timestamp_ee
+ ts.verify(req, intermediate_store)
+ end
+ end
+
+ def test_verify_ee_wrong_root_wrong_intermediate
+ assert_raise(OpenSSL::Timestamp::TimestampError) do
+ ts, req = timestamp_ee
+ ts.verify(req, intermediate_store, [ca_cert])
+ end
+ end
+
+ def test_verify_ee_nonce_mismatch
+ assert_raise(OpenSSL::Timestamp::TimestampError) do
+ ts, req = timestamp_ee
+ req.nonce = 1
+ ts.verify(req, ca_store, [intermediate_cert])
+ end
+ end
+
+ def test_verify_ee_intermediate_missing
+ assert_raise(OpenSSL::Timestamp::TimestampError) do
+ ts, req = timestamp_ee
+ ts.verify(req, ca_store)
+ end
+ end
+
+ def test_verify_ee_intermediate
+ ts, req = timestamp_ee
+ ts.verify(req, ca_store, [intermediate_cert])
+ end
+
+ def test_verify_ee_intermediate_type_error
+ ts, req = timestamp_ee
+ assert_raise(TypeError) { ts.verify(req, [ca_cert], 123) }
+ end
+
+ def test_verify_ee_def_policy
+ req = OpenSSL::Timestamp::Request.new
+ req.algorithm = "SHA1"
+ digest = OpenSSL::Digest::SHA1.new.digest("test")
+ req.message_imprint = digest
+ req.nonce = 42
+
+ fac = OpenSSL::Timestamp::Factory.new
+ fac.gen_time = Time.now
+ fac.serial_number = 1
+ fac.allowed_digests = ["sha1"]
+ fac.default_policy_id = "1.2.3.4.5"
+
+ ts = fac.create_timestamp(ee_key, ts_cert_ee, req)
+ ts.verify(req, ca_store, [intermediate_cert])
+ end
+
+ def test_verify_direct
+ ts, req = timestamp_direct
+ ts.verify(req, ca_store)
+ end
+
+ def test_verify_direct_redundant_untrusted
+ ts, req = timestamp_direct
+ ts.verify(req, ca_store, [ts.tsa_certificate, ts.tsa_certificate])
+ end
+
+ def test_verify_direct_unrelated_untrusted
+ ts, req = timestamp_direct
+ ts.verify(req, ca_store, [intermediate_cert])
+ end
+
+ def test_verify_direct_wrong_root
+ assert_raise(OpenSSL::Timestamp::TimestampError) do
+ ts, req = timestamp_direct
+ ts.verify(req, intermediate_store)
+ end
+ end
+
+ def test_verify_direct_no_cert_no_intermediate
+ assert_raise(OpenSSL::Timestamp::TimestampError) do
+ ts, req = timestamp_direct_no_cert
+ ts.verify(req, ca_store)
+ end
+ end
+
+ def test_verify_ee_no_cert
+ ts, req = timestamp_ee_no_cert
+ ts.verify(req, ca_store, [ts_cert_ee, intermediate_cert])
+ end
+
+ def test_verify_ee_no_cert_no_intermediate
+ assert_raise(OpenSSL::Timestamp::TimestampError) do
+ ts, req = timestamp_ee_no_cert
+ ts.verify(req, ca_store, [ts_cert_ee])
+ end
+ end
+
+ def test_verify_ee_additional_certs_array
+ req = OpenSSL::Timestamp::Request.new
+ req.algorithm = "SHA1"
+ digest = OpenSSL::Digest::SHA1.new.digest("test")
+ req.message_imprint = digest
+ req.policy_id = "1.2.3.4.5"
+ req.nonce = 42
+ fac = OpenSSL::Timestamp::Factory.new
+ fac.gen_time = Time.now
+ fac.serial_number = 1
+ fac.allowed_digests = ["sha1"]
+ fac.additional_certs = [intermediate_cert]
+ ts = fac.create_timestamp(ee_key, ts_cert_ee, req)
+ assert_equal(2, ts.token.certificates.size)
+ fac.additional_certs = nil
+ ts.verify(req, ca_store)
+ ts = fac.create_timestamp(ee_key, ts_cert_ee, req)
+ assert_equal(1, ts.token.certificates.size)
+ end
+
+ def test_verify_ee_additional_certs_with_root
+ req = OpenSSL::Timestamp::Request.new
+ req.algorithm = "SHA1"
+ digest = OpenSSL::Digest::SHA1.new.digest("test")
+ req.message_imprint = digest
+ req.policy_id = "1.2.3.4.5"
+ req.nonce = 42
+ fac = OpenSSL::Timestamp::Factory.new
+ fac.gen_time = Time.now
+ fac.serial_number = 1
+ fac.allowed_digests = ["sha1"]
+ fac.additional_certs = [intermediate_cert, ca_cert]
+ ts = fac.create_timestamp(ee_key, ts_cert_ee, req)
+ assert_equal(3, ts.token.certificates.size)
+ ts.verify(req, ca_store)
+ end
+
+ def test_verify_ee_cert_inclusion_not_requested
+ req = OpenSSL::Timestamp::Request.new
+ req.algorithm = "SHA1"
+ digest = OpenSSL::Digest::SHA1.new.digest("test")
+ req.message_imprint = digest
+ req.nonce = 42
+ req.cert_requested = false
+ fac = OpenSSL::Timestamp::Factory.new
+ fac.gen_time = Time.now
+ fac.serial_number = 1
+ fac.allowed_digests = ["sha1"]
+ #needed because the Request contained no policy identifier
+ fac.default_policy_id = '1.2.3.4.5'
+ fac.additional_certs = [ ts_cert_ee, intermediate_cert ]
+ ts = fac.create_timestamp(ee_key, ts_cert_ee, req)
+ assert_nil(ts.token.certificates) #since cert_requested? == false
+ ts.verify(req, ca_store, [ts_cert_ee, intermediate_cert])
+ end
+
+ def test_reusable
+ #test if req and faq are reusable, i.e. the internal
+ #CTX_free methods don't mess up e.g. the certificates
+ req = OpenSSL::Timestamp::Request.new
+ req.algorithm = "SHA1"
+ digest = OpenSSL::Digest::SHA1.new.digest("test")
+ req.message_imprint = digest
+ req.policy_id = "1.2.3.4.5"
+ req.nonce = 42
+
+ fac = OpenSSL::Timestamp::Factory.new
+ fac.gen_time = Time.now
+ fac.serial_number = 1
+ fac.allowed_digests = ["sha1"]
+ fac.additional_certs = [ intermediate_cert ]
+ ts1 = fac.create_timestamp(ee_key, ts_cert_ee, req)
+ ts1.verify(req, ca_store)
+ ts2 = fac.create_timestamp(ee_key, ts_cert_ee, req)
+ ts2.verify(req, ca_store)
+ refute_nil(ts1.tsa_certificate)
+ refute_nil(ts2.tsa_certificate)
+ end
+
+ def test_token_info_creation
+ req = OpenSSL::Timestamp::Request.new
+ req.algorithm = "SHA1"
+ digest = OpenSSL::Digest::SHA1.new.digest("test")
+ req.message_imprint = digest
+ req.policy_id = "1.2.3.4.5"
+ req.nonce = OpenSSL::BN.new(123)
+
+ fac = OpenSSL::Timestamp::Factory.new
+ time = Time.now
+ fac.gen_time = time
+ fac.serial_number = 1
+ fac.allowed_digests = ["sha1"]
+
+ resp = fac.create_timestamp(ee_key, ts_cert_ee, req)
+ info = resp.token_info
+ info = OpenSSL::Timestamp::TokenInfo.new(info.to_der)
+
+ assert_equal(1, info.version)
+ assert_equal("1.2.3.4.5", info.policy_id)
+ assert_equal("SHA1", info.algorithm)
+ assert_equal(digest, info.message_imprint)
+ assert_equal(1, info.serial_number)
+ assert_equal(time.to_i, info.gen_time.to_i)
+ assert_equal(false, info.ordering)
+ assert_equal(123, info.nonce)
+ end
+
+ private
+
+ def assert_cert expected, actual
+ assert_equal expected.to_der, actual.to_der
+ end
+
+ def timestamp_ee
+ req = OpenSSL::Timestamp::Request.new
+ req.algorithm = "SHA1"
+ digest = OpenSSL::Digest::SHA1.new.digest("test")
+ req.message_imprint = digest
+ req.policy_id = "1.2.3.4.5"
+ req.nonce = 42
+
+ fac = OpenSSL::Timestamp::Factory.new
+ fac.gen_time = Time.now
+ fac.serial_number = 1
+ fac.allowed_digests = ["sha1"]
+ return fac.create_timestamp(ee_key, ts_cert_ee, req), req
+ end
+
+ def timestamp_ee_no_cert
+ req = OpenSSL::Timestamp::Request.new
+ req.algorithm = "SHA1"
+ digest = OpenSSL::Digest::SHA1.new.digest("test")
+ req.message_imprint = digest
+ req.policy_id = "1.2.3.4.5"
+ req.nonce = 42
+ req.cert_requested = false
+
+ fac = OpenSSL::Timestamp::Factory.new
+ fac.gen_time = Time.now
+ fac.serial_number = 1
+ fac.allowed_digests = ["sha1"]
+ return fac.create_timestamp(ee_key, ts_cert_ee, req), req
+ end
+
+ def timestamp_direct
+ req = OpenSSL::Timestamp::Request.new
+ req.algorithm = "SHA1"
+ digest = OpenSSL::Digest::SHA1.new.digest("test")
+ req.message_imprint = digest
+ req.policy_id = "1.2.3.4.5"
+ req.nonce = 42
+
+ fac = OpenSSL::Timestamp::Factory.new
+ fac.gen_time = Time.now
+ fac.serial_number = 1
+ fac.allowed_digests = ["sha1"]
+ return fac.create_timestamp(ee_key, ts_cert_direct, req), req
+ end
+
+ def timestamp_direct_no_cert
+ req = OpenSSL::Timestamp::Request.new
+ req.algorithm = "SHA1"
+ digest = OpenSSL::Digest::SHA1.new.digest("test")
+ req.message_imprint = digest
+ req.policy_id = "1.2.3.4.5"
+ req.nonce = 42
+ req.cert_requested = false
+
+ fac = OpenSSL::Timestamp::Factory.new
+ fac.gen_time = Time.now
+ fac.serial_number = 1
+ fac.allowed_digests = ["sha1"]
+ return fac.create_timestamp(ee_key, ts_cert_direct, req), req
+ end
+end
+
+end
diff --git a/test/openssl/test_x509attr.rb b/test/openssl/test_x509attr.rb
index c6c48e86ab8..2919d23d2d3 100644
--- a/test/openssl/test_x509attr.rb
+++ b/test/openssl/test_x509attr.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative "utils"
if defined?(OpenSSL)
@@ -79,6 +79,16 @@ class OpenSSL::TestX509Attribute < OpenSSL::TestCase
assert_equal true, attr1 == attr2
assert_equal false, attr1 == attr3
end
+
+ def test_marshal
+ val = OpenSSL::ASN1::Set([
+ OpenSSL::ASN1::UTF8String("abc123")
+ ])
+ attr = OpenSSL::X509::Attribute.new("challengePassword", val)
+ deserialized = Marshal.load(Marshal.dump(attr))
+
+ assert_equal attr.to_der, deserialized.to_der
+ end
end
end
diff --git a/test/openssl/test_x509cert.rb b/test/openssl/test_x509cert.rb
index 40a5b0ad748..ed0758a6b3e 100644
--- a/test/openssl/test_x509cert.rb
+++ b/test/openssl/test_x509cert.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative "utils"
if defined?(OpenSSL)
@@ -73,9 +73,12 @@ class OpenSSL::TestX509Certificate < OpenSSL::TestCase
["basicConstraints","CA:TRUE",true],
["keyUsage","keyCertSign, cRLSign",true],
["subjectKeyIdentifier","hash",false],
- ["authorityKeyIdentifier","keyid:always",false],
+ ["authorityKeyIdentifier","issuer:always,keyid:always",false],
]
ca_cert = issue_cert(@ca, @rsa2048, 1, ca_exts, nil, nil)
+ keyid = get_subject_key_id(ca_cert.to_der, hex: false)
+ assert_equal keyid, ca_cert.authority_key_identifier
+ assert_equal keyid, ca_cert.subject_key_identifier
ca_cert.extensions.each_with_index{|ext, i|
assert_equal(ca_exts[i].first, ext.oid)
assert_equal(ca_exts[i].last, ext.critical?)
@@ -84,9 +87,10 @@ class OpenSSL::TestX509Certificate < OpenSSL::TestCase
ee1_exts = [
["keyUsage","Non Repudiation, Digital Signature, Key Encipherment",true],
["subjectKeyIdentifier","hash",false],
- ["authorityKeyIdentifier","keyid:always",false],
+ ["authorityKeyIdentifier","issuer:always,keyid:always",false],
["extendedKeyUsage","clientAuth, emailProtection, codeSigning",false],
["subjectAltName","email:ee1@ruby-lang.org",false],
+ ["authorityInfoAccess","caIssuers;URI:http://www.example.com/caIssuers,OCSP;URI:http://www.example.com/ocsp",false],
]
ee1_cert = issue_cert(@ee1, @rsa1024, 2, ee1_exts, ca_cert, @rsa2048)
assert_equal(ca_cert.subject.to_der, ee1_cert.issuer.to_der)
@@ -94,6 +98,78 @@ class OpenSSL::TestX509Certificate < OpenSSL::TestCase
assert_equal(ee1_exts[i].first, ext.oid)
assert_equal(ee1_exts[i].last, ext.critical?)
}
+ assert_nil(ee1_cert.crl_uris)
+
+ ef = OpenSSL::X509::ExtensionFactory.new
+ ef.config = OpenSSL::Config.parse(<<~_cnf_)
+ [crlDistPts]
+ URI.1 = http://www.example.com/crl
+ URI.2 = ldap://ldap.example.com/cn=ca?certificateRevocationList;binary
+ _cnf_
+ cdp_cert = generate_cert(@ee1, @rsa1024, 3, ca_cert)
+ ef.subject_certificate = cdp_cert
+ cdp_cert.add_extension(ef.create_extension("crlDistributionPoints", "@crlDistPts"))
+ cdp_cert.sign(@rsa2048, "sha256")
+ assert_equal(
+ ["http://www.example.com/crl", "ldap://ldap.example.com/cn=ca?certificateRevocationList;binary"],
+ cdp_cert.crl_uris
+ )
+
+ ef = OpenSSL::X509::ExtensionFactory.new
+ aia_cert = generate_cert(@ee1, @rsa1024, 4, ca_cert)
+ ef.subject_certificate = aia_cert
+ aia_cert.add_extension(
+ ef.create_extension(
+ "authorityInfoAccess",
+ "caIssuers;URI:http://www.example.com/caIssuers," \
+ "caIssuers;URI:ldap://ldap.example.com/cn=ca?authorityInfoAccessCaIssuers;binary," \
+ "OCSP;URI:http://www.example.com/ocsp," \
+ "OCSP;URI:ldap://ldap.example.com/cn=ca?authorityInfoAccessOcsp;binary",
+ false
+ )
+ )
+ aia_cert.sign(@rsa2048, "sha256")
+ assert_equal(
+ ["http://www.example.com/caIssuers", "ldap://ldap.example.com/cn=ca?authorityInfoAccessCaIssuers;binary"],
+ aia_cert.ca_issuer_uris
+ )
+ assert_equal(
+ ["http://www.example.com/ocsp", "ldap://ldap.example.com/cn=ca?authorityInfoAccessOcsp;binary"],
+ aia_cert.ocsp_uris
+ )
+
+ no_exts_cert = issue_cert(@ca, @rsa2048, 5, [], nil, nil)
+ assert_equal nil, no_exts_cert.authority_key_identifier
+ assert_equal nil, no_exts_cert.subject_key_identifier
+ assert_equal nil, no_exts_cert.crl_uris
+ assert_equal nil, no_exts_cert.ca_issuer_uris
+ assert_equal nil, no_exts_cert.ocsp_uris
+ end
+
+ def test_invalid_extension
+ integer = OpenSSL::ASN1::Integer.new(0)
+ invalid_exts_cert = generate_cert(@ee1, @rsa1024, 1, nil)
+ ["subjectKeyIdentifier", "authorityKeyIdentifier", "crlDistributionPoints", "authorityInfoAccess"].each do |ext|
+ invalid_exts_cert.add_extension(
+ OpenSSL::X509::Extension.new(ext, integer.to_der)
+ )
+ end
+
+ assert_raise(OpenSSL::ASN1::ASN1Error, "invalid extension") {
+ invalid_exts_cert.authority_key_identifier
+ }
+ assert_raise(OpenSSL::ASN1::ASN1Error, "invalid extension") {
+ invalid_exts_cert.subject_key_identifier
+ }
+ assert_raise(OpenSSL::ASN1::ASN1Error, "invalid extension") {
+ invalid_exts_cert.crl_uris
+ }
+ assert_raise(OpenSSL::ASN1::ASN1Error, "invalid extension") {
+ invalid_exts_cert.ca_issuer_uris
+ }
+ assert_raise(OpenSSL::ASN1::ASN1Error, "invalid extension") {
+ invalid_exts_cert.ocsp_uris
+ }
end
def test_sign_and_verify_rsa_sha1
@@ -189,6 +265,17 @@ class OpenSSL::TestX509Certificate < OpenSSL::TestCase
assert_equal false, cert3 == cert4
end
+ def test_marshal
+ now = Time.now
+ cacert = issue_cert(@ca, @rsa1024, 1, [], nil, nil,
+ not_before: now, not_after: now + 3600)
+ cert = issue_cert(@ee1, @rsa2048, 2, [], cacert, @rsa1024,
+ not_before: now, not_after: now + 3600)
+ deserialized = Marshal.load(Marshal.dump(cert))
+
+ assert_equal cert.to_der, deserialized.to_der
+ end
+
private
def certificate_error_returns_false
diff --git a/test/openssl/test_x509crl.rb b/test/openssl/test_x509crl.rb
index 03fdf64dd4f..a6d0adc5923 100644
--- a/test/openssl/test_x509crl.rb
+++ b/test/openssl/test_x509crl.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative "utils"
if defined?(OpenSSL)
@@ -118,7 +118,7 @@ class OpenSSL::TestX509CRL < OpenSSL::TestCase
["keyUsage", "cRLSign, keyCertSign", true],
]
crl_exts = [
- ["authorityKeyIdentifier", "keyid:always", false],
+ ["authorityKeyIdentifier", "issuer:always,keyid:always", false],
["issuerAltName", "issuer:copy", false],
]
@@ -131,6 +131,9 @@ class OpenSSL::TestX509CRL < OpenSSL::TestCase
assert_equal("crlNumber", exts[0].oid)
assert_equal(false, exts[0].critical?)
+ expected_keyid = OpenSSL::TestUtils.get_subject_key_id(cert, hex: false)
+ assert_equal expected_keyid, crl.authority_key_identifier
+
assert_equal("authorityKeyIdentifier", exts[1].oid)
keyid = OpenSSL::TestUtils.get_subject_key_id(cert)
assert_match(/^keyid:#{keyid}/, exts[1].value)
@@ -155,6 +158,10 @@ class OpenSSL::TestX509CRL < OpenSSL::TestCase
assert_equal("issuerAltName", exts[2].oid)
assert_equal("email:xyzzy@ruby-lang.org", exts[2].value)
assert_equal(false, exts[2].critical?)
+
+ no_ext_crl = issue_crl([], 1, Time.now, Time.now+1600, [],
+ cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+ assert_equal nil, no_ext_crl.authority_key_identifier
end
def test_crlnumber
@@ -249,6 +256,22 @@ class OpenSSL::TestX509CRL < OpenSSL::TestCase
assert_equal true, rev2 == crl2.revoked[1]
end
+ def test_marshal
+ now = Time.now
+
+ cacert = issue_cert(@ca, @rsa1024, 1, [], nil, nil)
+ crl = issue_crl([], 1, now, now + 3600, [], cacert, @rsa1024, "sha256")
+ rev = OpenSSL::X509::Revoked.new.tap { |rev|
+ rev.serial = 1
+ rev.time = now
+ }
+ crl.add_revoked(rev)
+ deserialized = Marshal.load(Marshal.dump(crl))
+
+ assert_equal crl.to_der, deserialized.to_der
+ assert_equal crl.revoked[0].to_der, deserialized.revoked[0].to_der
+ end
+
private
def crl_error_returns_false
diff --git a/test/openssl/test_x509ext.rb b/test/openssl/test_x509ext.rb
index 91ce202fecf..7ad010d1ed2 100644
--- a/test/openssl/test_x509ext.rb
+++ b/test/openssl/test_x509ext.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'utils'
if defined?(OpenSSL)
@@ -86,6 +86,19 @@ class OpenSSL::TestX509Extension < OpenSSL::TestCase
assert_equal true, ext1 == ext2
assert_equal false, ext1 == ext3
end
+
+ def test_marshal
+ ef = OpenSSL::X509::ExtensionFactory.new
+ ext = ef.create_extension("basicConstraints", "critical, CA:TRUE, pathlen:2")
+ deserialized = Marshal.load(Marshal.dump(ext))
+
+ assert_equal ext.to_der, deserialized.to_der
+ end
+
+ def test_value_der
+ ext = OpenSSL::X509::Extension.new(@basic_constraints.to_der)
+ assert_equal @basic_constraints_value.to_der, ext.value_der
+ end
end
end
diff --git a/test/openssl/test_x509name.rb b/test/openssl/test_x509name.rb
index 8a4596ea6ed..4ec5db20185 100644
--- a/test/openssl/test_x509name.rb
+++ b/test/openssl/test_x509name.rb
@@ -1,5 +1,5 @@
# coding: ASCII-8BIT
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'utils'
if defined?(OpenSSL)
@@ -242,16 +242,15 @@ class OpenSSL::TestX509Name < OpenSSL::TestCase
assert_match(/^multi-valued RDN is not supported: #{dn_r}/, ex.message)
}
- bad_dc = "exa#{"pm"}le" # <- typo of "example"
[
- ["DC=org,DC=#{bad_dc},CN", "CN"],
+ ["DC=org,DC=exapmle,CN", "CN"],
["DC=org,DC=example,", ""],
- ["DC=org,DC=#{bad_dc},CN=www.example.org;", "CN=www.example.org;"],
- ["DC=org,DC=#{bad_dc},CN=#www.example.org", "CN=#www.example.org"],
- ["DC=org,DC=#{bad_dc},CN=#777777.example.org", "CN=#777777.example.org"],
- ["DC=org,DC=#{bad_dc},CN=\"www.example\".org", "CN=\"www.example\".org"],
- ["DC=org,DC=#{bad_dc},CN=www.\"example.org\"", "CN=www.\"example.org\""],
- ["DC=org,DC=#{bad_dc},CN=www.\"example\".org", "CN=www.\"example\".org"],
+ ["DC=org,DC=exapmle,CN=www.example.org;", "CN=www.example.org;"],
+ ["DC=org,DC=exapmle,CN=#www.example.org", "CN=#www.example.org"],
+ ["DC=org,DC=exapmle,CN=#777777.example.org", "CN=#777777.example.org"],
+ ["DC=org,DC=exapmle,CN=\"www.example\".org", "CN=\"www.example\".org"],
+ ["DC=org,DC=exapmle,CN=www.\"example.org\"", "CN=www.\"example.org\""],
+ ["DC=org,DC=exapmle,CN=www.\"example\".org", "CN=www.\"example\".org"],
].each{|dn, msg|
ex = scanner.call(dn) rescue $!
assert_match(/^malformed RDN: .*=>#{Regexp.escape(msg)}/, ex.message)
@@ -390,7 +389,7 @@ class OpenSSL::TestX509Name < OpenSSL::TestCase
dn.each { |x| name.add_entry(*x) }
str = name.to_utf8
- expected = "CN=フー\\, バー,DC=ruby-lang,DC=org".force_encoding("UTF-8")
+ expected = String.new("CN=フー\\, バー,DC=ruby-lang,DC=org").force_encoding("UTF-8")
assert_equal expected, str
assert_equal Encoding.find("UTF-8"), str.encoding
@@ -403,6 +402,9 @@ class OpenSSL::TestX509Name < OpenSSL::TestCase
n2 = OpenSSL::X509::Name.parse_rfc2253 'CN=a'
assert_equal n1, n2
+
+ assert_equal(false, n1 == 'abc')
+ assert_equal(false, n2 == nil)
end
def test_spaceship
@@ -416,6 +418,9 @@ class OpenSSL::TestX509Name < OpenSSL::TestCase
assert_equal(-1, n2 <=> n3)
assert_equal(1, n3 <=> n1)
assert_equal(1, n3 <=> n2)
+ assert_equal(nil, n1 <=> 'abc')
+ assert_equal(nil, n2 <=> 123)
+ assert_equal(nil, n3 <=> nil)
end
def name_hash(name)
@@ -448,6 +453,13 @@ class OpenSSL::TestX509Name < OpenSSL::TestCase
assert_equal false, name0.eql?(name2)
end
+ def test_marshal
+ name = OpenSSL::X509::Name.new([["DC", "org"], ["DC", "ruby-lang"], ["CN", "bar.ruby-lang.org"]])
+ deserialized = Marshal.load(Marshal.dump(name))
+
+ assert_equal name.to_der, deserialized.to_der
+ end
+
def test_dup
name = OpenSSL::X509::Name.parse("/CN=ruby-lang.org")
assert_equal(name.to_der, name.dup.to_der)
diff --git a/test/openssl/test_x509req.rb b/test/openssl/test_x509req.rb
index 2c447ccdd51..bace06b3477 100644
--- a/test/openssl/test_x509req.rb
+++ b/test/openssl/test_x509req.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative "utils"
if defined?(OpenSSL)
@@ -151,6 +151,13 @@ class OpenSSL::TestX509Request < OpenSSL::TestCase
assert_equal false, req1 == req3
end
+ def test_marshal
+ req = issue_csr(0, @dn, @rsa1024, "sha256")
+ deserialized = Marshal.load(Marshal.dump(req))
+
+ assert_equal req.to_der, deserialized.to_der
+ end
+
private
def request_error_returns_false
diff --git a/test/openssl/test_x509store.rb b/test/openssl/test_x509store.rb
index 6412249b93e..8223c9b7acf 100644
--- a/test/openssl/test_x509store.rb
+++ b/test/openssl/test_x509store.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative "utils"
if defined?(OpenSSL)
diff --git a/test/openssl/ut_eof.rb b/test/openssl/ut_eof.rb
index bd62fd50f9d..cf1f2d423e0 100644
--- a/test/openssl/ut_eof.rb
+++ b/test/openssl/ut_eof.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'test/unit'
if defined?(OpenSSL)
@@ -18,12 +18,12 @@ module OpenSSL::TestEOF
assert_nil(f.read(1))
}
open_file("") {|f|
- s = "x"
+ s = +"x"
assert_equal("", f.read(nil, s))
assert_equal("", s)
}
open_file("") {|f|
- s = "x"
+ s = +"x"
assert_nil(f.read(10, s))
assert_equal("", s)
}
@@ -75,12 +75,12 @@ module OpenSSL::TestEOF
assert_equal("", f.read(0))
}
open_file("a") {|f|
- s = "x"
+ s = +"x"
assert_equal("a", f.read(nil, s))
assert_equal("a", s)
}
open_file("a") {|f|
- s = "x"
+ s = +"x"
assert_equal("a", f.read(10, s))
assert_equal("a", s)
}
diff --git a/test/openssl/utils.rb b/test/openssl/utils.rb
index bf191630521..acece979114 100644
--- a/test/openssl/utils.rb
+++ b/test/openssl/utils.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
begin
require "openssl"
@@ -52,15 +52,18 @@ module OpenSSL::TestUtils
@file_cache[[category, name]] ||=
File.read(File.join(__dir__, "fixtures", category, name + ".pem"))
end
+
+ def file_path(category, name)
+ File.join(__dir__, "fixtures", category, name)
+ end
end
module_function
- def issue_cert(dn, key, serial, extensions, issuer, issuer_key,
- not_before: nil, not_after: nil, digest: "sha256")
+ def generate_cert(dn, key, serial, issuer,
+ not_before: nil, not_after: nil)
cert = OpenSSL::X509::Certificate.new
issuer = cert unless issuer
- issuer_key = key unless issuer_key
cert.version = 2
cert.serial = serial
cert.subject = dn
@@ -69,6 +72,16 @@ module OpenSSL::TestUtils
now = Time.now
cert.not_before = not_before || now - 3600
cert.not_after = not_after || now + 3600
+ cert
+ end
+
+
+ def issue_cert(dn, key, serial, extensions, issuer, issuer_key,
+ not_before: nil, not_after: nil, digest: "sha256")
+ cert = generate_cert(dn, key, serial, issuer,
+ not_before: not_before, not_after: not_after)
+ issuer = cert unless issuer
+ issuer_key = key unless issuer_key
ef = OpenSSL::X509::ExtensionFactory.new
ef.subject_certificate = cert
ef.issuer_certificate = issuer
@@ -107,13 +120,18 @@ module OpenSSL::TestUtils
crl
end
- def get_subject_key_id(cert)
+ def get_subject_key_id(cert, hex: true)
asn1_cert = OpenSSL::ASN1.decode(cert)
tbscert = asn1_cert.value[0]
pkinfo = tbscert.value[6]
publickey = pkinfo.value[1]
pkvalue = publickey.value
- OpenSSL::Digest::SHA1.hexdigest(pkvalue).scan(/../).join(":").upcase
+ digest = OpenSSL::Digest::SHA1.digest(pkvalue)
+ if hex
+ digest.unpack("H2"*20).join(":").upcase
+ else
+ digest
+ end
end
def openssl?(major = nil, minor = nil, fix = nil, patch = 0)
@@ -189,6 +207,7 @@ class OpenSSL::SSLTestCase < OpenSSL::TestCase
def start_server(verify_mode: OpenSSL::SSL::VERIFY_NONE, start_immediately: true,
ctx_proc: nil, server_proc: method(:readwrite_loop),
+ accept_proc: proc{},
ignore_listener_error: false, &block)
IO.pipe {|stop_pipe_r, stop_pipe_w|
store = OpenSSL::X509::Store.new
@@ -222,6 +241,7 @@ class OpenSSL::SSLTestCase < OpenSSL::TestCase
readable, = IO.select([ssls, stop_pipe_r])
break if readable.include? stop_pipe_r
ssl = ssls.accept
+ accept_proc.call(ssl)
rescue OpenSSL::SSL::SSLError, IOError, Errno::EBADF, Errno::EINVAL,
Errno::ECONNABORTED, Errno::ENOTSOCK, Errno::ECONNRESET
retry if ignore_listener_error
@@ -268,7 +288,7 @@ class OpenSSL::SSLTestCase < OpenSSL::TestCase
begin
timeout = EnvUtil.apply_timeout_scale(30)
th.join(timeout) or
- th.raise(RuntimeError, "[start_server] thread did not exit in #{ timeout } secs")
+ th.raise(RuntimeError, "[start_server] thread did not exit in #{timeout} secs")
rescue (defined?(MiniTest::Skip) ? MiniTest::Skip : Test::Unit::PendedError)
# MiniTest::Skip is for the Ruby tree
pend = $!
@@ -316,4 +336,62 @@ class OpenSSL::PKeyTestCase < OpenSSL::TestCase
end
end
+module OpenSSL::Certs
+ include OpenSSL::TestUtils
+
+ module_function
+
+ def ca_cert
+ ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=Timestamp Root CA")
+
+ ca_exts = [
+ ["basicConstraints","CA:TRUE,pathlen:1",true],
+ ["keyUsage","keyCertSign, cRLSign",true],
+ ["subjectKeyIdentifier","hash",false],
+ ["authorityKeyIdentifier","keyid:always",false],
+ ]
+ OpenSSL::TestUtils.issue_cert(ca, Fixtures.pkey("rsa2048"), 1, ca_exts, nil, nil)
+ end
+
+ def ts_cert_direct(key, ca_cert)
+ dn = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/OU=Timestamp/CN=Server Direct")
+
+ exts = [
+ ["basicConstraints","CA:FALSE",true],
+ ["keyUsage","digitalSignature, nonRepudiation", true],
+ ["subjectKeyIdentifier", "hash",false],
+ ["authorityKeyIdentifier","keyid,issuer", false],
+ ["extendedKeyUsage", "timeStamping", true]
+ ]
+
+ OpenSSL::TestUtils.issue_cert(dn, key, 2, exts, ca_cert, Fixtures.pkey("rsa2048"))
+ end
+
+ def intermediate_cert(key, ca_cert)
+ dn = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/OU=Timestamp/CN=Timestamp Intermediate CA")
+
+ exts = [
+ ["basicConstraints","CA:TRUE,pathlen:0",true],
+ ["keyUsage","keyCertSign, cRLSign",true],
+ ["subjectKeyIdentifier","hash",false],
+ ["authorityKeyIdentifier","keyid:always",false],
+ ]
+
+ OpenSSL::TestUtils.issue_cert(dn, key, 3, exts, ca_cert, Fixtures.pkey("rsa2048"))
+ end
+
+ def ts_cert_ee(key, intermediate, im_key)
+ dn = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/OU=Timestamp/CN=Server End Entity")
+
+ exts = [
+ ["keyUsage","digitalSignature, nonRepudiation", true],
+ ["subjectKeyIdentifier", "hash",false],
+ ["authorityKeyIdentifier","keyid,issuer", false],
+ ["extendedKeyUsage", "timeStamping", true]
+ ]
+
+ OpenSSL::TestUtils.issue_cert(dn, key, 4, exts, intermediate, im_key)
+ end
+end
+
end