mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
609103dbb5
Import Ruby/OpenSSL 2.1.0.beta1. The full commit log since v2.0.5 (imported by r59567) can be found at: https://github.com/ruby/openssl/compare/v2.0.5...v2.1.0.beta1 ---------------------------------------------------------------- Antonio Terceiro (1): test/test_ssl: explicitly accept TLS 1.1 in corresponding test Colby Swandale (1): document using secure protocol to fetch git master in Bundler Colton Jenkins (1): Add fips_mode_get to return fips_mode Kazuki Yamaguchi (85): Start preparing for 2.1.0 Remove support for OpenSSL 0.9.8 and 1.0.0 bn: refine tests bn: implement unary {plus,minus} operators for OpenSSL::BN bn: implement OpenSSL::BN#negative? Don't define main() when built with --enable-debug test: let OpenSSL::TestCase include OpenSSL::TestUtils test: prepare test PKey instances on demand Add OpenSSL.print_mem_leaks Enable OSSL_MDEBUG on CI builds ssl: move default DH parameters from OpenSSL::PKey::DH Make exceptions with the same format regardless of OpenSSL.debug ssl: show reason of 'certificate verify error' in exception message ssl: remove OpenSSL::ExtConfig::TLS_DH_anon_WITH_AES_256_GCM_SHA384 ssl: do not confuse different ex_data index registries ssl: assume SSL/SSL_CTX always have a valid reference to the Ruby object Fix RDoc markup ssl: suppress compiler warning ext/openssl/deprecation.rb: remove broken-apple-openssl extconf.rb: print informative message if OpenSSL can't be found Rakefile: compile the extension before test kdf: introduce OpenSSL::KDF module ossl.h: add NUM2UINT64T() macro kdf: add scrypt Expand rb_define_copy_func() macro Expand FPTR_TO_FD() macro Remove SafeGet*() macros cipher: rename GetCipherPtr() to ossl_evp_get_cipherbyname() digest: rename GetDigestPtr() to ossl_evp_get_digestbyname() Add ossl_str_new(), an exception-safe rb_str_new() bio: simplify ossl_membio2str() using ossl_str_new() Remove unused functions and macros Drop support for LibreSSL 2.3 ocsp: add OpenSSL::OCSP::Request#signed? asn1: infinite length -> indefinite length asn1: rearrange tests ssl: remove a needless NULL check in SSL::SSLContext#ciphers ssl: return nil in SSL::SSLSocket#cipher if session is not started asn1: remove an unnecessary function prototype asn1: require tag information when instantiating generic type asn1: initialize 'unused_bits' attribute of BitString with 0 asn1: check for illegal 'unused_bits' value of BitString asn1: disallow NULL to be passed to asn1time_to_time() asn1: avoid truncating OID in OpenSSL::ASN1::ObjectId#oid asn1: allow constructed encoding with definite length form asn1: prohibit indefinite length form for primitive encoding asn1: allow tag number to be >= 32 for universal tag class asn1: use ossl_asn1_tag() asn1: clean up OpenSSL::ASN1::Constructive#to_der asn1: harmonize OpenSSL::ASN1::*#to_der asn1: prevent EOC octets from being in the middle of the content asn1: do not treat EOC octets as part of content octets x509name: add 'loc' and 'set' kwargs to OpenSSL::X509::Name#add_entry ssl: do not call session_remove_cb during GC Backport "Merge branch 'topic/test-memory-leak'" to maint cipher: update the documentation for Cipher#auth_tag= Rakefile: let sync:to_ruby know about test/openssl/fixtures test: fix formatting test/utils: remove OpenSSL::TestUtils.silent test/utils: add SSLTestCase#tls12_supported? test/utils: have start_server yield only the port number test/utils: do not set ecdh_curves in start_server test/utils: let server_loop close socket test/utils: improve error handling in start_server test/utils: add OpenSSL::TestUtils.openssl? and .libressl? test/utils: do not use DSA certificates in SSL tests test/test_ssl: remove test_invalid_shutdown_by_gc test/test_ssl: move test_multibyte_read_write to test_pair test/test_ssl_session: rearrange tests test/test_pair, test/test_ssl: fix for TLS 1.3 ssl: remove useless call to rb_thread_wait_fd() ssl: fix NPN support ssl: mark OpenSSL::SSL::SSLContext::DEFAULT_{1024,2048} as private ssl: use 2048-bit group in the default tmp_dh_cb ssl: ensure that SSL option flags are non-negative ssl: update OpenSSL::SSL::OP_* flags ssl: prefer TLS_method() over SSLv23_method() ssl: add SSLContext#min_version= and #max_version= ssl: rework SSLContext#ssl_version= test/test_x509name: change script encoding to ASCII-8BIT x509name: refactor OpenSSL::X509::Name#to_s x509name: add OpenSSL::X509::Name#to_utf8 x509name: add OpenSSL::X509::Name#inspect x509name: update regexp in OpenSSL::X509::Name.parse Ruby/OpenSSL 2.1.0.beta1 Marcus Stollsteimer (1): Fix rdoc for core Integer class nobu (4): [DOC] {read,write}_nonblock with exception: false [DOC] keyword argument _exception_ [DOC] mark up literals Revert r57690 except for read_nonblock git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@59734 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
315 lines
11 KiB
Ruby
315 lines
11 KiB
Ruby
# frozen_string_literal: false
|
|
require_relative 'utils'
|
|
|
|
if defined?(OpenSSL)
|
|
|
|
class OpenSSL::TestCipher < OpenSSL::TestCase
|
|
module Helper
|
|
def has_cipher?(name)
|
|
@ciphers ||= OpenSSL::Cipher.ciphers
|
|
@ciphers.include?(name)
|
|
end
|
|
end
|
|
include Helper
|
|
extend Helper
|
|
|
|
def test_encrypt_decrypt
|
|
# NIST SP 800-38A F.2.1
|
|
key = ["2b7e151628aed2a6abf7158809cf4f3c"].pack("H*")
|
|
iv = ["000102030405060708090a0b0c0d0e0f"].pack("H*")
|
|
pt = ["6bc1bee22e409f96e93d7e117393172a" \
|
|
"ae2d8a571e03ac9c9eb76fac45af8e51"].pack("H*")
|
|
ct = ["7649abac8119b246cee98e9b12e9197d" \
|
|
"5086cb9b507219ee95db113a917678b2"].pack("H*")
|
|
cipher = new_encryptor("aes-128-cbc", key: key, iv: iv, padding: 0)
|
|
assert_equal ct, cipher.update(pt) << cipher.final
|
|
cipher = new_decryptor("aes-128-cbc", key: key, iv: iv, padding: 0)
|
|
assert_equal pt, cipher.update(ct) << cipher.final
|
|
end
|
|
|
|
def test_pkcs5_keyivgen
|
|
pass = "\x00" * 8
|
|
salt = "\x01" * 8
|
|
num = 2048
|
|
pt = "data to be encrypted"
|
|
cipher = OpenSSL::Cipher.new("DES-EDE3-CBC").encrypt
|
|
cipher.pkcs5_keyivgen(pass, salt, num, "MD5")
|
|
s1 = cipher.update(pt) << cipher.final
|
|
|
|
d1 = num.times.inject(pass + salt) {|out, _| OpenSSL::Digest::MD5.digest(out) }
|
|
d2 = num.times.inject(d1 + pass + salt) {|out, _| OpenSSL::Digest::MD5.digest(out) }
|
|
key = (d1 + d2)[0, 24]
|
|
iv = (d1 + d2)[24, 8]
|
|
cipher = new_encryptor("DES-EDE3-CBC", key: key, iv: iv)
|
|
s2 = cipher.update(pt) << cipher.final
|
|
|
|
assert_equal s1, s2
|
|
end
|
|
|
|
def test_info
|
|
cipher = OpenSSL::Cipher.new("DES-EDE3-CBC").encrypt
|
|
assert_equal "DES-EDE3-CBC", cipher.name
|
|
assert_equal 24, cipher.key_len
|
|
assert_equal 8, cipher.iv_len
|
|
end
|
|
|
|
def test_dup
|
|
cipher = OpenSSL::Cipher.new("aes-128-cbc").encrypt
|
|
assert_equal cipher.name, cipher.dup.name
|
|
cipher.encrypt
|
|
cipher.random_key
|
|
cipher.random_iv
|
|
tmpc = cipher.dup
|
|
s1 = cipher.update("data") + cipher.final
|
|
s2 = tmpc.update("data") + tmpc.final
|
|
assert_equal(s1, s2, "encrypt dup")
|
|
end
|
|
|
|
def test_reset
|
|
cipher = OpenSSL::Cipher.new("aes-128-cbc").encrypt
|
|
cipher.encrypt
|
|
cipher.random_key
|
|
cipher.random_iv
|
|
s1 = cipher.update("data") + cipher.final
|
|
cipher.reset
|
|
s2 = cipher.update("data") + cipher.final
|
|
assert_equal(s1, s2, "encrypt reset")
|
|
end
|
|
|
|
def test_key_iv_set
|
|
cipher = OpenSSL::Cipher.new("DES-EDE3-CBC").encrypt
|
|
assert_raise(ArgumentError) { cipher.key = "\x01" * 23 }
|
|
assert_nothing_raised { cipher.key = "\x01" * 24 }
|
|
assert_raise(ArgumentError) { cipher.key = "\x01" * 25 }
|
|
assert_raise(ArgumentError) { cipher.iv = "\x01" * 7 }
|
|
assert_nothing_raised { cipher.iv = "\x01" * 8 }
|
|
assert_raise(ArgumentError) { cipher.iv = "\x01" * 9 }
|
|
end
|
|
|
|
def test_random_key_iv
|
|
data = "data"
|
|
s1, s2 = 2.times.map do
|
|
cipher = OpenSSL::Cipher.new("aes-128-cbc").encrypt
|
|
cipher.random_key
|
|
cipher.iv = "\x01" * 16
|
|
cipher.update(data) << cipher.final
|
|
end
|
|
assert_not_equal s1, s2
|
|
|
|
s1, s2 = 2.times.map do
|
|
cipher = OpenSSL::Cipher.new("aes-128-cbc").encrypt
|
|
cipher.key = "\x01" * 16
|
|
cipher.random_iv
|
|
cipher.update(data) << cipher.final
|
|
end
|
|
assert_not_equal s1, s2
|
|
end
|
|
|
|
def test_empty_data
|
|
cipher = OpenSSL::Cipher.new("DES-EDE3-CBC").encrypt
|
|
cipher.random_key
|
|
assert_raise(ArgumentError) { cipher.update("") }
|
|
end
|
|
|
|
def test_initialize
|
|
cipher = OpenSSL::Cipher.new("DES-EDE3-CBC")
|
|
assert_raise(RuntimeError) { cipher.__send__(:initialize, "DES-EDE3-CBC") }
|
|
assert_raise(RuntimeError) { OpenSSL::Cipher.allocate.final }
|
|
end
|
|
|
|
def test_ctr_if_exists
|
|
# NIST SP 800-38A F.5.1
|
|
key = ["2b7e151628aed2a6abf7158809cf4f3c"].pack("H*")
|
|
iv = ["f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"].pack("H*")
|
|
pt = ["6bc1bee22e409f96e93d7e117393172a" \
|
|
"ae2d8a571e03ac9c9eb76fac45af8e51"].pack("H*")
|
|
ct = ["874d6191b620e3261bef6864990db6ce" \
|
|
"9806f66b7970fdff8617187bb9fffdff"].pack("H*")
|
|
cipher = new_encryptor("aes-128-ctr", key: key, iv: iv, padding: 0)
|
|
assert_equal ct, cipher.update(pt) << cipher.final
|
|
cipher = new_decryptor("aes-128-ctr", key: key, iv: iv, padding: 0)
|
|
assert_equal pt, cipher.update(ct) << cipher.final
|
|
end
|
|
|
|
def test_ciphers
|
|
OpenSSL::Cipher.ciphers.each{|name|
|
|
next if /netbsd/ =~ RUBY_PLATFORM && /idea|rc5/i =~ name
|
|
begin
|
|
assert_kind_of(OpenSSL::Cipher, OpenSSL::Cipher.new(name))
|
|
rescue OpenSSL::Cipher::CipherError => e
|
|
raise unless /wrap/ =~ name and /wrap mode not allowed/ =~ e.message
|
|
end
|
|
}
|
|
end
|
|
|
|
def test_AES
|
|
pt = File.read(__FILE__)
|
|
%w(ECB CBC CFB OFB).each{|mode|
|
|
c1 = OpenSSL::Cipher::AES256.new(mode)
|
|
c1.encrypt
|
|
c1.pkcs5_keyivgen("passwd")
|
|
ct = c1.update(pt) + c1.final
|
|
|
|
c2 = OpenSSL::Cipher::AES256.new(mode)
|
|
c2.decrypt
|
|
c2.pkcs5_keyivgen("passwd")
|
|
assert_equal(pt, c2.update(ct) + c2.final)
|
|
}
|
|
end
|
|
|
|
def test_update_raise_if_key_not_set
|
|
assert_raise(OpenSSL::Cipher::CipherError) do
|
|
# it caused OpenSSL SEGV by uninitialized key [Bug #2768]
|
|
OpenSSL::Cipher::AES128.new("ECB").update "." * 17
|
|
end
|
|
end
|
|
|
|
def test_authenticated
|
|
cipher = OpenSSL::Cipher.new('aes-128-gcm')
|
|
assert_predicate(cipher, :authenticated?)
|
|
cipher = OpenSSL::Cipher.new('aes-128-cbc')
|
|
assert_not_predicate(cipher, :authenticated?)
|
|
end
|
|
|
|
def test_aes_gcm
|
|
# GCM spec Appendix B Test Case 4
|
|
key = ["feffe9928665731c6d6a8f9467308308"].pack("H*")
|
|
iv = ["cafebabefacedbaddecaf888"].pack("H*")
|
|
aad = ["feedfacedeadbeeffeedfacedeadbeef" \
|
|
"abaddad2"].pack("H*")
|
|
pt = ["d9313225f88406e5a55909c5aff5269a" \
|
|
"86a7a9531534f7da2e4c303d8a318a72" \
|
|
"1c3c0c95956809532fcf0e2449a6b525" \
|
|
"b16aedf5aa0de657ba637b39"].pack("H*")
|
|
ct = ["42831ec2217774244b7221b784d0d49c" \
|
|
"e3aa212f2c02a4e035c17e2329aca12e" \
|
|
"21d514b25466931c7d8f6a5aac84aa05" \
|
|
"1ba30b396a0aac973d58e091"].pack("H*")
|
|
tag = ["5bc94fbc3221a5db94fae95ae7121a47"].pack("H*")
|
|
|
|
cipher = new_encryptor("aes-128-gcm", key: key, iv: iv, auth_data: aad)
|
|
assert_equal ct, cipher.update(pt) << cipher.final
|
|
assert_equal tag, cipher.auth_tag
|
|
cipher = new_decryptor("aes-128-gcm", key: key, iv: iv, auth_tag: tag, auth_data: aad)
|
|
assert_equal pt, cipher.update(ct) << cipher.final
|
|
|
|
# truncated tag is accepted
|
|
cipher = new_encryptor("aes-128-gcm", key: key, iv: iv, auth_data: aad)
|
|
assert_equal ct, cipher.update(pt) << cipher.final
|
|
assert_equal tag[0, 8], cipher.auth_tag(8)
|
|
cipher = new_decryptor("aes-128-gcm", key: key, iv: iv, auth_tag: tag[0, 8], auth_data: aad)
|
|
assert_equal pt, cipher.update(ct) << cipher.final
|
|
|
|
# wrong tag is rejected
|
|
tag2 = tag.dup
|
|
tag2.setbyte(-1, (tag2.getbyte(-1) + 1) & 0xff)
|
|
cipher = new_decryptor("aes-128-gcm", key: key, iv: iv, auth_tag: tag2, auth_data: aad)
|
|
cipher.update(ct)
|
|
assert_raise(OpenSSL::Cipher::CipherError) { cipher.final }
|
|
|
|
# wrong aad is rejected
|
|
aad2 = aad[0..-2] << aad[-1].succ
|
|
cipher = new_decryptor("aes-128-gcm", key: key, iv: iv, auth_tag: tag, auth_data: aad2)
|
|
cipher.update(ct)
|
|
assert_raise(OpenSSL::Cipher::CipherError) { cipher.final }
|
|
|
|
# wrong ciphertext is rejected
|
|
ct2 = ct[0..-2] << ct[-1].succ
|
|
cipher = new_decryptor("aes-128-gcm", key: key, iv: iv, auth_tag: tag, auth_data: aad)
|
|
cipher.update(ct2)
|
|
assert_raise(OpenSSL::Cipher::CipherError) { cipher.final }
|
|
end
|
|
|
|
def test_aes_gcm_variable_iv_len
|
|
# GCM spec Appendix B Test Case 5
|
|
key = ["feffe9928665731c6d6a8f9467308308"].pack("H*")
|
|
iv = ["cafebabefacedbad"].pack("H*")
|
|
aad = ["feedfacedeadbeeffeedfacedeadbeef" \
|
|
"abaddad2"].pack("H*")
|
|
pt = ["d9313225f88406e5a55909c5aff5269a" \
|
|
"86a7a9531534f7da2e4c303d8a318a72" \
|
|
"1c3c0c95956809532fcf0e2449a6b525" \
|
|
"b16aedf5aa0de657ba637b39"].pack("H*")
|
|
ct = ["61353b4c2806934a777ff51fa22a4755" \
|
|
"699b2a714fcdc6f83766e5f97b6c7423" \
|
|
"73806900e49f24b22b097544d4896b42" \
|
|
"4989b5e1ebac0f07c23f4598"].pack("H*")
|
|
tag = ["3612d2e79e3b0785561be14aaca2fccb"].pack("H*")
|
|
|
|
cipher = new_encryptor("aes-128-gcm", key: key, iv_len: 8, iv: iv, auth_data: aad)
|
|
assert_equal ct, cipher.update(pt) << cipher.final
|
|
assert_equal tag, cipher.auth_tag
|
|
cipher = new_decryptor("aes-128-gcm", key: key, iv_len: 8, iv: iv, auth_tag: tag, auth_data: aad)
|
|
assert_equal pt, cipher.update(ct) << cipher.final
|
|
end
|
|
|
|
def test_aes_ocb_tag_len
|
|
# RFC 7253 Appendix A; the second sample
|
|
key = ["000102030405060708090A0B0C0D0E0F"].pack("H*")
|
|
iv = ["BBAA99887766554433221101"].pack("H*")
|
|
aad = ["0001020304050607"].pack("H*")
|
|
pt = ["0001020304050607"].pack("H*")
|
|
ct = ["6820B3657B6F615A"].pack("H*")
|
|
tag = ["5725BDA0D3B4EB3A257C9AF1F8F03009"].pack("H*")
|
|
|
|
cipher = new_encryptor("aes-128-ocb", key: key, iv: iv, auth_data: aad)
|
|
assert_equal ct, cipher.update(pt) << cipher.final
|
|
assert_equal tag, cipher.auth_tag
|
|
cipher = new_decryptor("aes-128-ocb", key: key, iv: iv, auth_tag: tag, auth_data: aad)
|
|
assert_equal pt, cipher.update(ct) << cipher.final
|
|
|
|
# RFC 7253 Appendix A; with 96 bits tag length
|
|
key = ["0F0E0D0C0B0A09080706050403020100"].pack("H*")
|
|
iv = ["BBAA9988776655443322110D"].pack("H*")
|
|
aad = ["000102030405060708090A0B0C0D0E0F1011121314151617" \
|
|
"18191A1B1C1D1E1F2021222324252627"].pack("H*")
|
|
pt = ["000102030405060708090A0B0C0D0E0F1011121314151617" \
|
|
"18191A1B1C1D1E1F2021222324252627"].pack("H*")
|
|
ct = ["1792A4E31E0755FB03E31B22116E6C2DDF9EFD6E33D536F1" \
|
|
"A0124B0A55BAE884ED93481529C76B6A"].pack("H*")
|
|
tag = ["D0C515F4D1CDD4FDAC4F02AA"].pack("H*")
|
|
|
|
cipher = new_encryptor("aes-128-ocb", auth_tag_len: 12, key: key, iv: iv, auth_data: aad)
|
|
assert_equal ct, cipher.update(pt) << cipher.final
|
|
assert_equal tag, cipher.auth_tag
|
|
cipher = new_decryptor("aes-128-ocb", auth_tag_len: 12, key: key, iv: iv, auth_tag: tag, auth_data: aad)
|
|
assert_equal pt, cipher.update(ct) << cipher.final
|
|
|
|
end if has_cipher?("aes-128-ocb")
|
|
|
|
def test_aes_gcm_key_iv_order_issue
|
|
pt = "[ruby/openssl#49]"
|
|
cipher = OpenSSL::Cipher.new("aes-128-gcm").encrypt
|
|
cipher.key = "x" * 16
|
|
cipher.iv = "a" * 12
|
|
ct1 = cipher.update(pt) << cipher.final
|
|
tag1 = cipher.auth_tag
|
|
|
|
cipher = OpenSSL::Cipher.new("aes-128-gcm").encrypt
|
|
cipher.iv = "a" * 12
|
|
cipher.key = "x" * 16
|
|
ct2 = cipher.update(pt) << cipher.final
|
|
tag2 = cipher.auth_tag
|
|
|
|
assert_equal ct1, ct2
|
|
assert_equal tag1, tag2
|
|
end
|
|
|
|
private
|
|
|
|
def new_encryptor(algo, **kwargs)
|
|
OpenSSL::Cipher.new(algo).tap do |cipher|
|
|
cipher.encrypt
|
|
kwargs.each {|k, v| cipher.send(:"#{k}=", v) }
|
|
end
|
|
end
|
|
|
|
def new_decryptor(algo, **kwargs)
|
|
OpenSSL::Cipher.new(algo).tap do |cipher|
|
|
cipher.decrypt
|
|
kwargs.each {|k, v| cipher.send(:"#{k}=", v) }
|
|
end
|
|
end
|
|
end
|
|
|
|
end
|