From f13718360605a45049bfa6e1117f2fab42cd6fff Mon Sep 17 00:00:00 2001 From: Justin Stoller Date: Thu, 11 Apr 2024 11:48:00 -0700 Subject: [PATCH] wip, import upstream asn.1 tests --- src/test/ruby/test_asn1.rb | 385 ++++++++++++++++++++++++++++++++++++- 1 file changed, 382 insertions(+), 3 deletions(-) diff --git a/src/test/ruby/test_asn1.rb b/src/test/ruby/test_asn1.rb index b9b2cf83..4976a6fc 100644 --- a/src/test/ruby/test_asn1.rb +++ b/src/test/ruby/test_asn1.rb @@ -3,8 +3,212 @@ class TestASN1 < TestCase + def test_decode_x509_certificate + subj = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=TestCA") + key = Fixtures.pkey("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.new('SHA256') + cert = issue_cert(subj, key, s, exts, nil, nil, digest: dgst, not_before: now, not_after: now+3600) + + + 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.11", 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(cert.public_key.n, spkey.value[0].value) + assert_equal(OpenSSL::ASN1::Integer, spkey.value[1].class) + assert_equal(cert.public_key.e, 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] # subjectKeyIdentifier + 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.new('SHA1') + 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.new('SHA256'), tbs_cert.to_der) + # TODO: Import Issue + # Fails from import with: + # <"\x9E\x19\xE3oI\xC0\x85n$\xF4\xCE\n" + + # "\x87\xA6\xFCu\x1AQbti\xB1\xE0o\xD5\x18?}\xFAEq\xC8\xEF\x17K\xCA|d\xDEu;%\xFB\xA1\xD4\x14\x04\x837\x90E\xAC.p=\x14\xA7\x8B\xAE\xC4\xBE-\x99\xBAx\xB8\x9B+\x87\x80\e\xA1\x17{\fV\xA0\xCF\xA60b\xDFc\x06\x81\xFB\xD3:\x01\x17\x8F\xC5[\xE0m\xAB,\xD3D\xBE\xA0\xA5\x8C\x1E\xCB\x18!\xBF&\x17\xA6\xCF\x8A\xDD\xF1\xB4\x1C\x89\xD8t\xAEz\x95\xC6\xE4\x9E\xA3\xA4"> + # expected but was + # <",\xF4.\x1CH\xD5y\xFE\x05~\xB2\x05\xB7\xCB{2VwdZ\xD7\r^\x87AF\x16\x1A\xC8+U\xA1\xCA'\x1Ca\xCE}\xD2H + #assert_equal(cululated_sig, sig_val.value) + end + def test_encode_boolean - encode_decode_test(OpenSSL::ASN1::Boolean, [true, false]) + encode_decode_test1(OpenSSL::ASN1::Boolean, [true, false]) + end + + def test_end_of_content + # TODO: Import Issue + # raises OpenSSL::ASN1::ASN1Error: unexpected end-of-contents marker + #encode_decode_test B(%w{ 00 00 }), OpenSSL::ASN1::EndOfContent.new + assert_raise(OpenSSL::ASN1::ASN1Error) { + OpenSSL::ASN1.decode(B(%w{ 00 01 00 })) + } end def test_encode_integer @@ -21,6 +225,13 @@ def test_encode_integer assert_equal i, OpenSSL::ASN1.decode(ai.to_der).value end + def test_enumerated + encode_decode_test B(%w{ 0A 01 00 }), OpenSSL::ASN1::Enumerated.new(0) + encode_decode_test B(%w{ 0A 01 48 }), OpenSSL::ASN1::Enumerated.new(72) + encode_decode_test B(%w{ 0A 02 00 80 }), OpenSSL::ASN1::Enumerated.new(128) + encode_decode_test B(%w{ 0A 09 01 00 00 00 00 00 00 00 00 }), OpenSSL::ASN1::Enumerated.new(2 ** 64) + end + def test_encode_nested_sequence_to_der data_sequence = ::OpenSSL::ASN1::Sequence([::OpenSSL::ASN1::Integer(0)]) asn1 = ::OpenSSL::ASN1::Sequence(data_sequence) @@ -33,6 +244,16 @@ def test_encode_nested_set_to_der assert_equal "1\x03\x02\x01\x00", asn1.to_der end + def test_null + # TODO: Import Issue -- Is this related to the comment below in test_encode_all? + # TypeError: nil value + # src/test/ruby/test_asn1.rb:851:in `encode_test' + #encode_decode_test B(%w{ 05 00 }), OpenSSL::ASN1::Null.new(nil) + assert_raise(OpenSSL::ASN1::ASN1Error) { + OpenSSL::ASN1.decode(B(%w{ 05 01 00 })) + } + end + def test_encode_nil #Primitives raise TypeError, Constructives NoMethodError @@ -40,6 +261,63 @@ def test_encode_nil assert_raise(TypeError) { OpenSSL::ASN1::Boolean.new(nil).to_der } end + def test_object_identifier + encode_decode_test B(%w{ 06 01 00 }), OpenSSL::ASN1::ObjectId.new("0.0".b) + encode_decode_test B(%w{ 06 01 28 }), OpenSSL::ASN1::ObjectId.new("1.0".b) + encode_decode_test B(%w{ 06 03 88 37 03 }), OpenSSL::ASN1::ObjectId.new("2.999.3".b) + encode_decode_test B(%w{ 06 05 2A 22 83 BB 55 }), OpenSSL::ASN1::ObjectId.new("1.2.34.56789".b) + obj = encode_decode_test B(%w{ 06 09 60 86 48 01 65 03 04 02 01 }), OpenSSL::ASN1::ObjectId.new("sha256") + assert_equal "2.16.840.1.101.3.4.2.1", obj.oid + assert_equal "SHA256", obj.sn + assert_equal "sha256", obj.ln + # TODO: Import Issue + # Fails with: expected but was ) + #assert_raise(OpenSSL::ASN1::ASN1Error) { + # OpenSSL::ASN1.decode(B(%w{ 06 00 })) + #} + #assert_raise(OpenSSL::ASN1::ASN1Error) { + # OpenSSL::ASN1.decode(B(%w{ 06 01 80 })) + #} + # expected but was ) + #assert_raise(OpenSSL::ASN1::ASN1Error) { OpenSSL::ASN1::ObjectId.new("3.0".b).to_der } + # exception was expected but none was thrown. + #assert_raise(OpenSSL::ASN1::ASN1Error) { OpenSSL::ASN1::ObjectId.new("0.40".b).to_der } + + oid = (0...100).to_a.join(".").b + obj = OpenSSL::ASN1::ObjectId.new(oid) + assert_equal oid, obj.oid + + aki = [ + OpenSSL::ASN1::ObjectId.new("authorityKeyIdentifier"), + OpenSSL::ASN1::ObjectId.new("X509v3 Authority Key Identifier"), + OpenSSL::ASN1::ObjectId.new("2.5.29.35") + ] + + ski = [ + OpenSSL::ASN1::ObjectId.new("subjectKeyIdentifier"), + OpenSSL::ASN1::ObjectId.new("X509v3 Subject Key Identifier"), + OpenSSL::ASN1::ObjectId.new("2.5.29.14") + ] + + aki.each do |a| + # TODO: Import Issue + # None of these are equivalent to each other + #aki.each do |b| + # assert a == b + #end + + ski.each do |b| + refute a == b + end + end + + # TODO: Import Issue + # exception was expected but none was thrown. + #assert_raise(TypeError) { + # OpenSSL::ASN1::ObjectId.new("authorityKeyIdentifier") == nil + #} + end + def test_instantiate # nothing shall raise : OpenSSL::ASN1::Null.new(nil) @@ -472,6 +750,33 @@ def test_decode #assert_equal calulated_sig, sig_val.value end + # This is from the upstream MRI tests, might be superseded by `test_bit_string_infinite_length`? + def test_bitstring + # TODO: Import Issue + # fails expected but was <0> + #encode_decode_test B(%w{ 03 01 00 }), OpenSSL::ASN1::BitString.new(B(%w{})) + # TODO: Import Issue + # fails with expected but was <0> + #encode_decode_test B(%w{ 03 02 00 01 }), OpenSSL::ASN1::BitString.new(B(%w{ 01 })) + obj = OpenSSL::ASN1::BitString.new(B(%w{ F0 })) + obj.unused_bits = 4 + encode_decode_test B(%w{ 03 02 04 F0 }), obj + assert_raise(OpenSSL::ASN1::ASN1Error) { + OpenSSL::ASN1.decode(B(%w{ 03 00 })) + } + assert_raise(OpenSSL::ASN1::ASN1Error) { + OpenSSL::ASN1.decode(B(%w{ 03 03 08 FF 00 })) + } + + # TODO: Import Issue + # exception was expected but none was thrown. + #assert_raise(OpenSSL::ASN1::ASN1Error) { + # obj = OpenSSL::ASN1::BitString.new(B(%w{ FF FF })) + # obj.unused_bits = 8 + # obj.to_der + #} + end + def test_bit_string_infinite_length begin content = [ OpenSSL::ASN1::BitString.new("\x01"), OpenSSL::ASN1::EndOfContent.new() ] @@ -480,11 +785,45 @@ def test_bit_string_infinite_length expected = %w{ 23 80 03 02 00 01 00 00 } raw = [expected.join('')].pack('H*') assert_equal raw, cons.to_der - # TODO for now we can no decode our own sh*t : + # TODO for now we can not decode our own sh*t : #assert_equal raw, OpenSSL::ASN1.decode(raw).to_der end end + def test_string_basic + test = -> (tag, klass) { + encode_decode_test tag.chr + B(%w{ 00 }), klass.new(B(%w{})) + encode_decode_test tag.chr + B(%w{ 02 00 01 }), klass.new(B(%w{ 00 01 })) + } + test.(4, OpenSSL::ASN1::OctetString) + test.(12, OpenSSL::ASN1::UTF8String) + # TODO: Import Issue + # The below tests cause NPEs in the first call to `encode_test` above + # org.jruby.ext.openssl.ASN1$Primitive.toDER(ASN1.java:1610) + # org.jruby.ext.openssl.ASN1$ASN1Data.to_der(ASN1.java:1414) + # org.jruby.ext.openssl.ASN1$Primitive.to_der(ASN1.java:1522) + # org.jruby.ext.openssl.ASN1$Primitive$INVOKER$i$0$0$to_der.call(ASN1$Primitive$INVOKER$i$0$0$to_der.gen) + #test.(18, OpenSSL::ASN1::NumericString) + #test.(19, OpenSSL::ASN1::PrintableString) + #test.(20, OpenSSL::ASN1::T61String) + #test.(21, OpenSSL::ASN1::VideotexString) + test.(22, OpenSSL::ASN1::IA5String) + # See above + #test.(25, OpenSSL::ASN1::GraphicString) + #test.(26, OpenSSL::ASN1::ISO64String) + #test.(27, OpenSSL::ASN1::GeneralString) + + # TODO: Import Issue + # This fails with: + # <""> expected but was <"#1C00"> + #test.(28, OpenSSL::ASN1::UniversalString) + + # TODO: Import Issue + # This fails with: + # <"\x1E\x02\x00\x01">(US-ASCII) expected but was <"\x1E\x04\x00\x00\x00\x01">(ASCII-8BIT) + #test.(30, OpenSSL::ASN1::BMPString) + end + def test_decode_all expected = %w{ 02 01 01 02 01 02 02 01 03 } raw = [expected.join('')].pack('H*') @@ -543,13 +882,53 @@ def test_encode_der_integer_wrapped private + def B(ary) + [ary.join].pack("H*") + end + + def assert_asn1_equal(a, b) + assert_equal a.class, b.class + assert_equal a.tag, b.tag + assert_equal a.tag_class, b.tag_class + assert_equal a.indefinite_length, b.indefinite_length + assert_equal a.unused_bits, b.unused_bits if a.respond_to?(:unused_bits) + case a.value + when Array + a.value.each_with_index { |ai, i| + assert_asn1_equal ai, b.value[i] + } + else + if OpenSSL::ASN1::ObjectId === a + assert_equal a.oid, b.oid + else + assert_equal a.value, b.value + end + end + assert_equal a.to_der, b.to_der + end + + def encode_test(der, obj) + assert_equal der, obj.to_der + end + + def decode_test(der, obj) + decoded = OpenSSL::ASN1.decode(der) + assert_asn1_equal obj, decoded + decoded + end + + def encode_decode_test(der, obj) + encode_test(der, obj) + decode_test(der, obj) + end + def assert_universal(tag, asn1, inf_len=false) assert_equal(tag, asn1.tag) assert_equal(:UNIVERSAL, asn1.tag_class) assert_equal(inf_len, asn1.infinite_length) end - def encode_decode_test(type, values) + def encode_decode_test1(type, values) values.each do |v| assert_equal(v, OpenSSL::ASN1.decode(type.new(v).to_der).value) end