diff --git a/.rubocop.yml b/.rubocop.yml index 8257249b..1d75dcd9 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -136,3 +136,8 @@ Style/ConditionalAssignment: Exclude: # BUG: this seems like a bug in rubocop - 'lib/ronin/support/network/ip_range.rb' + +Layout/IndentationConsistency: + Exclude: + # BUG: this seems like a bug in rubocop + - 'lib/ronin/support/network/ssl.rb' diff --git a/lib/ronin/support/network/ssl.rb b/lib/ronin/support/network/ssl.rb index 9b767dfd..7d473fe3 100644 --- a/lib/ronin/support/network/ssl.rb +++ b/lib/ronin/support/network/ssl.rb @@ -104,6 +104,12 @@ def self.cert=(new_cert) # @param [1, 1.1, 1.2, Symbol, nil] version # The SSL version to use. # + # @param [1, 1.1, 1.2, Symbol, nil] min_version + # The minimum SSL version to use. + # + # @param [1, 1.1, 1.2, Symbol, nil] version + # The maximum SSL version to use. + # # @param [Symbol, Boolean] verify # Specifies whether to verify the SSL certificate. # May be one of the following: @@ -139,19 +145,29 @@ def self.cert=(new_cert) # # @since 1.0.0 # - def self.context(version: nil, - verify: :none, - key: nil, - key_file: nil, - cert: nil, - cert_file: nil, - ca_bundle: nil) + def self.context(version: nil, + min_version: nil, + max_version: nil, + verify: :none, + key: nil, + key_file: nil, + cert: nil, + cert_file: nil, + ca_bundle: nil) context = OpenSSL::SSL::SSLContext.new if version version = VERSIONS.fetch(version,version) context.min_version = context.max_version = version + else min_version || max_version + if min_version + context.min_version = VERSIONS.fetch(min_version,min_version) + end + + if max_version + context.max_version = VERSIONS.fetch(max_version,max_version) + end end context.verify_mode = VERIFY[verify] diff --git a/spec/network/ssl/mixin_spec.rb b/spec/network/ssl/mixin_spec.rb index 676b227a..10ff602f 100644 --- a/spec/network/ssl/mixin_spec.rb +++ b/spec/network/ssl/mixin_spec.rb @@ -128,6 +128,158 @@ end end + context "when given the min_version: keyword argument" do + let(:context) { double(OpenSSL::SSL::SSLContext) } + + context "and it's 1" do + it "must call OpenSSL::SSL::SSLContext#min_version= with OpenSSL::SSL::TLS1_VERSION" do + expect(OpenSSL::SSL::SSLContext).to receive(:new).and_return(context) + expect(context).to receive(:min_version=).with(OpenSSL::SSL::TLS1_VERSION) + allow(context).to receive(:verify_mode=).with(0) + + subject.ssl_context(min_version: 1) + end + end + + context "and it's 1.1" do + it "must call OpenSSL::SSL::SSLContext#min_version= with OpenSSL::SSL::TLS1_1_VERSION" do + expect(OpenSSL::SSL::SSLContext).to receive(:new).and_return(context) + expect(context).to receive(:min_version=).with(OpenSSL::SSL::TLS1_1_VERSION) + allow(context).to receive(:verify_mode=).with(0) + + subject.ssl_context(min_version: 1.1) + end + end + + context "and it's 1_2" do + it "must call OpenSSL::SSL::SSLContext#min_version= with OpenSSL::SSL::TLS1_2_VERSION" do + expect(OpenSSL::SSL::SSLContext).to receive(:new).and_return(context) + expect(context).to receive(:min_version=).with(OpenSSL::SSL::TLS1_2_VERSION) + allow(context).to receive(:verify_mode=).with(0) + + subject.ssl_context(min_version: 1.2) + end + end + + context "and it's a Symbol" do + let(:symbol) { :TLS1 } + + it "must call OpenSSL::SSL::SSLContext#min_version= with the Symbol" do + expect(OpenSSL::SSL::SSLContext).to receive(:new).and_return(context) + expect(context).to receive(:min_version=).with(symbol) + allow(context).to receive(:verify_mode=).with(0) + + subject.ssl_context(min_version: symbol) + end + + context "but it's :TLSv1" do + it "must call OpenSSL::SSL::SSLContext#min_version= with OpenSSL::SSL::TLS1_VERSION" do + expect(OpenSSL::SSL::SSLContext).to receive(:new).and_return(context) + expect(context).to receive(:min_version=).with(OpenSSL::SSL::TLS1_VERSION) + allow(context).to receive(:verify_mode=).with(0) + + subject.ssl_context(min_version: :TLSv1) + end + end + + context "but it's :TLSv1_1" do + it "must call OpenSSL::SSL::SSLContext#min_version= with OpenSSL::SSL::TLS1_1_VERSION" do + expect(OpenSSL::SSL::SSLContext).to receive(:new).and_return(context) + expect(context).to receive(:min_version=).with(OpenSSL::SSL::TLS1_1_VERSION) + allow(context).to receive(:verify_mode=).with(0) + + subject.ssl_context(min_version: :TLSv1_1) + end + end + + context "but it's :TLSv1_2" do + it "must call OpenSSL::SSL::SSLContext#min_version= with OpenSSL::SSL::TLS1_2_VERSION" do + expect(OpenSSL::SSL::SSLContext).to receive(:new).and_return(context) + expect(context).to receive(:min_version=).with(OpenSSL::SSL::TLS1_2_VERSION) + allow(context).to receive(:verify_mode=).with(0) + + subject.ssl_context(min_version: :TLSv1_2) + end + end + end + end + + context "when given the max_version: keyword argument" do + let(:context) { double(OpenSSL::SSL::SSLContext) } + + context "and it's 1" do + it "must call OpenSSL::SSL::SSLContext#max_version= with OpenSSL::SSL::TLS1_VERSION" do + expect(OpenSSL::SSL::SSLContext).to receive(:new).and_return(context) + expect(context).to receive(:max_version=).with(OpenSSL::SSL::TLS1_VERSION) + allow(context).to receive(:verify_mode=).with(0) + + subject.ssl_context(max_version: 1) + end + end + + context "and it's 1.1" do + it "must call OpenSSL::SSL::SSLContext#max_version= with OpenSSL::SSL::TLS1_1_VERSION" do + expect(OpenSSL::SSL::SSLContext).to receive(:new).and_return(context) + expect(context).to receive(:max_version=).with(OpenSSL::SSL::TLS1_1_VERSION) + allow(context).to receive(:verify_mode=).with(0) + + subject.ssl_context(max_version: 1.1) + end + end + + context "and it's 1_2" do + it "must call OpenSSL::SSL::SSLContext#max_version= with OpenSSL::SSL::TLS1_2_VERSION" do + expect(OpenSSL::SSL::SSLContext).to receive(:new).and_return(context) + expect(context).to receive(:max_version=).with(OpenSSL::SSL::TLS1_2_VERSION) + allow(context).to receive(:verify_mode=).with(0) + + subject.ssl_context(max_version: 1.2) + end + end + + context "and it's a Symbol" do + let(:symbol) { :TLS1 } + + it "must call OpenSSL::SSL::SSLContext#max_version= with the Symbol" do + expect(OpenSSL::SSL::SSLContext).to receive(:new).and_return(context) + expect(context).to receive(:max_version=).with(symbol) + allow(context).to receive(:verify_mode=).with(0) + + subject.ssl_context(max_version: symbol) + end + + context "but it's :TLSv1" do + it "must call OpenSSL::SSL::SSLContext#max_version= with OpenSSL::SSL::TLS1_VERSION" do + expect(OpenSSL::SSL::SSLContext).to receive(:new).and_return(context) + expect(context).to receive(:max_version=).with(OpenSSL::SSL::TLS1_VERSION) + allow(context).to receive(:verify_mode=).with(0) + + subject.ssl_context(max_version: :TLSv1) + end + end + + context "but it's :TLSv1_1" do + it "must call OpenSSL::SSL::SSLContext#max_version= with OpenSSL::SSL::TLS1_1_VERSION" do + expect(OpenSSL::SSL::SSLContext).to receive(:new).and_return(context) + expect(context).to receive(:max_version=).with(OpenSSL::SSL::TLS1_1_VERSION) + allow(context).to receive(:verify_mode=).with(0) + + subject.ssl_context(max_version: :TLSv1_1) + end + end + + context "but it's :TLSv1_2" do + it "must call OpenSSL::SSL::SSLContext#max_version= with OpenSSL::SSL::TLS1_2_VERSION" do + expect(OpenSSL::SSL::SSLContext).to receive(:new).and_return(context) + expect(context).to receive(:max_version=).with(OpenSSL::SSL::TLS1_2_VERSION) + allow(context).to receive(:verify_mode=).with(0) + + subject.ssl_context(max_version: :TLSv1_2) + end + end + end + end + describe "when given the verify: keyword argument" do subject { super().ssl_context(verify: :peer) } diff --git a/spec/network/ssl_spec.rb b/spec/network/ssl_spec.rb index ffced8d9..cf743dae 100644 --- a/spec/network/ssl_spec.rb +++ b/spec/network/ssl_spec.rb @@ -203,6 +203,162 @@ end end + context "when given the min_version: keyword argument" do + subject { described_class } + + let(:context) { double(OpenSSL::SSL::SSLContext) } + + context "and it's 1" do + it "must call OpenSSL::SSL::SSLContext#min_version= with OpenSSL::SSL::TLS1_VERSION" do + expect(OpenSSL::SSL::SSLContext).to receive(:new).and_return(context) + expect(context).to receive(:min_version=).with(OpenSSL::SSL::TLS1_VERSION) + allow(context).to receive(:verify_mode=).with(0) + + subject.context(min_version: 1) + end + end + + context "and it's 1.1" do + it "must call OpenSSL::SSL::SSLContext#min_version= with OpenSSL::SSL::TLS1_1_VERSION" do + expect(OpenSSL::SSL::SSLContext).to receive(:new).and_return(context) + expect(context).to receive(:min_version=).with(OpenSSL::SSL::TLS1_1_VERSION) + allow(context).to receive(:verify_mode=).with(0) + + subject.context(min_version: 1.1) + end + end + + context "and it's 1_2" do + it "must call OpenSSL::SSL::SSLContext#min_version= with OpenSSL::SSL::TLS1_2_VERSION" do + expect(OpenSSL::SSL::SSLContext).to receive(:new).and_return(context) + expect(context).to receive(:min_version=).with(OpenSSL::SSL::TLS1_2_VERSION) + allow(context).to receive(:verify_mode=).with(0) + + subject.context(min_version: 1.2) + end + end + + context "and it's a Symbol" do + let(:symbol) { :TLS1 } + + it "must call OpenSSL::SSL::SSLContext#min_version= with the Symbol" do + expect(OpenSSL::SSL::SSLContext).to receive(:new).and_return(context) + expect(context).to receive(:min_version=).with(symbol) + allow(context).to receive(:verify_mode=).with(0) + + subject.context(min_version: symbol) + end + + context "but it's :TLSv1" do + it "must call OpenSSL::SSL::SSLContext#min_version= with OpenSSL::SSL::TLS1_VERSION" do + expect(OpenSSL::SSL::SSLContext).to receive(:new).and_return(context) + expect(context).to receive(:min_version=).with(OpenSSL::SSL::TLS1_VERSION) + allow(context).to receive(:verify_mode=).with(0) + + subject.context(min_version: :TLSv1) + end + end + + context "but it's :TLSv1_1" do + it "must call OpenSSL::SSL::SSLContext#min_version= with OpenSSL::SSL::TLS1_1_VERSION" do + expect(OpenSSL::SSL::SSLContext).to receive(:new).and_return(context) + expect(context).to receive(:min_version=).with(OpenSSL::SSL::TLS1_1_VERSION) + allow(context).to receive(:verify_mode=).with(0) + + subject.context(min_version: :TLSv1_1) + end + end + + context "but it's :TLSv1_2" do + it "must call OpenSSL::SSL::SSLContext#min_version= with OpenSSL::SSL::TLS1_2_VERSION" do + expect(OpenSSL::SSL::SSLContext).to receive(:new).and_return(context) + expect(context).to receive(:min_version=).with(OpenSSL::SSL::TLS1_2_VERSION) + allow(context).to receive(:verify_mode=).with(0) + + subject.context(min_version: :TLSv1_2) + end + end + end + end + + context "when given the max_version: keyword argument" do + subject { described_class } + + let(:context) { double(OpenSSL::SSL::SSLContext) } + + context "and it's 1" do + it "must call OpenSSL::SSL::SSLContext#max_version= with OpenSSL::SSL::TLS1_VERSION" do + expect(OpenSSL::SSL::SSLContext).to receive(:new).and_return(context) + expect(context).to receive(:max_version=).with(OpenSSL::SSL::TLS1_VERSION) + allow(context).to receive(:verify_mode=).with(0) + + subject.context(max_version: 1) + end + end + + context "and it's 1.1" do + it "must call OpenSSL::SSL::SSLContext#max_version= with OpenSSL::SSL::TLS1_1_VERSION" do + expect(OpenSSL::SSL::SSLContext).to receive(:new).and_return(context) + expect(context).to receive(:max_version=).with(OpenSSL::SSL::TLS1_1_VERSION) + allow(context).to receive(:verify_mode=).with(0) + + subject.context(max_version: 1.1) + end + end + + context "and it's 1_2" do + it "must call OpenSSL::SSL::SSLContext#max_version= with OpenSSL::SSL::TLS1_2_VERSION" do + expect(OpenSSL::SSL::SSLContext).to receive(:new).and_return(context) + expect(context).to receive(:max_version=).with(OpenSSL::SSL::TLS1_2_VERSION) + allow(context).to receive(:verify_mode=).with(0) + + subject.context(max_version: 1.2) + end + end + + context "and it's a Symbol" do + let(:symbol) { :TLS1 } + + it "must call OpenSSL::SSL::SSLContext#max_version= with the Symbol" do + expect(OpenSSL::SSL::SSLContext).to receive(:new).and_return(context) + expect(context).to receive(:max_version=).with(symbol) + allow(context).to receive(:verify_mode=).with(0) + + subject.context(max_version: symbol) + end + + context "but it's :TLSv1" do + it "must call OpenSSL::SSL::SSLContext#max_version= with OpenSSL::SSL::TLS1_VERSION" do + expect(OpenSSL::SSL::SSLContext).to receive(:new).and_return(context) + expect(context).to receive(:max_version=).with(OpenSSL::SSL::TLS1_VERSION) + allow(context).to receive(:verify_mode=).with(0) + + subject.context(max_version: :TLSv1) + end + end + + context "but it's :TLSv1_1" do + it "must call OpenSSL::SSL::SSLContext#max_version= with OpenSSL::SSL::TLS1_1_VERSION" do + expect(OpenSSL::SSL::SSLContext).to receive(:new).and_return(context) + expect(context).to receive(:max_version=).with(OpenSSL::SSL::TLS1_1_VERSION) + allow(context).to receive(:verify_mode=).with(0) + + subject.context(max_version: :TLSv1_1) + end + end + + context "but it's :TLSv1_2" do + it "must call OpenSSL::SSL::SSLContext#max_version= with OpenSSL::SSL::TLS1_2_VERSION" do + expect(OpenSSL::SSL::SSLContext).to receive(:new).and_return(context) + expect(context).to receive(:max_version=).with(OpenSSL::SSL::TLS1_2_VERSION) + allow(context).to receive(:verify_mode=).with(0) + + subject.context(max_version: :TLSv1_2) + end + end + end + end + context "when given the verify: keyword argument" do subject { described_class.context(verify: :peer) }