diff options
Diffstat (limited to 'jni/ruby/test/openssl')
29 files changed, 6431 insertions, 0 deletions
diff --git a/jni/ruby/test/openssl/test_asn1.rb b/jni/ruby/test/openssl/test_asn1.rb new file mode 100644 index 0000000..9fb5a55 --- /dev/null +++ b/jni/ruby/test/openssl/test_asn1.rb @@ -0,0 +1,609 @@ +require_relative 'utils' + +class OpenSSL::TestASN1 < Test::Unit::TestCase + def test_decode + subj = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=TestCA") + key = OpenSSL::TestUtils::TEST_KEY_RSA1024 + now = Time.at(Time.now.to_i) # suppress usec + s = 0xdeadbeafdeadbeafdeadbeafdeadbeaf + exts = [ + ["basicConstraints","CA:TRUE,pathlen:1",true], + ["keyUsage","keyCertSign, cRLSign",true], + ["subjectKeyIdentifier","hash",false], + ] + dgst = OpenSSL::Digest::SHA1.new + cert = OpenSSL::TestUtils.issue_cert( + subj, key, s, now, now+3600, exts, nil, nil, dgst) + + + asn1 = OpenSSL::ASN1.decode(cert) + assert_equal(OpenSSL::ASN1::Sequence, asn1.class) + assert_equal(3, asn1.value.size) + tbs_cert, sig_alg, sig_val = *asn1.value + + assert_equal(OpenSSL::ASN1::Sequence, tbs_cert.class) + assert_equal(8, tbs_cert.value.size) + + version = tbs_cert.value[0] + assert_equal(:CONTEXT_SPECIFIC, version.tag_class) + assert_equal(0, version.tag) + assert_equal(1, version.value.size) + assert_equal(OpenSSL::ASN1::Integer, version.value[0].class) + assert_equal(2, version.value[0].value) + + serial = tbs_cert.value[1] + assert_equal(OpenSSL::ASN1::Integer, serial.class) + assert_equal(0xdeadbeafdeadbeafdeadbeafdeadbeaf, serial.value) + + sig = tbs_cert.value[2] + assert_equal(OpenSSL::ASN1::Sequence, sig.class) + assert_equal(2, sig.value.size) + assert_equal(OpenSSL::ASN1::ObjectId, sig.value[0].class) + assert_equal("1.2.840.113549.1.1.5", sig.value[0].oid) + assert_equal(OpenSSL::ASN1::Null, sig.value[1].class) + + dn = tbs_cert.value[3] # issuer + assert_equal(subj.hash, OpenSSL::X509::Name.new(dn).hash) + assert_equal(OpenSSL::ASN1::Sequence, dn.class) + assert_equal(3, dn.value.size) + assert_equal(OpenSSL::ASN1::Set, dn.value[0].class) + assert_equal(OpenSSL::ASN1::Set, dn.value[1].class) + assert_equal(OpenSSL::ASN1::Set, dn.value[2].class) + assert_equal(1, dn.value[0].value.size) + assert_equal(1, dn.value[1].value.size) + assert_equal(1, dn.value[2].value.size) + assert_equal(OpenSSL::ASN1::Sequence, dn.value[0].value[0].class) + assert_equal(OpenSSL::ASN1::Sequence, dn.value[1].value[0].class) + assert_equal(OpenSSL::ASN1::Sequence, dn.value[2].value[0].class) + assert_equal(2, dn.value[0].value[0].value.size) + assert_equal(2, dn.value[1].value[0].value.size) + assert_equal(2, dn.value[2].value[0].value.size) + oid, value = *dn.value[0].value[0].value + assert_equal(OpenSSL::ASN1::ObjectId, oid.class) + assert_equal("0.9.2342.19200300.100.1.25", oid.oid) + assert_equal(OpenSSL::ASN1::IA5String, value.class) + assert_equal("org", value.value) + oid, value = *dn.value[1].value[0].value + assert_equal(OpenSSL::ASN1::ObjectId, oid.class) + assert_equal("0.9.2342.19200300.100.1.25", oid.oid) + assert_equal(OpenSSL::ASN1::IA5String, value.class) + assert_equal("ruby-lang", value.value) + oid, value = *dn.value[2].value[0].value + assert_equal(OpenSSL::ASN1::ObjectId, oid.class) + assert_equal("2.5.4.3", oid.oid) + assert_equal(OpenSSL::ASN1::UTF8String, value.class) + assert_equal("TestCA", value.value) + + validity = tbs_cert.value[4] + assert_equal(OpenSSL::ASN1::Sequence, validity.class) + assert_equal(2, validity.value.size) + assert_equal(OpenSSL::ASN1::UTCTime, validity.value[0].class) + assert_equal(now, validity.value[0].value) + assert_equal(OpenSSL::ASN1::UTCTime, validity.value[1].class) + assert_equal(now+3600, validity.value[1].value) + + dn = tbs_cert.value[5] # subject + assert_equal(subj.hash, OpenSSL::X509::Name.new(dn).hash) + assert_equal(OpenSSL::ASN1::Sequence, dn.class) + assert_equal(3, dn.value.size) + assert_equal(OpenSSL::ASN1::Set, dn.value[0].class) + assert_equal(OpenSSL::ASN1::Set, dn.value[1].class) + assert_equal(OpenSSL::ASN1::Set, dn.value[2].class) + assert_equal(1, dn.value[0].value.size) + assert_equal(1, dn.value[1].value.size) + assert_equal(1, dn.value[2].value.size) + assert_equal(OpenSSL::ASN1::Sequence, dn.value[0].value[0].class) + assert_equal(OpenSSL::ASN1::Sequence, dn.value[1].value[0].class) + assert_equal(OpenSSL::ASN1::Sequence, dn.value[2].value[0].class) + assert_equal(2, dn.value[0].value[0].value.size) + assert_equal(2, dn.value[1].value[0].value.size) + assert_equal(2, dn.value[2].value[0].value.size) + oid, value = *dn.value[0].value[0].value + assert_equal(OpenSSL::ASN1::ObjectId, oid.class) + assert_equal("0.9.2342.19200300.100.1.25", oid.oid) + assert_equal(OpenSSL::ASN1::IA5String, value.class) + assert_equal("org", value.value) + oid, value = *dn.value[1].value[0].value + assert_equal(OpenSSL::ASN1::ObjectId, oid.class) + assert_equal("0.9.2342.19200300.100.1.25", oid.oid) + assert_equal(OpenSSL::ASN1::IA5String, value.class) + assert_equal("ruby-lang", value.value) + oid, value = *dn.value[2].value[0].value + assert_equal(OpenSSL::ASN1::ObjectId, oid.class) + assert_equal("2.5.4.3", oid.oid) + assert_equal(OpenSSL::ASN1::UTF8String, value.class) + assert_equal("TestCA", value.value) + + pkey = tbs_cert.value[6] + assert_equal(OpenSSL::ASN1::Sequence, pkey.class) + assert_equal(2, pkey.value.size) + assert_equal(OpenSSL::ASN1::Sequence, pkey.value[0].class) + assert_equal(2, pkey.value[0].value.size) + assert_equal(OpenSSL::ASN1::ObjectId, pkey.value[0].value[0].class) + assert_equal("1.2.840.113549.1.1.1", pkey.value[0].value[0].oid) + assert_equal(OpenSSL::ASN1::BitString, pkey.value[1].class) + assert_equal(0, pkey.value[1].unused_bits) + spkey = OpenSSL::ASN1.decode(pkey.value[1].value) + assert_equal(OpenSSL::ASN1::Sequence, spkey.class) + assert_equal(2, spkey.value.size) + assert_equal(OpenSSL::ASN1::Integer, spkey.value[0].class) + assert_equal(143085709396403084580358323862163416700436550432664688288860593156058579474547937626086626045206357324274536445865308750491138538454154232826011964045825759324933943290377903384882276841880081931690695505836279972214003660451338124170055999155993192881685495391496854691199517389593073052473319331505702779271, spkey.value[0].value) + assert_equal(OpenSSL::ASN1::Integer, spkey.value[1].class) + assert_equal(65537, spkey.value[1].value) + + extensions = tbs_cert.value[7] + assert_equal(:CONTEXT_SPECIFIC, extensions.tag_class) + assert_equal(3, extensions.tag) + assert_equal(1, extensions.value.size) + assert_equal(OpenSSL::ASN1::Sequence, extensions.value[0].class) + assert_equal(3, extensions.value[0].value.size) + + ext = extensions.value[0].value[0] # basicConstraints + assert_equal(OpenSSL::ASN1::Sequence, ext.class) + assert_equal(3, ext.value.size) + assert_equal(OpenSSL::ASN1::ObjectId, ext.value[0].class) + assert_equal("2.5.29.19", ext.value[0].oid) + assert_equal(OpenSSL::ASN1::Boolean, ext.value[1].class) + assert_equal(true, ext.value[1].value) + assert_equal(OpenSSL::ASN1::OctetString, ext.value[2].class) + extv = OpenSSL::ASN1.decode(ext.value[2].value) + assert_equal(OpenSSL::ASN1::Sequence, extv.class) + assert_equal(2, extv.value.size) + assert_equal(OpenSSL::ASN1::Boolean, extv.value[0].class) + assert_equal(true, extv.value[0].value) + assert_equal(OpenSSL::ASN1::Integer, extv.value[1].class) + assert_equal(1, extv.value[1].value) + + ext = extensions.value[0].value[1] # keyUsage + assert_equal(OpenSSL::ASN1::Sequence, ext.class) + assert_equal(3, ext.value.size) + assert_equal(OpenSSL::ASN1::ObjectId, ext.value[0].class) + assert_equal("2.5.29.15", ext.value[0].oid) + assert_equal(OpenSSL::ASN1::Boolean, ext.value[1].class) + assert_equal(true, ext.value[1].value) + 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 + assert_equal(str, extv.value) + + ext = extensions.value[0].value[2] # subjetKeyIdentifier + assert_equal(OpenSSL::ASN1::Sequence, ext.class) + assert_equal(2, ext.value.size) + assert_equal(OpenSSL::ASN1::ObjectId, ext.value[0].class) + assert_equal("2.5.29.14", ext.value[0].oid) + assert_equal(OpenSSL::ASN1::OctetString, ext.value[1].class) + extv = OpenSSL::ASN1.decode(ext.value[1].value) + assert_equal(OpenSSL::ASN1::OctetString, extv.class) + sha1 = OpenSSL::Digest::SHA1.new + sha1.update(pkey.value[1].value) + assert_equal(sha1.digest, extv.value) + + assert_equal(OpenSSL::ASN1::Sequence, sig_alg.class) + assert_equal(2, sig_alg.value.size) + assert_equal(OpenSSL::ASN1::ObjectId, pkey.value[0].value[0].class) + assert_equal("1.2.840.113549.1.1.1", pkey.value[0].value[0].oid) + assert_equal(OpenSSL::ASN1::Null, pkey.value[0].value[1].class) + + assert_equal(OpenSSL::ASN1::BitString, sig_val.class) + cululated_sig = key.sign(OpenSSL::Digest::SHA1.new, tbs_cert.to_der) + assert_equal(cululated_sig, sig_val.value) + end + + def test_encode_boolean + encode_decode_test(OpenSSL::ASN1::Boolean, [true, false]) + end + + def test_encode_integer + encode_decode_test(OpenSSL::ASN1::Integer, [72, -127, -128, 128, -1, 0, 1, -(2**12345), 2**12345]) + end + + def test_encode_nil + m = OpenSSL::ASN1 + [ + m::Boolean, m::Integer, m::BitString, m::OctetString, + m::ObjectId, m::Enumerated, m::UTF8String, m::UTCTime, + m::GeneralizedTime, m::Sequence, m::Set + ].each do |klass| + #Primitives raise TypeError, Constructives NoMethodError + assert_raise(TypeError, NoMethodError) { klass.send(:new, nil).to_der } + end + end + + def encode_decode_test(type, values) + values.each do |v| + assert_equal(v, OpenSSL::ASN1.decode(type.new(v).to_der).value) + end + end + + def test_decode_pem #should fail gracefully (cf. [ruby-dev:44542]) + pem = <<-_EOS_ +-----BEGIN CERTIFICATE----- +MIIC8zCCAdugAwIBAgIBATANBgkqhkiG9w0BAQUFADA9MRMwEQYKCZImiZPyLGQB +GRYDb3JnMRkwFwYKCZImiZPyLGQBGRYJcnVieS1sYW5nMQswCQYDVQQDDAJDQTAe +Fw0xMTA5MjUxMzQ4MjZaFw0xMTA5MjUxNDQ4MjZaMD0xEzARBgoJkiaJk/IsZAEZ +FgNvcmcxGTAXBgoJkiaJk/IsZAEZFglydWJ5LWxhbmcxCzAJBgNVBAMMAkNBMIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuV9ht9J7k4NBs38jOXvvTKY9 +gW8nLICSno5EETR1cuF7i4pNs9I1QJGAFAX0BEO4KbzXmuOvfCpD3CU+Slp1enen +fzq/t/e/1IRW0wkJUJUFQign4CtrkJL+P07yx18UjyPlBXb81ApEmAB5mrJVSrWm +qbjs07JbuS4QQGGXLc+Su96DkYKmSNVjBiLxVVSpyZfAY3hD37d60uG+X8xdW5v6 +8JkRFIhdGlb6JL8fllf/A/blNwdJOhVr9mESHhwGjwfSeTDPfd8ZLE027E5lyAVX +9KZYcU00mOX+fdxOSnGqS/8JDRh0EPHDL15RcJjV2J6vZjPb0rOYGDoMcH+94wID +AQABMA0GCSqGSIb3DQEBBQUAA4IBAQAiAtrIr1pLX4GYN5klviWKb8HC9ICYuAFI +NfE3FwqzErEVXotuMe3yPVyB3Bv6rjYY/x5EtS5+WPTbHlvHZTkfcsnTpizcn4mW +dJ6dDRaFCHt1YKKjUxqBt9lvvrc3nReYZN/P+s1mrDhWzGf8iPZgf8sFUHgnaK7W +CXRVXmPFgCDRNpDDVQ0MQkr509yYfTH+dujNzqTCwSvkyZFyQ7Oe8Yj0VR6kquG3 +rEzBQ0F9dUyqQ9gyRg8KHhDfv9HzT1d/rnUZMkoombwYBRIUChGCYV0GnJcan2Zm +/93PnPG1IvPjYNd5VlV+sXSnaxQn974HRCsMv7jA8BD6IgSaX6WK +-----END CERTIFICATE----- + _EOS_ + assert_raise(OpenSSL::ASN1::ASN1Error) { OpenSSL::ASN1.decode(pem) } + assert_raise(OpenSSL::ASN1::ASN1Error) { OpenSSL::ASN1.decode_all(pem) } + end + + def test_primitive_cannot_set_infinite_length + begin + prim = OpenSSL::ASN1::Integer.new(50) + assert_equal(false, prim.infinite_length) + prim.infinite_length = true + flunk('Could set infinite length on primitive value') + rescue NoMethodError + #ok + end + end + + def test_decode_all + expected = %w{ 02 01 01 02 01 02 02 01 03 } + raw = [expected.join('')].pack('H*') + ary = OpenSSL::ASN1.decode_all(raw) + assert_equal(3, ary.size) + ary.each_with_index do |asn1, i| + assert_universal(OpenSSL::ASN1::INTEGER, asn1) + assert_equal(i + 1, asn1.value) + end + end + + def test_decode_utctime + expected = Time.at 1374535380 + assert_equal expected, OpenSSL::ASN1.decode("\x17\v1307222323Z").value + + expected += 17 + assert_equal expected, OpenSSL::ASN1.decode("\x17\r130722232317Z").value + end + + def test_create_inf_length_primitive + expected = %w{ 24 80 04 01 61 00 00 } + raw = [expected.join('')].pack('H*') + val = OpenSSL::ASN1::OctetString.new('a') + cons = OpenSSL::ASN1::Constructive.new([val, + OpenSSL::ASN1::EndOfContent.new], + OpenSSL::ASN1::OCTET_STRING, + nil, + :UNIVERSAL) + cons.infinite_length = true + assert_equal(nil, cons.tagging) + assert_equal(raw, cons.to_der) + asn1 = OpenSSL::ASN1.decode(raw) + assert(asn1.infinite_length) + assert_equal(raw, asn1.to_der) + end + + def test_cons_without_inf_length_forbidden + assert_raise(OpenSSL::ASN1::ASN1Error) do + val = OpenSSL::ASN1::OctetString.new('a') + cons = OpenSSL::ASN1::Constructive.new([val], + OpenSSL::ASN1::OCTET_STRING, + nil, + :UNIVERSAL) + cons.to_der + end + end + + def test_cons_without_array_forbidden + assert_raise(OpenSSL::ASN1::ASN1Error) do + val = OpenSSL::ASN1::OctetString.new('a') + cons = OpenSSL::ASN1::Constructive.new(val, + OpenSSL::ASN1::OCTET_STRING, + nil, + :UNIVERSAL) + cons.infinite_length = true + cons.to_der + end + end + + def test_parse_empty_sequence + expected = %w{ A0 07 30 02 30 00 02 01 00 } + raw = [expected.join('')].pack('H*') + asn1 = OpenSSL::ASN1.decode(raw) + assert_equal(raw, asn1.to_der) + assert_equal(2, asn1.value.size) + seq = asn1.value[0] + assert_equal(1, seq.value.size) + inner_seq = seq.value[0] + assert_equal(0, inner_seq.value.size) + end + + def test_parse_tagged_0_infinite + expected = %w{ 30 80 02 01 01 80 01 02 00 00 } + raw = [expected.join('')].pack('H*') + asn1 = OpenSSL::ASN1.decode(raw) + assert_equal(3, asn1.value.size) + int = asn1.value[0] + assert_universal(OpenSSL::ASN1::INTEGER, int) + tagged = asn1.value[1] + assert_equal(0, tagged.tag) + assert_universal(OpenSSL::ASN1::EOC, asn1.value[2]) + assert_equal(raw, asn1.to_der) + end + + def test_seq_infinite_length + begin + content = [ OpenSSL::ASN1::Null.new(nil), + OpenSSL::ASN1::EndOfContent.new ] + cons = OpenSSL::ASN1::Sequence.new(content) + cons.infinite_length = true + expected = %w{ 30 80 05 00 00 00 } + raw = [expected.join('')].pack('H*') + assert_equal(raw, cons.to_der) + assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) + end + end + + def test_set_infinite_length + begin + content = [ OpenSSL::ASN1::Null.new(nil), + OpenSSL::ASN1::EndOfContent.new() ] + cons = OpenSSL::ASN1::Set.new(content) + cons.infinite_length = true + expected = %w{ 31 80 05 00 00 00 } + raw = [expected.join('')].pack('H*') + assert_equal(raw, cons.to_der) + assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) + end + end + + def test_octet_string_infinite_length + begin + octets = [ OpenSSL::ASN1::OctetString.new('aaa'), + OpenSSL::ASN1::EndOfContent.new() ] + cons = OpenSSL::ASN1::Constructive.new( + octets, + OpenSSL::ASN1::OCTET_STRING, + nil, + :UNIVERSAL) + cons.infinite_length = true + expected = %w{ 24 80 04 03 61 61 61 00 00 } + raw = [expected.join('')].pack('H*') + assert_equal(raw, cons.to_der) + assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) + end + end + + def test_prim_explicit_tagging + begin + oct_str = OpenSSL::ASN1::OctetString.new("a", 0, :EXPLICIT) + expected = %w{ A0 03 04 01 61 } + raw = [expected.join('')].pack('H*') + assert_equal(raw, oct_str.to_der) + assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) + end + end + + def test_prim_explicit_tagging_tag_class + begin + oct_str = OpenSSL::ASN1::OctetString.new("a", 0, :EXPLICIT) + oct_str2 = OpenSSL::ASN1::OctetString.new( + "a", + 0, + :EXPLICIT, + :CONTEXT_SPECIFIC) + assert_equal(oct_str.to_der, oct_str2.to_der) + end + end + + def test_prim_implicit_tagging + begin + int = OpenSSL::ASN1::Integer.new(1, 0, :IMPLICIT) + expected = %w{ 80 01 01 } + raw = [expected.join('')].pack('H*') + assert_equal(raw, int.to_der) + assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) + end + end + + def test_prim_implicit_tagging_tag_class + begin + int = OpenSSL::ASN1::Integer.new(1, 0, :IMPLICIT) + int2 = OpenSSL::ASN1::Integer.new(1, 0, :IMPLICIT, :CONTEXT_SPECIFIC); + assert_equal(int.to_der, int2.to_der) + end + end + + def test_cons_explicit_tagging + begin + content = [ OpenSSL::ASN1::PrintableString.new('abc') ] + seq = OpenSSL::ASN1::Sequence.new(content, 2, :EXPLICIT) + expected = %w{ A2 07 30 05 13 03 61 62 63 } + raw = [expected.join('')].pack('H*') + assert_equal(raw, seq.to_der) + assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) + end + end + + def test_cons_explicit_tagging_inf_length + begin + content = [ OpenSSL::ASN1::PrintableString.new('abc') , + OpenSSL::ASN1::EndOfContent.new() ] + seq = OpenSSL::ASN1::Sequence.new(content, 2, :EXPLICIT) + seq.infinite_length = true + expected = %w{ A2 80 30 80 13 03 61 62 63 00 00 00 00 } + raw = [expected.join('')].pack('H*') + assert_equal(raw, seq.to_der) + assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) + end + end + + def test_cons_implicit_tagging + begin + content = [ OpenSSL::ASN1::Null.new(nil) ] + seq = OpenSSL::ASN1::Sequence.new(content, 1, :IMPLICIT) + expected = %w{ A1 02 05 00 } + raw = [expected.join('')].pack('H*') + assert_equal(raw, seq.to_der) + assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) + end + end + + def test_cons_implicit_tagging_inf_length + begin + content = [ OpenSSL::ASN1::Null.new(nil), + OpenSSL::ASN1::EndOfContent.new() ] + seq = OpenSSL::ASN1::Sequence.new(content, 1, :IMPLICIT) + seq.infinite_length = true + expected = %w{ A1 80 05 00 00 00 } + raw = [expected.join('')].pack('H*') + assert_equal(raw, seq.to_der) + assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) + end + end + + def test_octet_string_infinite_length_explicit_tagging + begin + octets = [ OpenSSL::ASN1::OctetString.new('aaa'), + OpenSSL::ASN1::EndOfContent.new() ] + cons = OpenSSL::ASN1::Constructive.new( + octets, + 1, + :EXPLICIT) + cons.infinite_length = true + expected = %w{ A1 80 24 80 04 03 61 61 61 00 00 00 00 } + raw = [expected.join('')].pack('H*') + assert_equal(raw, cons.to_der) + assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) + end + end + + def test_octet_string_infinite_length_implicit_tagging + begin + octets = [ OpenSSL::ASN1::OctetString.new('aaa'), + OpenSSL::ASN1::EndOfContent.new() ] + cons = OpenSSL::ASN1::Constructive.new( + octets, + 0, + :IMPLICIT) + cons.infinite_length = true + expected = %w{ A0 80 04 03 61 61 61 00 00 } + raw = [expected.join('')].pack('H*') + assert_equal(raw, cons.to_der) + assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) + end + end + + def test_recursive_octet_string_infinite_length + begin + octets_sub1 = [ OpenSSL::ASN1::OctetString.new("\x01"), + OpenSSL::ASN1::EndOfContent.new() ] + octets_sub2 = [ OpenSSL::ASN1::OctetString.new("\x02"), + OpenSSL::ASN1::EndOfContent.new() ] + container1 = OpenSSL::ASN1::Constructive.new( + octets_sub1, + OpenSSL::ASN1::OCTET_STRING, + nil, + :UNIVERSAL) + container1.infinite_length = true + container2 = OpenSSL::ASN1::Constructive.new( + octets_sub2, + OpenSSL::ASN1::OCTET_STRING, + nil, + :UNIVERSAL) + container2.infinite_length = true + octets3 = OpenSSL::ASN1::OctetString.new("\x03") + + octets = [ container1, container2, octets3, + OpenSSL::ASN1::EndOfContent.new() ] + cons = OpenSSL::ASN1::Constructive.new( + octets, + OpenSSL::ASN1::OCTET_STRING, + nil, + :UNIVERSAL) + cons.infinite_length = true + expected = %w{ 24 80 24 80 04 01 01 00 00 24 80 04 01 02 00 00 04 01 03 00 00 } + raw = [expected.join('')].pack('H*') + assert_equal(raw, cons.to_der) + assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) + end + end + + def test_bit_string_infinite_length + begin + content = [ OpenSSL::ASN1::BitString.new("\x01"), + OpenSSL::ASN1::EndOfContent.new() ] + cons = OpenSSL::ASN1::Constructive.new( + content, + OpenSSL::ASN1::BIT_STRING, + nil, + :UNIVERSAL) + cons.infinite_length = true + expected = %w{ 23 80 03 02 00 01 00 00 } + raw = [expected.join('')].pack('H*') + assert_equal(raw, cons.to_der) + assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) + end + end + + def test_primitive_inf_length + assert_raises(OpenSSL::ASN1::ASN1Error) do + spec = %w{ 02 80 02 01 01 00 00 } + raw = [spec.join('')].pack('H*') + OpenSSL::ASN1.decode(raw) + OpenSSL::ASN1.decode_all(raw) + end + end + + def test_recursive_octet_string_parse + test = %w{ 24 80 24 80 04 01 01 00 00 24 80 04 01 02 00 00 04 01 03 00 00 } + raw = [test.join('')].pack('H*') + asn1 = OpenSSL::ASN1.decode(raw) + assert_equal(OpenSSL::ASN1::Constructive, asn1.class) + assert_universal(OpenSSL::ASN1::OCTET_STRING, asn1) + assert_equal(true, asn1.infinite_length) + assert_equal(4, asn1.value.size) + nested1 = asn1.value[0] + assert_equal(OpenSSL::ASN1::Constructive, nested1.class) + assert_universal(OpenSSL::ASN1::OCTET_STRING, nested1) + assert_equal(true, nested1.infinite_length) + assert_equal(2, nested1.value.size) + oct1 = nested1.value[0] + assert_universal(OpenSSL::ASN1::OCTET_STRING, oct1) + assert_equal(false, oct1.infinite_length) + assert_universal(OpenSSL::ASN1::EOC, nested1.value[1]) + assert_equal(false, nested1.value[1].infinite_length) + nested2 = asn1.value[1] + assert_equal(OpenSSL::ASN1::Constructive, nested2.class) + assert_universal(OpenSSL::ASN1::OCTET_STRING, nested2) + assert_equal(true, nested2.infinite_length) + assert_equal(2, nested2.value.size) + oct2 = nested2.value[0] + assert_universal(OpenSSL::ASN1::OCTET_STRING, oct2) + assert_equal(false, oct2.infinite_length) + assert_universal(OpenSSL::ASN1::EOC, nested2.value[1]) + assert_equal(false, nested2.value[1].infinite_length) + oct3 = asn1.value[2] + assert_universal(OpenSSL::ASN1::OCTET_STRING, oct3) + assert_equal(false, oct3.infinite_length) + assert_universal(OpenSSL::ASN1::EOC, asn1.value[3]) + assert_equal(false, asn1.value[3].infinite_length) + end + + private + + def assert_universal(tag, asn1) + assert_equal(tag, asn1.tag) + if asn1.respond_to?(:tagging) + assert_nil(asn1.tagging) + end + assert_equal(:UNIVERSAL, asn1.tag_class) + end + +end if defined?(OpenSSL::TestUtils) + diff --git a/jni/ruby/test/openssl/test_bn.rb b/jni/ruby/test/openssl/test_bn.rb new file mode 100644 index 0000000..667cb29 --- /dev/null +++ b/jni/ruby/test/openssl/test_bn.rb @@ -0,0 +1,52 @@ +require_relative 'utils' + +if defined?(OpenSSL::TestUtils) + +class OpenSSL::TestBN < Test::Unit::TestCase + def test_new_str + e1 = OpenSSL::BN.new(999.to_s(16), 16) # OpenSSL::BN.new(str, 16) must be most stable + e2 = OpenSSL::BN.new((2**107-1).to_s(16), 16) + assert_equal(e1, OpenSSL::BN.new("999")) + assert_equal(e2, OpenSSL::BN.new((2**107-1).to_s)) + assert_equal(e1, OpenSSL::BN.new("999", 10)) + assert_equal(e2, OpenSSL::BN.new((2**107-1).to_s, 10)) + assert_equal(e1, OpenSSL::BN.new("\x03\xE7", 2)) + assert_equal(e2, OpenSSL::BN.new("\a\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 2)) + assert_equal(e1, OpenSSL::BN.new("\x00\x00\x00\x02\x03\xE7", 0)) + assert_equal(e2, OpenSSL::BN.new("\x00\x00\x00\x0E\a\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 0)) + end + + def test_new_bn + e1 = OpenSSL::BN.new(999.to_s(16), 16) + e2 = OpenSSL::BN.new((2**107-1).to_s(16), 16) + assert_equal(e1, OpenSSL::BN.new(e1)) + assert_equal(e2, OpenSSL::BN.new(e2)) + end + + def test_new_integer + assert_equal(999.to_bn, OpenSSL::BN.new(999)) + assert_equal((2 ** 107 - 1).to_bn, OpenSSL::BN.new(2 ** 107 - 1)) + assert_equal(-999.to_bn, OpenSSL::BN.new(-999)) + assert_equal((-(2 ** 107 - 1)).to_bn, OpenSSL::BN.new(-(2 ** 107 - 1))) + end + + def test_to_bn + e1 = OpenSSL::BN.new(999.to_s(16), 16) + e2 = OpenSSL::BN.new((2**107-1).to_s(16), 16) + assert_equal(e1, 999.to_bn) + assert_equal(e2, (2**107-1).to_bn) + end + + def test_prime_p + assert_equal(true, OpenSSL::BN.new((2 ** 107 - 1).to_s(16), 16).prime?) + assert_equal(true, OpenSSL::BN.new((2 ** 127 - 1).to_s(16), 16).prime?(1)) + end + + def test_cmp_nil + bn = OpenSSL::BN.new('1') + assert_equal(false, bn == nil) + assert_equal(true, bn != nil) + end +end + +end diff --git a/jni/ruby/test/openssl/test_buffering.rb b/jni/ruby/test/openssl/test_buffering.rb new file mode 100644 index 0000000..c62dd4d --- /dev/null +++ b/jni/ruby/test/openssl/test_buffering.rb @@ -0,0 +1,87 @@ +require_relative 'utils' +require 'stringio' + +class OpenSSL::TestBuffering < Test::Unit::TestCase + + class IO + include OpenSSL::Buffering + + attr_accessor :sync + + def initialize + @io = "" + def @io.sync + true + end + + super + + @sync = false + end + + def string + @io + end + + def sysread(size) + str = @io.slice!(0, size) + raise EOFError if str.empty? + str + end + + def syswrite(str) + @io << str + str.size + end + end + + def setup + @io = IO.new + end + + def test_flush + @io.write 'a' + + refute @io.sync + assert_empty @io.string + + assert_equal @io, @io.flush + + refute @io.sync + assert_equal 'a', @io.string + end + + def test_flush_error + @io.write 'a' + + refute @io.sync + assert_empty @io.string + + def @io.syswrite *a + raise SystemCallError, 'fail' + end + + assert_raises SystemCallError do + @io.flush + end + + refute @io.sync, 'sync must not change' + end + + def test_getc + @io.syswrite('abc') + assert_equal(?a, @io.getc) + assert_equal(?b, @io.getc) + assert_equal(?c, @io.getc) + end + + def test_each_byte + @io.syswrite('abc') + res = [] + @io.each_byte do |c| + res << c + end + assert_equal([97, 98, 99], res) + end + +end if defined?(OpenSSL::TestUtils) diff --git a/jni/ruby/test/openssl/test_cipher.rb b/jni/ruby/test/openssl/test_cipher.rb new file mode 100644 index 0000000..6f92c38 --- /dev/null +++ b/jni/ruby/test/openssl/test_cipher.rb @@ -0,0 +1,260 @@ +require_relative 'utils' + +if defined?(OpenSSL::TestUtils) + +class OpenSSL::TestCipher < Test::Unit::TestCase + + class << self + + def has_cipher?(name) + ciphers = OpenSSL::Cipher.ciphers + # redefine method so we can use the cached ciphers value from the closure + # and need not recompute the list each time + define_singleton_method :has_cipher? do |name| + ciphers.include?(name) + end + has_cipher?(name) + end + + def has_ciphers?(list) + list.all? { |name| has_cipher?(name) } + end + + end + + def setup + @c1 = OpenSSL::Cipher::Cipher.new("DES-EDE3-CBC") + @c2 = OpenSSL::Cipher::DES.new(:EDE3, "CBC") + @key = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + @iv = "\0\0\0\0\0\0\0\0" + @hexkey = "0000000000000000000000000000000000000000000000" + @hexiv = "0000000000000000" + @data = "DATA" + end + + def teardown + @c1 = @c2 = nil + end + + def test_crypt + @c1.encrypt.pkcs5_keyivgen(@key, @iv) + @c2.encrypt.pkcs5_keyivgen(@key, @iv) + s1 = @c1.update(@data) + @c1.final + s2 = @c2.update(@data) + @c2.final + assert_equal(s1, s2, "encrypt") + + @c1.decrypt.pkcs5_keyivgen(@key, @iv) + @c2.decrypt.pkcs5_keyivgen(@key, @iv) + assert_equal(@data, @c1.update(s1)+@c1.final, "decrypt") + assert_equal(@data, @c2.update(s2)+@c2.final, "decrypt") + end + + def test_info + assert_equal("DES-EDE3-CBC", @c1.name, "name") + assert_equal("DES-EDE3-CBC", @c2.name, "name") + assert_kind_of(Fixnum, @c1.key_len, "key_len") + assert_kind_of(Fixnum, @c1.iv_len, "iv_len") + end + + def test_dup + assert_equal(@c1.name, @c1.dup.name, "dup") + assert_equal(@c1.name, @c1.clone.name, "clone") + @c1.encrypt + @c1.key = @key + @c1.iv = @iv + tmpc = @c1.dup + s1 = @c1.update(@data) + @c1.final + s2 = tmpc.update(@data) + tmpc.final + assert_equal(s1, s2, "encrypt dup") + end + + def test_reset + @c1.encrypt + @c1.key = @key + @c1.iv = @iv + s1 = @c1.update(@data) + @c1.final + @c1.reset + s2 = @c1.update(@data) + @c1.final + assert_equal(s1, s2, "encrypt reset") + end + + def test_empty_data + @c1.encrypt + assert_raise(ArgumentError){ @c1.update("") } + end + + def test_initialize + assert_raise(RuntimeError) {@c1.__send__(:initialize, "DES-EDE3-CBC")} + assert_raise(RuntimeError) {OpenSSL::Cipher.allocate.final} + end + + def test_ctr_if_exists + begin + cipher = OpenSSL::Cipher.new('aes-128-ctr') + cipher.encrypt + cipher.pkcs5_keyivgen('password') + c = cipher.update('hello,world') + cipher.final + cipher.decrypt + cipher.pkcs5_keyivgen('password') + assert_equal('hello,world', cipher.update(c) + cipher.final) + end + end if has_cipher?('aes-128-ctr') + + if OpenSSL::OPENSSL_VERSION_NUMBER > 0x00907000 + def test_ciphers + OpenSSL::Cipher.ciphers.each{|name| + next if /netbsd/ =~ RUBY_PLATFORM && /idea|rc5/i =~ name + begin + assert_kind_of(OpenSSL::Cipher::Cipher, OpenSSL::Cipher::Cipher.new(name)) + rescue OpenSSL::Cipher::CipherError => e + next if /wrap/ =~ name and e.message == 'wrap mode not allowed' + raise + 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_AES_crush + 500.times do + assert_nothing_raised("[Bug #2768]") do + # it caused OpenSSL SEGV by uninitialized key + OpenSSL::Cipher::AES128.new("ECB").update "." * 17 + end + end + end + end + + if has_ciphers?(['aes-128-gcm', 'aes-192-gcm', 'aes-256-gcm']) + + def test_authenticated + cipher = OpenSSL::Cipher.new('aes-128-gcm') + assert(cipher.authenticated?) + cipher = OpenSSL::Cipher.new('aes-128-cbc') + refute(cipher.authenticated?) + end + + def test_aes_gcm + ['aes-128-gcm', 'aes-192-gcm', 'aes-256-gcm'].each do |algo| + pt = "You should all use Authenticated Encryption!" + cipher, key, iv = new_encryptor(algo) + + cipher.auth_data = "aad" + ct = cipher.update(pt) + cipher.final + tag = cipher.auth_tag + assert_equal(16, tag.size) + + decipher = new_decryptor(algo, key, iv) + decipher.auth_tag = tag + decipher.auth_data = "aad" + + assert_equal(pt, decipher.update(ct) + decipher.final) + end + end + + def test_aes_gcm_short_tag + ['aes-128-gcm', 'aes-192-gcm', 'aes-256-gcm'].each do |algo| + pt = "You should all use Authenticated Encryption!" + cipher, key, iv = new_encryptor(algo) + + cipher.auth_data = "aad" + ct = cipher.update(pt) + cipher.final + tag = cipher.auth_tag(8) + assert_equal(8, tag.size) + + decipher = new_decryptor(algo, key, iv) + decipher.auth_tag = tag + decipher.auth_data = "aad" + + assert_equal(pt, decipher.update(ct) + decipher.final) + end + end + + def test_aes_gcm_wrong_tag + pt = "You should all use Authenticated Encryption!" + cipher, key, iv = new_encryptor('aes-128-gcm') + + cipher.auth_data = "aad" + ct = cipher.update(pt) + cipher.final + tag = cipher.auth_tag + + decipher = new_decryptor('aes-128-gcm', key, iv) + tag.setbyte(-1, (tag.getbyte(-1) + 1) & 0xff) + decipher.auth_tag = tag + decipher.auth_data = "aad" + + assert_raise OpenSSL::Cipher::CipherError do + decipher.update(ct) + decipher.final + end + end + + def test_aes_gcm_wrong_auth_data + pt = "You should all use Authenticated Encryption!" + cipher, key, iv = new_encryptor('aes-128-gcm') + + cipher.auth_data = "aad" + ct = cipher.update(pt) + cipher.final + tag = cipher.auth_tag + + decipher = new_decryptor('aes-128-gcm', key, iv) + decipher.auth_tag = tag + decipher.auth_data = "daa" + + assert_raise OpenSSL::Cipher::CipherError do + decipher.update(ct) + decipher.final + end + end + + def test_aes_gcm_wrong_ciphertext + pt = "You should all use Authenticated Encryption!" + cipher, key, iv = new_encryptor('aes-128-gcm') + + cipher.auth_data = "aad" + ct = cipher.update(pt) + cipher.final + tag = cipher.auth_tag + + decipher = new_decryptor('aes-128-gcm', key, iv) + decipher.auth_tag = tag + decipher.auth_data = "aad" + + assert_raise OpenSSL::Cipher::CipherError do + decipher.update(ct[0..-2] << ct[-1].succ) + decipher.final + end + end + + end + + private + + def new_encryptor(algo) + cipher = OpenSSL::Cipher.new(algo) + cipher.encrypt + key = cipher.random_key + iv = cipher.random_iv + [cipher, key, iv] + end + + def new_decryptor(algo, key, iv) + OpenSSL::Cipher.new(algo).tap do |cipher| + cipher.decrypt + cipher.key = key + cipher.iv = iv + end + end + +end + +end diff --git a/jni/ruby/test/openssl/test_config.rb b/jni/ruby/test/openssl/test_config.rb new file mode 100644 index 0000000..62f9fab --- /dev/null +++ b/jni/ruby/test/openssl/test_config.rb @@ -0,0 +1,297 @@ +require_relative 'utils' + +class OpenSSL::TestConfig < Test::Unit::TestCase + def setup + file = Tempfile.open("openssl.cnf") + file << <<__EOD__ +HOME = . +[ ca ] +default_ca = CA_default +[ CA_default ] +dir = ./demoCA +certs = ./certs +__EOD__ + file.close + @tmpfile = file + @it = OpenSSL::Config.new(file.path) + end + + def teardown + @tmpfile.close! + end + + def test_constants + assert(defined?(OpenSSL::Config::DEFAULT_CONFIG_FILE)) + config_file = OpenSSL::Config::DEFAULT_CONFIG_FILE + skip "DEFAULT_CONFIG_FILE may return a wrong path on your platforms. [Bug #6830]" unless File.readable?(config_file) + assert_nothing_raised do + OpenSSL::Config.load(config_file) + end + end + + def test_s_parse + c = OpenSSL::Config.parse('') + assert_equal("[ default ]\n\n", c.to_s) + c = OpenSSL::Config.parse(@it.to_s) + assert_equal(['CA_default', 'ca', 'default'], c.sections.sort) + end + + def test_s_parse_format + c = OpenSSL::Config.parse(<<__EOC__) + baz =qx\t # "baz = qx" + +foo::bar = baz # shortcut section::key format + default::bar = baz # ditto +a=\t \t # "a = ": trailing spaces are ignored + =b # " = b": empty key + =c # " = c": empty key (override the above line) + d= # "c = ": trailing comment is ignored + +sq = 'foo''b\\'ar' + dq ="foo""''\\"" + dq2 = foo""bar +esc=a\\r\\n\\b\\tb +foo\\bar = foo\\b\\\\ar +foo\\bar::foo\\bar = baz +[default1 default2]\t\t # space is allowed in section name + fo =b ar # space allowed in value +[emptysection] + [doller ] +foo=bar +bar = $(foo) +baz = 123$(default::bar)456${foo}798 +qux = ${baz} +quxx = $qux.$qux +__EOC__ + 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']) + assert_equal('qx', c['default']['baz']) + assert_equal('', c['default']['d']) + assert_equal('baz', c['default']['bar']) + assert_equal("foob'ar", c['default']['sq']) + assert_equal("foo''\"", c['default']['dq']) + assert_equal("foobar", c['default']['dq2']) + assert_equal("a\r\n\b\tb", c['default']['esc']) + assert_equal("foo\b\\ar", c['default']['foo\\bar']) + assert_equal('baz', c['foo']['bar']) + assert_equal('baz', c['foo\\bar']['foo\\bar']) + assert_equal('b ar', c['default1 default2']['fo']) + + # 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") + end + assert_equal("error in line 1: variable has no value", excn.message) + + excn = assert_raise(OpenSSL::ConfigError) do + OpenSSL::Config.parse("foo = $(bar") + end + assert_equal("error in line 1: no close brace", excn.message) + + excn = assert_raise(OpenSSL::ConfigError) do + OpenSSL::Config.parse("f o =b ar # no space in key") + end + assert_equal("error in line 1: missing equal sign", excn.message) + + excn = assert_raise(OpenSSL::ConfigError) do + OpenSSL::Config.parse(<<__EOC__) +# comment 1 # comments + +# + # comment 2 +\t#comment 3 + [second ]\t +[third # section not terminated +__EOC__ + end + assert_equal("error in line 7: missing close square bracket", excn.message) + end + + def test_s_load + # alias of new + c = OpenSSL::Config.load + assert_equal("", c.to_s) + assert_equal([], c.sections) + # + Tempfile.create("openssl.cnf") {|file| + file.close + c = OpenSSL::Config.load(file.path) + assert_equal("[ default ]\n\n", c.to_s) + assert_equal(['default'], c.sections) + } + end + + def test_initialize + c = OpenSSL::Config.new + assert_equal("", c.to_s) + assert_equal([], c.sections) + end + + def test_initialize_with_empty_file + Tempfile.create("openssl.cnf") {|file| + file.close + c = OpenSSL::Config.new(file.path) + assert_equal("[ default ]\n\n", c.to_s) + assert_equal(['default'], c.sections) + } + end + + def test_initialize_with_example_file + assert_equal(['CA_default', 'ca', 'default'], @it.sections.sort) + end + + def test_get_value + assert_equal('CA_default', @it.get_value('ca', 'default_ca')) + assert_equal(nil, @it.get_value('ca', 'no such key')) + assert_equal(nil, @it.get_value('no such section', 'no such key')) + assert_equal('.', @it.get_value('', 'HOME')) + assert_raise(TypeError) do + @it.get_value(nil, 'HOME') # not allowed unlike Config#value + end + # fallback to 'default' ugly... + assert_equal('.', @it.get_value('unknown', 'HOME')) + end + + def test_get_value_ENV + key = ENV.keys.first + assert_not_nil(key) # make sure we have at least one ENV var. + assert_equal(ENV[key], @it.get_value('ENV', key)) + end + + def test_value + # supress deprecation warnings + OpenSSL::TestUtils.silent do + assert_equal('CA_default', @it.value('ca', 'default_ca')) + assert_equal(nil, @it.value('ca', 'no such key')) + assert_equal(nil, @it.value('no such section', 'no such key')) + assert_equal('.', @it.value('', 'HOME')) + assert_equal('.', @it.value(nil, 'HOME')) + assert_equal('.', @it.value('HOME')) + # fallback to 'default' ugly... + assert_equal('.', @it.value('unknown', 'HOME')) + end + end + + def test_value_ENV + OpenSSL::TestUtils.silent do + key = ENV.keys.first + assert_not_nil(key) # make sure we have at least one ENV var. + assert_equal(ENV[key], @it.value('ENV', key)) + end + end + + def test_aref + assert_equal({'HOME' => '.'}, @it['default']) + assert_equal({'dir' => './demoCA', 'certs' => './certs'}, @it['CA_default']) + assert_equal({}, @it['no_such_section']) + assert_equal({}, @it['']) + end + + def test_section + OpenSSL::TestUtils.silent do + assert_equal({'HOME' => '.'}, @it.section('default')) + assert_equal({'dir' => './demoCA', 'certs' => './certs'}, @it.section('CA_default')) + assert_equal({}, @it.section('no_such_section')) + assert_equal({}, @it.section('')) + end + end + + def test_sections + assert_equal(['CA_default', 'ca', 'default'], @it.sections.sort) + @it['new_section'] = {'foo' => 'bar'} + assert_equal(['CA_default', 'ca', 'default', 'new_section'], @it.sections.sort) + @it['new_section'] = {} + assert_equal(['CA_default', 'ca', 'default', 'new_section'], @it.sections.sort) + end + + def test_add_value + c = OpenSSL::Config.new + assert_equal("", c.to_s) + # add key + c.add_value('default', 'foo', 'bar') + assert_equal("[ default ]\nfoo=bar\n\n", c.to_s) + # add another key + c.add_value('default', 'baz', 'qux') + assert_equal('bar', c['default']['foo']) + assert_equal('qux', c['default']['baz']) + # update the value + c.add_value('default', 'baz', 'quxxx') + assert_equal('bar', c['default']['foo']) + assert_equal('quxxx', c['default']['baz']) + # add section and key + c.add_value('section', 'foo', 'bar') + assert_equal('bar', c['default']['foo']) + assert_equal('quxxx', c['default']['baz']) + assert_equal('bar', c['section']['foo']) + end + + def test_aset + @it['foo'] = {'bar' => 'baz'} + assert_equal({'bar' => 'baz'}, @it['foo']) + @it['foo'] = {'bar' => 'qux', 'baz' => 'quxx'} + assert_equal({'bar' => 'qux', 'baz' => 'quxx'}, @it['foo']) + + # OpenSSL::Config is add only for now. + @it['foo'] = {'foo' => 'foo'} + assert_equal({'foo' => 'foo', 'bar' => 'qux', 'baz' => 'quxx'}, @it['foo']) + # you cannot override or remove any section and key. + @it['foo'] = {} + assert_equal({'foo' => 'foo', 'bar' => 'qux', 'baz' => 'quxx'}, @it['foo']) + end + + def test_each + # each returns [section, key, value] array. + ary = @it.map { |e| e }.sort { |a, b| a[0] <=> b[0] } + assert_equal(4, ary.size) + assert_equal('CA_default', ary[0][0]) + assert_equal('CA_default', ary[1][0]) + assert_equal(["ca", "default_ca", "CA_default"], ary[2]) + assert_equal(["default", "HOME", "."], ary[3]) + end + + def test_to_s + c = OpenSSL::Config.parse("[empty]\n") + assert_equal("[ default ]\n\n[ empty ]\n\n", c.to_s) + end + + def test_inspect + assert_match(/#<OpenSSL::Config sections=\[.*\]>/, @it.inspect) + end + + def test_freeze + c = OpenSSL::Config.new + c['foo'] = [['key', 'value']] + c.freeze + + bug = '[ruby-core:18377]' + # RuntimeError for 1.9, TypeError for 1.8 + e = assert_raise(TypeError, bug) do + c['foo'] = [['key', 'wrong']] + end + assert_match(/can't modify/, e.message, bug) + end + + def test_dup + assert(!@it.sections.empty?) + c = @it.dup + assert_equal(@it.sections.sort, c.sections.sort) + @it['newsection'] = {'a' => 'b'} + assert_not_equal(@it.sections.sort, c.sections.sort) + end + + def test_clone + assert(!@it.sections.empty?) + c = @it.clone + assert_equal(@it.sections.sort, c.sections.sort) + @it['newsection'] = {'a' => 'b'} + assert_not_equal(@it.sections.sort, c.sections.sort) + end +end if defined?(OpenSSL::TestUtils) diff --git a/jni/ruby/test/openssl/test_digest.rb b/jni/ruby/test/openssl/test_digest.rb new file mode 100644 index 0000000..a23b2ef --- /dev/null +++ b/jni/ruby/test/openssl/test_digest.rb @@ -0,0 +1,126 @@ +require_relative 'utils' + +if defined?(OpenSSL::TestUtils) + +class OpenSSL::TestDigest < Test::Unit::TestCase + def setup + @d1 = OpenSSL::Digest.new("MD5") + @d2 = OpenSSL::Digest::MD5.new + @md = Digest::MD5.new + @data = "DATA" + end + + def teardown + @d1 = @d2 = @md = nil + end + + def test_digest + assert_equal(@md.digest, @d1.digest) + assert_equal(@md.hexdigest, @d1.hexdigest) + @d1 << @data + @d2 << @data + @md << @data + assert_equal(@md.digest, @d1.digest) + assert_equal(@md.hexdigest, @d1.hexdigest) + assert_equal(@d1.digest, @d2.digest) + assert_equal(@d1.hexdigest, @d2.hexdigest) + assert_equal(@md.digest, OpenSSL::Digest::MD5.digest(@data)) + assert_equal(@md.hexdigest, OpenSSL::Digest::MD5.hexdigest(@data)) + end + + def test_eql + assert(@d1 == @d2, "==") + d = @d1.clone + assert(d == @d1, "clone") + end + + def test_info + assert_equal("MD5", @d1.name, "name") + assert_equal("MD5", @d2.name, "name") + assert_equal(16, @d1.size, "size") + end + + def test_dup + @d1.update(@data) + assert_equal(@d1.name, @d1.dup.name, "dup") + assert_equal(@d1.name, @d1.clone.name, "clone") + assert_equal(@d1.digest, @d1.clone.digest, "clone .digest") + end + + def test_reset + @d1.update(@data) + dig1 = @d1.digest + @d1.reset + @d1.update(@data) + dig2 = @d1.digest + assert_equal(dig1, dig2, "reset") + end + + def test_digest_constants + algs = %w(DSS1 MD4 MD5 RIPEMD160 SHA SHA1) + if OpenSSL::OPENSSL_VERSION_NUMBER > 0x00908000 + algs += %w(SHA224 SHA256 SHA384 SHA512) + end + algs.each do |alg| + assert_not_nil(OpenSSL::Digest.new(alg)) + klass = OpenSSL::Digest.const_get(alg) + assert_not_nil(klass.new) + end + end + + def test_digest_by_oid_and_name + check_digest(OpenSSL::ASN1::ObjectId.new("MD5")) + check_digest(OpenSSL::ASN1::ObjectId.new("SHA1")) + end + + if OpenSSL::OPENSSL_VERSION_NUMBER > 0x00908000 + def encode16(str) + str.unpack("H*").first + end + + def test_098_features + sha224_a = "abd37534c7d9a2efb9465de931cd7055ffdb8879563ae98078d6d6d5" + sha256_a = "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb" + sha384_a = "54a59b9f22b0b80880d8427e548b7c23abd873486e1f035dce9cd697e85175033caa88e6d57bc35efae0b5afd3145f31" + sha512_a = "1f40fc92da241694750979ee6cf582f2d5d7d28e18335de05abc54d0560e0f5302860c652bf08d560252aa5e74210546f369fbbbce8c12cfc7957b2652fe9a75" + + assert_equal(sha224_a, OpenSSL::Digest::SHA224.hexdigest("a")) + assert_equal(sha256_a, OpenSSL::Digest::SHA256.hexdigest("a")) + assert_equal(sha384_a, OpenSSL::Digest::SHA384.hexdigest("a")) + assert_equal(sha512_a, OpenSSL::Digest::SHA512.hexdigest("a")) + + assert_equal(sha224_a, encode16(OpenSSL::Digest::SHA224.digest("a"))) + assert_equal(sha256_a, encode16(OpenSSL::Digest::SHA256.digest("a"))) + assert_equal(sha384_a, encode16(OpenSSL::Digest::SHA384.digest("a"))) + assert_equal(sha512_a, encode16(OpenSSL::Digest::SHA512.digest("a"))) + end + + def test_digest_by_oid_and_name_sha2 + check_digest(OpenSSL::ASN1::ObjectId.new("SHA224")) + check_digest(OpenSSL::ASN1::ObjectId.new("SHA256")) + check_digest(OpenSSL::ASN1::ObjectId.new("SHA384")) + check_digest(OpenSSL::ASN1::ObjectId.new("SHA512")) + end + end + + def test_openssl_digest + assert_equal OpenSSL::Digest::MD5, OpenSSL::Digest("MD5") + + assert_raises NameError do + OpenSSL::Digest("no such digest") + end + end + + private + + def check_digest(oid) + d = OpenSSL::Digest.new(oid.sn) + assert_not_nil(d) + d = OpenSSL::Digest.new(oid.ln) + assert_not_nil(d) + d = OpenSSL::Digest.new(oid.oid) + assert_not_nil(d) + end +end + +end diff --git a/jni/ruby/test/openssl/test_engine.rb b/jni/ruby/test/openssl/test_engine.rb new file mode 100644 index 0000000..a7264d0 --- /dev/null +++ b/jni/ruby/test/openssl/test_engine.rb @@ -0,0 +1,75 @@ +require_relative 'utils' + +class OpenSSL::TestEngine < Test::Unit::TestCase + + def teardown + OpenSSL::Engine.cleanup # [ruby-core:40669] + assert_equal(0, OpenSSL::Engine.engines.size) + end + + def test_engines_free # [ruby-dev:44173] + OpenSSL::Engine.load("openssl") + OpenSSL::Engine.engines + OpenSSL::Engine.engines + end + + def test_openssl_engine_builtin + engine = OpenSSL::Engine.load("openssl") + assert_equal(true, engine) + assert_equal(1, OpenSSL::Engine.engines.size) + end + + def test_openssl_engine_by_id_string + engine = get_engine + assert_not_nil(engine) + assert_equal(1, OpenSSL::Engine.engines.size) + end + + def test_openssl_engine_id_name_inspect + engine = get_engine + assert_equal("openssl", engine.id) + assert_not_nil(engine.name) + assert_not_nil(engine.inspect) + end + + def test_openssl_engine_digest_sha1 + engine = get_engine + digest = engine.digest("SHA1") + assert_not_nil(digest) + data = "test" + assert_equal(OpenSSL::Digest::SHA1.digest(data), digest.digest(data)) + end + + def test_openssl_engine_cipher_rc4 + engine = get_engine + algo = "RC4" #AES is not supported by openssl Engine (<=1.0.0e) + data = "a" * 1000 + key = OpenSSL::Random.random_bytes(16) + # suppress message from openssl Engine's RC4 cipher [ruby-core:41026] + err_back = $stderr.dup + $stderr.reopen(IO::NULL) + encrypted = crypt_data(data, key, :encrypt) { engine.cipher(algo) } + decrypted = crypt_data(encrypted, key, :decrypt) { OpenSSL::Cipher.new(algo) } + assert_equal(data, decrypted) + ensure + if err_back + $stderr.reopen(err_back) + err_back.close + end + end + + private + + def get_engine + OpenSSL::Engine.by_id("openssl") + end + + def crypt_data(data, key, mode) + cipher = yield + cipher.send mode + cipher.key = key + cipher.update(data) + cipher.final + end + +end if defined?(OpenSSL::TestUtils) + diff --git a/jni/ruby/test/openssl/test_fips.rb b/jni/ruby/test/openssl/test_fips.rb new file mode 100644 index 0000000..6e4ac6d --- /dev/null +++ b/jni/ruby/test/openssl/test_fips.rb @@ -0,0 +1,14 @@ +require_relative 'utils' + +if defined?(OpenSSL::TestUtils) + +class OpenSSL::TestFIPS < Test::Unit::TestCase + + def test_fips_mode_is_reentrant + OpenSSL.fips_mode = false + OpenSSL.fips_mode = false + end + +end + +end diff --git a/jni/ruby/test/openssl/test_hmac.rb b/jni/ruby/test/openssl/test_hmac.rb new file mode 100644 index 0000000..f709ebd --- /dev/null +++ b/jni/ruby/test/openssl/test_hmac.rb @@ -0,0 +1,41 @@ +# coding: UTF-8 + +require_relative 'utils' + +class OpenSSL::TestHMAC < Test::Unit::TestCase + def setup + @digest = OpenSSL::Digest::MD5 + @key = "KEY" + @data = "DATA" + @h1 = OpenSSL::HMAC.new(@key, @digest.new) + @h2 = OpenSSL::HMAC.new(@key, "MD5") + end + + def teardown + end + + def test_hmac + @h1.update(@data) + @h2.update(@data) + assert_equal(@h1.digest, @h2.digest) + + assert_equal(OpenSSL::HMAC.digest(@digest.new, @key, @data), @h1.digest, "digest") + assert_equal(OpenSSL::HMAC.hexdigest(@digest.new, @key, @data), @h1.hexdigest, "hexdigest") + + assert_equal(OpenSSL::HMAC.digest("MD5", @key, @data), @h2.digest, "digest") + assert_equal(OpenSSL::HMAC.hexdigest("MD5", @key, @data), @h2.hexdigest, "hexdigest") + end + + def test_dup + @h1.update(@data) + h = @h1.dup + assert_equal(@h1.digest, h.digest, "dup digest") + end + + def test_binary_update + data = "LücÃllé: Bût... yøü sáîd hé wÃ¥s âlrîght.\nDr. FÃshmÃ¥n: Yés. Hé's løst hîs léft hÃ¥nd, sø hé's gøîng tø bé Ã¥ll rîght" + hmac = OpenSSL::HMAC.new("qShkcwN92rsM9nHfdnP4ugcVU2iI7iM/trovs01ZWok", "SHA256") + result = hmac.update(data).hexdigest + assert_equal "a13984b929a07912e4e21c5720876a8e150d6f67f854437206e7f86547248396", result + end +end if defined?(OpenSSL::TestUtils) diff --git a/jni/ruby/test/openssl/test_ns_spki.rb b/jni/ruby/test/openssl/test_ns_spki.rb new file mode 100644 index 0000000..ab07bfb --- /dev/null +++ b/jni/ruby/test/openssl/test_ns_spki.rb @@ -0,0 +1,51 @@ +require_relative 'utils' + +if defined?(OpenSSL::TestUtils) + +class OpenSSL::TestNSSPI < Test::Unit::TestCase + def setup + # 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 << "WtKQbsshxk5ZhcUwcwyvcnIq9b82QhJdoACdD34rqfCAIND46fXKQUnb0mvKzQID" + @b64 << "AQABFhFNb3ppbGxhSXNNeUZyaWVuZDANBgkqhkiG9w0BAQQFAANBAAKv2Eex2n/S" + @b64 << "r/7iJNroWlSzSMtTiQTEB+ADWHGj9u1xrUrOilq/o2cuQxIfZcNZkYAkWP4DubqW" + @b64 << "i0//rgBvmco=" + end + + def test_build_data + key1 = OpenSSL::TestUtils::TEST_KEY_RSA1024 + key2 = OpenSSL::TestUtils::TEST_KEY_RSA2048 + spki = OpenSSL::Netscape::SPKI.new + spki.challenge = "RandomString" + spki.public_key = key1.public_key + spki.sign(key1, OpenSSL::Digest::SHA1.new) + assert(spki.verify(spki.public_key)) + assert(spki.verify(key1.public_key)) + assert(!spki.verify(key2.public_key)) + + der = spki.to_der + spki = OpenSSL::Netscape::SPKI.new(der) + assert_equal("RandomString", spki.challenge) + assert_equal(key1.public_key.to_der, spki.public_key.to_der) + assert(spki.verify(spki.public_key)) + assert_not_nil(spki.to_text) + end + + def test_decode_data + spki = OpenSSL::Netscape::SPKI.new(@b64) + assert_equal(@b64, spki.to_pem) + assert_equal(@b64.unpack("m").first, spki.to_der) + assert_equal("MozillaIsMyFriend", spki.challenge) + assert_equal(OpenSSL::PKey::RSA, spki.public_key.class) + + spki = OpenSSL::Netscape::SPKI.new(@b64.unpack("m").first) + assert_equal(@b64, spki.to_pem) + assert_equal(@b64.unpack("m").first, spki.to_der) + assert_equal("MozillaIsMyFriend", spki.challenge) + assert_equal(OpenSSL::PKey::RSA, spki.public_key.class) + end +end + +end diff --git a/jni/ruby/test/openssl/test_ocsp.rb b/jni/ruby/test/openssl/test_ocsp.rb new file mode 100644 index 0000000..af727d8 --- /dev/null +++ b/jni/ruby/test/openssl/test_ocsp.rb @@ -0,0 +1,47 @@ +require_relative "utils" + +if defined?(OpenSSL::TestUtils) + +class OpenSSL::TestOCSP < Test::Unit::TestCase + def setup + ca_subj = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=TestCA") + ca_key = OpenSSL::TestUtils::TEST_KEY_RSA1024 + ca_serial = 0xabcabcabcabc + + subj = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=TestCert") + @key = OpenSSL::TestUtils::TEST_KEY_RSA1024 + serial = 0xabcabcabcabd + + now = Time.at(Time.now.to_i) # suppress usec + dgst = OpenSSL::Digest::SHA1.new + + @ca_cert = OpenSSL::TestUtils.issue_cert( + ca_subj, ca_key, ca_serial, now, now+3600, [], nil, nil, dgst) + @cert = OpenSSL::TestUtils.issue_cert( + subj, @key, serial, now, now+3600, [], @ca_cert, nil, dgst) + end + + def test_new_certificate_id + cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert) + assert_kind_of OpenSSL::OCSP::CertificateId, cid + assert_equal @cert.serial, cid.serial + end + + def test_new_certificate_id_with_digest + cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA256.new) + assert_kind_of OpenSSL::OCSP::CertificateId, cid + assert_equal @cert.serial, cid.serial + end if defined?(OpenSSL::Digest::SHA256) + + def test_new_ocsp_request + request = OpenSSL::OCSP::Request.new + cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA1.new) + request.add_certid(cid) + request.sign(@cert, @key, [@cert]) + assert_kind_of OpenSSL::OCSP::Request, request + # in current implementation not same instance of certificate id, but should contain same data + assert_equal cid.serial, request.certid.first.serial + end +end + +end diff --git a/jni/ruby/test/openssl/test_pair.rb b/jni/ruby/test/openssl/test_pair.rb new file mode 100644 index 0000000..3aca5f4 --- /dev/null +++ b/jni/ruby/test/openssl/test_pair.rb @@ -0,0 +1,372 @@ +require_relative 'utils' + +if defined?(OpenSSL::TestUtils) + +require 'socket' +require_relative '../ruby/ut_eof' + +module OpenSSL::SSLPairM + def server + host = "127.0.0.1" + port = 0 + ctx = OpenSSL::SSL::SSLContext.new() + ctx.ciphers = "ADH" + ctx.tmp_dh_callback = proc { OpenSSL::TestUtils::TEST_KEY_DH1024 } + tcps = create_tcp_server(host, port) + ssls = OpenSSL::SSL::SSLServer.new(tcps, ctx) + return ssls + end + + def client(port) + host = "127.0.0.1" + ctx = OpenSSL::SSL::SSLContext.new() + ctx.ciphers = "ADH" + s = create_tcp_client(host, port) + ssl = OpenSSL::SSL::SSLSocket.new(s, ctx) + ssl.connect + ssl.sync_close = true + ssl + end + + def ssl_pair + ssls = server + th = Thread.new { + ns = ssls.accept + ssls.close + ns + } + port = ssls.to_io.local_address.ip_port + c = client(port) + s = th.value + if block_given? + begin + yield c, s + ensure + c.close unless c.closed? + s.close unless s.closed? + end + else + return c, s + end + ensure + if th && th.alive? + th.kill + th.join + end + end +end + +module OpenSSL::SSLPair + include OpenSSL::SSLPairM + + def create_tcp_server(host, port) + TCPServer.new(host, port) + end + + def create_tcp_client(host, port) + TCPSocket.new(host, port) + end +end + +module OpenSSL::SSLPairLowlevelSocket + include OpenSSL::SSLPairM + + def create_tcp_server(host, port) + Addrinfo.tcp(host, port).listen + end + + def create_tcp_client(host, port) + Addrinfo.tcp(host, port).connect + end +end + +module OpenSSL::TestEOF1M + def open_file(content) + s1, s2 = ssl_pair + th = Thread.new { s2 << content; s2.close } + yield s1 + ensure + th.join + s1.close + end +end + +module OpenSSL::TestEOF2M + def open_file(content) + s1, s2 = ssl_pair + th = Thread.new { s1 << content; s1.close } + yield s2 + ensure + th.join + s2.close + end +end + +module OpenSSL::TestPairM + def test_getc + ssl_pair {|s1, s2| + s1 << "a" + assert_equal(?a, s2.getc) + } + end + + def test_readpartial + ssl_pair {|s1, s2| + s2.write "a\nbcd" + assert_equal("a\n", s1.gets) + result = "" + result << s1.readpartial(10) until result.length == 3 + assert_equal("bcd", result) + s2.write "efg" + result = "" + result << s1.readpartial(10) until result.length == 3 + assert_equal("efg", result) + s2.close + assert_raise(EOFError) { s1.readpartial(10) } + assert_raise(EOFError) { s1.readpartial(10) } + assert_equal("", s1.readpartial(0)) + } + end + + def test_readall + ssl_pair {|s1, s2| + s2.close + assert_equal("", s1.read) + } + end + + def test_readline + ssl_pair {|s1, s2| + s2.close + assert_raise(EOFError) { s1.readline } + } + end + + def test_puts_meta + ssl_pair {|s1, s2| + begin + old = $/ + $/ = '*' + s1.puts 'a' + ensure + $/ = old + end + s1.close + assert_equal("a\n", s2.read) + } + end + + def test_puts_empty + ssl_pair {|s1, s2| + s1.puts + s1.close + assert_equal("\n", s2.read) + } + end + + def test_read_nonblock + ssl_pair {|s1, s2| + err = nil + assert_raise(OpenSSL::SSL::SSLErrorWaitReadable) { + begin + s2.read_nonblock(10) + ensure + err = $! + end + } + assert_kind_of(IO::WaitReadable, err) + s1.write "abc\ndef\n" + IO.select([s2]) + assert_equal("ab", s2.read_nonblock(2)) + assert_equal("c\n", s2.gets) + ret = nil + assert_nothing_raised("[ruby-core:20298]") { ret = s2.read_nonblock(10) } + assert_equal("def\n", ret) + s1.close + sleep 0.1 + assert_raise(EOFError) { s2.read_nonblock(10) } + } + end + + def test_read_nonblock_no_exception + ssl_pair {|s1, s2| + assert_equal :wait_readable, s2.read_nonblock(10, exception: false) + s1.write "abc\ndef\n" + IO.select([s2]) + assert_equal("ab", s2.read_nonblock(2, exception: false)) + assert_equal("c\n", s2.gets) + ret = nil + assert_nothing_raised("[ruby-core:20298]") { ret = s2.read_nonblock(10, exception: false) } + assert_equal("def\n", ret) + s1.close + sleep 0.1 + assert_equal(nil, s2.read_nonblock(10, exception: false)) + } + end + + def write_nonblock(socket, meth, str) + ret = socket.send(meth, str) + ret.is_a?(Symbol) ? 0 : ret + end + + def write_nonblock_no_ex(socket, str) + ret = socket.write_nonblock str, exception: false + ret.is_a?(Symbol) ? 0 : ret + end + + def test_write_nonblock + ssl_pair {|s1, s2| + n = 0 + begin + n += write_nonblock s1, :write_nonblock, "a" * 100000 + n += write_nonblock s1, :write_nonblock, "b" * 100000 + n += write_nonblock s1, :write_nonblock, "c" * 100000 + n += write_nonblock s1, :write_nonblock, "d" * 100000 + n += write_nonblock s1, :write_nonblock, "e" * 100000 + n += write_nonblock s1, :write_nonblock, "f" * 100000 + rescue IO::WaitWritable + end + s1.close + assert_equal(n, s2.read.length) + } + end + + def test_write_nonblock_no_exceptions + ssl_pair {|s1, s2| + n = 0 + begin + n += write_nonblock_no_ex s1, "a" * 100000 + n += write_nonblock_no_ex s1, "b" * 100000 + n += write_nonblock_no_ex s1, "c" * 100000 + n += write_nonblock_no_ex s1, "d" * 100000 + n += write_nonblock_no_ex s1, "e" * 100000 + n += write_nonblock_no_ex s1, "f" * 100000 + rescue OpenSSL::SSL::SSLError => e + # on some platforms (maybe depend on OpenSSL version), writing to + # SSLSocket after SSL_ERROR_WANT_WRITE causes this error. + raise e if n == 0 + end + s1.close + assert_equal(n, s2.read.length) + } + end + + def test_write_nonblock_with_buffered_data + ssl_pair {|s1, s2| + s1.write "foo" + s1.write_nonblock("bar") + s1.write "baz" + s1.close + assert_equal("foobarbaz", s2.read) + } + end + + def test_write_nonblock_with_buffered_data_no_exceptions + ssl_pair {|s1, s2| + s1.write "foo" + s1.write_nonblock("bar", exception: false) + s1.write "baz" + s1.close + assert_equal("foobarbaz", s2.read) + } + end + + def tcp_pair + host = "127.0.0.1" + serv = TCPServer.new(host, 0) + port = serv.connect_address.ip_port + sock1 = TCPSocket.new(host, port) + sock2 = serv.accept + serv.close + [sock1, sock2] + ensure + serv.close if serv && !serv.closed? + end + + def test_connect_accept_nonblock + ctx = OpenSSL::SSL::SSLContext.new() + ctx.ciphers = "ADH" + ctx.tmp_dh_callback = proc { OpenSSL::TestUtils::TEST_KEY_DH1024 } + + sock1, sock2 = tcp_pair + + th = Thread.new { + s2 = OpenSSL::SSL::SSLSocket.new(sock2, ctx) + s2.sync_close = true + begin + sleep 0.2 + s2.accept_nonblock + rescue IO::WaitReadable + IO.select([s2]) + retry + rescue IO::WaitWritable + IO.select(nil, [s2]) + retry + end + s2 + } + + sleep 0.1 + ctx = OpenSSL::SSL::SSLContext.new() + ctx.ciphers = "ADH" + s1 = OpenSSL::SSL::SSLSocket.new(sock1, ctx) + begin + sleep 0.2 + s1.connect_nonblock + rescue IO::WaitReadable + IO.select([s1]) + retry + rescue IO::WaitWritable + IO.select(nil, [s1]) + retry + end + s1.sync_close = true + + s2 = th.value + + s1.print "a\ndef" + assert_equal("a\n", s2.gets) + ensure + th.join + s1.close if s1 && !s1.closed? + s2.close if s2 && !s2.closed? + sock1.close if sock1 && !sock1.closed? + sock2.close if sock2 && !sock2.closed? + end +end + +class OpenSSL::TestEOF1 < Test::Unit::TestCase + include TestEOF + include OpenSSL::SSLPair + include OpenSSL::TestEOF1M +end + +class OpenSSL::TestEOF1LowlevelSocket < Test::Unit::TestCase + include TestEOF + include OpenSSL::SSLPairLowlevelSocket + include OpenSSL::TestEOF1M +end + +class OpenSSL::TestEOF2 < Test::Unit::TestCase + include TestEOF + include OpenSSL::SSLPair + include OpenSSL::TestEOF2M +end + +class OpenSSL::TestEOF2LowlevelSocket < Test::Unit::TestCase + include TestEOF + include OpenSSL::SSLPairLowlevelSocket + include OpenSSL::TestEOF2M +end + +class OpenSSL::TestPair < Test::Unit::TestCase + include OpenSSL::SSLPair + include OpenSSL::TestPairM +end + +class OpenSSL::TestPairLowlevelSocket < Test::Unit::TestCase + include OpenSSL::SSLPairLowlevelSocket + include OpenSSL::TestPairM +end + +end diff --git a/jni/ruby/test/openssl/test_partial_record_read.rb b/jni/ruby/test/openssl/test_partial_record_read.rb new file mode 100644 index 0000000..1899a30 --- /dev/null +++ b/jni/ruby/test/openssl/test_partial_record_read.rb @@ -0,0 +1,34 @@ +require_relative "utils" + +if defined?(OpenSSL::TestUtils) + + class OpenSSL::TestPartialRecordRead < OpenSSL::SSLTestCase + def test_partial_tls_record_read_nonblock + start_server(OpenSSL::SSL::VERIFY_NONE, true, :server_proc => + Proc.new do |server_ctx, server_ssl| + begin + server_ssl.io.write("\x01") # the beginning of a TLS record + sleep 6 # do not finish prematurely before the read by the client is attempted + ensure + server_ssl.close + end + end + ) do |server, port| + sock = TCPSocket.new("127.0.0.1", port) + ssl = OpenSSL::SSL::SSLSocket.new(sock) + ssl.sync_close = true + begin + ssl.connect + sleep 3 # wait is required for the (incomplete) TLS record to arrive at the client socket + + # Should raise a IO::WaitReadable since a full TLS record is not available for reading. + assert_raise(IO::WaitReadable) { ssl.read_nonblock(1) } + ensure + ssl.close + end + end + end + + end + +end diff --git a/jni/ruby/test/openssl/test_pkcs12.rb b/jni/ruby/test/openssl/test_pkcs12.rb new file mode 100644 index 0000000..4e37904 --- /dev/null +++ b/jni/ruby/test/openssl/test_pkcs12.rb @@ -0,0 +1,209 @@ +require_relative "utils" + +if defined?(OpenSSL::TestUtils) + +module OpenSSL + class TestPKCS12 < Test::Unit::TestCase + include OpenSSL::TestUtils + + def setup + ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA") + + now = Time.now + ca_exts = [ + ["basicConstraints","CA:TRUE",true], + ["keyUsage","keyCertSign, cRLSign",true], + ["subjectKeyIdentifier","hash",false], + ["authorityKeyIdentifier","keyid:always",false], + ] + + @cacert = issue_cert(ca, TEST_KEY_RSA2048, 1, now, now+3600, ca_exts, + nil, nil, OpenSSL::Digest::SHA1.new) + + inter_ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=Intermediate CA") + inter_ca_key = OpenSSL::PKey.read <<-_EOS_ +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQDp7hIG0SFMG/VWv1dBUWziAPrNmkMXJgTCAoB7jffzRtyyN04K +oq/89HAszTMStZoMigQURfokzKsjpUp8OYCAEsBtt9d5zPndWMz/gHN73GrXk3LT +ZsxEn7Xv5Da+Y9F/Hx2QZUHarV5cdZixq2NbzWGwrToogOQMh2pxN3Z/0wIDAQAB +AoGBAJysUyx3olpsGzv3OMRJeahASbmsSKTXVLZvoIefxOINosBFpCIhZccAG6UV +5c/xCvS89xBw8aD15uUfziw3AuT8QPEtHCgfSjeT7aWzBfYswEgOW4XPuWr7EeI9 +iNHGD6z+hCN/IQr7FiEBgTp6A+i/hffcSdR83fHWKyb4M7TRAkEA+y4BNd668HmC +G5MPRx25n6LixuBxrNp1umfjEI6UZgEFVpYOg4agNuimN6NqM253kcTR94QNTUs5 +Kj3EhG1YWwJBAO5rUjiOyCNVX2WUQrOMYK/c1lU7fvrkdygXkvIGkhsPoNRzLPeA +HGJszKtrKD8bNihWpWNIyqKRHfKVD7yXT+kCQGCAhVCIGTRoypcDghwljHqLnysf +ci0h5ZdPcIqc7ODfxYhFsJ/Rql5ONgYsT5Ig/+lOQAkjf+TRYM4c2xKx2/8CQBvG +jv6dy70qDgIUgqzONtlmHeYyFzn9cdBO5sShdVYHvRHjFSMEXsosqK9zvW2UqvuK +FJx7d3f29gkzynCLJDkCQGQZlEZJC4vWmWJGRKJ24P6MyQn3VsPfErSKOg4lvyM3 +Li8JsX5yIiuVYaBg/6ha3tOg4TCa5K/3r3tVliRZ2Es= +-----END RSA PRIVATE KEY----- + _EOS_ + + @inter_cacert = issue_cert(inter_ca, inter_ca_key, 2, now, now+3600, ca_exts, + @cacert, TEST_KEY_RSA2048, OpenSSL::Digest::SHA1.new) + + exts = [ + ["keyUsage","digitalSignature",true], + ["subjectKeyIdentifier","hash",false], + ] + ee = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=Ruby PKCS12 Test Certificate") + @mycert = issue_cert(ee, TEST_KEY_RSA1024, 3, now, now+3600, exts, + @inter_cacert, inter_ca_key, OpenSSL::Digest::SHA1.new) + end + + def test_create + pkcs12 = OpenSSL::PKCS12.create( + "omg", + "hello", + TEST_KEY_RSA1024, + @mycert + ) + assert_equal @mycert, pkcs12.certificate + assert_equal TEST_KEY_RSA1024, pkcs12.key + assert_nil pkcs12.ca_certs + end + + def test_create_no_pass + pkcs12 = OpenSSL::PKCS12.create( + nil, + "hello", + TEST_KEY_RSA1024, + @mycert + ) + assert_equal @mycert, pkcs12.certificate + assert_equal TEST_KEY_RSA1024, pkcs12.key + assert_nil pkcs12.ca_certs + + decoded = OpenSSL::PKCS12.new(pkcs12.to_der) + assert_cert @mycert, decoded.certificate + end + + def test_create_with_chain + chain = [@inter_cacert, @cacert] + + pkcs12 = OpenSSL::PKCS12.create( + "omg", + "hello", + TEST_KEY_RSA1024, + @mycert, + chain + ) + assert_equal chain, pkcs12.ca_certs + end + + def test_create_with_chain_decode + chain = [@cacert, @inter_cacert] + + passwd = "omg" + + pkcs12 = OpenSSL::PKCS12.create( + passwd, + "hello", + TEST_KEY_RSA1024, + @mycert, + chain + ) + + decoded = OpenSSL::PKCS12.new(pkcs12.to_der, passwd) + assert_equal chain.size, decoded.ca_certs.size + assert_include_cert @cacert, decoded.ca_certs + assert_include_cert @inter_cacert, decoded.ca_certs + assert_cert @mycert, decoded.certificate + assert_equal TEST_KEY_RSA1024.to_der, decoded.key.to_der + end + + def test_create_with_bad_nid + assert_raises(ArgumentError) do + OpenSSL::PKCS12.create( + "omg", + "hello", + TEST_KEY_RSA1024, + @mycert, + [], + "foo" + ) + end + end + + def test_create_with_itr + OpenSSL::PKCS12.create( + "omg", + "hello", + TEST_KEY_RSA1024, + @mycert, + [], + nil, + nil, + 2048 + ) + + assert_raises(TypeError) do + OpenSSL::PKCS12.create( + "omg", + "hello", + TEST_KEY_RSA1024, + @mycert, + [], + nil, + nil, + "omg" + ) + end + end + + def test_create_with_mac_itr + OpenSSL::PKCS12.create( + "omg", + "hello", + TEST_KEY_RSA1024, + @mycert, + [], + nil, + nil, + nil, + 2048 + ) + + assert_raises(TypeError) do + OpenSSL::PKCS12.create( + "omg", + "hello", + TEST_KEY_RSA1024, + @mycert, + [], + nil, + nil, + nil, + "omg" + ) + end + end + + private + def assert_cert expected, actual + [ + :subject, + :issuer, + :serial, + :not_before, + :not_after, + ].each do |attribute| + assert_equal expected.send(attribute), actual.send(attribute) + end + assert_equal expected.to_der, actual.to_der + end + + def assert_include_cert cert, ary + der = cert.to_der + ary.each do |candidate| + if candidate.to_der == der + return true + end + end + false + end + + end +end + +end diff --git a/jni/ruby/test/openssl/test_pkcs5.rb b/jni/ruby/test/openssl/test_pkcs5.rb new file mode 100644 index 0000000..5e85dde --- /dev/null +++ b/jni/ruby/test/openssl/test_pkcs5.rb @@ -0,0 +1,97 @@ +require_relative 'utils' + +class OpenSSL::TestPKCS5 < Test::Unit::TestCase + + def test_pbkdf2_hmac_sha1_rfc6070_c_1_len_20 + p ="password" + s = "salt" + c = 1 + dk_len = 20 + raw = %w{ 0c 60 c8 0f 96 1f 0e 71 + f3 a9 b5 24 af 60 12 06 + 2f e0 37 a6 } + expected = [raw.join('')].pack('H*') + value = OpenSSL::PKCS5.pbkdf2_hmac_sha1(p, s, c, dk_len) + assert_equal(expected, value) + end + + def test_pbkdf2_hmac_sha1_rfc6070_c_2_len_20 + p ="password" + s = "salt" + c = 2 + dk_len = 20 + raw = %w{ ea 6c 01 4d c7 2d 6f 8c + cd 1e d9 2a ce 1d 41 f0 + d8 de 89 57 } + expected = [raw.join('')].pack('H*') + value = OpenSSL::PKCS5.pbkdf2_hmac_sha1(p, s, c, dk_len) + assert_equal(expected, value) + end + + def test_pbkdf2_hmac_sha1_rfc6070_c_4096_len_20 + p ="password" + s = "salt" + c = 4096 + dk_len = 20 + raw = %w{ 4b 00 79 01 b7 65 48 9a + be ad 49 d9 26 f7 21 d0 + 65 a4 29 c1 } + expected = [raw.join('')].pack('H*') + value = OpenSSL::PKCS5.pbkdf2_hmac_sha1(p, s, c, dk_len) + assert_equal(expected, value) + end + +# takes too long! +# def test_pbkdf2_hmac_sha1_rfc6070_c_16777216_len_20 +# p ="password" +# s = "salt" +# c = 16777216 +# dk_len = 20 +# raw = %w{ ee fe 3d 61 cd 4d a4 e4 +# e9 94 5b 3d 6b a2 15 8c +# 26 34 e9 84 } +# expected = [raw.join('')].pack('H*') +# value = OpenSSL::PKCS5.pbkdf2_hmac_sha1(p, s, c, dk_len) +# assert_equal(expected, value) +# end + + def test_pbkdf2_hmac_sha1_rfc6070_c_4096_len_25 + p ="passwordPASSWORDpassword" + s = "saltSALTsaltSALTsaltSALTsaltSALTsalt" + c = 4096 + dk_len = 25 + + raw = %w{ 3d 2e ec 4f e4 1c 84 9b + 80 c8 d8 36 62 c0 e4 4a + 8b 29 1a 96 4c f2 f0 70 + 38 } + expected = [raw.join('')].pack('H*') + value = OpenSSL::PKCS5.pbkdf2_hmac_sha1(p, s, c, dk_len) + assert_equal(expected, value) + end + + def test_pbkdf2_hmac_sha1_rfc6070_c_4096_len_16 + p ="pass\0word" + s = "sa\0lt" + c = 4096 + dk_len = 16 + raw = %w{ 56 fa 6a a7 55 48 09 9d + cc 37 d7 f0 34 25 e0 c3 } + expected = [raw.join('')].pack('H*') + value = OpenSSL::PKCS5.pbkdf2_hmac_sha1(p, s, c, dk_len) + assert_equal(expected, value) + end + + def test_pbkdf2_hmac_sha256_c_20000_len_32 + #unfortunately no official test vectors available yet for SHA-2 + p ="password" + s = OpenSSL::Random.random_bytes(16) + c = 20000 + dk_len = 32 + digest = OpenSSL::Digest::SHA256.new + value1 = OpenSSL::PKCS5.pbkdf2_hmac(p, s, c, dk_len, digest) + value2 = OpenSSL::PKCS5.pbkdf2_hmac(p, s, c, dk_len, digest) + assert_equal(value1, value2) + end if OpenSSL::PKCS5.respond_to?(:pbkdf2_hmac) + +end if defined?(OpenSSL::TestUtils) diff --git a/jni/ruby/test/openssl/test_pkcs7.rb b/jni/ruby/test/openssl/test_pkcs7.rb new file mode 100644 index 0000000..47bd4f3 --- /dev/null +++ b/jni/ruby/test/openssl/test_pkcs7.rb @@ -0,0 +1,297 @@ +require_relative 'utils' + +if defined?(OpenSSL::TestUtils) + +class OpenSSL::TestPKCS7 < Test::Unit::TestCase + def setup + @rsa1024 = OpenSSL::TestUtils::TEST_KEY_RSA1024 + @rsa2048 = OpenSSL::TestUtils::TEST_KEY_RSA2048 + ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA") + ee1 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE1") + ee2 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE2") + + now = Time.now + ca_exts = [ + ["basicConstraints","CA:TRUE",true], + ["keyUsage","keyCertSign, cRLSign",true], + ["subjectKeyIdentifier","hash",false], + ["authorityKeyIdentifier","keyid:always",false], + ] + @ca_cert = issue_cert(ca, @rsa2048, 1, now, now+3600, ca_exts, + nil, nil, OpenSSL::Digest::SHA1.new) + ee_exts = [ + ["keyUsage","Non Repudiation, Digital Signature, Key Encipherment",true], + ["authorityKeyIdentifier","keyid:always",false], + ["extendedKeyUsage","clientAuth, emailProtection, codeSigning",false], + ] + @ee1_cert = issue_cert(ee1, @rsa1024, 2, now, now+1800, ee_exts, + @ca_cert, @rsa2048, OpenSSL::Digest::SHA1.new) + @ee2_cert = issue_cert(ee2, @rsa1024, 3, now, now+1800, ee_exts, + @ca_cert, @rsa2048, OpenSSL::Digest::SHA1.new) + end + + def issue_cert(*args) + OpenSSL::TestUtils.issue_cert(*args) + end + + def test_signed + 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) + certs = p7.certificates + signers = p7.signers + assert(p7.verify([], store)) + assert_equal(data, p7.data) + assert_equal(2, certs.size) + assert_equal(@ee1_cert.subject.to_s, certs[0].subject.to_s) + assert_equal(@ca_cert.subject.to_s, certs[1].subject.to_s) + assert_equal(1, signers.size) + assert_equal(@ee1_cert.serial, signers[0].serial) + assert_equal(@ee1_cert.issuer.to_s, signers[0].issuer.to_s) + + # Normaly OpenSSL tries to translate the supplied content into canonical + # MIME format (e.g. a newline character is converted into CR+LF). + # If the content is a binary, PKCS7::BINARY flag should be used. + + data = "aaaaa\nbbbbb\nccccc\n" + flag = OpenSSL::PKCS7::BINARY + tmp = OpenSSL::PKCS7.sign(@ee1_cert, @rsa1024, data, ca_certs, flag) + p7 = OpenSSL::PKCS7.new(tmp.to_der) + certs = p7.certificates + signers = p7.signers + assert(p7.verify([], store)) + assert_equal(data, p7.data) + assert_equal(2, certs.size) + assert_equal(@ee1_cert.subject.to_s, certs[0].subject.to_s) + assert_equal(@ca_cert.subject.to_s, certs[1].subject.to_s) + assert_equal(1, signers.size) + assert_equal(@ee1_cert.serial, signers[0].serial) + assert_equal(@ee1_cert.issuer.to_s, signers[0].issuer.to_s) + + # A signed-data which have multiple signatures can be created + # through the following steps. + # 1. create two signed-data + # 2. copy signerInfo and certificate from one to another + + tmp1 = OpenSSL::PKCS7.sign(@ee1_cert, @rsa1024, data, [], flag) + tmp2 = OpenSSL::PKCS7.sign(@ee2_cert, @rsa1024, data, [], flag) + tmp1.add_signer(tmp2.signers[0]) + tmp1.add_certificate(@ee2_cert) + + p7 = OpenSSL::PKCS7.new(tmp1.to_der) + certs = p7.certificates + signers = p7.signers + assert(p7.verify([], store)) + assert_equal(data, p7.data) + assert_equal(2, certs.size) + assert_equal(2, signers.size) + assert_equal(@ee1_cert.serial, signers[0].serial) + assert_equal(@ee1_cert.issuer.to_s, signers[0].issuer.to_s) + assert_equal(@ee2_cert.serial, signers[1].serial) + assert_equal(@ee2_cert.issuer.to_s, signers[1].issuer.to_s) + end + + def test_detached_sign + store = OpenSSL::X509::Store.new + store.add_cert(@ca_cert) + ca_certs = [@ca_cert] + + data = "aaaaa\nbbbbb\nccccc\n" + flag = OpenSSL::PKCS7::BINARY|OpenSSL::PKCS7::DETACHED + tmp = OpenSSL::PKCS7.sign(@ee1_cert, @rsa1024, data, ca_certs, flag) + p7 = OpenSSL::PKCS7.new(tmp.to_der) + assert_nothing_raised do + OpenSSL::ASN1.decode(p7) + end + + certs = p7.certificates + signers = p7.signers + assert(!p7.verify([], store)) + assert(p7.verify([], store, data)) + assert_equal(data, p7.data) + assert_equal(2, certs.size) + assert_equal(@ee1_cert.subject.to_s, certs[0].subject.to_s) + assert_equal(@ca_cert.subject.to_s, certs[1].subject.to_s) + assert_equal(1, signers.size) + assert_equal(@ee1_cert.serial, signers[0].serial) + assert_equal(@ee1_cert.issuer.to_s, signers[0].issuer.to_s) + end + + def test_enveloped + if OpenSSL::OPENSSL_VERSION_NUMBER <= 0x0090704f + # PKCS7_encrypt() of OpenSSL-0.9.7d goes to SEGV. + # http://www.mail-archive.com/openssl-dev@openssl.org/msg17376.html + return + end + + certs = [@ee1_cert, @ee2_cert] + cipher = OpenSSL::Cipher::AES.new("128-CBC") + data = "aaaaa\nbbbbb\nccccc\n" + + tmp = OpenSSL::PKCS7.encrypt(certs, data, cipher, OpenSSL::PKCS7::BINARY) + p7 = OpenSSL::PKCS7.new(tmp.to_der) + recip = p7.recipients + assert_equal(:enveloped, p7.type) + assert_equal(2, recip.size) + + assert_equal(@ca_cert.subject.to_s, recip[0].issuer.to_s) + assert_equal(2, recip[0].serial) + assert_equal(data, p7.decrypt(@rsa1024, @ee1_cert)) + + assert_equal(@ca_cert.subject.to_s, recip[1].issuer.to_s) + assert_equal(3, recip[1].serial) + assert_equal(data, p7.decrypt(@rsa1024, @ee2_cert)) + end + + def test_graceful_parsing_failure #[ruby-core:43250] + contents = File.read(__FILE__) + assert_raise(ArgumentError) { OpenSSL::PKCS7.new(contents) } + end + + def test_set_type_signed + p7 = OpenSSL::PKCS7.new + p7.type = "signed" + assert_equal(:signed, p7.type) + end + + def test_set_type_data + p7 = OpenSSL::PKCS7.new + p7.type = "data" + assert_equal(:data, p7.type) + end + + def test_set_type_signed_and_enveloped + p7 = OpenSSL::PKCS7.new + p7.type = "signedAndEnveloped" + assert_equal(:signedAndEnveloped, p7.type) + end + + def test_set_type_enveloped + p7 = OpenSSL::PKCS7.new + p7.type = "enveloped" + assert_equal(:enveloped, p7.type) + end + + def test_set_type_encrypted + p7 = OpenSSL::PKCS7.new + p7.type = "encrypted" + assert_equal(:encrypted, p7.type) + end + + def test_degenerate_pkcs7 + ca_cert_pem = <<END +-----BEGIN CERTIFICATE----- +MIID4DCCAsigAwIBAgIJAL1oVI72wmQwMA0GCSqGSIb3DQEBBQUAMFMxCzAJBgNV +BAYTAkFVMQ4wDAYDVQQIEwVTdGF0ZTENMAsGA1UEBxMEQ2l0eTEQMA4GA1UEChMH +RXhhbXBsZTETMBEGA1UEAxMKRXhhbXBsZSBDQTAeFw0xMjEwMTgwOTE2NTBaFw0y +MjEwMTYwOTE2NTBaMFMxCzAJBgNVBAYTAkFVMQ4wDAYDVQQIEwVTdGF0ZTENMAsG +A1UEBxMEQ2l0eTEQMA4GA1UEChMHRXhhbXBsZTETMBEGA1UEAxMKRXhhbXBsZSBD +QTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMTSPNxOkd5NN19XO0fJ +tGVlWN4DWuvVL9WbWnXJXX9rU6X8sSOL9RrRA64eEZf2UBFjz9fMHZj/OGcxZpus +4YtzfSrMU6xfvsIHeqX+mT60ms2RfX4UXab50MQArBin3JVKHGnOi25uyAOylVFU +TuzzQJvKyB67vjuRPMlVAgVAZAP07ru9gW0ajt/ODxvUfvXxp5SFF68mVP2ipMBr +4fujUwQC6cVHmnuL6p87VFoo9uk87TSQVDOQGL8MK4moMFtEW9oUTU22CgnxnCsS +sCCELYhy9BdaTWQH26LzMfhnwSuIRHZyprW4WZtU0akrYXNiCj8o92rZmQWXJDbl +qNECAwEAAaOBtjCBszAdBgNVHQ4EFgQUNtVw4jvkZZbkdQbkYi2/F4QN79owgYMG +A1UdIwR8MHqAFDbVcOI75GWW5HUG5GItvxeEDe/aoVekVTBTMQswCQYDVQQGEwJB +VTEOMAwGA1UECBMFU3RhdGUxDTALBgNVBAcTBENpdHkxEDAOBgNVBAoTB0V4YW1w +bGUxEzARBgNVBAMTCkV4YW1wbGUgQ0GCCQC9aFSO9sJkMDAMBgNVHRMEBTADAQH/ +MA0GCSqGSIb3DQEBBQUAA4IBAQBvJIsY9bIqliZ3WD1KoN4cvAQeRAPsoLXQkkHg +P6Nrcw9rJ5JvoHfYbo5aNlwbnkbt/B2xlVEXUYpJoBZFXafgxG2gJleioIgnaDS4 +FPPwZf1C5ZrOgUBfxTGjHex4ghSAoNGOd35jQzin5NGKOvZclPjZ2vQ++LP3aA2l +9Fn2qASS46IzMGJlC75mlTOTQwDM16UunMAK26lNG9J6q02o4d/oU2a7x0fD80yF +64kNA1wDAwaVCYiUH541qKp+b4iDqer8nf8HqzYDFlpje18xYZMEd1hj8dVOharM +pISJ+D52hV/BGEYF8r5k3hpC5d76gSP2oCcaY0XvLBf97qik +-----END CERTIFICATE----- +END + p7 = OpenSSL::PKCS7.new + p7.type = "signed" + ca_cert = OpenSSL::X509::Certificate.new(ca_cert_pem) + p7.add_certificate ca_cert + p7.add_data "" + + assert_nothing_raised do + p7.to_pem + end + end + + def test_split_content + pki_message_pem = <<END +-----BEGIN PKCS7----- +MIIHSwYJKoZIhvcNAQcCoIIHPDCCBzgCAQExCzAJBgUrDgMCGgUAMIIDiAYJKoZI +hvcNAQcBoIIDeQSCA3UwgAYJKoZIhvcNAQcDoIAwgAIBADGCARAwggEMAgEAMHUw +cDEQMA4GA1UECgwHZXhhbXBsZTEXMBUGA1UEAwwOVEFSTUFDIFJPT1QgQ0ExIjAg +BgkqhkiG9w0BCQEWE3NvbWVvbmVAZXhhbXBsZS5vcmcxCzAJBgNVBAYTAlVTMRIw +EAYDVQQHDAlUb3duIEhhbGwCAWYwDQYJKoZIhvcNAQEBBQAEgYBspXXse8ZhG1FE +E3PVAulbvrdR52FWPkpeLvSjgEkYzTiUi0CC3poUL1Ku5mOlavWAJgoJpFICDbvc +N4ZNDCwOhnzoI9fMGmm1gvPQy15BdhhZRo9lP7Ga/Hg2APKT0/0yhPsmJ+w+u1e7 +OoJEVeEZ27x3+u745bGEcu8of5th6TCABgkqhkiG9w0BBwEwFAYIKoZIhvcNAwcE +CBNs2U5mMsd/oIAEggIQU6cur8QBz02/4eMpHdlU9IkyrRMiaMZ/ky9zecOAjnvY +d2jZqS7RhczpaNJaSli3GmDsKrF+XqE9J58s9ScGqUigzapusTsxIoRUPr7Ztb0a +pg8VWDipAsuw7GfEkgx868sV93uC4v6Isfjbhd+JRTFp/wR1kTi7YgSXhES+RLUW +gQbDIDgEQYxJ5U951AJtnSpjs9za2ZkTdd8RSEizJK0bQ1vqLoApwAVgZqluATqQ +AHSDCxhweVYw6+y90B9xOrqPC0eU7Wzryq2+Raq5ND2Wlf5/N11RQ3EQdKq/l5Te +ijp9PdWPlkUhWVoDlOFkysjk+BE+7AkzgYvz9UvBjmZsMsWqf+KsZ4S8/30ndLzu +iucsu6eOnFLLX8DKZxV6nYffZOPzZZL8hFBcE7PPgSdBEkazMrEBXq1j5mN7exbJ +NOA5uGWyJNBMOCe+1JbxG9UeoqvCCTHESxEeDu7xR3NnSOD47n7cXwHr81YzK2zQ +5oWpP3C8jzI7tUjLd1S0Z3Psd17oaCn+JOfUtuB0nc3wfPF/WPo0xZQodWxp2/Cl +EltR6qr1zf5C7GwmLzBZ6bHFAIT60/JzV0/56Pn8ztsRFtI4cwaBfTfvnwi8/sD9 +/LYOMY+/b6UDCUSR7RTN7XfrtAqDEzSdzdJkOWm1jvM8gkLmxpZdvxG3ZvDYnEQE +5Nq+un5nAny1wf3rWierBAjE5ntiAmgs5AAAAAAAAAAAAACgggHqMIIB5jCCAU+g +AwIBAgIBATANBgkqhkiG9w0BAQUFADAvMS0wKwYDVQQDEyQwQUM5RjAyNi1EQ0VB +LTRDMTItOTEyNy1DMEZEN0QyQThCNUEwHhcNMTIxMDE5MDk0NTQ3WhcNMTMxMDE5 +MDk0NTQ3WjAvMS0wKwYDVQQDEyQwQUM5RjAyNi1EQ0VBLTRDMTItOTEyNy1DMEZE +N0QyQThCNUEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALTsTNyGIsKvyw56 +WI3Gll/RmjsupkrdEtPbx7OjS9MEgyhOAf9+u6CV0LJGHpy7HUeROykF6xpbSdCm +Mr6kNObl5N0ljOb8OmV4atKjmGg1rWawDLyDQ9Dtuby+dzfHtzAzP+J/3ZoOtSqq +AHVTnCclU1pm/uHN0HZ5nL5iLJTvAgMBAAGjEjAQMA4GA1UdDwEB/wQEAwIFoDAN +BgkqhkiG9w0BAQUFAAOBgQA8K+BouEV04HRTdMZd3akjTQOm6aEGW4nIRnYIf8ZV +mvUpLirVlX/unKtJinhGisFGpuYLMpemx17cnGkBeLCQRvHQjC+ho7l8/LOGheMS +nvu0XHhvmJtRbm8MKHhogwZqHFDnXonvjyqhnhEtK5F2Fimcce3MoF2QtEe0UWv/ +8DGCAaowggGmAgEBMDQwLzEtMCsGA1UEAxMkMEFDOUYwMjYtRENFQS00QzEyLTkx +MjctQzBGRDdEMkE4QjVBAgEBMAkGBSsOAwIaBQCggc0wEgYKYIZIAYb4RQEJAjEE +EwIxOTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0x +MjEwMTkwOTQ1NDdaMCAGCmCGSAGG+EUBCQUxEgQQ2EFUJdQNwQDxclIQ8qNyYzAj +BgkqhkiG9w0BCQQxFgQUy8GFXPpAwRJUT3rdvNC9Pn+4eoswOAYKYIZIAYb4RQEJ +BzEqEygwRkU3QzJEQTVEMDc2NzFFOTcxNDlCNUE3MDRCMERDNkM4MDYwRDJBMA0G +CSqGSIb3DQEBAQUABIGAWUNdzvU2iiQOtihBwF0h48Nnw/2qX8uRjg6CVTOMcGji +BxjUMifEbT//KJwljshl4y3yBLqeVYLOd04k6aKSdjgdZnrnUPI6p5tL5PfJkTAE +L6qflZ9YCU5erE4T5U98hCQBMh4nOYxgaTjnZzhpkKQuEiKq/755cjzTzlI/eok= +-----END PKCS7----- +END + pki_message_content_pem = <<END +-----BEGIN PKCS7----- +MIIDawYJKoZIhvcNAQcDoIIDXDCCA1gCAQAxggEQMIIBDAIBADB1MHAxEDAOBgNV +BAoMB2V4YW1wbGUxFzAVBgNVBAMMDlRBUk1BQyBST09UIENBMSIwIAYJKoZIhvcN +AQkBFhNzb21lb25lQGV4YW1wbGUub3JnMQswCQYDVQQGEwJVUzESMBAGA1UEBwwJ +VG93biBIYWxsAgFmMA0GCSqGSIb3DQEBAQUABIGAbKV17HvGYRtRRBNz1QLpW763 +UedhVj5KXi70o4BJGM04lItAgt6aFC9SruZjpWr1gCYKCaRSAg273DeGTQwsDoZ8 +6CPXzBpptYLz0MteQXYYWUaPZT+xmvx4NgDyk9P9MoT7JifsPrtXuzqCRFXhGdu8 +d/ru+OWxhHLvKH+bYekwggI9BgkqhkiG9w0BBwEwFAYIKoZIhvcNAwcECBNs2U5m +Msd/gIICGFOnLq/EAc9Nv+HjKR3ZVPSJMq0TImjGf5Mvc3nDgI572Hdo2aku0YXM +6WjSWkpYtxpg7Cqxfl6hPSefLPUnBqlIoM2qbrE7MSKEVD6+2bW9GqYPFVg4qQLL +sOxnxJIMfOvLFfd7guL+iLH424XfiUUxaf8EdZE4u2IEl4REvkS1FoEGwyA4BEGM +SeVPedQCbZ0qY7Pc2tmZE3XfEUhIsyStG0Nb6i6AKcAFYGapbgE6kAB0gwsYcHlW +MOvsvdAfcTq6jwtHlO1s68qtvkWquTQ9lpX+fzddUUNxEHSqv5eU3oo6fT3Vj5ZF +IVlaA5ThZMrI5PgRPuwJM4GL8/VLwY5mbDLFqn/irGeEvP99J3S87ornLLunjpxS +y1/AymcVep2H32Tj82WS/IRQXBOzz4EnQRJGszKxAV6tY+Zje3sWyTTgObhlsiTQ +TDgnvtSW8RvVHqKrwgkxxEsRHg7u8UdzZ0jg+O5+3F8B6/NWMyts0OaFqT9wvI8y +O7VIy3dUtGdz7Hde6Ggp/iTn1LbgdJ3N8Hzxf1j6NMWUKHVsadvwpRJbUeqq9c3+ +QuxsJi8wWemxxQCE+tPyc1dP+ej5/M7bERbSOHMGgX03758IvP7A/fy2DjGPv2+l +AwlEke0Uze1367QKgxM0nc3SZDlptY7zPIJC5saWXb8Rt2bw2JxEBOTavrp+ZwJ8 +tcH961onq8Tme2ICaCzk +-----END PKCS7----- +END + pki_msg = OpenSSL::PKCS7.new(pki_message_pem) + store = OpenSSL::X509::Store.new + pki_msg.verify(nil, store, nil, OpenSSL::PKCS7::NOVERIFY) + p7enc = OpenSSL::PKCS7.new(pki_msg.data) + assert_equal(pki_message_content_pem, p7enc.to_pem) + end +end + +end diff --git a/jni/ruby/test/openssl/test_pkey_dh.rb b/jni/ruby/test/openssl/test_pkey_dh.rb new file mode 100644 index 0000000..67dd3e7 --- /dev/null +++ b/jni/ruby/test/openssl/test_pkey_dh.rb @@ -0,0 +1,82 @@ +require_relative 'utils' + +if defined?(OpenSSL::TestUtils) + +class OpenSSL::TestPKeyDH < Test::Unit::TestCase + + NEW_KEYLEN = 256 + + def test_new + dh = OpenSSL::PKey::DH.new(NEW_KEYLEN) + assert_key(dh) + end + + def test_new_break + assert_nil(OpenSSL::PKey::DH.new(NEW_KEYLEN) { break }) + assert_raises(RuntimeError) do + OpenSSL::PKey::DH.new(NEW_KEYLEN) { raise } + end + end + + def test_to_der + dh = OpenSSL::TestUtils::TEST_KEY_DH1024 + der = dh.to_der + dh2 = OpenSSL::PKey::DH.new(der) + assert_equal_params(dh, dh2) + assert_no_key(dh2) + end + + def test_to_pem + dh = OpenSSL::TestUtils::TEST_KEY_DH1024 + pem = dh.to_pem + dh2 = OpenSSL::PKey::DH.new(pem) + assert_equal_params(dh, dh2) + assert_no_key(dh2) + end + + def test_public_key + dh = OpenSSL::TestUtils::TEST_KEY_DH1024 + public_key = dh.public_key + assert_no_key(public_key) #implies public_key.public? is false! + assert_equal(dh.to_der, public_key.to_der) + assert_equal(dh.to_pem, public_key.to_pem) + end + + def test_generate_key + dh = OpenSSL::TestUtils::TEST_KEY_DH512_PUB.public_key # creates a copy + assert_no_key(dh) + dh.generate_key! + assert_key(dh) + end + + def test_key_exchange + dh = OpenSSL::TestUtils::TEST_KEY_DH512_PUB + dh2 = dh.public_key + dh.generate_key! + dh2.generate_key! + assert_equal(dh.compute_key(dh2.pub_key), dh2.compute_key(dh.pub_key)) + end + + private + + def assert_equal_params(dh1, dh2) + assert_equal(dh1.g, dh2.g) + assert_equal(dh1.p, dh2.p) + end + + def assert_no_key(dh) + assert_equal(false, dh.public?) + assert_equal(false, dh.private?) + assert_equal(nil, dh.pub_key) + assert_equal(nil, dh.priv_key) + end + + def assert_key(dh) + assert(dh.public?) + assert(dh.private?) + assert(dh.pub_key) + assert(dh.priv_key) + end +end + +end diff --git a/jni/ruby/test/openssl/test_pkey_dsa.rb b/jni/ruby/test/openssl/test_pkey_dsa.rb new file mode 100644 index 0000000..e4ea1b5 --- /dev/null +++ b/jni/ruby/test/openssl/test_pkey_dsa.rb @@ -0,0 +1,240 @@ +require_relative 'utils' +require 'base64' + +if defined?(OpenSSL::TestUtils) + +class OpenSSL::TestPKeyDSA < Test::Unit::TestCase + def test_private + key = OpenSSL::PKey::DSA.new(256) + assert(key.private?) + key2 = OpenSSL::PKey::DSA.new(key.to_der) + assert(key2.private?) + key3 = key.public_key + assert(!key3.private?) + key4 = OpenSSL::PKey::DSA.new(key3.to_der) + assert(!key4.private?) + end + + def test_new + key = OpenSSL::PKey::DSA.new 256 + pem = key.public_key.to_pem + OpenSSL::PKey::DSA.new pem + assert_equal([], OpenSSL.errors) + end + + def test_new_break + assert_nil(OpenSSL::PKey::DSA.new(512) { break }) + assert_raise(RuntimeError) do + OpenSSL::PKey::DSA.new(512) { raise } + end + end + + def test_sys_sign_verify + key = OpenSSL::TestUtils::TEST_KEY_DSA256 + data = 'Sign me!' + digest = OpenSSL::Digest::SHA1.digest(data) + sig = key.syssign(digest) + assert(key.sysverify(digest, sig)) + end + + def test_sign_verify + check_sign_verify(OpenSSL::Digest::DSS1.new) + end + +if (OpenSSL::OPENSSL_VERSION_NUMBER > 0x10000000) + def test_sign_verify_sha1 + check_sign_verify(OpenSSL::Digest::SHA1.new) + end + + def test_sign_verify_sha256 + check_sign_verify(OpenSSL::Digest::SHA256.new) + end +end + + def test_digest_state_irrelevant_verify + key = OpenSSL::TestUtils::TEST_KEY_DSA256 + digest1 = OpenSSL::Digest::DSS1.new + digest2 = OpenSSL::Digest::DSS1.new + data = 'Sign me!' + sig = key.sign(digest1, data) + digest1.reset + digest1 << 'Change state of digest1' + assert(key.verify(digest1, sig, data)) + assert(key.verify(digest2, sig, data)) + end + + def test_read_DSA_PUBKEY + p = 7188211954100152441468596248707152960171255279130004340103875772401008316444412091945435731597638374542374929457672178957081124632837356913990200866056699 + q = 957032439192465935099784319494405376402293318491 + g = 122928973717064636255205666162891733518376475981809749897454444301389338825906076467196186192907631719698166056821519884939865041993585844526937010746285 + y = 1235756183583465414789073313502727057075641172514181938731172021825149551960029708596057102104063395063907739571546165975727369183495540798749742124846271 + algo = OpenSSL::ASN1::ObjectId.new('DSA') + params = OpenSSL::ASN1::Sequence.new([OpenSSL::ASN1::Integer.new(p), + OpenSSL::ASN1::Integer.new(q), + OpenSSL::ASN1::Integer.new(g)]) + algo_id = OpenSSL::ASN1::Sequence.new ([algo, params]) + pub_key = OpenSSL::ASN1::Integer.new(y) + seq = OpenSSL::ASN1::Sequence.new([algo_id, OpenSSL::ASN1::BitString.new(pub_key.to_der)]) + key = OpenSSL::PKey::DSA.new(seq.to_der) + assert(key.public?) + assert(!key.private?) + assert_equal(p, key.p) + assert_equal(q, key.q) + assert_equal(g, key.g) + assert_equal(y, key.pub_key) + assert_equal(nil, key.priv_key) + assert_equal([], OpenSSL.errors) + end + + def test_read_DSAPublicKey_pem + p = 12260055936871293565827712385212529106400444521449663325576634579961635627321079536132296996623400607469624537382977152381984332395192110731059176842635699 + q = 979494906553787301107832405790107343409973851677 + g = 3731695366899846297271147240305742456317979984190506040697507048095553842519347835107669437969086119948785140453492839427038591924536131566350847469993845 + y = 10505239074982761504240823422422813362721498896040719759460296306305851824586095328615844661273887569281276387605297130014564808567159023649684010036304695 + pem = <<-EOF +-----BEGIN DSA PUBLIC KEY----- +MIHfAkEAyJSJ+g+P/knVcgDwwTzC7Pwg/pWs2EMd/r+lYlXhNfzg0biuXRul8VR4 +VUC/phySExY0PdcqItkR/xYAYNMbNwJBAOoV57X0FxKO/PrNa/MkoWzkCKV/hzhE +p0zbFdsicw+hIjJ7S6Sd/FlDlo89HQZ2FuvWJ6wGLM1j00r39+F2qbMCFQCrkhIX +SG+is37hz1IaBeEudjB2HQJAR0AloavBvtsng8obsjLb7EKnB+pSeHr/BdIQ3VH7 +fWLOqqkzFeRrYMDzUpl36XktY6Yq8EJYlW9pCMmBVNy/dQ== +-----END DSA PUBLIC KEY----- + EOF + key = OpenSSL::PKey::DSA.new(pem) + assert(key.public?) + assert(!key.private?) + assert_equal(p, key.p) + assert_equal(q, key.q) + assert_equal(g, key.g) + assert_equal(y, key.pub_key) + assert_equal(nil, key.priv_key) + assert_equal([], OpenSSL.errors) + end + + def test_read_DSA_PUBKEY_pem + p = 12260055936871293565827712385212529106400444521449663325576634579961635627321079536132296996623400607469624537382977152381984332395192110731059176842635699 + q = 979494906553787301107832405790107343409973851677 + g = 3731695366899846297271147240305742456317979984190506040697507048095553842519347835107669437969086119948785140453492839427038591924536131566350847469993845 + y = 10505239074982761504240823422422813362721498896040719759460296306305851824586095328615844661273887569281276387605297130014564808567159023649684010036304695 + pem = <<-EOF +-----BEGIN PUBLIC KEY----- +MIHxMIGoBgcqhkjOOAQBMIGcAkEA6hXntfQXEo78+s1r8yShbOQIpX+HOESnTNsV +2yJzD6EiMntLpJ38WUOWjz0dBnYW69YnrAYszWPTSvf34XapswIVAKuSEhdIb6Kz +fuHPUhoF4S52MHYdAkBHQCWhq8G+2yeDyhuyMtvsQqcH6lJ4ev8F0hDdUft9Ys6q +qTMV5GtgwPNSmXfpeS1jpirwQliVb2kIyYFU3L91A0QAAkEAyJSJ+g+P/knVcgDw +wTzC7Pwg/pWs2EMd/r+lYlXhNfzg0biuXRul8VR4VUC/phySExY0PdcqItkR/xYA +YNMbNw== +-----END PUBLIC KEY----- + EOF + key = OpenSSL::PKey::DSA.new(pem) + assert(key.public?) + assert(!key.private?) + assert_equal(p, key.p) + assert_equal(q, key.q) + assert_equal(g, key.g) + assert_equal(y, key.pub_key) + assert_equal(nil, key.priv_key) + assert_equal([], OpenSSL.errors) + end + + def test_export_format_is_DSA_PUBKEY_pem + key = OpenSSL::TestUtils::TEST_KEY_DSA256 + pem = key.public_key.to_pem + pem.gsub!(/^-+(\w|\s)+-+$/, "") # eliminate --------BEGIN...------- + asn1 = OpenSSL::ASN1.decode(Base64.decode64(pem)) + assert_equal(OpenSSL::ASN1::SEQUENCE, asn1.tag) + assert_equal(2, asn1.value.size) + seq = asn1.value + assert_equal(OpenSSL::ASN1::SEQUENCE, seq[0].tag) + assert_equal(2, seq[0].value.size) + algo_id = seq[0].value + assert_equal(OpenSSL::ASN1::OBJECT, algo_id[0].tag) + assert_equal('DSA', algo_id[0].value) + assert_equal(OpenSSL::ASN1::SEQUENCE, algo_id[1].tag) + assert_equal(3, algo_id[1].value.size) + params = algo_id[1].value + assert_equal(OpenSSL::ASN1::INTEGER, params[0].tag) + assert_equal(key.p, params[0].value) + assert_equal(OpenSSL::ASN1::INTEGER, params[1].tag) + assert_equal(key.q, params[1].value) + assert_equal(OpenSSL::ASN1::INTEGER, params[2].tag) + assert_equal(key.g, params[2].value) + assert_equal(OpenSSL::ASN1::BIT_STRING, seq[1].tag) + assert_equal(0, seq[1].unused_bits) + pub_key = OpenSSL::ASN1.decode(seq[1].value) + assert_equal(OpenSSL::ASN1::INTEGER, pub_key.tag) + assert_equal(key.pub_key, pub_key.value) + assert_equal([], OpenSSL.errors) + end + + def test_read_private_key_der + key = OpenSSL::TestUtils::TEST_KEY_DSA256 + der = key.to_der + key2 = OpenSSL::PKey.read(der) + assert(key2.private?) + assert_equal(der, key2.to_der) + assert_equal([], OpenSSL.errors) + end + + def test_read_private_key_pem + key = OpenSSL::TestUtils::TEST_KEY_DSA256 + pem = key.to_pem + key2 = OpenSSL::PKey.read(pem) + assert(key2.private?) + assert_equal(pem, key2.to_pem) + assert_equal([], OpenSSL.errors) + end + + def test_read_public_key_der + key = OpenSSL::TestUtils::TEST_KEY_DSA256.public_key + der = key.to_der + key2 = OpenSSL::PKey.read(der) + assert(!key2.private?) + assert_equal(der, key2.to_der) + assert_equal([], OpenSSL.errors) + end + + def test_read_public_key_pem + key = OpenSSL::TestUtils::TEST_KEY_DSA256.public_key + pem = key.to_pem + key2 = OpenSSL::PKey.read(pem) + assert(!key2.private?) + assert_equal(pem, key2.to_pem) + assert_equal([], OpenSSL.errors) + end + + def test_read_private_key_pem_pw + key = OpenSSL::TestUtils::TEST_KEY_DSA256 + pem = key.to_pem(OpenSSL::Cipher.new('AES-128-CBC'), 'secret') + #callback form for password + key2 = OpenSSL::PKey.read(pem) do + 'secret' + end + assert(key2.private?) + # pass password directly + key2 = OpenSSL::PKey.read(pem, 'secret') + assert(key2.private?) + #omit pem equality check, will be different due to cipher iv + assert_equal([], OpenSSL.errors) + end + + def test_export_password_length + key = OpenSSL::TestUtils::TEST_KEY_DSA256 + assert_raise(OpenSSL::OpenSSLError) do + key.export(OpenSSL::Cipher.new('AES-128-CBC'), 'sec') + end + pem = key.export(OpenSSL::Cipher.new('AES-128-CBC'), 'secr') + assert(pem) + end + + private + + def check_sign_verify(digest) + key = OpenSSL::TestUtils::TEST_KEY_DSA256 + data = 'Sign me!' + sig = key.sign(digest, data) + assert(key.verify(digest, sig, data)) + end +end + +end diff --git a/jni/ruby/test/openssl/test_pkey_ec.rb b/jni/ruby/test/openssl/test_pkey_ec.rb new file mode 100644 index 0000000..1693ace --- /dev/null +++ b/jni/ruby/test/openssl/test_pkey_ec.rb @@ -0,0 +1,211 @@ +require_relative 'utils' + +if defined?(OpenSSL::TestUtils) && defined?(OpenSSL::PKey::EC) + +class OpenSSL::TestEC < Test::Unit::TestCase + def setup + @data1 = 'foo' + @data2 = 'bar' * 1000 # data too long for DSA sig + + @groups = [] + @keys = [] + + OpenSSL::PKey::EC.builtin_curves.each do |curve, comment| + next if curve.start_with?("Oakley") # Oakley curves are not suitable for ECDSA + group = OpenSSL::PKey::EC::Group.new(curve) + + key = OpenSSL::PKey::EC.new(group) + key.generate_key + + @groups << group + @keys << key + end + end + + def compare_keys(k1, k2) + assert_equal(k1.to_pem, k2.to_pem) + end + + def test_builtin_curves + assert(!OpenSSL::PKey::EC.builtin_curves.empty?) + end + + def test_curve_names + @groups.each_with_index do |group, idx| + key = @keys[idx] + assert_equal(group.curve_name, key.group.curve_name) + end + end + + def test_check_key + for key in @keys + assert_equal(key.check_key, true) + assert_equal(key.private_key?, true) + assert_equal(key.public_key?, true) + end + end + + def test_group_encoding + for group in @groups + for meth in [:to_der, :to_pem] + txt = group.send(meth) + gr = OpenSSL::PKey::EC::Group.new(txt) + + assert_equal(txt, gr.send(meth)) + + assert_equal(group.generator.to_bn, gr.generator.to_bn) + assert_equal(group.cofactor, gr.cofactor) + assert_equal(group.order, gr.order) + assert_equal(group.seed, gr.seed) + assert_equal(group.degree, gr.degree) + end + end + end + + def test_key_encoding + for key in @keys + group = key.group + + for meth in [:to_der, :to_pem] + txt = key.send(meth) + assert_equal(txt, OpenSSL::PKey::EC.new(txt).send(meth)) + end + + bn = key.public_key.to_bn + assert_equal(bn, OpenSSL::PKey::EC::Point.new(group, bn).to_bn) + end + end + + def test_set_keys + for key in @keys + k = OpenSSL::PKey::EC.new + k.group = key.group + k.private_key = key.private_key + k.public_key = key.public_key + + compare_keys(key, k) + end + end + + def test_dsa_sign_verify + for key in @keys + sig = key.dsa_sign_asn1(@data1) + assert(key.dsa_verify_asn1(@data1, sig)) + end + end + + def test_dsa_sign_asn1_FIPS186_3 + for key in @keys + size = key.group.order.num_bits / 8 + 1 + dgst = (1..size).to_a.pack('C*') + begin + sig = key.dsa_sign_asn1(dgst) + # dgst is auto-truncated according to FIPS186-3 after openssl-0.9.8m + assert(key.dsa_verify_asn1(dgst + "garbage", sig)) + rescue OpenSSL::PKey::ECError => e + # just an exception for longer dgst before openssl-0.9.8m + assert_equal('ECDSA_sign: data too large for key size', e.message) + # no need to do following tests + return + end + end + end + + def test_dh_compute_key + for key in @keys + k = OpenSSL::PKey::EC.new(key.group) + k.generate_key + + puba = key.public_key + pubb = k.public_key + a = key.dh_compute_key(pubb) + b = k.dh_compute_key(puba) + assert_equal(a, b) + end + end + + def test_read_private_key_der + ec = OpenSSL::TestUtils::TEST_KEY_EC_P256V1 + der = ec.to_der + ec2 = OpenSSL::PKey.read(der) + assert(ec2.private_key?) + assert_equal(der, ec2.to_der) + assert_equal([], OpenSSL.errors) + end + + def test_read_private_key_pem + ec = OpenSSL::TestUtils::TEST_KEY_EC_P256V1 + pem = ec.to_pem + ec2 = OpenSSL::PKey.read(pem) + assert(ec2.private_key?) + assert_equal(pem, ec2.to_pem) + assert_equal([], OpenSSL.errors) + end + + def test_read_public_key_der + ec = OpenSSL::TestUtils::TEST_KEY_EC_P256V1 + ec2 = OpenSSL::PKey::EC.new(ec.group) + ec2.public_key = ec.public_key + der = ec2.to_der + ec3 = OpenSSL::PKey.read(der) + assert(!ec3.private_key?) + assert_equal(der, ec3.to_der) + assert_equal([], OpenSSL.errors) + end + + def test_read_public_key_pem + ec = OpenSSL::TestUtils::TEST_KEY_EC_P256V1 + ec2 = OpenSSL::PKey::EC.new(ec.group) + ec2.public_key = ec.public_key + pem = ec2.to_pem + ec3 = OpenSSL::PKey.read(pem) + assert(!ec3.private_key?) + assert_equal(pem, ec3.to_pem) + assert_equal([], OpenSSL.errors) + end + + def test_read_private_key_pem_pw + ec = OpenSSL::TestUtils::TEST_KEY_EC_P256V1 + pem = ec.to_pem(OpenSSL::Cipher.new('AES-128-CBC'), 'secret') + #callback form for password + ec2 = OpenSSL::PKey.read(pem) do + 'secret' + end + assert(ec2.private_key?) + # pass password directly + ec2 = OpenSSL::PKey.read(pem, 'secret') + assert(ec2.private_key?) + #omit pem equality check, will be different due to cipher iv + assert_equal([], OpenSSL.errors) + end + + def test_export_password_length + key = OpenSSL::TestUtils::TEST_KEY_EC_P256V1 + assert_raise(OpenSSL::OpenSSLError) do + key.export(OpenSSL::Cipher.new('AES-128-CBC'), 'sec') + end + pem = key.export(OpenSSL::Cipher.new('AES-128-CBC'), 'secr') + assert(pem) + end + + def test_ec_point_mul + ec = OpenSSL::TestUtils::TEST_KEY_EC_P256V1 + p1 = ec.public_key + bn1 = OpenSSL::BN.new('10') + bn2 = OpenSSL::BN.new('20') + + p2 = p1.mul(bn1) + assert(p1.group == p2.group) + p2 = p1.mul(bn1, bn2) + assert(p1.group == p2.group) + p2 = p1.mul([bn1, bn2], [p1]) + assert(p1.group == p2.group) + p2 = p1.mul([bn1, bn2], [p1], bn2) + assert(p1.group == p2.group) + end + +# test Group: asn1_flag, point_conversion + +end + +end diff --git a/jni/ruby/test/openssl/test_pkey_rsa.rb b/jni/ruby/test/openssl/test_pkey_rsa.rb new file mode 100644 index 0000000..ea042c2 --- /dev/null +++ b/jni/ruby/test/openssl/test_pkey_rsa.rb @@ -0,0 +1,313 @@ +require_relative 'utils' +require 'base64' + +if defined?(OpenSSL::TestUtils) + +class OpenSSL::TestPKeyRSA < Test::Unit::TestCase + def test_padding + key = OpenSSL::PKey::RSA.new(512, 3) + + # Need right size for raw mode + plain0 = "x" * (512/8) + cipher = key.private_encrypt(plain0, OpenSSL::PKey::RSA::NO_PADDING) + plain1 = key.public_decrypt(cipher, OpenSSL::PKey::RSA::NO_PADDING) + assert_equal(plain0, plain1) + + # Need smaller size for pkcs1 mode + plain0 = "x" * (512/8 - 11) + cipher1 = key.private_encrypt(plain0, OpenSSL::PKey::RSA::PKCS1_PADDING) + plain1 = key.public_decrypt(cipher1, OpenSSL::PKey::RSA::PKCS1_PADDING) + assert_equal(plain0, plain1) + + cipherdef = key.private_encrypt(plain0) # PKCS1_PADDING is default + plain1 = key.public_decrypt(cipherdef) + assert_equal(plain0, plain1) + assert_equal(cipher1, cipherdef) + + # Failure cases + assert_raise(ArgumentError){ key.private_encrypt() } + assert_raise(ArgumentError){ key.private_encrypt("hi", 1, nil) } + assert_raise(OpenSSL::PKey::RSAError){ key.private_encrypt(plain0, 666) } + end + + def test_private + key = OpenSSL::PKey::RSA.new(512, 3) + assert(key.private?) + key2 = OpenSSL::PKey::RSA.new(key.to_der) + assert(key2.private?) + key3 = key.public_key + assert(!key3.private?) + key4 = OpenSSL::PKey::RSA.new(key3.to_der) + assert(!key4.private?) + end + + def test_new + key = OpenSSL::PKey::RSA.new 512 + pem = key.public_key.to_pem + OpenSSL::PKey::RSA.new pem + assert_equal([], OpenSSL.errors) + end + + def test_new_exponent_default + assert_equal(65537, OpenSSL::PKey::RSA.new(512).e) + end + + def test_new_with_exponent + 1.upto(30) do |idx| + e = (2 ** idx) + 1 + key = OpenSSL::PKey::RSA.new(512, e) + assert_equal(e, key.e) + end + end + + def test_new_break + assert_nil(OpenSSL::PKey::RSA.new(1024) { break }) + assert_raise(RuntimeError) do + OpenSSL::PKey::RSA.new(1024) { raise } + end + end + + def test_sign_verify + key = OpenSSL::TestUtils::TEST_KEY_RSA1024 + digest = OpenSSL::Digest::SHA1.new + data = 'Sign me!' + sig = key.sign(digest, data) + assert(key.verify(digest, sig, data)) + end + + def test_sign_verify_memory_leak + bug9743 = '[ruby-core:62038] [Bug #9743]' + assert_no_memory_leak(%w[-ropenssl], <<-PREP, <<-CODE, bug9743, rss: true, timeout: 30) + data = 'Sign me!' + digest = OpenSSL::Digest::SHA512.new + pkey = OpenSSL::PKey::RSA.new(2048) + signature = pkey.sign(digest, data) + pub_key = pkey.public_key + PREP + 20_000.times { + pub_key.verify(digest, signature, data) + } + CODE + + assert_no_memory_leak(%w[-ropenssl], <<-PREP, <<-CODE, bug9743, rss: true, timeout: 30) + data = 'Sign me!' + digest = OpenSSL::Digest::SHA512.new + pkey = OpenSSL::PKey::RSA.new(2048) + signature = pkey.sign(digest, data) + pub_key = pkey.public_key + PREP + 20_000.times { + begin + pub_key.verify(digest, signature, 1) + rescue TypeError + end + } + CODE + end + + def test_digest_state_irrelevant_sign + key = OpenSSL::TestUtils::TEST_KEY_RSA1024 + digest1 = OpenSSL::Digest::SHA1.new + digest2 = OpenSSL::Digest::SHA1.new + data = 'Sign me!' + digest1 << 'Change state of digest1' + sig1 = key.sign(digest1, data) + sig2 = key.sign(digest2, data) + assert_equal(sig1, sig2) + end + + def test_digest_state_irrelevant_verify + key = OpenSSL::TestUtils::TEST_KEY_RSA1024 + digest1 = OpenSSL::Digest::SHA1.new + digest2 = OpenSSL::Digest::SHA1.new + data = 'Sign me!' + sig = key.sign(digest1, data) + digest1.reset + digest1 << 'Change state of digest1' + assert(key.verify(digest1, sig, data)) + assert(key.verify(digest2, sig, data)) + end + + def test_read_RSAPublicKey + modulus = 10664264882656732240315063514678024569492171560814833397008094754351396057398262071307709191731289492697968568138092052265293364132872019762410446076526351 + exponent = 65537 + seq = OpenSSL::ASN1::Sequence.new([OpenSSL::ASN1::Integer.new(modulus), OpenSSL::ASN1::Integer.new(exponent)]) + key = OpenSSL::PKey::RSA.new(seq.to_der) + assert(key.public?) + assert(!key.private?) + assert_equal(modulus, key.n) + assert_equal(exponent, key.e) + assert_equal(nil, key.d) + assert_equal(nil, key.p) + assert_equal(nil, key.q) + assert_equal([], OpenSSL.errors) + end + + def test_read_RSA_PUBKEY + modulus = 10664264882656732240315063514678024569492171560814833397008094754351396057398262071307709191731289492697968568138092052265293364132872019762410446076526351 + exponent = 65537 + algo = OpenSSL::ASN1::ObjectId.new('rsaEncryption') + null_params = OpenSSL::ASN1::Null.new(nil) + algo_id = OpenSSL::ASN1::Sequence.new ([algo, null_params]) + pub_key = OpenSSL::ASN1::Sequence.new([OpenSSL::ASN1::Integer.new(modulus), OpenSSL::ASN1::Integer.new(exponent)]) + seq = OpenSSL::ASN1::Sequence.new([algo_id, OpenSSL::ASN1::BitString.new(pub_key.to_der)]) + key = OpenSSL::PKey::RSA.new(seq.to_der) + assert(key.public?) + assert(!key.private?) + assert_equal(modulus, key.n) + assert_equal(exponent, key.e) + assert_equal(nil, key.d) + assert_equal(nil, key.p) + assert_equal(nil, key.q) + assert_equal([], OpenSSL.errors) + end + + def test_read_RSAPublicKey_pem + modulus = 9416340886363418692990906464787534854462163316648195510702927337693641649864839352187127240942127674615733815606532506566068276485089353644309497938966061 + exponent = 65537 + pem = <<-EOF +-----BEGIN RSA PUBLIC KEY----- +MEgCQQCzyh2RIZK62E2PbTWqUljD+K23XR9AGBKNtXjal6WD2yRGcLqzPJLNCa60 +AudJR1JobbIbDJrQu6AXnWh5k/YtAgMBAAE= +-----END RSA PUBLIC KEY----- + EOF + key = OpenSSL::PKey::RSA.new(pem) + assert(key.public?) + assert(!key.private?) + assert_equal(modulus, key.n) + assert_equal(exponent, key.e) + assert_equal(nil, key.d) + assert_equal(nil, key.p) + assert_equal(nil, key.q) + assert_equal([], OpenSSL.errors) + end + + def test_read_RSA_PUBKEY_pem + modulus = 9416340886363418692990906464787534854462163316648195510702927337693641649864839352187127240942127674615733815606532506566068276485089353644309497938966061 + exponent = 65537 + pem = <<-EOF +-----BEGIN PUBLIC KEY----- +MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALPKHZEhkrrYTY9tNapSWMP4rbdd +H0AYEo21eNqXpYPbJEZwurM8ks0JrrQC50lHUmhtshsMmtC7oBedaHmT9i0C +AwEAAQ== +-----END PUBLIC KEY----- + EOF + key = OpenSSL::PKey::RSA.new(pem) + assert(key.public?) + assert(!key.private?) + assert_equal(modulus, key.n) + assert_equal(exponent, key.e) + assert_equal(nil, key.d) + assert_equal(nil, key.p) + assert_equal(nil, key.q) + assert_equal([], OpenSSL.errors) + end + + def test_export_format_is_RSA_PUBKEY + key = OpenSSL::PKey::RSA.new(512) + asn1 = OpenSSL::ASN1.decode(key.public_key.to_der) + check_PUBKEY(asn1, key) + end + + def test_export_format_is_RSA_PUBKEY_pem + key = OpenSSL::PKey::RSA.new(512) + pem = key.public_key.to_pem + pem.gsub!(/^-+(\w|\s)+-+$/, "") # eliminate --------BEGIN...------- + asn1 = OpenSSL::ASN1.decode(Base64.decode64(pem)) + check_PUBKEY(asn1, key) + end + + def test_read_private_key_der + der = OpenSSL::TestUtils::TEST_KEY_RSA1024.to_der + key = OpenSSL::PKey.read(der) + assert(key.private?) + assert_equal(der, key.to_der) + assert_equal([], OpenSSL.errors) + end + + def test_read_private_key_pem + pem = OpenSSL::TestUtils::TEST_KEY_RSA1024.to_pem + key = OpenSSL::PKey.read(pem) + assert(key.private?) + assert_equal(pem, key.to_pem) + assert_equal([], OpenSSL.errors) + end + + def test_read_public_key_der + der = OpenSSL::TestUtils::TEST_KEY_RSA1024.public_key.to_der + key = OpenSSL::PKey.read(der) + assert(!key.private?) + assert_equal(der, key.to_der) + assert_equal([], OpenSSL.errors) + end + + def test_read_public_key_pem + pem = OpenSSL::TestUtils::TEST_KEY_RSA1024.public_key.to_pem + key = OpenSSL::PKey.read(pem) + assert(!key.private?) + assert_equal(pem, key.to_pem) + assert_equal([], OpenSSL.errors) + end + + def test_read_private_key_pem_pw + pem = OpenSSL::TestUtils::TEST_KEY_RSA1024.to_pem(OpenSSL::Cipher.new('AES-128-CBC'), 'secret') + #callback form for password + key = OpenSSL::PKey.read(pem) do + 'secret' + end + assert(key.private?) + # pass password directly + key = OpenSSL::PKey.read(pem, 'secret') + assert(key.private?) + #omit pem equality check, will be different due to cipher iv + assert_equal([], OpenSSL.errors) + end + + def test_read_private_key_pem_pw_exception + pem = OpenSSL::TestUtils::TEST_KEY_RSA1024.to_pem(OpenSSL::Cipher.new('AES-128-CBC'), 'secret') + # it raises an ArgumentError from PEM reading. The exception raised inside are ignored for now. + assert_raise(ArgumentError) do + OpenSSL::PKey.read(pem) do + raise RuntimeError + end + end + assert_equal([], OpenSSL.errors) + end + + def test_export_password_length + key = OpenSSL::TestUtils::TEST_KEY_RSA1024 + assert_raise(OpenSSL::OpenSSLError) do + key.export(OpenSSL::Cipher.new('AES-128-CBC'), 'sec') + end + pem = key.export(OpenSSL::Cipher.new('AES-128-CBC'), 'secr') + assert(pem) + end + + private + + def check_PUBKEY(asn1, key) + assert_equal(OpenSSL::ASN1::SEQUENCE, asn1.tag) + assert_equal(2, asn1.value.size) + seq = asn1.value + assert_equal(OpenSSL::ASN1::SEQUENCE, seq[0].tag) + assert_equal(2, seq[0].value.size) + algo_id = seq[0].value + assert_equal(OpenSSL::ASN1::OBJECT, algo_id[0].tag) + assert_equal('rsaEncryption', algo_id[0].value) + assert_equal(OpenSSL::ASN1::NULL, algo_id[1].tag) + assert_equal(nil, algo_id[1].value) + assert_equal(OpenSSL::ASN1::BIT_STRING, seq[1].tag) + assert_equal(0, seq[1].unused_bits) + pub_key = OpenSSL::ASN1.decode(seq[1].value) + assert_equal(OpenSSL::ASN1::SEQUENCE, pub_key.tag) + assert_equal(2, pub_key.value.size) + assert_equal(OpenSSL::ASN1::INTEGER, pub_key.value[0].tag) + assert_equal(key.n, pub_key.value[0].value) + assert_equal(OpenSSL::ASN1::INTEGER, pub_key.value[1].tag) + assert_equal(key.e, pub_key.value[1].value) + assert_equal([], OpenSSL.errors) + end + +end + +end diff --git a/jni/ruby/test/openssl/test_ssl.rb b/jni/ruby/test/openssl/test_ssl.rb new file mode 100644 index 0000000..8aa18e5 --- /dev/null +++ b/jni/ruby/test/openssl/test_ssl.rb @@ -0,0 +1,933 @@ +require_relative "utils" + +if defined?(OpenSSL::TestUtils) + +class OpenSSL::TestSSL < OpenSSL::SSLTestCase + + def test_ctx_setup + ctx = OpenSSL::SSL::SSLContext.new + assert_equal(ctx.setup, true) + assert_equal(ctx.setup, nil) + end + + def test_ctx_setup_no_compression + ctx = OpenSSL::SSL::SSLContext.new + ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_COMPRESSION + assert_equal(ctx.setup, true) + assert_equal(ctx.setup, nil) + assert_equal(OpenSSL::SSL::OP_NO_COMPRESSION, + ctx.options & OpenSSL::SSL::OP_NO_COMPRESSION) + end if defined?(OpenSSL::SSL::OP_NO_COMPRESSION) + + def test_ctx_setup_with_extra_chain_cert + ctx = OpenSSL::SSL::SSLContext.new + ctx.extra_chain_cert = [@ca_cert, @cli_cert] + assert_equal(ctx.setup, true) + assert_equal(ctx.setup, nil) + end + + def test_not_started_session + skip "non socket argument of SSLSocket.new is not supported on this platform" if /mswin|mingw/ =~ RUBY_PLATFORM + open(__FILE__) do |f| + assert_nil OpenSSL::SSL::SSLSocket.new(f).cert + end + end + + def test_ssl_gets + start_server(OpenSSL::SSL::VERIFY_NONE, true) { |server, port| + server_connect(port) { |ssl| + ssl.write "abc\n" + IO.select [ssl] + + line = ssl.gets + + assert_equal "abc\n", line + assert_equal Encoding::BINARY, line.encoding + } + } + end + + def test_ssl_read_nonblock + start_server(OpenSSL::SSL::VERIFY_NONE, true) { |server, port| + server_connect(port) { |ssl| + assert_raise(IO::WaitReadable) { ssl.read_nonblock(100) } + ssl.write("abc\n") + IO.select [ssl] + assert_equal('a', ssl.read_nonblock(1)) + assert_equal("bc\n", ssl.read_nonblock(100)) + assert_raise(IO::WaitReadable) { ssl.read_nonblock(100) } + } + } + end + + def test_connect_and_close + start_server(OpenSSL::SSL::VERIFY_NONE, true){|server, port| + sock = TCPSocket.new("127.0.0.1", port) + ssl = OpenSSL::SSL::SSLSocket.new(sock) + assert(ssl.connect) + ssl.close + assert(!sock.closed?) + sock.close + + sock = TCPSocket.new("127.0.0.1", port) + ssl = OpenSSL::SSL::SSLSocket.new(sock) + ssl.sync_close = true # !! + assert(ssl.connect) + ssl.close + assert(sock.closed?) + } + end + + def test_read_and_write + start_server(OpenSSL::SSL::VERIFY_NONE, true){|server, port| + server_connect(port) { |ssl| + # syswrite and sysread + ITERATIONS.times{|i| + str = "x" * 100 + "\n" + ssl.syswrite(str) + newstr = '' + newstr << ssl.sysread(str.size - newstr.size) until newstr.size == str.size + assert_equal(str, newstr) + + str = "x" * i * 100 + "\n" + buf = "" + ssl.syswrite(str) + assert_equal(buf.object_id, ssl.sysread(str.size, buf).object_id) + newstr = buf + newstr << ssl.sysread(str.size - newstr.size) until newstr.size == str.size + assert_equal(str, newstr) + } + + # puts and gets + ITERATIONS.times{ + str = "x" * 100 + "\n" + ssl.puts(str) + assert_equal(str, ssl.gets) + + str = "x" * 100 + ssl.puts(str) + assert_equal(str, ssl.gets("\n", 100)) + assert_equal("\n", ssl.gets) + } + + # read and write + ITERATIONS.times{|i| + str = "x" * 100 + "\n" + ssl.write(str) + assert_equal(str, ssl.read(str.size)) + + str = "x" * i * 100 + "\n" + buf = "" + ssl.write(str) + assert_equal(buf.object_id, ssl.read(str.size, buf).object_id) + assert_equal(str, buf) + } + } + } + end + + def test_client_auth_failure + vflag = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT + start_server(vflag, true, :ignore_listener_error => true){|server, port| + assert_raise(OpenSSL::SSL::SSLError, Errno::ECONNRESET){ + sock = TCPSocket.new("127.0.0.1", port) + ssl = OpenSSL::SSL::SSLSocket.new(sock) + ssl.sync_close = true + begin + ssl.connect + ensure + ssl.close + end + } + } + end + + def test_client_auth_success + vflag = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT + start_server(vflag, true){|server, port| + ctx = OpenSSL::SSL::SSLContext.new + ctx.key = @cli_key + ctx.cert = @cli_cert + + server_connect(port, ctx) { |ssl| + ssl.puts("foo") + assert_equal("foo\n", ssl.gets) + } + + called = nil + ctx = OpenSSL::SSL::SSLContext.new + ctx.client_cert_cb = Proc.new{ |sslconn| + called = true + [@cli_cert, @cli_key] + } + + server_connect(port, ctx) { |ssl| + assert(called) + ssl.puts("foo") + assert_equal("foo\n", ssl.gets) + } + } + end + + def test_client_ca + ctx_proc = Proc.new do |ctx| + ctx.client_ca = [@ca_cert] + end + + vflag = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT + start_server(vflag, true, :ctx_proc => ctx_proc){|server, port| + ctx = OpenSSL::SSL::SSLContext.new + client_ca_from_server = nil + ctx.client_cert_cb = Proc.new do |sslconn| + client_ca_from_server = sslconn.client_ca + [@cli_cert, @cli_key] + end + server_connect(port, ctx) { |ssl| assert_equal([@ca], client_ca_from_server) } + } + end + + def test_read_nonblock_without_session + OpenSSL::TestUtils.silent do + start_server(OpenSSL::SSL::VERIFY_NONE, false){|server, port| + sock = TCPSocket.new("127.0.0.1", port) + ssl = OpenSSL::SSL::SSLSocket.new(sock) + ssl.sync_close = true + + assert_equal :wait_readable, ssl.read_nonblock(100, exception: false) + ssl.write("abc\n") + IO.select [ssl] + assert_equal('a', ssl.read_nonblock(1)) + assert_equal("bc\n", ssl.read_nonblock(100)) + assert_equal :wait_readable, ssl.read_nonblock(100, exception: false) + ssl.close + } + end + end + + def test_starttls + OpenSSL::TestUtils.silent do + start_server(OpenSSL::SSL::VERIFY_NONE, false){|server, port| + sock = TCPSocket.new("127.0.0.1", port) + ssl = OpenSSL::SSL::SSLSocket.new(sock) + ssl.sync_close = true + str = "x" * 1000 + "\n" + + ITERATIONS.times{ + ssl.puts(str) + assert_equal(str, ssl.gets) + } + starttls(ssl) + + ITERATIONS.times{ + ssl.puts(str) + assert_equal(str, ssl.gets) + } + + ssl.close + } + end + end + + def test_parallel + GC.start + start_server(OpenSSL::SSL::VERIFY_NONE, true){|server, port| + ssls = [] + 10.times{ + sock = TCPSocket.new("127.0.0.1", port) + ssl = OpenSSL::SSL::SSLSocket.new(sock) + ssl.connect + ssl.sync_close = true + ssls << ssl + } + str = "x" * 1000 + "\n" + ITERATIONS.times{ + ssls.each{|ssl| + ssl.puts(str) + assert_equal(str, ssl.gets) + } + } + ssls.each{|ssl| ssl.close } + } + end + + def test_verify_result + start_server(OpenSSL::SSL::VERIFY_NONE, true, :ignore_listener_error => true){|server, port| + sock = TCPSocket.new("127.0.0.1", port) + ctx = OpenSSL::SSL::SSLContext.new + ctx.set_params + ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) + ssl.sync_close = true + begin + assert_raise(OpenSSL::SSL::SSLError){ ssl.connect } + assert_equal(OpenSSL::X509::V_ERR_SELF_SIGNED_CERT_IN_CHAIN, ssl.verify_result) + ensure + ssl.close + end + } + + start_server(OpenSSL::SSL::VERIFY_NONE, true){|server, port| + sock = TCPSocket.new("127.0.0.1", port) + ctx = OpenSSL::SSL::SSLContext.new + ctx.set_params( + :verify_callback => Proc.new do |preverify_ok, store_ctx| + store_ctx.error = OpenSSL::X509::V_OK + true + end + ) + ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) + ssl.sync_close = true + begin + ssl.connect + assert_equal(OpenSSL::X509::V_OK, ssl.verify_result) + ensure + ssl.close + end + } + + start_server(OpenSSL::SSL::VERIFY_NONE, true, :ignore_listener_error => true){|server, port| + sock = TCPSocket.new("127.0.0.1", port) + ctx = OpenSSL::SSL::SSLContext.new + ctx.set_params( + :verify_callback => Proc.new do |preverify_ok, store_ctx| + store_ctx.error = OpenSSL::X509::V_ERR_APPLICATION_VERIFICATION + false + end + ) + ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) + ssl.sync_close = true + begin + assert_raise(OpenSSL::SSL::SSLError){ ssl.connect } + assert_equal(OpenSSL::X509::V_ERR_APPLICATION_VERIFICATION, ssl.verify_result) + ensure + ssl.close + end + } + end + + def test_exception_in_verify_callback_is_ignored + start_server(OpenSSL::SSL::VERIFY_NONE, true, :ignore_listener_error => true){|server, port| + sock = TCPSocket.new("127.0.0.1", port) + ctx = OpenSSL::SSL::SSLContext.new + ctx.set_params( + :verify_callback => Proc.new do |preverify_ok, store_ctx| + store_ctx.error = OpenSSL::X509::V_OK + raise RuntimeError + end + ) + ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) + ssl.sync_close = true + begin + OpenSSL::TestUtils.silent do + # SSLError, not RuntimeError + assert_raise(OpenSSL::SSL::SSLError) { ssl.connect } + end + assert_equal(OpenSSL::X509::V_ERR_CERT_REJECTED, ssl.verify_result) + ensure + ssl.close + end + } + end + + def test_sslctx_set_params + start_server(OpenSSL::SSL::VERIFY_NONE, true, :ignore_listener_error => true){|server, port| + sock = TCPSocket.new("127.0.0.1", port) + ctx = OpenSSL::SSL::SSLContext.new + ctx.set_params + assert_equal(OpenSSL::SSL::VERIFY_PEER, ctx.verify_mode) + assert_equal(OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:options], ctx.options) + ciphers = ctx.ciphers + ciphers_versions = ciphers.collect{|_, v, _, _| v } + ciphers_names = ciphers.collect{|v, _, _, _| v } + assert(ciphers_names.all?{|v| /ADH/ !~ v }) + assert(ciphers_versions.all?{|v| /SSLv2/ !~ v }) + ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) + ssl.sync_close = true + begin + assert_raise(OpenSSL::SSL::SSLError){ ssl.connect } + assert_equal(OpenSSL::X509::V_ERR_SELF_SIGNED_CERT_IN_CHAIN, ssl.verify_result) + ensure + ssl.close + end + } + end + + def test_post_connect_check_with_anon_ciphers + sslerr = OpenSSL::SSL::SSLError + + start_server(OpenSSL::SSL::VERIFY_NONE, true, {use_anon_cipher: true}){|server, port| + ctx = OpenSSL::SSL::SSLContext.new + ctx.ciphers = "aNULL" + server_connect(port, ctx) { |ssl| + msg = "Peer verification enabled, but no certificate received. Anonymous cipher suite " \ + "ADH-AES256-GCM-SHA384 was negotiated. Anonymous suites must be disabled to use peer verification." + assert_raise_with_message(sslerr,msg){ssl.post_connection_check("localhost.localdomain")} + } + } + end + + def test_post_connection_check + sslerr = OpenSSL::SSL::SSLError + + start_server(OpenSSL::SSL::VERIFY_NONE, true){|server, port| + server_connect(port) { |ssl| + assert_raise(sslerr){ssl.post_connection_check("localhost.localdomain")} + assert_raise(sslerr){ssl.post_connection_check("127.0.0.1")} + assert(ssl.post_connection_check("localhost")) + assert_raise(sslerr){ssl.post_connection_check("foo.example.com")} + + cert = ssl.peer_cert + assert(!OpenSSL::SSL.verify_certificate_identity(cert, "localhost.localdomain")) + assert(!OpenSSL::SSL.verify_certificate_identity(cert, "127.0.0.1")) + assert(OpenSSL::SSL.verify_certificate_identity(cert, "localhost")) + assert(!OpenSSL::SSL.verify_certificate_identity(cert, "foo.example.com")) + } + } + + now = Time.now + exts = [ + ["keyUsage","keyEncipherment,digitalSignature",true], + ["subjectAltName","DNS:localhost.localdomain",false], + ["subjectAltName","IP:127.0.0.1",false], + ] + @svr_cert = issue_cert(@svr, @svr_key, 4, now, now+1800, exts, + @ca_cert, @ca_key, OpenSSL::Digest::SHA1.new) + start_server(OpenSSL::SSL::VERIFY_NONE, true){|server, port| + server_connect(port) { |ssl| + assert(ssl.post_connection_check("localhost.localdomain")) + assert(ssl.post_connection_check("127.0.0.1")) + assert_raise(sslerr){ssl.post_connection_check("localhost")} + assert_raise(sslerr){ssl.post_connection_check("foo.example.com")} + + cert = ssl.peer_cert + assert(OpenSSL::SSL.verify_certificate_identity(cert, "localhost.localdomain")) + assert(OpenSSL::SSL.verify_certificate_identity(cert, "127.0.0.1")) + assert(!OpenSSL::SSL.verify_certificate_identity(cert, "localhost")) + assert(!OpenSSL::SSL.verify_certificate_identity(cert, "foo.example.com")) + } + } + + now = Time.now + exts = [ + ["keyUsage","keyEncipherment,digitalSignature",true], + ["subjectAltName","DNS:*.localdomain",false], + ] + @svr_cert = issue_cert(@svr, @svr_key, 5, now, now+1800, exts, + @ca_cert, @ca_key, OpenSSL::Digest::SHA1.new) + start_server(OpenSSL::SSL::VERIFY_NONE, true){|server, port| + server_connect(port) { |ssl| + assert(ssl.post_connection_check("localhost.localdomain")) + assert_raise(sslerr){ssl.post_connection_check("127.0.0.1")} + assert_raise(sslerr){ssl.post_connection_check("localhost")} + assert_raise(sslerr){ssl.post_connection_check("foo.example.com")} + cert = ssl.peer_cert + assert(OpenSSL::SSL.verify_certificate_identity(cert, "localhost.localdomain")) + assert(!OpenSSL::SSL.verify_certificate_identity(cert, "127.0.0.1")) + assert(!OpenSSL::SSL.verify_certificate_identity(cert, "localhost")) + assert(!OpenSSL::SSL.verify_certificate_identity(cert, "foo.example.com")) + } + } + end + + def test_verify_certificate_identity + [true, false].each do |criticality| + cert = create_null_byte_SAN_certificate(criticality) + assert_equal(false, OpenSSL::SSL.verify_certificate_identity(cert, 'www.example.com')) + assert_equal(true, OpenSSL::SSL.verify_certificate_identity(cert, "www.example.com\0.evil.com")) + assert_equal(false, OpenSSL::SSL.verify_certificate_identity(cert, '192.168.7.255')) + assert_equal(true, OpenSSL::SSL.verify_certificate_identity(cert, '192.168.7.1')) + assert_equal(false, OpenSSL::SSL.verify_certificate_identity(cert, '13::17')) + assert_equal(true, OpenSSL::SSL.verify_certificate_identity(cert, '13:0:0:0:0:0:0:17')) + end + end + + def test_verify_hostname + assert_equal(true, OpenSSL::SSL.verify_hostname("www.example.com", "*.example.com")) + assert_equal(false, OpenSSL::SSL.verify_hostname("www.subdomain.example.com", "*.example.com")) + end + + def test_verify_wildcard + assert_equal(false, OpenSSL::SSL.verify_wildcard("foo", "x*")) + assert_equal(true, OpenSSL::SSL.verify_wildcard("foo", "foo")) + assert_equal(true, OpenSSL::SSL.verify_wildcard("foo", "f*")) + assert_equal(true, OpenSSL::SSL.verify_wildcard("foo", "*")) + assert_equal(false, OpenSSL::SSL.verify_wildcard("abc*bcd", "abcd")) + assert_equal(false, OpenSSL::SSL.verify_wildcard("xn--qdk4b9b", "x*")) + assert_equal(false, OpenSSL::SSL.verify_wildcard("xn--qdk4b9b", "*--qdk4b9b")) + assert_equal(true, OpenSSL::SSL.verify_wildcard("xn--qdk4b9b", "xn--qdk4b9b")) + end + + # Comments in this test is excerpted from http://tools.ietf.org/html/rfc6125#page-27 + def test_post_connection_check_wildcard_san + # case-insensitive ASCII comparison + # RFC 6125, section 6.4.1 + # + # "..matching of the reference identifier against the presented identifier + # is performed by comparing the set of domain name labels using a + # case-insensitive ASCII comparison, as clarified by [DNS-CASE] (e.g., + # "WWW.Example.Com" would be lower-cased to "www.example.com" for + # comparison purposes) + assert_equal(true, OpenSSL::SSL.verify_certificate_identity( + create_cert_with_san('DNS:*.example.com'), 'www.example.com')) + assert_equal(true, OpenSSL::SSL.verify_certificate_identity( + create_cert_with_san('DNS:*.Example.COM'), 'www.example.com')) + assert_equal(true, OpenSSL::SSL.verify_certificate_identity( + create_cert_with_san('DNS:*.example.com'), 'WWW.Example.COM')) + # 1. The client SHOULD NOT attempt to match a presented identifier in + # which the wildcard character comprises a label other than the + # left-most label (e.g., do not match bar.*.example.net). + assert_equal(false, OpenSSL::SSL.verify_certificate_identity( + create_cert_with_san('DNS:www.*.com'), 'www.example.com')) + # 2. If the wildcard character is the only character of the left-most + # label in the presented identifier, the client SHOULD NOT compare + # against anything but the left-most label of the reference + # identifier (e.g., *.example.com would match foo.example.com but + # not bar.foo.example.com or example.com). + assert_equal(true, OpenSSL::SSL.verify_certificate_identity( + create_cert_with_san('DNS:*.example.com'), 'foo.example.com')) + assert_equal(false, OpenSSL::SSL.verify_certificate_identity( + create_cert_with_san('DNS:*.example.com'), 'bar.foo.example.com')) + # 3. The client MAY match a presented identifier in which the wildcard + # character is not the only character of the label (e.g., + # baz*.example.net and *baz.example.net and b*z.example.net would + # be taken to match baz1.example.net and foobaz.example.net and + # buzz.example.net, respectively). ... + assert_equal(true, OpenSSL::SSL.verify_certificate_identity( + create_cert_with_san('DNS:baz*.example.com'), 'baz1.example.com')) + assert_equal(true, OpenSSL::SSL.verify_certificate_identity( + create_cert_with_san('DNS:*baz.example.com'), 'foobaz.example.com')) + assert_equal(true, OpenSSL::SSL.verify_certificate_identity( + create_cert_with_san('DNS:b*z.example.com'), 'buzz.example.com')) + # Section 6.4.3 of RFC6125 states that client should NOT match identifier + # where wildcard is other than left-most label. + # + # Also implicitly mentions the wildcard character only in singular form, + # and discourages matching against more than one wildcard. + # + # See RFC 6125, section 7.2, subitem 2. + assert_equal(false, OpenSSL::SSL.verify_certificate_identity( + create_cert_with_san('DNS:*b*.example.com'), 'abc.example.com')) + assert_equal(false, OpenSSL::SSL.verify_certificate_identity( + create_cert_with_san('DNS:*b*.example.com'), 'ab.example.com')) + assert_equal(false, OpenSSL::SSL.verify_certificate_identity( + create_cert_with_san('DNS:*b*.example.com'), 'bc.example.com')) + # ... However, the client SHOULD NOT + # attempt to match a presented identifier where the wildcard + # character is embedded within an A-label or U-label [IDNA-DEFS] of + # an internationalized domain name [IDNA-PROTO]. + assert_equal(true, OpenSSL::SSL.verify_certificate_identity( + create_cert_with_san('DNS:xn*.example.com'), 'xn1ca.example.com')) + # part of A-label + assert_equal(false, OpenSSL::SSL.verify_certificate_identity( + create_cert_with_san('DNS:xn--*.example.com'), 'xn--1ca.example.com')) + # part of U-label + # dNSName in RFC5280 is an IA5String so U-label should NOT be allowed + # regardless of wildcard. + # + # See Section 7.2 of RFC 5280: + # IA5String is limited to the set of ASCII characters. + assert_equal(false, OpenSSL::SSL.verify_certificate_identity( + create_cert_with_san('DNS:á*.example.com'), 'á1.example.com')) + end + + def test_post_connection_check_wildcard_cn + assert_equal(true, OpenSSL::SSL.verify_certificate_identity( + create_cert_with_name('*.example.com'), 'www.example.com')) + assert_equal(true, OpenSSL::SSL.verify_certificate_identity( + create_cert_with_name('*.Example.COM'), 'www.example.com')) + assert_equal(true, OpenSSL::SSL.verify_certificate_identity( + create_cert_with_name('*.example.com'), 'WWW.Example.COM')) + assert_equal(false, OpenSSL::SSL.verify_certificate_identity( + create_cert_with_name('www.*.com'), 'www.example.com')) + assert_equal(true, OpenSSL::SSL.verify_certificate_identity( + create_cert_with_name('*.example.com'), 'foo.example.com')) + assert_equal(false, OpenSSL::SSL.verify_certificate_identity( + create_cert_with_name('*.example.com'), 'bar.foo.example.com')) + assert_equal(true, OpenSSL::SSL.verify_certificate_identity( + create_cert_with_name('baz*.example.com'), 'baz1.example.com')) + assert_equal(true, OpenSSL::SSL.verify_certificate_identity( + create_cert_with_name('*baz.example.com'), 'foobaz.example.com')) + assert_equal(true, OpenSSL::SSL.verify_certificate_identity( + create_cert_with_name('b*z.example.com'), 'buzz.example.com')) + # Section 6.4.3 of RFC6125 states that client should NOT match identifier + # where wildcard is other than left-most label. + # + # Also implicitly mentions the wildcard character only in singular form, + # and discourages matching against more than one wildcard. + # + # See RFC 6125, section 7.2, subitem 2. + assert_equal(false, OpenSSL::SSL.verify_certificate_identity( + create_cert_with_name('*b*.example.com'), 'abc.example.com')) + assert_equal(false, OpenSSL::SSL.verify_certificate_identity( + create_cert_with_name('*b*.example.com'), 'ab.example.com')) + assert_equal(false, OpenSSL::SSL.verify_certificate_identity( + create_cert_with_name('*b*.example.com'), 'bc.example.com')) + assert_equal(true, OpenSSL::SSL.verify_certificate_identity( + create_cert_with_name('xn*.example.com'), 'xn1ca.example.com')) + assert_equal(false, OpenSSL::SSL.verify_certificate_identity( + create_cert_with_name('xn--*.example.com'), 'xn--1ca.example.com')) + # part of U-label + # Subject in RFC5280 states case-insensitive ASCII comparison. + # + # See Section 7.2 of RFC 5280: + # IA5String is limited to the set of ASCII characters. + assert_equal(false, OpenSSL::SSL.verify_certificate_identity( + create_cert_with_name('á*.example.com'), 'á1.example.com')) + end + + def create_cert_with_san(san) + ef = OpenSSL::X509::ExtensionFactory.new + cert = OpenSSL::X509::Certificate.new + cert.subject = OpenSSL::X509::Name.parse("/DC=some/DC=site/CN=Some Site") + ext = ef.create_ext('subjectAltName', san) + cert.add_extension(ext) + cert + end + + def create_cert_with_name(name) + cert = OpenSSL::X509::Certificate.new + cert.subject = OpenSSL::X509::Name.new([['DC', 'some'], ['DC', 'site'], ['CN', name]]) + cert + end + + + # Create NULL byte SAN certificate + def create_null_byte_SAN_certificate(critical = false) + ef = OpenSSL::X509::ExtensionFactory.new + cert = OpenSSL::X509::Certificate.new + cert.subject = OpenSSL::X509::Name.parse "/DC=some/DC=site/CN=Some Site" + ext = ef.create_ext('subjectAltName', 'DNS:placeholder,IP:192.168.7.1,IP:13::17', critical) + ext_asn1 = OpenSSL::ASN1.decode(ext.to_der) + san_list_der = ext_asn1.value.reduce(nil) { |memo,val| val.tag == 4 ? val.value : memo } + san_list_asn1 = OpenSSL::ASN1.decode(san_list_der) + san_list_asn1.value[0].value = "www.example.com\0.evil.com" + pos = critical ? 2 : 1 + ext_asn1.value[pos].value = san_list_asn1.to_der + real_ext = OpenSSL::X509::Extension.new ext_asn1 + cert.add_extension(real_ext) + cert + end + + def test_tlsext_hostname + return unless OpenSSL::SSL::SSLSocket.instance_methods.include?(:hostname) + + ctx_proc = Proc.new do |ctx, ssl| + foo_ctx = ctx.dup + + ctx.servername_cb = Proc.new do |ssl2, hostname| + case hostname + when 'foo.example.com' + foo_ctx + when 'bar.example.com' + nil + else + raise "unknown hostname #{hostname.inspect}" + end + end + end + + server_proc = Proc.new do |ctx, ssl| + readwrite_loop(ctx, ssl) + end + + start_server(OpenSSL::SSL::VERIFY_NONE, true, :ctx_proc => ctx_proc, :server_proc => server_proc) do |server, port| + 2.times do |i| + ctx = OpenSSL::SSL::SSLContext.new + if defined?(OpenSSL::SSL::OP_NO_TICKET) + # disable RFC4507 support + ctx.options = OpenSSL::SSL::OP_NO_TICKET + end + server_connect(port, ctx) { |ssl| + ssl.hostname = (i & 1 == 0) ? 'foo.example.com' : 'bar.example.com' + str = "x" * 100 + "\n" + ssl.puts(str) + assert_equal(str, ssl.gets) + } + end + end + end + + def test_multibyte_read_write + #German a umlaut + auml = [%w{ C3 A4 }.join('')].pack('H*') + auml.force_encoding(Encoding::UTF_8) + + [10, 1000, 100000].each {|i| + str = nil + num_written = nil + server_proc = Proc.new {|ctx, ssl| + cmp = ssl.read + raw_size = cmp.size + cmp.force_encoding(Encoding::UTF_8) + assert_equal(str, cmp) + assert_equal(num_written, raw_size) + ssl.close + } + start_server(OpenSSL::SSL::VERIFY_NONE, true, :server_proc => server_proc){|server, port| + server_connect(port) { |ssl| + str = auml * i + num_written = ssl.write(str) + } + } + } + end + + def test_unset_OP_ALL + ctx_proc = Proc.new { |ctx| + # If OP_DONT_INSERT_EMPTY_FRAGMENTS is not defined, this test is + # redundant because the default options already are equal to OP_ALL. + # But it also degrades gracefully, so keep it + ctx.options = OpenSSL::SSL::OP_ALL + } + start_server(OpenSSL::SSL::VERIFY_NONE, true, :ctx_proc => ctx_proc){|server, port| + server_connect(port) { |ssl| + ssl.puts('hello') + assert_equal("hello\n", ssl.gets) + } + } + end + + # different OpenSSL versions react differently when facing a SSL/TLS version + # that has been marked as forbidden, therefore either of these may be raised + HANDSHAKE_ERRORS = [OpenSSL::SSL::SSLError, Errno::ECONNRESET] + +if OpenSSL::SSL::SSLContext::METHODS.include? :TLSv1 + + def test_forbid_ssl_v3_for_client + ctx_proc = Proc.new { |ctx| ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_SSLv3 } + start_server_version(:SSLv23, ctx_proc) { |server, port| + ctx = OpenSSL::SSL::SSLContext.new + ctx.ssl_version = :SSLv3 + assert_raise(*HANDSHAKE_ERRORS) { server_connect(port, ctx) } + } + end + + def test_forbid_ssl_v3_from_server + start_server_version(:SSLv3) { |server, port| + ctx = OpenSSL::SSL::SSLContext.new + ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_SSLv3 + assert_raise(*HANDSHAKE_ERRORS) { server_connect(port, ctx) } + } + end + +end + +if OpenSSL::SSL::SSLContext::METHODS.include? :TLSv1_1 + + def test_tls_v1_1 + start_server_version(:TLSv1_1) { |server, port| + server_connect(port) { |ssl| assert_equal("TLSv1.1", ssl.ssl_version) } + } + end + + def test_forbid_tls_v1_for_client + ctx_proc = Proc.new { |ctx| ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_TLSv1 } + start_server_version(:SSLv23, ctx_proc) { |server, port| + ctx = OpenSSL::SSL::SSLContext.new + ctx.ssl_version = :TLSv1 + assert_raise(*HANDSHAKE_ERRORS) { server_connect(port, ctx) } + } + end + + def test_forbid_tls_v1_from_server + start_server_version(:TLSv1) { |server, port| + ctx = OpenSSL::SSL::SSLContext.new + ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_TLSv1 + assert_raise(*HANDSHAKE_ERRORS) { server_connect(port, ctx) } + } + end + +end + +if OpenSSL::SSL::SSLContext::METHODS.include? :TLSv1_2 + + def test_tls_v1_2 + start_server_version(:TLSv1_2) { |server, port| + ctx = OpenSSL::SSL::SSLContext.new + ctx.ssl_version = :TLSv1_2_client + server_connect(port, ctx) { |ssl| assert_equal("TLSv1.2", ssl.ssl_version) } + } + end if OpenSSL::OPENSSL_VERSION_NUMBER > 0x10001000 + + def test_forbid_tls_v1_1_for_client + ctx_proc = Proc.new { |ctx| ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_TLSv1_1 } + start_server_version(:SSLv23, ctx_proc) { |server, port| + ctx = OpenSSL::SSL::SSLContext.new + ctx.ssl_version = :TLSv1_1 + assert_raise(*HANDSHAKE_ERRORS) { server_connect(port, ctx) } + } + end if defined?(OpenSSL::SSL::OP_NO_TLSv1_1) + + def test_forbid_tls_v1_1_from_server + start_server_version(:TLSv1_1) { |server, port| + ctx = OpenSSL::SSL::SSLContext.new + ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_TLSv1_1 + assert_raise(*HANDSHAKE_ERRORS) { server_connect(port, ctx) } + } + end if defined?(OpenSSL::SSL::OP_NO_TLSv1_1) + + def test_forbid_tls_v1_2_for_client + ctx_proc = Proc.new { |ctx| ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_TLSv1_2 } + start_server_version(:SSLv23, ctx_proc) { |server, port| + ctx = OpenSSL::SSL::SSLContext.new + ctx.ssl_version = :TLSv1_2 + assert_raise(*HANDSHAKE_ERRORS) { server_connect(port, ctx) } + } + end if defined?(OpenSSL::SSL::OP_NO_TLSv1_2) + + def test_forbid_tls_v1_2_from_server + start_server_version(:TLSv1_2) { |server, port| + ctx = OpenSSL::SSL::SSLContext.new + ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_TLSv1_2 + assert_raise(*HANDSHAKE_ERRORS) { server_connect(port, ctx) } + } + end if defined?(OpenSSL::SSL::OP_NO_TLSv1_2) + +end + + def test_renegotiation_cb + num_handshakes = 0 + renegotiation_cb = Proc.new { |ssl| num_handshakes += 1 } + ctx_proc = Proc.new { |ctx| ctx.renegotiation_cb = renegotiation_cb } + start_server_version(:SSLv23, ctx_proc) { |server, port| + server_connect(port) { |ssl| + assert_equal(1, num_handshakes) + } + } + end + +if OpenSSL::OPENSSL_VERSION_NUMBER > 0x10001000 + + def test_npn_protocol_selection_ary + advertised = ["http/1.1", "spdy/2"] + ctx_proc = Proc.new { |ctx| ctx.npn_protocols = advertised } + start_server_version(:SSLv23, ctx_proc) { |server, port| + selector = lambda { |which| + ctx = OpenSSL::SSL::SSLContext.new + ctx.npn_select_cb = -> (protocols) { protocols.send(which) } + server_connect(port, ctx) { |ssl| + assert_equal(advertised.send(which), ssl.npn_protocol) + } + } + selector.call(:first) + selector.call(:last) + } + end + + def test_npn_protocol_selection_enum + advertised = Object.new + def advertised.each + yield "http/1.1" + yield "spdy/2" + end + ctx_proc = Proc.new { |ctx| ctx.npn_protocols = advertised } + start_server_version(:SSLv23, ctx_proc) { |server, port| + selector = lambda { |selected, which| + ctx = OpenSSL::SSL::SSLContext.new + ctx.npn_select_cb = -> (protocols) { protocols.to_a.send(which) } + server_connect(port, ctx) { |ssl| + assert_equal(selected, ssl.npn_protocol) + } + } + selector.call("http/1.1", :first) + selector.call("spdy/2", :last) + } + end + + def test_npn_protocol_selection_cancel + ctx_proc = Proc.new { |ctx| ctx.npn_protocols = ["http/1.1"] } + start_server_version(:SSLv23, ctx_proc) { |server, port| + ctx = OpenSSL::SSL::SSLContext.new + ctx.npn_select_cb = -> (protocols) { raise RuntimeError.new } + assert_raise(RuntimeError) { server_connect(port, ctx) } + } + end + + def test_npn_advertised_protocol_too_long + ctx_proc = Proc.new { |ctx| ctx.npn_protocols = ["a" * 256] } + start_server_version(:SSLv23, ctx_proc) { |server, port| + ctx = OpenSSL::SSL::SSLContext.new + ctx.npn_select_cb = -> (protocols) { protocols.first } + assert_raise(*HANDSHAKE_ERRORS) { server_connect(port, ctx) } + } + end + + def test_npn_selected_protocol_too_long + ctx_proc = Proc.new { |ctx| ctx.npn_protocols = ["http/1.1"] } + start_server_version(:SSLv23, ctx_proc) { |server, port| + ctx = OpenSSL::SSL::SSLContext.new + ctx.npn_select_cb = -> (protocols) { "a" * 256 } + assert_raise(*HANDSHAKE_ERRORS) { server_connect(port, ctx) } + } + end + +end + + def test_invalid_shutdown_by_gc + assert_nothing_raised { + start_server(OpenSSL::SSL::VERIFY_NONE, true){|server, port| + 10.times { + sock = TCPSocket.new("127.0.0.1", port) + ssl = OpenSSL::SSL::SSLSocket.new(sock) + GC.start + ssl.connect + sock.close + } + } + } + end + + def test_close_after_socket_close + start_server(OpenSSL::SSL::VERIFY_NONE, true){|server, port| + sock = TCPSocket.new("127.0.0.1", port) + ssl = OpenSSL::SSL::SSLSocket.new(sock) + ssl.sync_close = true + ssl.connect + sock.close + assert_nothing_raised do + ssl.close + end + } + end + + def test_sync_close_without_connect + Socket.open(:INET, :STREAM) {|s| + ssl = OpenSSL::SSL::SSLSocket.new(s) + ssl.sync_close = true + ssl.close + assert(s.closed?) + } + end + + private + + def start_server_version(version, ctx_proc=nil, server_proc=nil, &blk) + ctx_wrap = Proc.new { |ctx| + ctx.ssl_version = version + ctx_proc.call(ctx) if ctx_proc + } + start_server( + OpenSSL::SSL::VERIFY_NONE, + true, + :ctx_proc => ctx_wrap, + :server_proc => server_proc, + :ignore_listener_error => true, + &blk + ) + end + + def server_connect(port, ctx=nil) + sock = TCPSocket.new("127.0.0.1", port) + ssl = ctx ? OpenSSL::SSL::SSLSocket.new(sock, ctx) : OpenSSL::SSL::SSLSocket.new(sock) + ssl.sync_close = true + ssl.connect + yield ssl if block_given? + ensure + if ssl + ssl.close + elsif sock + sock.close + end + end +end + +end diff --git a/jni/ruby/test/openssl/test_ssl_session.rb b/jni/ruby/test/openssl/test_ssl_session.rb new file mode 100644 index 0000000..d4c2220 --- /dev/null +++ b/jni/ruby/test/openssl/test_ssl_session.rb @@ -0,0 +1,380 @@ +require_relative "utils" + +if defined?(OpenSSL::TestUtils) + +class OpenSSL::TestSSLSession < OpenSSL::SSLTestCase + def test_session_equals + session = OpenSSL::SSL::Session.new <<-SESSION +-----BEGIN SSL SESSION PARAMETERS----- +MIIDFgIBAQICAwEEAgA5BCCY3pW6iTkPoD5SENuztz/gZjhvey6XnHbsxd22k0Ol +dgQw8uaN3hCRnlhoIKPWInCFzrp/tQsDRFs9jDjc9pwpy/oKHmJdQQMQA1g8FYnO +gpdVoQYCBE52ikKiBAICASyjggKOMIICijCCAXKgAwIBAgIBAjANBgkqhkiG9w0B +AQUFADA9MRMwEQYKCZImiZPyLGQBGRYDb3JnMRkwFwYKCZImiZPyLGQBGRYJcnVi +eS1sYW5nMQswCQYDVQQDDAJDQTAeFw0xMTA5MTkwMDE4MTBaFw0xMTA5MTkwMDQ4 +MTBaMEQxEzARBgoJkiaJk/IsZAEZFgNvcmcxGTAXBgoJkiaJk/IsZAEZFglydWJ5 +LWxhbmcxEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAw +gYkCgYEAy8LEsNRApz7U/j5DoB4XBgO9Z8Atv5y/OVQRp0ag8Tqo1YewsWijxEWB +7JOATwpBN267U4T1nPZIxxEEO7n/WNa2ws9JWsjah8ssEBFSxZqdXKSLf0N4Hi7/ +GQ/aYoaMCiQ8jA4jegK2FJmXM71uPe+jFN/peeBOpRfyXxRFOYcCAwEAAaMSMBAw +DgYDVR0PAQH/BAQDAgWgMA0GCSqGSIb3DQEBBQUAA4IBAQARC7GP7InX1t7VEXz2 +I8RI57S0/HSJL4fDIYP3zFpitHX1PZeo+7XuzMilvPjjBo/ky9Jzo8TYiY+N+JEz +mY/A/zPA4ZsJ7KYj6/FEdIc/vRlS0CvsbClbNjw1jl/PoB2FLr2b3uuBcZEsyZeP +yq154ijq37Ajf8K5Mi5FgshoP41BPtRPj+VVf61rv1IcEnNWdDCS6DR4XsaNC+zt +G6AqCqkytIXWRuDw6n6vYLF3A/tn2sldLo7/scY0PMDNbo63O/LTxkDHmPhSkD68 +8m9SsMeTR+RCiDEZWFPVcAH/8mDfi+5k8uN3qS+gOU/PPrmHGgl5ykiSFgqs4v61 +tddwpBAEDjcwMzA5NTYzMTU1MzAwpQMCARM= +-----END SSL SESSION PARAMETERS----- + SESSION + + start_server(OpenSSL::SSL::VERIFY_NONE, true, :ignore_listener_error => true) { |_, port| + ctx = OpenSSL::SSL::SSLContext.new + ctx.session_cache_mode = OpenSSL::SSL::SSLContext::SESSION_CACHE_CLIENT + ctx.session_id_context = self.object_id.to_s + + sock = TCPSocket.new '127.0.0.1', port + begin + ssl = OpenSSL::SSL::SSLSocket.new sock, ctx + ssl.session = session + + assert_equal session, ssl.session + ensure + sock.close + end + } + end + + def test_session + timeout(5) do + start_server(OpenSSL::SSL::VERIFY_NONE, true) do |server, port| + sock = TCPSocket.new("127.0.0.1", port) + ctx = OpenSSL::SSL::SSLContext.new("TLSv1") + ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) + ssl.sync_close = true + ssl.connect + session = ssl.session + assert(session == OpenSSL::SSL::Session.new(session.to_pem)) + assert(session == OpenSSL::SSL::Session.new(ssl)) + assert_equal(300, session.timeout) + session.timeout = 5 + assert_equal(5, session.timeout) + assert_not_nil(session.time) + # SSL_SESSION_time keeps long value so we can't keep nsec fragment. + session.time = t1 = Time.now.to_i + assert_equal(Time.at(t1), session.time) + if session.respond_to?(:id) + assert_not_nil(session.id) + end + pem = session.to_pem + assert_match(/\A-----BEGIN SSL SESSION PARAMETERS-----/, pem) + assert_match(/-----END SSL SESSION PARAMETERS-----\Z/, pem) + pem.gsub!(/-----(BEGIN|END) SSL SESSION PARAMETERS-----/, '').gsub!(/[\r\n]+/m, '') + assert_equal(session.to_der, pem.unpack('m*')[0]) + assert_not_nil(session.to_text) + ssl.close + end + end + end + + DUMMY_SESSION = <<__EOS__ +-----BEGIN SSL SESSION PARAMETERS----- +MIIDzQIBAQICAwEEAgA5BCAF219w9ZEV8dNA60cpEGOI34hJtIFbf3bkfzSgMyad +MQQwyGLbkCxE4OiMLdKKem+pyh8V7ifoP7tCxhdmwoDlJxI1v6nVCjai+FGYuncy +NNSWoQYCBE4DDWuiAwIBCqOCAo4wggKKMIIBcqADAgECAgECMA0GCSqGSIb3DQEB +BQUAMD0xEzARBgoJkiaJk/IsZAEZFgNvcmcxGTAXBgoJkiaJk/IsZAEZFglydWJ5 +LWxhbmcxCzAJBgNVBAMMAkNBMB4XDTExMDYyMzA5NTQ1MVoXDTExMDYyMzEwMjQ1 +MVowRDETMBEGCgmSJomT8ixkARkWA29yZzEZMBcGCgmSJomT8ixkARkWCXJ1Ynkt +bGFuZzESMBAGA1UEAwwJbG9jYWxob3N0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB +iQKBgQDLwsSw1ECnPtT+PkOgHhcGA71nwC2/nL85VBGnRqDxOqjVh7CxaKPERYHs +k4BPCkE3brtThPWc9kjHEQQ7uf9Y1rbCz0layNqHyywQEVLFmp1cpIt/Q3geLv8Z +D9pihowKJDyMDiN6ArYUmZczvW4976MU3+l54E6lF/JfFEU5hwIDAQABoxIwEDAO +BgNVHQ8BAf8EBAMCBaAwDQYJKoZIhvcNAQEFBQADggEBACj5WhoZ/ODVeHpwgq1d +8fW/13ICRYHYpv6dzlWihyqclGxbKMlMnaVCPz+4JaVtMz3QB748KJQgL3Llg3R1 +ek+f+n1MBCMfFFsQXJ2gtLB84zD6UCz8aaCWN5/czJCd7xMz7fRLy3TOIW5boXAU +zIa8EODk+477K1uznHm286ab0Clv+9d304hwmBZgkzLg6+31Of6d6s0E0rwLGiS2 +sOWYg34Y3r4j8BS9Ak4jzpoLY6cJ0QAKCOJCgmjGr4XHpyXMLbicp3ga1uSbwtVO +gF/gTfpLhJC+y0EQ5x3Ftl88Cq7ZJuLBDMo/TLIfReJMQu/HlrTT7+LwtneSWGmr +KkSkAgQApQMCAROqgcMEgcAuDkAVfj6QAJMz9yqTzW5wPFyty7CxUEcwKjUqj5UP +/Yvky1EkRuM/eQfN7ucY+MUvMqv+R8ZSkHPsnjkBN5ChvZXjrUSZKFVjR4eFVz2V +jismLEJvIFhQh6pqTroRrOjMfTaM5Lwoytr2FTGobN9rnjIRsXeFQW1HLFbXn7Dh +8uaQkMwIVVSGRB8T7t6z6WIdWruOjCZ6G5ASI5XoqAHwGezhLodZuvJEfsVyCF9y +j+RBGfCFrrQbBdnkFI/ztgM= +-----END SSL SESSION PARAMETERS----- +__EOS__ + + DUMMY_SESSION_NO_EXT = <<-__EOS__ +-----BEGIN SSL SESSION PARAMETERS----- +MIIDCAIBAQICAwAEAgA5BCDyAW7rcpzMjDSosH+Tv6sukymeqgq3xQVVMez628A+ +lAQw9TrKzrIqlHEh6ltuQaqv/Aq83AmaAlogYktZgXAjOGnhX7ifJDNLMuCfQq53 +hPAaoQYCBE4iDeeiBAICASyjggKOMIICijCCAXKgAwIBAgIBAjANBgkqhkiG9w0B +AQUFADA9MRMwEQYKCZImiZPyLGQBGRYDb3JnMRkwFwYKCZImiZPyLGQBGRYJcnVi +eS1sYW5nMQswCQYDVQQDDAJDQTAeFw0xMTA3MTYyMjE3MTFaFw0xMTA3MTYyMjQ3 +MTFaMEQxEzARBgoJkiaJk/IsZAEZFgNvcmcxGTAXBgoJkiaJk/IsZAEZFglydWJ5 +LWxhbmcxEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAw +gYkCgYEAy8LEsNRApz7U/j5DoB4XBgO9Z8Atv5y/OVQRp0ag8Tqo1YewsWijxEWB +7JOATwpBN267U4T1nPZIxxEEO7n/WNa2ws9JWsjah8ssEBFSxZqdXKSLf0N4Hi7/ +GQ/aYoaMCiQ8jA4jegK2FJmXM71uPe+jFN/peeBOpRfyXxRFOYcCAwEAAaMSMBAw +DgYDVR0PAQH/BAQDAgWgMA0GCSqGSIb3DQEBBQUAA4IBAQA3TRzABRG3kz8jEEYr +tDQqXgsxwTsLhTT5d1yF0D8uFw+y15hJAJnh6GJHjqhWBrF4zNoTApFo+4iIL6g3 +q9C3mUsxIVAHx41DwZBh/FI7J4FqlAoGOguu7892CNVY3ZZjc3AXMTdKjcNoWPzz +FCdj5fNT24JMMe+ZdGZK97ChahJsdn/6B3j6ze9NK9mfYEbiJhejGTPLOFVHJCGR +KYYZ3ZcKhLDr9ql4d7cCo1gBtemrmFQGPui7GttNEqmXqUKvV8mYoa8farf5i7T4 +L6a/gp2cVZTaDIS1HjbJsA/Ag7AajZqiN6LfqShNUVsrMZ+5CoV8EkBDTZPJ9MSr +a3EqpAIEAKUDAgET +-----END SSL SESSION PARAMETERS----- +__EOS__ + + + def test_session_time + sess = OpenSSL::SSL::Session.new(DUMMY_SESSION_NO_EXT) + sess.time = (now = Time.now) + assert_equal(now.to_i, sess.time.to_i) + sess.time = 1 + assert_equal(1, sess.time.to_i) + sess.time = 1.2345 + assert_equal(1, sess.time.to_i) + # Can OpenSSL handle t>2038y correctly? Version? + sess.time = 2**31 - 1 + assert_equal(2**31 - 1, sess.time.to_i) + end + + def test_session_timeout + sess = OpenSSL::SSL::Session.new(DUMMY_SESSION_NO_EXT) + assert_raise(TypeError) do + sess.timeout = Time.now + end + sess.timeout = 1 + assert_equal(1, sess.timeout.to_i) + sess.timeout = 1.2345 + assert_equal(1, sess.timeout.to_i) + sess.timeout = 2**31 - 1 + assert_equal(2**31 - 1, sess.timeout.to_i) + end + + def test_session_exts_read + assert(OpenSSL::SSL::Session.new(DUMMY_SESSION)) + end if OpenSSL::OPENSSL_VERSION_NUMBER >= 0x009080bf + + def test_client_session + last_session = nil + start_server(OpenSSL::SSL::VERIFY_NONE, true) do |server, port| + 2.times do + sock = TCPSocket.new("127.0.0.1", port) + # Debian's openssl 0.9.8g-13 failed at assert(ssl.session_reused?), + # when use default SSLContext. [ruby-dev:36167] + ctx = OpenSSL::SSL::SSLContext.new("TLSv1") + ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) + ssl.sync_close = true + ssl.session = last_session if last_session + ssl.connect + + session = ssl.session + if last_session + assert(ssl.session_reused?) + + if session.respond_to?(:id) + assert_equal(session.id, last_session.id) + end + assert_equal(session.to_pem, last_session.to_pem) + assert_equal(session.to_der, last_session.to_der) + # Older version of OpenSSL may not be consistent. Look up which versions later. + assert_equal(session.to_text, last_session.to_text) + else + assert(!ssl.session_reused?) + end + last_session = session + + str = "x" * 100 + "\n" + ssl.puts(str) + assert_equal(str, ssl.gets) + + ssl.close + end + end + end + + def test_server_session + connections = 0 + saved_session = nil + + ctx_proc = Proc.new do |ctx, ssl| +# add test for session callbacks here + end + + server_proc = Proc.new do |ctx, ssl| + session = ssl.session + stats = ctx.session_cache_stats + + case connections + when 0 + assert_equal(stats[:cache_num], 1) + assert_equal(stats[:cache_hits], 0) + assert_equal(stats[:cache_misses], 0) + assert(!ssl.session_reused?) + when 1 + assert_equal(stats[:cache_num], 1) + assert_equal(stats[:cache_hits], 1) + assert_equal(stats[:cache_misses], 0) + assert(ssl.session_reused?) + ctx.session_remove(session) + saved_session = session + when 2 + assert_equal(stats[:cache_num], 1) + assert_equal(stats[:cache_hits], 1) + assert_equal(stats[:cache_misses], 1) + assert(!ssl.session_reused?) + ctx.session_add(saved_session) + when 3 + assert_equal(stats[:cache_num], 2) + assert_equal(stats[:cache_hits], 2) + assert_equal(stats[:cache_misses], 1) + assert(ssl.session_reused?) + ctx.flush_sessions(Time.now + 5000) + when 4 + assert_equal(stats[:cache_num], 1) + assert_equal(stats[:cache_hits], 2) + assert_equal(stats[:cache_misses], 2) + assert(!ssl.session_reused?) + ctx.session_add(saved_session) + end + connections += 1 + + readwrite_loop(ctx, ssl) + end + + first_session = nil + start_server(OpenSSL::SSL::VERIFY_NONE, true, :ctx_proc => ctx_proc, :server_proc => server_proc) do |server, port| + 10.times do |i| + sock = TCPSocket.new("127.0.0.1", port) + ctx = OpenSSL::SSL::SSLContext.new + if defined?(OpenSSL::SSL::OP_NO_TICKET) + # disable RFC4507 support + ctx.options = OpenSSL::SSL::OP_NO_TICKET + end + ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) + ssl.sync_close = true + ssl.session = first_session if first_session + ssl.connect + + session = ssl.session + if first_session + case i + when 1; assert(ssl.session_reused?) + when 2; assert(!ssl.session_reused?) + when 3; assert(ssl.session_reused?) + when 4; assert(!ssl.session_reused?) + when 5..10; assert(ssl.session_reused?) + end + end + first_session ||= session + + str = "x" * 100 + "\n" + ssl.puts(str) + assert_equal(str, ssl.gets) + + ssl.close + end + end + end + + def test_ctx_client_session_cb + called = {} + ctx = OpenSSL::SSL::SSLContext.new("SSLv3") + ctx.session_cache_mode = OpenSSL::SSL::SSLContext::SESSION_CACHE_CLIENT + + ctx.session_new_cb = lambda { |ary| + sock, sess = ary + called[:new] = [sock, sess] + } + + ctx.session_remove_cb = lambda { |ary| + ctx, sess = ary + called[:remove] = [ctx, sess] + # any resulting value is OK (ignored) + } + + start_server(OpenSSL::SSL::VERIFY_NONE, true) do |server, port| + sock = TCPSocket.new("127.0.0.1", port) + begin + ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) + ssl.sync_close = true + ssl.connect + assert_equal(1, ctx.session_cache_stats[:cache_num]) + assert_equal(1, ctx.session_cache_stats[:connect_good]) + assert_equal([ssl, ssl.session], called[:new]) + assert(ctx.session_remove(ssl.session)) + assert(!ctx.session_remove(ssl.session)) + assert_equal([ctx, ssl.session], called[:remove]) + ssl.close + ensure + sock.close if !sock.closed? + end + end + end + + def test_ctx_server_session_cb + called = {} + + ctx_proc = Proc.new { |ctx, ssl| + ctx.session_cache_mode = OpenSSL::SSL::SSLContext::SESSION_CACHE_SERVER + last_server_session = nil + + # get_cb is called whenever a client proposed to resume a session but + # the session could not be found in the internal session cache. + ctx.session_get_cb = lambda { |ary| + sess, data = ary + if last_server_session + called[:get2] = [sess, data] + last_server_session + else + called[:get1] = [sess, data] + last_server_session = sess + nil + end + } + + ctx.session_new_cb = lambda { |ary| + sock, sess = ary + called[:new] = [sock, sess] + # SSL server doesn't cache sessions so get_cb is called next time. + ctx.session_remove(sess) + } + + ctx.session_remove_cb = lambda { |ary| + ctx, sess = ary + called[:remove] = [ctx, sess] + } + } + + server_proc = Proc.new { |c, ssl| + ssl.session + c.session_cache_stats + readwrite_loop(c, ssl) + } + start_server(OpenSSL::SSL::VERIFY_NONE, true, :ctx_proc => ctx_proc, :server_proc => server_proc) do |server, port| + last_client_session = nil + 3.times do + sock = TCPSocket.new("127.0.0.1", port) + begin + ssl = OpenSSL::SSL::SSLSocket.new(sock, OpenSSL::SSL::SSLContext.new("SSLv3")) + ssl.sync_close = true + ssl.session = last_client_session if last_client_session + ssl.connect + last_client_session = ssl.session + ssl.close + timeout(5) do + Thread.pass until called.key?(:new) + assert(called.delete(:new)) + Thread.pass until called.key?(:remove) + assert(called.delete(:remove)) + end + ensure + sock.close if !sock.closed? + end + end + end + assert(called[:get1]) + assert(called[:get2]) + end +end + +end diff --git a/jni/ruby/test/openssl/test_x509cert.rb b/jni/ruby/test/openssl/test_x509cert.rb new file mode 100644 index 0000000..783677a --- /dev/null +++ b/jni/ruby/test/openssl/test_x509cert.rb @@ -0,0 +1,226 @@ +require_relative "utils" + +if defined?(OpenSSL::TestUtils) + +class OpenSSL::TestX509Certificate < Test::Unit::TestCase + def setup + @rsa1024 = OpenSSL::TestUtils::TEST_KEY_RSA1024 + @rsa2048 = OpenSSL::TestUtils::TEST_KEY_RSA2048 + @dsa256 = OpenSSL::TestUtils::TEST_KEY_DSA256 + @dsa512 = OpenSSL::TestUtils::TEST_KEY_DSA512 + @ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA") + @ee1 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE1") + @ee2 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE2") + end + + def teardown + end + + def issue_cert(*args) + OpenSSL::TestUtils.issue_cert(*args) + end + + def test_serial + [1, 2**32, 2**100].each{|s| + cert = issue_cert(@ca, @rsa2048, s, Time.now, Time.now+3600, [], + nil, nil, OpenSSL::Digest::SHA1.new) + assert_equal(s, cert.serial) + cert = OpenSSL::X509::Certificate.new(cert.to_der) + assert_equal(s, cert.serial) + } + end + + def test_public_key + exts = [ + ["basicConstraints","CA:TRUE",true], + ["subjectKeyIdentifier","hash",false], + ["authorityKeyIdentifier","keyid:always",false], + ] + + sha1 = OpenSSL::Digest::SHA1.new + dsa_digest = OpenSSL::TestUtils::DSA_SIGNATURE_DIGEST.new + + [ + [@rsa1024, sha1], [@rsa2048, sha1], [@dsa256, dsa_digest], [@dsa512, dsa_digest] + ].each{|pk, digest| + cert = issue_cert(@ca, pk, 1, Time.now, Time.now+3600, exts, + nil, nil, digest) + assert_equal(cert.extensions.sort_by(&:to_s)[2].value, + OpenSSL::TestUtils.get_subject_key_id(cert)) + cert = OpenSSL::X509::Certificate.new(cert.to_der) + assert_equal(cert.extensions.sort_by(&:to_s)[2].value, + OpenSSL::TestUtils.get_subject_key_id(cert)) + } + end + + def test_validity + now = Time.now until now && now.usec != 0 + cert = issue_cert(@ca, @rsa2048, 1, now, now+3600, [], + nil, nil, OpenSSL::Digest::SHA1.new) + assert_not_equal(now, cert.not_before) + assert_not_equal(now+3600, cert.not_after) + + now = Time.at(now.to_i) + cert = issue_cert(@ca, @rsa2048, 1, now, now+3600, [], + nil, nil, OpenSSL::Digest::SHA1.new) + assert_equal(now.getutc, cert.not_before) + assert_equal((now+3600).getutc, cert.not_after) + + now = Time.at(0) + cert = issue_cert(@ca, @rsa2048, 1, now, now, [], + nil, nil, OpenSSL::Digest::SHA1.new) + assert_equal(now.getutc, cert.not_before) + assert_equal(now.getutc, cert.not_after) + + now = Time.at(0x7fffffff) + cert = issue_cert(@ca, @rsa2048, 1, now, now, [], + nil, nil, OpenSSL::Digest::SHA1.new) + assert_equal(now.getutc, cert.not_before) + assert_equal(now.getutc, cert.not_after) + end + + def test_extension + ca_exts = [ + ["basicConstraints","CA:TRUE",true], + ["keyUsage","keyCertSign, cRLSign",true], + ["subjectKeyIdentifier","hash",false], + ["authorityKeyIdentifier","keyid:always",false], + ] + ca_cert = issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, ca_exts, + nil, nil, OpenSSL::Digest::SHA1.new) + ca_cert.extensions.each_with_index{|ext, i| + assert_equal(ca_exts[i].first, ext.oid) + assert_equal(ca_exts[i].last, ext.critical?) + } + + ee1_exts = [ + ["keyUsage","Non Repudiation, Digital Signature, Key Encipherment",true], + ["subjectKeyIdentifier","hash",false], + ["authorityKeyIdentifier","keyid:always",false], + ["extendedKeyUsage","clientAuth, emailProtection, codeSigning",false], + ["subjectAltName","email:ee1@ruby-lang.org",false], + ] + ee1_cert = issue_cert(@ee1, @rsa1024, 2, Time.now, Time.now+1800, ee1_exts, + ca_cert, @rsa2048, OpenSSL::Digest::SHA1.new) + assert_equal(ca_cert.subject.to_der, ee1_cert.issuer.to_der) + ee1_cert.extensions.each_with_index{|ext, i| + assert_equal(ee1_exts[i].first, ext.oid) + assert_equal(ee1_exts[i].last, ext.critical?) + } + + ee2_exts = [ + ["keyUsage","Non Repudiation, Digital Signature, Key Encipherment",true], + ["subjectKeyIdentifier","hash",false], + ["authorityKeyIdentifier","issuer:always",false], + ["extendedKeyUsage","clientAuth, emailProtection, codeSigning",false], + ["subjectAltName","email:ee2@ruby-lang.org",false], + ] + ee2_cert = issue_cert(@ee2, @rsa1024, 3, Time.now, Time.now+1800, ee2_exts, + ca_cert, @rsa2048, OpenSSL::Digest::MD5.new) + assert_equal(ca_cert.subject.to_der, ee2_cert.issuer.to_der) + ee2_cert.extensions.each_with_index{|ext, i| + assert_equal(ee2_exts[i].first, ext.oid) + assert_equal(ee2_exts[i].last, ext.critical?) + } + + end + + def test_sign_and_verify_rsa_sha1 + cert = issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, [], + nil, nil, OpenSSL::Digest::SHA1.new) + assert_equal(false, cert.verify(@rsa1024)) + assert_equal(true, cert.verify(@rsa2048)) + assert_equal(false, certificate_error_returns_false { cert.verify(@dsa256) }) + assert_equal(false, certificate_error_returns_false { cert.verify(@dsa512) }) + cert.serial = 2 + assert_equal(false, cert.verify(@rsa2048)) + end + + def test_sign_and_verify_rsa_md5 + cert = issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, [], + nil, nil, OpenSSL::Digest::MD5.new) + assert_equal(false, cert.verify(@rsa1024)) + assert_equal(true, cert.verify(@rsa2048)) + + assert_equal(false, certificate_error_returns_false { cert.verify(@dsa256) }) + assert_equal(false, certificate_error_returns_false { cert.verify(@dsa512) }) + cert.subject = @ee1 + assert_equal(false, cert.verify(@rsa2048)) + rescue OpenSSL::X509::CertificateError # RHEL7 disables MD5 + end + + def test_sign_and_verify_dsa + cert = issue_cert(@ca, @dsa512, 1, Time.now, Time.now+3600, [], + nil, nil, OpenSSL::TestUtils::DSA_SIGNATURE_DIGEST.new) + assert_equal(false, certificate_error_returns_false { cert.verify(@rsa1024) }) + assert_equal(false, certificate_error_returns_false { cert.verify(@rsa2048) }) + assert_equal(false, cert.verify(@dsa256)) + assert_equal(true, cert.verify(@dsa512)) + cert.not_after = Time.now + assert_equal(false, cert.verify(@dsa512)) + end + + def test_sign_and_verify_rsa_dss1 + cert = issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, [], + nil, nil, OpenSSL::Digest::DSS1.new) + assert_equal(false, cert.verify(@rsa1024)) + assert_equal(true, cert.verify(@rsa2048)) + assert_equal(false, certificate_error_returns_false { cert.verify(@dsa256) }) + assert_equal(false, certificate_error_returns_false { cert.verify(@dsa512) }) + cert.subject = @ee1 + assert_equal(false, cert.verify(@rsa2048)) + rescue OpenSSL::X509::CertificateError + end + + def test_sign_and_verify_dsa_md5 + assert_raise(OpenSSL::X509::CertificateError){ + issue_cert(@ca, @dsa512, 1, Time.now, Time.now+3600, [], + nil, nil, OpenSSL::Digest::MD5.new) + } + end + + def test_dsig_algorithm_mismatch + assert_raise(OpenSSL::X509::CertificateError) do + issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, [], + nil, nil, OpenSSL::Digest::DSS1.new) + end if OpenSSL::OPENSSL_VERSION_NUMBER < 0x10001000 # [ruby-core:42949] + + assert_raise(OpenSSL::X509::CertificateError) do + issue_cert(@ca, @dsa512, 1, Time.now, Time.now+3600, [], + nil, nil, OpenSSL::Digest::MD5.new) + end + end + + def test_dsa_with_sha2 + begin + cert = issue_cert(@ca, @dsa256, 1, Time.now, Time.now+3600, [], + nil, nil, OpenSSL::Digest::SHA256.new) + assert_equal("dsa_with_SHA256", cert.signature_algorithm) + rescue OpenSSL::X509::CertificateError + # dsa_with_sha2 not supported. skip following test. + return + end + # TODO: need more tests for dsa + sha2 + + # SHA1 is allowed from OpenSSL 1.0.0 (0.9.8 requires DSS1) + cert = issue_cert(@ca, @dsa256, 1, Time.now, Time.now+3600, [], + nil, nil, OpenSSL::Digest::SHA1.new) + assert_equal("dsaWithSHA1", cert.signature_algorithm) + end if defined?(OpenSSL::Digest::SHA256) + + def test_check_private_key + cert = issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, [], + nil, nil, OpenSSL::Digest::SHA1.new) + assert_equal(true, cert.check_private_key(@rsa2048)) + end + + private + + def certificate_error_returns_false + yield + rescue OpenSSL::X509::CertificateError + false + end +end + +end diff --git a/jni/ruby/test/openssl/test_x509crl.rb b/jni/ruby/test/openssl/test_x509crl.rb new file mode 100644 index 0000000..9dc1b1c --- /dev/null +++ b/jni/ruby/test/openssl/test_x509crl.rb @@ -0,0 +1,220 @@ +require_relative "utils" + +if defined?(OpenSSL::TestUtils) + +class OpenSSL::TestX509CRL < Test::Unit::TestCase + def setup + @rsa1024 = OpenSSL::TestUtils::TEST_KEY_RSA1024 + @rsa2048 = OpenSSL::TestUtils::TEST_KEY_RSA2048 + @dsa256 = OpenSSL::TestUtils::TEST_KEY_DSA256 + @dsa512 = OpenSSL::TestUtils::TEST_KEY_DSA512 + @ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA") + @ee1 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE1") + @ee2 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE2") + end + + def teardown + end + + def issue_crl(*args) + OpenSSL::TestUtils.issue_crl(*args) + end + + def issue_cert(*args) + OpenSSL::TestUtils.issue_cert(*args) + end + + def test_basic + now = Time.at(Time.now.to_i) + + cert = issue_cert(@ca, @rsa2048, 1, now, now+3600, [], + nil, nil, OpenSSL::Digest::SHA1.new) + crl = issue_crl([], 1, now, now+1600, [], + cert, @rsa2048, OpenSSL::Digest::SHA1.new) + assert_equal(1, crl.version) + assert_equal(cert.issuer.to_der, crl.issuer.to_der) + assert_equal(now, crl.last_update) + assert_equal(now+1600, crl.next_update) + + crl = OpenSSL::X509::CRL.new(crl.to_der) + assert_equal(1, crl.version) + assert_equal(cert.issuer.to_der, crl.issuer.to_der) + assert_equal(now, crl.last_update) + assert_equal(now+1600, crl.next_update) + end + + def test_revoked + + # CRLReason ::= ENUMERATED { + # unspecified (0), + # keyCompromise (1), + # cACompromise (2), + # affiliationChanged (3), + # superseded (4), + # cessationOfOperation (5), + # certificateHold (6), + # removeFromCRL (8), + # privilegeWithdrawn (9), + # aACompromise (10) } + + now = Time.at(Time.now.to_i) + revoke_info = [ + [1, Time.at(0), 1], + [2, Time.at(0x7fffffff), 2], + [3, now, 3], + [4, now, 4], + [5, now, 5], + ] + cert = issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, [], + nil, nil, OpenSSL::Digest::SHA1.new) + crl = issue_crl(revoke_info, 1, Time.now, Time.now+1600, [], + cert, @rsa2048, OpenSSL::Digest::SHA1.new) + revoked = crl.revoked + assert_equal(5, revoked.size) + assert_equal(1, revoked[0].serial) + assert_equal(2, revoked[1].serial) + assert_equal(3, revoked[2].serial) + assert_equal(4, revoked[3].serial) + assert_equal(5, revoked[4].serial) + + assert_equal(Time.at(0), revoked[0].time) + assert_equal(Time.at(0x7fffffff), revoked[1].time) + assert_equal(now, revoked[2].time) + assert_equal(now, revoked[3].time) + assert_equal(now, revoked[4].time) + + assert_equal("CRLReason", revoked[0].extensions[0].oid) + assert_equal("CRLReason", revoked[1].extensions[0].oid) + assert_equal("CRLReason", revoked[2].extensions[0].oid) + assert_equal("CRLReason", revoked[3].extensions[0].oid) + assert_equal("CRLReason", revoked[4].extensions[0].oid) + + assert_equal("Key Compromise", revoked[0].extensions[0].value) + assert_equal("CA Compromise", revoked[1].extensions[0].value) + assert_equal("Affiliation Changed", revoked[2].extensions[0].value) + assert_equal("Superseded", revoked[3].extensions[0].value) + assert_equal("Cessation Of Operation", revoked[4].extensions[0].value) + + assert_equal(false, revoked[0].extensions[0].critical?) + assert_equal(false, revoked[1].extensions[0].critical?) + assert_equal(false, revoked[2].extensions[0].critical?) + assert_equal(false, revoked[3].extensions[0].critical?) + assert_equal(false, revoked[4].extensions[0].critical?) + + assert_equal("Key Compromise", revoked[0].extensions[0].value) + assert_equal("CA Compromise", revoked[1].extensions[0].value) + assert_equal("Affiliation Changed", revoked[2].extensions[0].value) + assert_equal("Superseded", revoked[3].extensions[0].value) + assert_equal("Cessation Of Operation", revoked[4].extensions[0].value) + + revoke_info = (1..1000).collect{|i| [i, now, 0] } + crl = issue_crl(revoke_info, 1, Time.now, Time.now+1600, [], + cert, @rsa2048, OpenSSL::Digest::SHA1.new) + revoked = crl.revoked + assert_equal(1000, revoked.size) + assert_equal(1, revoked[0].serial) + assert_equal(1000, revoked[999].serial) + end + + def test_extension + cert_exts = [ + ["basicConstraints", "CA:TRUE", true], + ["subjectKeyIdentifier", "hash", false], + ["authorityKeyIdentifier", "keyid:always", false], + ["subjectAltName", "email:xyzzy@ruby-lang.org", false], + ["keyUsage", "cRLSign, keyCertSign", true], + ] + crl_exts = [ + ["authorityKeyIdentifier", "keyid:always", false], + ["issuerAltName", "issuer:copy", false], + ] + + cert = issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, cert_exts, + nil, nil, OpenSSL::Digest::SHA1.new) + crl = issue_crl([], 1, Time.now, Time.now+1600, crl_exts, + cert, @rsa2048, OpenSSL::Digest::SHA1.new) + exts = crl.extensions + assert_equal(3, exts.size) + assert_equal("1", exts[0].value) + assert_equal("crlNumber", exts[0].oid) + assert_equal(false, exts[0].critical?) + + assert_equal("authorityKeyIdentifier", exts[1].oid) + keyid = OpenSSL::TestUtils.get_subject_key_id(cert) + assert_match(/^keyid:#{keyid}/, exts[1].value) + assert_equal(false, exts[1].critical?) + + assert_equal("issuerAltName", exts[2].oid) + assert_equal("email:xyzzy@ruby-lang.org", exts[2].value) + assert_equal(false, exts[2].critical?) + + crl = OpenSSL::X509::CRL.new(crl.to_der) + exts = crl.extensions + assert_equal(3, exts.size) + assert_equal("1", exts[0].value) + assert_equal("crlNumber", exts[0].oid) + assert_equal(false, exts[0].critical?) + + assert_equal("authorityKeyIdentifier", exts[1].oid) + keyid = OpenSSL::TestUtils.get_subject_key_id(cert) + assert_match(/^keyid:#{keyid}/, exts[1].value) + assert_equal(false, exts[1].critical?) + + assert_equal("issuerAltName", exts[2].oid) + assert_equal("email:xyzzy@ruby-lang.org", exts[2].value) + assert_equal(false, exts[2].critical?) + end + + def test_crlnumber + cert = issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, [], + nil, nil, OpenSSL::Digest::SHA1.new) + crl = issue_crl([], 1, Time.now, Time.now+1600, [], + cert, @rsa2048, OpenSSL::Digest::SHA1.new) + assert_match(1.to_s, crl.extensions[0].value) + assert_match(/X509v3 CRL Number:\s+#{1}/m, crl.to_text) + + crl = issue_crl([], 2**32, Time.now, Time.now+1600, [], + cert, @rsa2048, OpenSSL::Digest::SHA1.new) + assert_match((2**32).to_s, crl.extensions[0].value) + assert_match(/X509v3 CRL Number:\s+#{2**32}/m, crl.to_text) + + crl = issue_crl([], 2**100, Time.now, Time.now+1600, [], + cert, @rsa2048, OpenSSL::Digest::SHA1.new) + assert_match(/X509v3 CRL Number:\s+#{2**100}/m, crl.to_text) + assert_match((2**100).to_s, crl.extensions[0].value) + end + + def test_sign_and_verify + cert = issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, [], + nil, nil, OpenSSL::Digest::SHA1.new) + crl = issue_crl([], 1, Time.now, Time.now+1600, [], + cert, @rsa2048, OpenSSL::Digest::SHA1.new) + assert_equal(false, crl.verify(@rsa1024)) + assert_equal(true, crl.verify(@rsa2048)) + assert_equal(false, crl_error_returns_false { crl.verify(@dsa256) }) + assert_equal(false, crl_error_returns_false { crl.verify(@dsa512) }) + crl.version = 0 + assert_equal(false, crl.verify(@rsa2048)) + + cert = issue_cert(@ca, @dsa512, 1, Time.now, Time.now+3600, [], + nil, nil, OpenSSL::TestUtils::DSA_SIGNATURE_DIGEST.new) + crl = issue_crl([], 1, Time.now, Time.now+1600, [], + cert, @dsa512, OpenSSL::TestUtils::DSA_SIGNATURE_DIGEST.new) + assert_equal(false, crl_error_returns_false { crl.verify(@rsa1024) }) + assert_equal(false, crl_error_returns_false { crl.verify(@rsa2048) }) + assert_equal(false, crl.verify(@dsa256)) + assert_equal(true, crl.verify(@dsa512)) + crl.version = 0 + assert_equal(false, crl.verify(@dsa512)) + end + + private + + def crl_error_returns_false + yield + rescue OpenSSL::X509::CRLError + false + end +end + +end diff --git a/jni/ruby/test/openssl/test_x509ext.rb b/jni/ruby/test/openssl/test_x509ext.rb new file mode 100644 index 0000000..29e9f1d --- /dev/null +++ b/jni/ruby/test/openssl/test_x509ext.rb @@ -0,0 +1,69 @@ +require_relative 'utils' + +if defined?(OpenSSL::TestUtils) + +class OpenSSL::TestX509Extension < Test::Unit::TestCase + def setup + @basic_constraints_value = OpenSSL::ASN1::Sequence([ + OpenSSL::ASN1::Boolean(true), # CA + OpenSSL::ASN1::Integer(2) # pathlen + ]) + @basic_constraints = OpenSSL::ASN1::Sequence([ + OpenSSL::ASN1::ObjectId("basicConstraints"), + OpenSSL::ASN1::Boolean(true), + OpenSSL::ASN1::OctetString(@basic_constraints_value.to_der), + ]) + end + + def teardown + end + + def test_new + ext = OpenSSL::X509::Extension.new(@basic_constraints.to_der) + assert_equal("basicConstraints", ext.oid) + assert_equal(true, ext.critical?) + assert_equal("CA:TRUE, pathlen:2", ext.value) + + ext = OpenSSL::X509::Extension.new("2.5.29.19", + @basic_constraints_value.to_der, true) + assert_equal(@basic_constraints.to_der, ext.to_der) + end + + def test_create_by_factory + ef = OpenSSL::X509::ExtensionFactory.new + + bc = ef.create_extension("basicConstraints", "critical, CA:TRUE, pathlen:2") + assert_equal(@basic_constraints.to_der, bc.to_der) + + bc = ef.create_extension("basicConstraints", "CA:TRUE, pathlen:2", true) + assert_equal(@basic_constraints.to_der, bc.to_der) + + begin + ef.config = OpenSSL::Config.parse(<<-_end_of_cnf_) + [crlDistPts] + URI.1 = http://www.example.com/crl + URI.2 = ldap://ldap.example.com/cn=ca?certificateRevocationList;binary + _end_of_cnf_ + rescue NotImplementedError + return + end + + cdp = ef.create_extension("crlDistributionPoints", "@crlDistPts") + assert_equal(false, cdp.critical?) + assert_equal("crlDistributionPoints", cdp.oid) + assert_match(%{URI:http://www\.example\.com/crl}, cdp.value) + assert_match( + %r{URI:ldap://ldap\.example\.com/cn=ca\?certificateRevocationList;binary}, + cdp.value) + + cdp = ef.create_extension("crlDistributionPoints", "critical, @crlDistPts") + assert_equal(true, cdp.critical?) + assert_equal("crlDistributionPoints", cdp.oid) + assert_match(%{URI:http://www.example.com/crl}, cdp.value) + assert_match( + %r{URI:ldap://ldap.example.com/cn=ca\?certificateRevocationList;binary}, + cdp.value) + end +end + +end diff --git a/jni/ruby/test/openssl/test_x509name.rb b/jni/ruby/test/openssl/test_x509name.rb new file mode 100644 index 0000000..a92af53 --- /dev/null +++ b/jni/ruby/test/openssl/test_x509name.rb @@ -0,0 +1,367 @@ +# coding: US-ASCII +require_relative 'utils' + +if defined?(OpenSSL::TestUtils) + +class OpenSSL::TestX509Name < Test::Unit::TestCase + OpenSSL::ASN1::ObjectId.register( + "1.2.840.113549.1.9.1", "emailAddress", "emailAddress") + OpenSSL::ASN1::ObjectId.register( + "2.5.4.5", "serialNumber", "serialNumber") + + def setup + @obj_type_tmpl = Hash.new(OpenSSL::ASN1::PRINTABLESTRING) + @obj_type_tmpl.update(OpenSSL::X509::Name::OBJECT_TYPE_TEMPLATE) + end + + def teardown + end + + def test_s_new + dn = [ ["C", "JP"], ["O", "example"], ["CN", "www.example.jp"] ] + name = OpenSSL::X509::Name.new(dn) + ary = name.to_a + assert_equal("/C=JP/O=example/CN=www.example.jp", name.to_s) + assert_equal("C", ary[0][0]) + assert_equal("O", ary[1][0]) + assert_equal("CN", ary[2][0]) + assert_equal("JP", ary[0][1]) + assert_equal("example", ary[1][1]) + assert_equal("www.example.jp", ary[2][1]) + assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[0][2]) + assert_equal(OpenSSL::ASN1::UTF8STRING, ary[1][2]) + assert_equal(OpenSSL::ASN1::UTF8STRING, ary[2][2]) + + dn = [ + ["countryName", "JP"], + ["organizationName", "example"], + ["commonName", "www.example.jp"] + ] + name = OpenSSL::X509::Name.new(dn) + ary = name.to_a + assert_equal("/C=JP/O=example/CN=www.example.jp", name.to_s) + assert_equal("C", ary[0][0]) + assert_equal("O", ary[1][0]) + assert_equal("CN", ary[2][0]) + assert_equal("JP", ary[0][1]) + assert_equal("example", ary[1][1]) + assert_equal("www.example.jp", ary[2][1]) + assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[0][2]) + assert_equal(OpenSSL::ASN1::UTF8STRING, ary[1][2]) + assert_equal(OpenSSL::ASN1::UTF8STRING, ary[2][2]) + + name = OpenSSL::X509::Name.new(dn, @obj_type_tmpl) + ary = name.to_a + assert_equal("/C=JP/O=example/CN=www.example.jp", name.to_s) + assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[0][2]) + assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[1][2]) + assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[2][2]) + + dn = [ + ["countryName", "JP", OpenSSL::ASN1::PRINTABLESTRING], + ["organizationName", "example", OpenSSL::ASN1::PRINTABLESTRING], + ["commonName", "www.example.jp", OpenSSL::ASN1::PRINTABLESTRING] + ] + name = OpenSSL::X509::Name.new(dn) + ary = name.to_a + assert_equal("/C=JP/O=example/CN=www.example.jp", name.to_s) + assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[0][2]) + assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[1][2]) + assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[2][2]) + + dn = [ + ["DC", "org"], + ["DC", "ruby-lang"], + ["CN", "GOTOU Yuuzou"], + ["emailAddress", "gotoyuzo@ruby-lang.org"], + ["serialNumber", "123"], + ] + name = OpenSSL::X509::Name.new(dn) + ary = name.to_a + assert_equal("/DC=org/DC=ruby-lang/CN=GOTOU Yuuzou/emailAddress=gotoyuzo@ruby-lang.org/serialNumber=123", name.to_s) + assert_equal("DC", ary[0][0]) + assert_equal("DC", ary[1][0]) + assert_equal("CN", ary[2][0]) + assert_equal("emailAddress", ary[3][0]) + assert_equal("serialNumber", ary[4][0]) + assert_equal("org", ary[0][1]) + assert_equal("ruby-lang", ary[1][1]) + assert_equal("GOTOU Yuuzou", ary[2][1]) + assert_equal("gotoyuzo@ruby-lang.org", ary[3][1]) + assert_equal("123", ary[4][1]) + assert_equal(OpenSSL::ASN1::IA5STRING, ary[0][2]) + assert_equal(OpenSSL::ASN1::IA5STRING, ary[1][2]) + assert_equal(OpenSSL::ASN1::UTF8STRING, ary[2][2]) + assert_equal(OpenSSL::ASN1::IA5STRING, ary[3][2]) + assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[4][2]) + + name_from_der = OpenSSL::X509::Name.new(name.to_der) + assert_equal(name_from_der.to_s, name.to_s) + assert_equal(name_from_der.to_a, name.to_a) + assert_equal(name_from_der.to_der, name.to_der) + end + + def test_unrecognized_oid + dn = [ ["1.2.3.4.5.6.7.8.9.7.5.3.1", "Unknown OID 1"], + ["1.1.2.3.5.8.13.21.34", "Unknown OID 2"], + ["C", "US"], + ["postalCode", "60602"], + ["ST", "Illinois"], + ["L", "Chicago"], + #["street", "123 Fake St"], + ["O", "Some Company LLC"], + ["CN", "mydomain.com"] ] + + name = OpenSSL::X509::Name.new(dn) + ary = name.to_a + #assert_equal("/1.2.3.4.5.6.7.8.9.7.5.3.1=Unknown OID 1/1.1.2.3.5.8.13.21.34=Unknown OID 2/C=US/postalCode=60602/ST=Illinois/L=Chicago/street=123 Fake St/O=Some Company LLC/CN=mydomain.com", name.to_s) + assert_equal("/1.2.3.4.5.6.7.8.9.7.5.3.1=Unknown OID 1/1.1.2.3.5.8.13.21.34=Unknown OID 2/C=US/postalCode=60602/ST=Illinois/L=Chicago/O=Some Company LLC/CN=mydomain.com", name.to_s) + assert_equal("1.2.3.4.5.6.7.8.9.7.5.3.1", ary[0][0]) + assert_equal("1.1.2.3.5.8.13.21.34", ary[1][0]) + assert_equal("C", ary[2][0]) + assert_equal("postalCode", ary[3][0]) + assert_equal("ST", ary[4][0]) + assert_equal("L", ary[5][0]) + #assert_equal("street", ary[6][0]) + assert_equal("O", ary[6][0]) + assert_equal("CN", ary[7][0]) + assert_equal("Unknown OID 1", ary[0][1]) + assert_equal("Unknown OID 2", ary[1][1]) + assert_equal("US", ary[2][1]) + assert_equal("60602", ary[3][1]) + assert_equal("Illinois", ary[4][1]) + assert_equal("Chicago", ary[5][1]) + #assert_equal("123 Fake St", ary[6][1]) + assert_equal("Some Company LLC", ary[6][1]) + assert_equal("mydomain.com", ary[7][1]) + end + + def test_unrecognized_oid_parse_encode_equality + dn = [ ["1.2.3.4.5.6.7.8.9.7.5.3.2", "Unknown OID1"], + ["1.1.2.3.5.8.13.21.35", "Unknown OID2"], + ["C", "US"], + ["postalCode", "60602"], + ["ST", "Illinois"], + ["L", "Chicago"], + #["street", "123 Fake St"], + ["O", "Some Company LLC"], + ["CN", "mydomain.com"] ] + + name1 = OpenSSL::X509::Name.new(dn) + name2 = OpenSSL::X509::Name.parse(name1.to_s) + assert_equal(name1.to_s, name2.to_s) + assert_equal(name1.to_a, name2.to_a) + end + + def test_s_parse + dn = "/DC=org/DC=ruby-lang/CN=www.ruby-lang.org" + name = OpenSSL::X509::Name.parse(dn) + assert_equal(dn, name.to_s) + ary = name.to_a + assert_equal("DC", ary[0][0]) + assert_equal("DC", ary[1][0]) + assert_equal("CN", ary[2][0]) + assert_equal("org", ary[0][1]) + assert_equal("ruby-lang", ary[1][1]) + assert_equal("www.ruby-lang.org", ary[2][1]) + assert_equal(OpenSSL::ASN1::IA5STRING, ary[0][2]) + assert_equal(OpenSSL::ASN1::IA5STRING, ary[1][2]) + assert_equal(OpenSSL::ASN1::UTF8STRING, ary[2][2]) + + dn2 = "DC=org, DC=ruby-lang, CN=www.ruby-lang.org" + name = OpenSSL::X509::Name.parse(dn2) + ary = name.to_a + assert_equal(dn, name.to_s) + assert_equal("org", ary[0][1]) + assert_equal("ruby-lang", ary[1][1]) + assert_equal("www.ruby-lang.org", ary[2][1]) + + name = OpenSSL::X509::Name.parse(dn2, @obj_type_tmpl) + ary = name.to_a + assert_equal(OpenSSL::ASN1::IA5STRING, ary[0][2]) + assert_equal(OpenSSL::ASN1::IA5STRING, ary[1][2]) + assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[2][2]) + end + + def test_s_parse_rfc2253 + scanner = OpenSSL::X509::Name::RFC2253DN.method(:scan) + + assert_equal([["C", "JP"]], scanner.call("C=JP")) + assert_equal([ + ["DC", "org"], + ["DC", "ruby-lang"], + ["CN", "GOTOU Yuuzou"], + ["emailAddress", "gotoyuzo@ruby-lang.org"], + ], + scanner.call( + "emailAddress=gotoyuzo@ruby-lang.org,CN=GOTOU Yuuzou,"+ + "DC=ruby-lang,DC=org") + ) + + u8 = OpenSSL::ASN1::UTF8STRING + assert_equal([ + ["DC", "org"], + ["DC", "ruby-lang"], + ["O", ",=+<>#;"], + ["O", ",=+<>#;"], + ["OU", ""], + ["OU", ""], + ["L", "aaa=\"bbb, ccc\""], + ["L", "aaa=\"bbb, ccc\""], + ["CN", "\345\276\214\350\227\244\350\243\225\350\224\265"], + ["CN", "\345\276\214\350\227\244\350\243\225\350\224\265"], + ["CN", "\345\276\214\350\227\244\350\243\225\350\224\265"], + ["CN", "\345\276\214\350\227\244\350\243\225\350\224\265", u8], + ["2.5.4.3", "GOTOU, Yuuzou"], + ["2.5.4.3", "GOTOU, Yuuzou"], + ["2.5.4.3", "GOTOU, Yuuzou"], + ["2.5.4.3", "GOTOU, Yuuzou"], + ["CN", "GOTOU \"gotoyuzo\" Yuuzou"], + ["CN", "GOTOU \"gotoyuzo\" Yuuzou"], + ["1.2.840.113549.1.9.1", "gotoyuzo@ruby-lang.org"], + ["emailAddress", "gotoyuzo@ruby-lang.org"], + ], + scanner.call( + "emailAddress=gotoyuzo@ruby-lang.org," + + "1.2.840.113549.1.9.1=gotoyuzo@ruby-lang.org," + + 'CN=GOTOU \"gotoyuzo\" Yuuzou,' + + 'CN="GOTOU \"gotoyuzo\" Yuuzou",' + + '2.5.4.3=GOTOU\,\20Yuuzou,' + + '2.5.4.3=GOTOU\, Yuuzou,' + + '2.5.4.3="GOTOU, Yuuzou",' + + '2.5.4.3="GOTOU\, Yuuzou",' + + "CN=#0C0CE5BE8CE897A4E8A395E894B5," + + 'CN=\E5\BE\8C\E8\97\A4\E8\A3\95\E8\94\B5,' + + "CN=\"\xE5\xBE\x8C\xE8\x97\xA4\xE8\xA3\x95\xE8\x94\xB5\"," + + "CN=\xE5\xBE\x8C\xE8\x97\xA4\xE8\xA3\x95\xE8\x94\xB5," + + 'L=aaa\=\"bbb\, ccc\",' + + 'L="aaa=\"bbb, ccc\"",' + + 'OU=,' + + 'OU="",' + + 'O=\,\=\+\<\>\#\;,' + + 'O=",=+<>#;",' + + "DC=ruby-lang," + + "DC=org") + ) + + [ + "DC=org+DC=jp", + "DC=org,DC=ruby-lang+DC=rubyist,DC=www" + ].each{|dn| + ex = scanner.call(dn) rescue $! + dn_r = Regexp.escape(dn) + assert_match(/^multi-valued RDN is not supported: #{dn_r}/, ex.message) + } + + [ + ["DC=org,DC=exapmle,CN", "CN"], + ["DC=org,DC=example,", ""], + ["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) + } + + dn = "CN=www.ruby-lang.org,DC=ruby-lang,DC=org" + name = OpenSSL::X509::Name.parse_rfc2253(dn) + assert_equal(dn, name.to_s(OpenSSL::X509::Name::RFC2253)) + ary = name.to_a + assert_equal("DC", ary[0][0]) + assert_equal("DC", ary[1][0]) + assert_equal("CN", ary[2][0]) + assert_equal("org", ary[0][1]) + assert_equal("ruby-lang", ary[1][1]) + assert_equal("www.ruby-lang.org", ary[2][1]) + assert_equal(OpenSSL::ASN1::IA5STRING, ary[0][2]) + assert_equal(OpenSSL::ASN1::IA5STRING, ary[1][2]) + assert_equal(OpenSSL::ASN1::UTF8STRING, ary[2][2]) + end + + def test_add_entry + dn = [ + ["DC", "org"], + ["DC", "ruby-lang"], + ["CN", "GOTOU Yuuzou"], + ["emailAddress", "gotoyuzo@ruby-lang.org"], + ["serialNumber", "123"], + ] + name = OpenSSL::X509::Name.new + dn.each{|attr| name.add_entry(*attr) } + ary = name.to_a + assert_equal("/DC=org/DC=ruby-lang/CN=GOTOU Yuuzou/emailAddress=gotoyuzo@ruby-lang.org/serialNumber=123", name.to_s) + assert_equal("DC", ary[0][0]) + assert_equal("DC", ary[1][0]) + assert_equal("CN", ary[2][0]) + assert_equal("emailAddress", ary[3][0]) + assert_equal("serialNumber", ary[4][0]) + assert_equal("org", ary[0][1]) + assert_equal("ruby-lang", ary[1][1]) + assert_equal("GOTOU Yuuzou", ary[2][1]) + assert_equal("gotoyuzo@ruby-lang.org", ary[3][1]) + assert_equal("123", ary[4][1]) + assert_equal(OpenSSL::ASN1::IA5STRING, ary[0][2]) + assert_equal(OpenSSL::ASN1::IA5STRING, ary[1][2]) + assert_equal(OpenSSL::ASN1::UTF8STRING, ary[2][2]) + assert_equal(OpenSSL::ASN1::IA5STRING, ary[3][2]) + assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[4][2]) + end + + def test_add_entry_street + return if OpenSSL::OPENSSL_VERSION_NUMBER < 0x009080df # 0.9.8m + # openssl/crypto/objects/obj_mac.h 1.83 + dn = [ + ["DC", "org"], + ["DC", "ruby-lang"], + ["CN", "GOTOU Yuuzou"], + ["emailAddress", "gotoyuzo@ruby-lang.org"], + ["serialNumber", "123"], + ["street", "Namiki"], + ] + name = OpenSSL::X509::Name.new + dn.each{|attr| name.add_entry(*attr) } + ary = name.to_a + assert_equal("/DC=org/DC=ruby-lang/CN=GOTOU Yuuzou/emailAddress=gotoyuzo@ruby-lang.org/serialNumber=123/street=Namiki", name.to_s) + assert_equal("Namiki", ary[5][1]) + end + + def test_equals2 + n1 = OpenSSL::X509::Name.parse 'CN=a' + n2 = OpenSSL::X509::Name.parse 'CN=a' + + assert_equal n1, n2 + end + + def test_spaceship + n1 = OpenSSL::X509::Name.parse 'CN=a' + n2 = OpenSSL::X509::Name.parse 'CN=b' + + assert_equal(-1, n1 <=> n2) + end + + def name_hash(name) + # OpenSSL 1.0.0 uses SHA1 for canonical encoding (not just a der) of + # X509Name for X509_NAME_hash. + name.respond_to?(:hash_old) ? name.hash_old : name.hash + end + + def test_hash + dn = "/DC=org/DC=ruby-lang/CN=www.ruby-lang.org" + name = OpenSSL::X509::Name.parse(dn) + d = Digest::MD5.digest(name.to_der) + expected = (d[0].ord & 0xff) | (d[1].ord & 0xff) << 8 | (d[2].ord & 0xff) << 16 | (d[3].ord & 0xff) << 24 + assert_equal(expected, name_hash(name)) + # + dn = "/DC=org/DC=ruby-lang/CN=baz.ruby-lang.org" + name = OpenSSL::X509::Name.parse(dn) + d = Digest::MD5.digest(name.to_der) + expected = (d[0].ord & 0xff) | (d[1].ord & 0xff) << 8 | (d[2].ord & 0xff) << 16 | (d[3].ord & 0xff) << 24 + assert_equal(expected, name_hash(name)) + end +end + +end diff --git a/jni/ruby/test/openssl/test_x509req.rb b/jni/ruby/test/openssl/test_x509req.rb new file mode 100644 index 0000000..27040cb --- /dev/null +++ b/jni/ruby/test/openssl/test_x509req.rb @@ -0,0 +1,158 @@ +require_relative "utils" + +if defined?(OpenSSL::TestUtils) + +class OpenSSL::TestX509Request < Test::Unit::TestCase + def setup + @rsa1024 = OpenSSL::TestUtils::TEST_KEY_RSA1024 + @rsa2048 = OpenSSL::TestUtils::TEST_KEY_RSA2048 + @dsa256 = OpenSSL::TestUtils::TEST_KEY_DSA256 + @dsa512 = OpenSSL::TestUtils::TEST_KEY_DSA512 + @dn = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=GOTOU Yuuzou") + end + + def issue_csr(ver, dn, key, digest) + req = OpenSSL::X509::Request.new + req.version = ver + req.subject = dn + req.public_key = key.public_key + req.sign(key, digest) + req + end + + def test_public_key + req = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest::SHA1.new) + assert_equal(@rsa1024.public_key.to_der, req.public_key.to_der) + req = OpenSSL::X509::Request.new(req.to_der) + assert_equal(@rsa1024.public_key.to_der, req.public_key.to_der) + + req = issue_csr(0, @dn, @dsa512, OpenSSL::TestUtils::DSA_SIGNATURE_DIGEST.new) + assert_equal(@dsa512.public_key.to_der, req.public_key.to_der) + req = OpenSSL::X509::Request.new(req.to_der) + assert_equal(@dsa512.public_key.to_der, req.public_key.to_der) + end + + def test_version + req = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest::SHA1.new) + assert_equal(0, req.version) + req = OpenSSL::X509::Request.new(req.to_der) + assert_equal(0, req.version) + + req = issue_csr(1, @dn, @rsa1024, OpenSSL::Digest::SHA1.new) + assert_equal(1, req.version) + req = OpenSSL::X509::Request.new(req.to_der) + assert_equal(1, req.version) + end + + def test_subject + req = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest::SHA1.new) + assert_equal(@dn.to_der, req.subject.to_der) + req = OpenSSL::X509::Request.new(req.to_der) + assert_equal(@dn.to_der, req.subject.to_der) + end + + def create_ext_req(exts) + ef = OpenSSL::X509::ExtensionFactory.new + exts = exts.collect{|e| ef.create_extension(*e) } + return OpenSSL::ASN1::Set([OpenSSL::ASN1::Sequence(exts)]) + end + + def get_ext_req(ext_req_value) + set = OpenSSL::ASN1.decode(ext_req_value) + seq = set.value[0] + seq.value.collect{|asn1ext| + OpenSSL::X509::Extension.new(asn1ext).to_a + } + end + + def test_attr + exts = [ + ["keyUsage", "Digital Signature, Key Encipherment", true], + ["subjectAltName", "email:gotoyuzo@ruby-lang.org", false], + ] + attrval = create_ext_req(exts) + attrs = [ + OpenSSL::X509::Attribute.new("extReq", attrval), + OpenSSL::X509::Attribute.new("msExtReq", attrval), + ] + + req0 = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest::SHA1.new) + attrs.each{|attr| req0.add_attribute(attr) } + req1 = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest::SHA1.new) + req1.attributes = attrs + assert_equal(req0.to_der, req1.to_der) + + attrs = req0.attributes + assert_equal(2, attrs.size) + assert_equal("extReq", attrs[0].oid) + assert_equal("msExtReq", attrs[1].oid) + assert_equal(exts, get_ext_req(attrs[0].value)) + assert_equal(exts, get_ext_req(attrs[1].value)) + + req = OpenSSL::X509::Request.new(req0.to_der) + attrs = req.attributes + assert_equal(2, attrs.size) + assert_equal("extReq", attrs[0].oid) + assert_equal("msExtReq", attrs[1].oid) + assert_equal(exts, get_ext_req(attrs[0].value)) + assert_equal(exts, get_ext_req(attrs[1].value)) + end + + def test_sign_and_verify_rsa_sha1 + req = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest::SHA1.new) + assert_equal(true, req.verify(@rsa1024)) + assert_equal(false, req.verify(@rsa2048)) + assert_equal(false, request_error_returns_false { req.verify(@dsa256) }) + assert_equal(false, request_error_returns_false { req.verify(@dsa512) }) + req.version = 1 + assert_equal(false, req.verify(@rsa1024)) + end + + def test_sign_and_verify_rsa_md5 + req = issue_csr(0, @dn, @rsa2048, OpenSSL::Digest::MD5.new) + assert_equal(false, req.verify(@rsa1024)) + assert_equal(true, req.verify(@rsa2048)) + assert_equal(false, request_error_returns_false { req.verify(@dsa256) }) + assert_equal(false, request_error_returns_false { req.verify(@dsa512) }) + req.subject = OpenSSL::X509::Name.parse("/C=JP/CN=FooBar") + assert_equal(false, req.verify(@rsa2048)) + rescue OpenSSL::X509::RequestError # RHEL7 disables MD5 + end + + def test_sign_and_verify_dsa + req = issue_csr(0, @dn, @dsa512, OpenSSL::TestUtils::DSA_SIGNATURE_DIGEST.new) + assert_equal(false, request_error_returns_false { req.verify(@rsa1024) }) + assert_equal(false, request_error_returns_false { req.verify(@rsa2048) }) + assert_equal(false, req.verify(@dsa256)) + assert_equal(true, req.verify(@dsa512)) + req.public_key = @rsa1024.public_key + assert_equal(false, req.verify(@dsa512)) + end + + def test_sign_and_verify_rsa_dss1 + req = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest::DSS1.new) + assert_equal(true, req.verify(@rsa1024)) + assert_equal(false, req.verify(@rsa2048)) + assert_equal(false, request_error_returns_false { req.verify(@dsa256) }) + assert_equal(false, request_error_returns_false { req.verify(@dsa512) }) + req.version = 1 + assert_equal(false, req.verify(@rsa1024)) + rescue OpenSSL::X509::RequestError + skip + end + + def test_sign_and_verify_dsa_md5 + assert_raise(OpenSSL::X509::RequestError){ + issue_csr(0, @dn, @dsa512, OpenSSL::Digest::MD5.new) } + end + + private + + def request_error_returns_false + yield + rescue OpenSSL::X509::RequestError + false + end +end + +end diff --git a/jni/ruby/test/openssl/test_x509store.rb b/jni/ruby/test/openssl/test_x509store.rb new file mode 100644 index 0000000..f3e144f --- /dev/null +++ b/jni/ruby/test/openssl/test_x509store.rb @@ -0,0 +1,231 @@ +require_relative "utils" + +if defined?(OpenSSL::TestUtils) + +class OpenSSL::TestX509Store < Test::Unit::TestCase + def setup + @rsa1024 = OpenSSL::TestUtils::TEST_KEY_RSA1024 + @rsa2048 = OpenSSL::TestUtils::TEST_KEY_RSA2048 + @dsa256 = OpenSSL::TestUtils::TEST_KEY_DSA256 + @dsa512 = OpenSSL::TestUtils::TEST_KEY_DSA512 + @ca1 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA1") + @ca2 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA2") + @ee1 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE1") + @ee2 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE2") + end + + def teardown + end + + def test_nosegv_on_cleanup + cert = OpenSSL::X509::Certificate.new + store = OpenSSL::X509::Store.new + ctx = OpenSSL::X509::StoreContext.new(store, cert, []) + EnvUtil.suppress_warning do + ctx.cleanup + end + ctx.verify + end + + def issue_cert(*args) + OpenSSL::TestUtils.issue_cert(*args) + end + + def issue_crl(*args) + OpenSSL::TestUtils.issue_crl(*args) + end + + def test_verify + now = Time.at(Time.now.to_i) + ca_exts = [ + ["basicConstraints","CA:TRUE",true], + ["keyUsage","cRLSign,keyCertSign",true], + ] + ee_exts = [ + ["keyUsage","keyEncipherment,digitalSignature",true], + ] + ca1_cert = issue_cert(@ca1, @rsa2048, 1, now, now+3600, ca_exts, + nil, nil, OpenSSL::Digest::SHA1.new) + ca2_cert = issue_cert(@ca2, @rsa1024, 2, now, now+1800, ca_exts, + ca1_cert, @rsa2048, OpenSSL::Digest::SHA1.new) + ee1_cert = issue_cert(@ee1, @dsa256, 10, now, now+1800, ee_exts, + ca2_cert, @rsa1024, OpenSSL::Digest::SHA1.new) + ee2_cert = issue_cert(@ee2, @dsa512, 20, now, now+1800, ee_exts, + ca2_cert, @rsa1024, OpenSSL::Digest::SHA1.new) + ee3_cert = issue_cert(@ee2, @dsa512, 30, now-100, now-1, ee_exts, + ca2_cert, @rsa1024, OpenSSL::Digest::SHA1.new) + ee4_cert = issue_cert(@ee2, @dsa512, 40, now+1000, now+2000, ee_exts, + ca2_cert, @rsa1024, OpenSSL::Digest::SHA1.new) + + revoke_info = [] + crl1 = issue_crl(revoke_info, 1, now, now+1800, [], + ca1_cert, @rsa2048, OpenSSL::Digest::SHA1.new) + revoke_info = [ [2, now, 1], ] + crl1_2 = issue_crl(revoke_info, 2, now, now+1800, [], + ca1_cert, @rsa2048, OpenSSL::Digest::SHA1.new) + revoke_info = [ [20, now, 1], ] + crl2 = issue_crl(revoke_info, 1, now, now+1800, [], + ca2_cert, @rsa1024, OpenSSL::Digest::SHA1.new) + revoke_info = [] + crl2_2 = issue_crl(revoke_info, 2, now-100, now-1, [], + ca2_cert, @rsa1024, OpenSSL::Digest::SHA1.new) + + assert_equal(true, ca1_cert.verify(ca1_cert.public_key)) # self signed + assert_equal(true, ca2_cert.verify(ca1_cert.public_key)) # issued by ca1 + assert_equal(true, ee1_cert.verify(ca2_cert.public_key)) # issued by ca2 + assert_equal(true, ee2_cert.verify(ca2_cert.public_key)) # issued by ca2 + assert_equal(true, ee3_cert.verify(ca2_cert.public_key)) # issued by ca2 + assert_equal(true, crl1.verify(ca1_cert.public_key)) # issued by ca1 + assert_equal(true, crl1_2.verify(ca1_cert.public_key)) # issued by ca1 + assert_equal(true, crl2.verify(ca2_cert.public_key)) # issued by ca2 + assert_equal(true, crl2_2.verify(ca2_cert.public_key)) # issued by ca2 + + store = OpenSSL::X509::Store.new + assert_equal(false, store.verify(ca1_cert)) + assert_not_equal(OpenSSL::X509::V_OK, store.error) + + assert_equal(false, store.verify(ca2_cert)) + assert_not_equal(OpenSSL::X509::V_OK, store.error) + + store.add_cert(ca1_cert) + assert_equal(true, store.verify(ca2_cert)) + assert_equal(OpenSSL::X509::V_OK, store.error) + assert_equal("ok", store.error_string) + chain = store.chain + assert_equal(2, chain.size) + assert_equal(@ca2.to_der, chain[0].subject.to_der) + assert_equal(@ca1.to_der, chain[1].subject.to_der) + + store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT + assert_equal(false, store.verify(ca2_cert)) + assert_not_equal(OpenSSL::X509::V_OK, store.error) + + store.purpose = OpenSSL::X509::PURPOSE_CRL_SIGN + assert_equal(true, store.verify(ca2_cert)) + assert_equal(OpenSSL::X509::V_OK, store.error) + + store.add_cert(ca2_cert) + store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT + assert_equal(true, store.verify(ee1_cert)) + assert_equal(true, store.verify(ee2_cert)) + assert_equal(OpenSSL::X509::V_OK, store.error) + assert_equal("ok", store.error_string) + chain = store.chain + assert_equal(3, chain.size) + assert_equal(@ee2.to_der, chain[0].subject.to_der) + assert_equal(@ca2.to_der, chain[1].subject.to_der) + assert_equal(@ca1.to_der, chain[2].subject.to_der) + assert_equal(false, store.verify(ee3_cert)) + assert_equal(OpenSSL::X509::V_ERR_CERT_HAS_EXPIRED, store.error) + assert_match(/expire/i, store.error_string) + assert_equal(false, store.verify(ee4_cert)) + assert_equal(OpenSSL::X509::V_ERR_CERT_NOT_YET_VALID, store.error) + assert_match(/not yet valid/i, store.error_string) + + store = OpenSSL::X509::Store.new + store.add_cert(ca1_cert) + store.add_cert(ca2_cert) + store.time = now + 1500 + assert_equal(true, store.verify(ca1_cert)) + assert_equal(true, store.verify(ca2_cert)) + assert_equal(true, store.verify(ee4_cert)) + store.time = now + 1900 + assert_equal(true, store.verify(ca1_cert)) + assert_equal(false, store.verify(ca2_cert)) + assert_equal(OpenSSL::X509::V_ERR_CERT_HAS_EXPIRED, store.error) + assert_equal(false, store.verify(ee4_cert)) + assert_equal(OpenSSL::X509::V_ERR_CERT_HAS_EXPIRED, store.error) + store.time = now + 4000 + assert_equal(false, store.verify(ee1_cert)) + assert_equal(OpenSSL::X509::V_ERR_CERT_HAS_EXPIRED, store.error) + assert_equal(false, store.verify(ee4_cert)) + assert_equal(OpenSSL::X509::V_ERR_CERT_HAS_EXPIRED, store.error) + + # the underlying X509 struct caches the result of the last + # verification for signature and not-before. so the following code + # rebuilds new objects to avoid site effect. + store.time = Time.now - 4000 + assert_equal(false, store.verify(OpenSSL::X509::Certificate.new(ca2_cert))) + assert_equal(OpenSSL::X509::V_ERR_CERT_NOT_YET_VALID, store.error) + assert_equal(false, store.verify(OpenSSL::X509::Certificate.new(ee1_cert))) + assert_equal(OpenSSL::X509::V_ERR_CERT_NOT_YET_VALID, store.error) + + return unless defined?(OpenSSL::X509::V_FLAG_CRL_CHECK) + + store = OpenSSL::X509::Store.new + store.purpose = OpenSSL::X509::PURPOSE_ANY + store.flags = OpenSSL::X509::V_FLAG_CRL_CHECK + store.add_cert(ca1_cert) + store.add_crl(crl1) # revoke no cert + store.add_crl(crl2) # revoke ee2_cert + assert_equal(true, store.verify(ca1_cert)) + assert_equal(true, store.verify(ca2_cert)) + assert_equal(true, store.verify(ee1_cert, [ca2_cert])) + assert_equal(false, store.verify(ee2_cert, [ca2_cert])) + + store = OpenSSL::X509::Store.new + store.purpose = OpenSSL::X509::PURPOSE_ANY + store.flags = OpenSSL::X509::V_FLAG_CRL_CHECK + store.add_cert(ca1_cert) + store.add_crl(crl1_2) # revoke ca2_cert + store.add_crl(crl2) # revoke ee2_cert + assert_equal(true, store.verify(ca1_cert)) + assert_equal(false, store.verify(ca2_cert)) + assert_equal(true, store.verify(ee1_cert, [ca2_cert]), + "This test is expected to be success with OpenSSL 0.9.7c or later.") + assert_equal(false, store.verify(ee2_cert, [ca2_cert])) + + store.flags = + OpenSSL::X509::V_FLAG_CRL_CHECK|OpenSSL::X509::V_FLAG_CRL_CHECK_ALL + assert_equal(true, store.verify(ca1_cert)) + assert_equal(false, store.verify(ca2_cert)) + assert_equal(false, store.verify(ee1_cert, [ca2_cert])) + assert_equal(false, store.verify(ee2_cert, [ca2_cert])) + + store = OpenSSL::X509::Store.new + store.purpose = OpenSSL::X509::PURPOSE_ANY + store.flags = + OpenSSL::X509::V_FLAG_CRL_CHECK|OpenSSL::X509::V_FLAG_CRL_CHECK_ALL + store.add_cert(ca1_cert) + store.add_cert(ca2_cert) + store.add_crl(crl1) + store.add_crl(crl2_2) # issued by ca2 but expired. + assert_equal(true, store.verify(ca1_cert)) + assert_equal(true, store.verify(ca2_cert)) + assert_equal(false, store.verify(ee1_cert)) + assert_equal(OpenSSL::X509::V_ERR_CRL_HAS_EXPIRED, store.error) + assert_equal(false, store.verify(ee2_cert)) + end + + def test_set_errors + now = Time.now + ca1_cert = issue_cert(@ca1, @rsa2048, 1, now, now+3600, [], + nil, nil, OpenSSL::Digest::SHA1.new) + store = OpenSSL::X509::Store.new + store.add_cert(ca1_cert) + assert_raise(OpenSSL::X509::StoreError){ + store.add_cert(ca1_cert) # add same certificate twice + } + + revoke_info = [] + crl1 = issue_crl(revoke_info, 1, now, now+1800, [], + ca1_cert, @rsa2048, OpenSSL::Digest::SHA1.new) + revoke_info = [ [2, now, 1], ] + crl2 = issue_crl(revoke_info, 2, now+1800, now+3600, [], + ca1_cert, @rsa2048, OpenSSL::Digest::SHA1.new) + store.add_crl(crl1) + if /0\.9\.8.*-rhel/ =~ OpenSSL::OPENSSL_VERSION + # RedHat is distributing a patched version of OpenSSL that allows + # multiple CRL for a key (multi-crl.patch) + assert_nothing_raised do + store.add_crl(crl2) # add CRL issued by same CA twice. + end + else + assert_raise(OpenSSL::X509::StoreError){ + store.add_crl(crl2) # add CRL issued by same CA twice. + } + end + end +end + +end diff --git a/jni/ruby/test/openssl/utils.rb b/jni/ruby/test/openssl/utils.rb new file mode 100644 index 0000000..bd936be --- /dev/null +++ b/jni/ruby/test/openssl/utils.rb @@ -0,0 +1,333 @@ +begin + require "openssl" + + # Disable FIPS mode for tests for installations + # where FIPS mode would be enabled by default. + # Has no effect on all other installations. + OpenSSL.fips_mode=false +rescue LoadError +end +require "test/unit" +require "digest/md5" +require 'tempfile' +require "rbconfig" +require "socket" + +module OpenSSL::TestUtils + TEST_KEY_RSA1024 = OpenSSL::PKey::RSA.new <<-_end_of_pem_ +-----BEGIN RSA PRIVATE KEY----- +MIICXgIBAAKBgQDLwsSw1ECnPtT+PkOgHhcGA71nwC2/nL85VBGnRqDxOqjVh7Cx +aKPERYHsk4BPCkE3brtThPWc9kjHEQQ7uf9Y1rbCz0layNqHyywQEVLFmp1cpIt/ +Q3geLv8ZD9pihowKJDyMDiN6ArYUmZczvW4976MU3+l54E6lF/JfFEU5hwIDAQAB +AoGBAKSl/MQarye1yOysqX6P8fDFQt68VvtXkNmlSiKOGuzyho0M+UVSFcs6k1L0 +maDE25AMZUiGzuWHyaU55d7RXDgeskDMakD1v6ZejYtxJkSXbETOTLDwUWTn618T +gnb17tU1jktUtU67xK/08i/XodlgnQhs6VoHTuCh3Hu77O6RAkEA7+gxqBuZR572 +74/akiW/SuXm0SXPEviyO1MuSRwtI87B02D0qgV8D1UHRm4AhMnJ8MCs1809kMQE +JiQUCrp9mQJBANlt2ngBO14us6NnhuAseFDTBzCHXwUUu1YKHpMMmxpnGqaldGgX +sOZB3lgJsT9VlGf3YGYdkLTNVbogQKlKpB8CQQDiSwkb4vyQfDe8/NpU5Not0fII +8jsDUCb+opWUTMmfbxWRR3FBNu8wnym/m19N4fFj8LqYzHX4KY0oVPu6qvJxAkEA +wa5snNekFcqONLIE4G5cosrIrb74sqL8GbGb+KuTAprzj5z1K8Bm0UW9lTjVDjDi +qRYgZfZSL+x1P/54+xTFSwJAY1FxA/N3QPCXCjPh5YqFxAMQs2VVYTfg+t0MEcJD +dPMQD5JX6g5HKnHFg2mZtoXQrWmJSn7p8GJK8yNTopEErA== +-----END RSA PRIVATE KEY----- + _end_of_pem_ + + TEST_KEY_RSA2048 = OpenSSL::PKey::RSA.new <<-_end_of_pem_ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAuV9ht9J7k4NBs38jOXvvTKY9gW8nLICSno5EETR1cuF7i4pN +s9I1QJGAFAX0BEO4KbzXmuOvfCpD3CU+Slp1enenfzq/t/e/1IRW0wkJUJUFQign +4CtrkJL+P07yx18UjyPlBXb81ApEmAB5mrJVSrWmqbjs07JbuS4QQGGXLc+Su96D +kYKmSNVjBiLxVVSpyZfAY3hD37d60uG+X8xdW5v68JkRFIhdGlb6JL8fllf/A/bl +NwdJOhVr9mESHhwGjwfSeTDPfd8ZLE027E5lyAVX9KZYcU00mOX+fdxOSnGqS/8J +DRh0EPHDL15RcJjV2J6vZjPb0rOYGDoMcH+94wIDAQABAoIBAAzsamqfYQAqwXTb +I0CJtGg6msUgU7HVkOM+9d3hM2L791oGHV6xBAdpXW2H8LgvZHJ8eOeSghR8+dgq +PIqAffo4x1Oma+FOg3A0fb0evyiACyrOk+EcBdbBeLo/LcvahBtqnDfiUMQTpy6V +seSoFCwuN91TSCeGIsDpRjbG1vxZgtx+uI+oH5+ytqJOmfCksRDCkMglGkzyfcl0 +Xc5CUhIJ0my53xijEUQl19rtWdMnNnnkdbG8PT3LZlOta5Do86BElzUYka0C6dUc +VsBDQ0Nup0P6rEQgy7tephHoRlUGTYamsajGJaAo1F3IQVIrRSuagi7+YpSpCqsW +wORqorkCgYEA7RdX6MDVrbw7LePnhyuaqTiMK+055/R1TqhB1JvvxJ1CXk2rDL6G +0TLHQ7oGofd5LYiemg4ZVtWdJe43BPZlVgT6lvL/iGo8JnrncB9Da6L7nrq/+Rvj +XGjf1qODCK+LmreZWEsaLPURIoR/Ewwxb9J2zd0CaMjeTwafJo1CZvcCgYEAyCgb +aqoWvUecX8VvARfuA593Lsi50t4MEArnOXXcd1RnXoZWhbx5rgO8/ATKfXr0BK/n +h2GF9PfKzHFm/4V6e82OL7gu/kLy2u9bXN74vOvWFL5NOrOKPM7Kg+9I131kNYOw +Ivnr/VtHE5s0dY7JChYWE1F3vArrOw3T00a4CXUCgYEA0SqY+dS2LvIzW4cHCe9k +IQqsT0yYm5TFsUEr4sA3xcPfe4cV8sZb9k/QEGYb1+SWWZ+AHPV3UW5fl8kTbSNb +v4ng8i8rVVQ0ANbJO9e5CUrepein2MPL0AkOATR8M7t7dGGpvYV0cFk8ZrFx0oId +U0PgYDotF/iueBWlbsOM430CgYEAqYI95dFyPI5/AiSkY5queeb8+mQH62sdcCCr +vd/w/CZA/K5sbAo4SoTj8dLk4evU6HtIa0DOP63y071eaxvRpTNqLUOgmLh+D6gS +Cc7TfLuFrD+WDBatBd5jZ+SoHccVrLR/4L8jeodo5FPW05A+9gnKXEXsTxY4LOUC +9bS4e1kCgYAqVXZh63JsMwoaxCYmQ66eJojKa47VNrOeIZDZvd2BPVf30glBOT41 +gBoDG3WMPZoQj9pb7uMcrnvs4APj2FIhMU8U15LcPAj59cD6S6rWnAxO8NFK7HQG +4Jxg3JNNf8ErQoCHb1B3oVdXJkmbJkARoDpBKmTCgKtP8ADYLmVPQw== +-----END RSA PRIVATE KEY----- + _end_of_pem_ + + TEST_KEY_DSA256 = OpenSSL::PKey::DSA.new <<-_end_of_pem_ +-----BEGIN DSA PRIVATE KEY----- +MIH3AgEAAkEAhk2libbY2a8y2Pt21+YPYGZeW6wzaW2yfj5oiClXro9XMR7XWLkE +9B7XxLNFCS2gmCCdMsMW1HulaHtLFQmB2wIVAM43JZrcgpu6ajZ01VkLc93gu/Ed +AkAOhujZrrKV5CzBKutKLb0GVyVWmdC7InoNSMZEeGU72rT96IjM59YzoqmD0pGM +3I1o4cGqg1D1DfM1rQlnN1eSAkBq6xXfEDwJ1mLNxF6q8Zm/ugFYWR5xcX/3wFiT +b4+EjHP/DbNh9Vm5wcfnDBJ1zKvrMEf2xqngYdrV/3CiGJeKAhRvL57QvJZcQGvn +ISNX5cMzFHRW3Q== +-----END DSA PRIVATE KEY----- + _end_of_pem_ + + TEST_KEY_DSA512 = OpenSSL::PKey::DSA.new <<-_end_of_pem_ +-----BEGIN DSA PRIVATE KEY----- +MIH4AgEAAkEA5lB4GvEwjrsMlGDqGsxrbqeFRh6o9OWt6FgTYiEEHaOYhkIxv0Ok +RZPDNwOG997mDjBnvDJ1i56OmS3MbTnovwIVAJgub/aDrSDB4DZGH7UyarcaGy6D +AkB9HdFw/3td8K4l1FZHv7TCZeJ3ZLb7dF3TWoGUP003RCqoji3/lHdKoVdTQNuR +S/m6DlCwhjRjiQ/lBRgCLCcaAkEAjN891JBjzpMj4bWgsACmMggFf57DS0Ti+5++ +Q1VB8qkJN7rA7/2HrCR3gTsWNb1YhAsnFsoeRscC+LxXoXi9OAIUBG98h4tilg6S +55jreJD3Se3slps= +-----END DSA PRIVATE KEY----- + _end_of_pem_ + +if defined?(OpenSSL::PKey::EC) + + TEST_KEY_EC_P256V1 = OpenSSL::PKey::EC.new <<-_end_of_pem_ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIID49FDqcf1O1eO8saTgG70UbXQw9Fqwseliit2aWhH1oAoGCCqGSM49 +AwEHoUQDQgAEFglk2c+oVUIKQ64eZG9bhLNPWB7lSZ/ArK41eGy5wAzU/0G51Xtt +CeBUl+MahZtn9fO1JKdF4qJmS39dXnpENg== +-----END EC PRIVATE KEY----- + _end_of_pem_ + +end + + TEST_KEY_DH512_PUB = OpenSSL::PKey::DH.new <<-_end_of_pem_ +-----BEGIN DH PARAMETERS----- +MEYCQQDmWXGPqk76sKw/edIOdhAQD4XzjJ+AR/PTk2qzaGs+u4oND2yU5D2NN4wr +aPgwHyJBiK1/ebK3tYcrSKrOoRyrAgEC +-----END DH PARAMETERS----- + _end_of_pem_ + + TEST_KEY_DH1024 = OpenSSL::PKey::DH.new <<-_end_of_pem_ +-----BEGIN DH PARAMETERS----- +MIGHAoGBAKnKQ8MNK6nYZzLrrcuTsLxuiJGXoOO5gT+tljOTbHBuiktdMTITzIY0 +pFxIvjG05D7HoBZQfrR0c92NGWPkAiCkhQKB8JCbPVzwNLDy6DZ0pmofDKrEsYHG +AQjjxMXhwULlmuR/K+WwlaZPiLIBYalLAZQ7ZbOPeVkJ8ePao0eLAgEC +-----END DH PARAMETERS----- + _end_of_pem_ + + TEST_KEY_DH1024.priv_key = OpenSSL::BN.new("48561834C67E65FFD2A9B47F41E5E78FDC95C387428FDB1E4B0188B64D1643C3A8D3455B945B7E8C4D166010C7C2CE23BFB9BEF43D0348FE7FA5284B0225E7FE1537546D114E3D8A4411B9B9351AB451E1A358F50ED61B1F00DA29336EEBBD649980AC86D76AF8BBB065298C2052672EEF3EF13AB47A15275FC2836F3AC74CEA", 16) + + DSA_SIGNATURE_DIGEST = OpenSSL::OPENSSL_VERSION_NUMBER > 0x10000000 ? + OpenSSL::Digest::SHA1 : + OpenSSL::Digest::DSS1 + + module_function + + def issue_cert(dn, key, serial, not_before, not_after, extensions, + issuer, issuer_key, digest) + cert = OpenSSL::X509::Certificate.new + issuer = cert unless issuer + issuer_key = key unless issuer_key + cert.version = 2 + cert.serial = serial + cert.subject = dn + cert.issuer = issuer.subject + cert.public_key = key.public_key + cert.not_before = not_before + cert.not_after = not_after + ef = OpenSSL::X509::ExtensionFactory.new + ef.subject_certificate = cert + ef.issuer_certificate = issuer + extensions.each{|oid, value, critical| + cert.add_extension(ef.create_extension(oid, value, critical)) + } + cert.sign(issuer_key, digest) + cert + end + + def issue_crl(revoke_info, serial, lastup, nextup, extensions, + issuer, issuer_key, digest) + crl = OpenSSL::X509::CRL.new + crl.issuer = issuer.subject + crl.version = 1 + crl.last_update = lastup + crl.next_update = nextup + revoke_info.each{|rserial, time, reason_code| + revoked = OpenSSL::X509::Revoked.new + revoked.serial = rserial + revoked.time = time + enum = OpenSSL::ASN1::Enumerated(reason_code) + ext = OpenSSL::X509::Extension.new("CRLReason", enum) + revoked.add_extension(ext) + crl.add_revoked(revoked) + } + ef = OpenSSL::X509::ExtensionFactory.new + ef.issuer_certificate = issuer + ef.crl = crl + crlnum = OpenSSL::ASN1::Integer(serial) + crl.add_extension(OpenSSL::X509::Extension.new("crlNumber", crlnum)) + extensions.each{|oid, value, critical| + crl.add_extension(ef.create_extension(oid, value, critical)) + } + crl.sign(issuer_key, digest) + crl + end + + def get_subject_key_id(cert) + 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 + end + + def silent + begin + back, $VERBOSE = $VERBOSE, nil + yield + ensure + $VERBOSE = back + end + end + + class OpenSSL::SSLTestCase < Test::Unit::TestCase + RUBY = EnvUtil.rubybin + ITERATIONS = ($0 == __FILE__) ? 100 : 10 + + def setup + @ca_key = OpenSSL::TestUtils::TEST_KEY_RSA2048 + @svr_key = OpenSSL::TestUtils::TEST_KEY_RSA1024 + @cli_key = OpenSSL::TestUtils::TEST_KEY_DSA256 + @ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA") + @svr = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=localhost") + @cli = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=localhost") + now = Time.at(Time.now.to_i) + ca_exts = [ + ["basicConstraints","CA:TRUE",true], + ["keyUsage","cRLSign,keyCertSign",true], + ] + ee_exts = [ + ["keyUsage","keyEncipherment,digitalSignature",true], + ] + @ca_cert = issue_cert(@ca, @ca_key, 1, now, now+3600, ca_exts, nil, nil, OpenSSL::Digest::SHA1.new) + @svr_cert = issue_cert(@svr, @svr_key, 2, now, now+1800, ee_exts, @ca_cert, @ca_key, OpenSSL::Digest::SHA1.new) + @cli_cert = issue_cert(@cli, @cli_key, 3, now, now+1800, ee_exts, @ca_cert, @ca_key, OpenSSL::Digest::SHA1.new) + @server = nil + end + + def teardown + end + + def issue_cert(*arg) + OpenSSL::TestUtils.issue_cert(*arg) + end + + def issue_crl(*arg) + OpenSSL::TestUtils.issue_crl(*arg) + end + + def readwrite_loop(ctx, ssl) + while line = ssl.gets + if line =~ /^STARTTLS$/ + ssl.accept + next + end + ssl.write(line) + end + rescue OpenSSL::SSL::SSLError + rescue IOError + ensure + ssl.close rescue nil + end + + def server_loop(ctx, ssls, stop_pipe_r, ignore_listener_error, server_proc, threads) + loop do + ssl = nil + begin + readable, = IO.select([ssls, stop_pipe_r]) + if readable.include? stop_pipe_r + return + end + ssl = ssls.accept + rescue OpenSSL::SSL::SSLError + if ignore_listener_error + retry + else + raise + end + end + + th = Thread.start do + server_proc.call(ctx, ssl) + end + threads << th + end + rescue Errno::EBADF, IOError, Errno::EINVAL, Errno::ECONNABORTED, Errno::ENOTSOCK, Errno::ECONNRESET + if !ignore_listener_error + raise + end + end + + def start_server(verify_mode, start_immediately, args = {}, &block) + IO.pipe {|stop_pipe_r, stop_pipe_w| + ctx_proc = args[:ctx_proc] + server_proc = args[:server_proc] + ignore_listener_error = args.fetch(:ignore_listener_error, false) + use_anon_cipher = args.fetch(:use_anon_cipher, false) + server_proc ||= method(:readwrite_loop) + + store = OpenSSL::X509::Store.new + store.add_cert(@ca_cert) + store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT + ctx = OpenSSL::SSL::SSLContext.new + ctx.ciphers = "ADH-AES256-GCM-SHA384" if use_anon_cipher + ctx.cert_store = store + #ctx.extra_chain_cert = [ ca_cert ] + ctx.cert = @svr_cert + ctx.key = @svr_key + ctx.tmp_dh_callback = proc { OpenSSL::TestUtils::TEST_KEY_DH1024 } + ctx.verify_mode = verify_mode + ctx_proc.call(ctx) if ctx_proc + + Socket.do_not_reverse_lookup = true + tcps = nil + tcps = TCPServer.new("127.0.0.1", 0) + port = tcps.connect_address.ip_port + + ssls = OpenSSL::SSL::SSLServer.new(tcps, ctx) + ssls.start_immediately = start_immediately + + threads = [] + begin + server = Thread.new do + begin + server_loop(ctx, ssls, stop_pipe_r, ignore_listener_error, server_proc, threads) + ensure + tcps.close + end + end + threads.unshift server + + $stderr.printf("SSL server started: pid=%d port=%d\n", $$, port) if $DEBUG + + client = Thread.new do + begin + block.call(server, port.to_i) + ensure + stop_pipe_w.close + end + end + threads.unshift client + ensure + assert_join_threads(threads) + end + } + end + + def starttls(ssl) + ssl.puts("STARTTLS") + sleep 1 # When this line is eliminated, process on Cygwin blocks + # forever at ssl.connect. But I don't know why it does. + ssl.connect + end + end + +end if defined?(OpenSSL::OPENSSL_LIBRARY_VERSION) and + /\AOpenSSL +0\./ !~ OpenSSL::OPENSSL_LIBRARY_VERSION |