From ce773fb21c1845039b4091c1c4a17a0728a7455b Mon Sep 17 00:00:00 2001 From: kares Date: Mon, 29 Apr 2024 09:56:45 +0200 Subject: [PATCH] [fix] ASN1 primitive tagging (encoding) part (#122) --- src/main/java/org/jruby/ext/openssl/ASN1.java | 62 +++++++++++-------- src/test/ruby/test_asn1.rb | 37 ++++------- 2 files changed, 50 insertions(+), 49 deletions(-) diff --git a/src/main/java/org/jruby/ext/openssl/ASN1.java b/src/main/java/org/jruby/ext/openssl/ASN1.java index a387a542..2444d9e9 100644 --- a/src/main/java/org/jruby/ext/openssl/ASN1.java +++ b/src/main/java/org/jruby/ext/openssl/ASN1.java @@ -1336,6 +1336,10 @@ boolean isEOC() { return "EndOfContent".equals( getClassBaseName() ); } + IRubyObject tagging() { + return getInstanceVariable("@tagging"); + } + boolean isExplicitTagging() { return ! isImplicitTagging(); } boolean isImplicitTagging() { return true; } @@ -1364,19 +1368,24 @@ ASN1Encodable toASN1(final ThreadContext context) { final ASN1TaggedObject toASN1TaggedObject(final ThreadContext context) { final int tag = getTag(context); - final IRubyObject val = callMethod(context, "value"); - if ( val instanceof RubyArray ) { - final RubyArray arr = (RubyArray) val; + final IRubyObject value = callMethod(context, "value"); + if (value instanceof RubyArray) { + final RubyArray arr = (RubyArray) value; assert ! arr.isEmpty(); ASN1EncodableVector vec = new ASN1EncodableVector(); - for ( final IRubyObject obj : arr.toJavaArray() ) { + for (final IRubyObject obj : arr.toJavaArray()) { ASN1Encodable data = ((ASN1Data) obj).toASN1(context); - if ( data == null ) break; vec.add( data ); + if ( data == null ) break; + vec.add( data ); } return new DERTaggedObject(isExplicitTagging(), tag, new DERSequence(vec)); } - return new DERTaggedObject(isExplicitTagging(), tag, ((ASN1Data) val).toASN1(context)); + + if (!(value instanceof ASN1Data)) { + throw new UnsupportedOperationException("toASN1 " + inspect() + " value: " + value.inspect() + " (" + value.getMetaClass() + ")"); + } + return new DERTaggedObject(isExplicitTagging(), tag, ((ASN1Data) value).toASN1(context)); } @JRubyMethod @@ -1559,9 +1568,13 @@ static void initializeImpl(final ThreadContext context, self.setInstanceVariable("@indefinite_length", runtime.getFalse()); } + boolean isTagged() { + return !tagging().isNil(); + } + @Override boolean isExplicitTagging() { - return "EXPLICIT".equals( getInstanceVariable("@tagging").toString() ); + return "EXPLICIT".equals( tagging().toString() ); } @Override @@ -1581,19 +1594,16 @@ byte[] toDER(final ThreadContext context) throws IOException { return toASN1(context).toASN1Primitive().getEncoded(ASN1Encoding.DER); } - /* - private static final Class DERBooleanClass; - static { - Class klass; - try { - klass = Class.forName("org.bouncycastle.asn1.DERBoolean"); - } - catch(ClassNotFoundException e) { klass = null; } - DERBooleanClass = klass; - } */ - @Override ASN1Encodable toASN1(final ThreadContext context) { + final ASN1Encodable primitive = toASN1Primitive(context); + if (isTagged()) { + return new DERTaggedObject(isExplicitTagging(), getTagClass(context), getTag(context), primitive); + } + return primitive; + } + + private ASN1Encodable toASN1Primitive(final ThreadContext context) { Class type = typeClass( getMetaClass() ); if ( type == null ) { final int tag = getTag(context); @@ -1691,9 +1701,9 @@ ASN1Encodable toASN1(final ThreadContext context) { } if (isDebug(context.runtime)) { - debug(this + " toASN1() could not handle class " + getMetaClass() + " and value " + val.inspect() + " (" + val.getClass().getName() + ")"); + debug(this + " toASN1() could not handle class " + getMetaClass() + " and value: " + val.inspect() + " (" + val.getMetaClass() + ")"); } - throw context.runtime.newNotImplementedError("unimplemented method called: OpenSSL::ASN1Data#toASN1 (" + type + ")"); + throw new UnsupportedOperationException("OpenSSL::ASN1Data#toASN1 (" + type + ") not implemented"); // should not happen } private static char[] toBMPChars(final ByteList string) { @@ -1785,15 +1795,18 @@ private boolean isInfiniteLength() { return getInstanceVariable("@indefinite_length").isTrue(); } + private boolean isTagged() { + return !tagging().isNil(); + } + @Override boolean isExplicitTagging() { - IRubyObject tagging = getInstanceVariable("@tagging"); - return tagging.isNil() || "EXPLICIT".equals( tagging.toString() ); + return "EXPLICIT".equals( tagging().toString() ); // nil.toString() == "" } @Override boolean isImplicitTagging() { - return "IMPLICIT".equals( getInstanceVariable("@tagging").toString() ); + return "IMPLICIT".equals( tagging().toString() ); } @Override @@ -1940,8 +1953,7 @@ public ASN1Primitive toASN1Primitive() { } - private static boolean addEntry(final ThreadContext context, - final ASN1EncodableVector vec, final IRubyObject entry) { + private static boolean addEntry(final ThreadContext context, final ASN1EncodableVector vec, final IRubyObject entry) { try { if ( entry instanceof Constructive ) { final Constructive constructive = (Constructive) entry; diff --git a/src/test/ruby/test_asn1.rb b/src/test/ruby/test_asn1.rb index d0f12531..334fbe14 100644 --- a/src/test/ruby/test_asn1.rb +++ b/src/test/ruby/test_asn1.rb @@ -608,35 +608,25 @@ def test_constructive end def test_prim_explicit_tagging - # TODO: Import Issue - # <"\xA0\x03\x04\x01a"> expected but was <"\x04\x01a"> - #oct_str = OpenSSL::ASN1::OctetString.new("a", 0, :EXPLICIT) - #encode_test B(%w{ A0 03 04 01 61 }), oct_str - # <"a\x03\x04\x01a"> expected but was <"\x04\x01a"> + oct_str = OpenSSL::ASN1::OctetString.new("a", 0, :EXPLICIT) + encode_test B(%w{ A0 03 04 01 61 }), oct_str oct_str2 = OpenSSL::ASN1::OctetString.new("a", 1, :EXPLICIT, :APPLICATION) - #encode_test B(%w{ 61 03 04 01 61 }), oct_str2 + encode_test B(%w{ 61 03 04 01 61 }), oct_str2 decoded = OpenSSL::ASN1.decode(oct_str2.to_der) - # <:APPLICATION> expected but was <:UNIVERSAL> - #assert_equal :APPLICATION, decoded.tag_class - # <1> expected but was <4> - #assert_equal 1, decoded.tag + assert_equal :APPLICATION, decoded.tag_class + assert_equal 1, decoded.tag assert_equal 1, decoded.value.size - #inner = decoded.value[0] - # expected but was - #assert_equal OpenSSL::ASN1::OctetString, inner.class - # NoMethodError: undefined method `value' for "a":String - #assert_equal B(%w{ 61 }), inner.value + inner = decoded.value[0] + assert_equal OpenSSL::ASN1::OctetString, inner.class + assert_equal B(%w{ 61 }), inner.value end def test_prim_implicit_tagging - #int = OpenSSL::ASN1::Integer.new(1, 0, :IMPLICIT) - # TODO: Import Issue - # <"\x80\x01\x01"> expected but was <"\x02\x01\x01"> - #encode_test B(%w{ 80 01 01 }), int - #int2 = OpenSSL::ASN1::Integer.new(1, 1, :IMPLICIT, :APPLICATION) - # <"A\x01\x01"> expected but was <"\x02\x01\x01"> - #encode_test B(%w{ 41 01 01 }), int2 + int = OpenSSL::ASN1::Integer.new(1, 0, :IMPLICIT) + encode_test B(%w{ 80 01 01 }), int + int2 = OpenSSL::ASN1::Integer.new(1, 1, :IMPLICIT, :APPLICATION) + encode_test B(%w{ 41 01 01 }), int2 #decoded = OpenSSL::ASN1.decode(int2.to_der) # <:APPLICATION> expected but was <:UNIVERSAL> #assert_equal :APPLICATION, decoded.tag_class @@ -907,8 +897,7 @@ def test_raw_constructive nil, :UNIVERSAL ) assert_equal false, inf_octets.infinite_length - # The real value of inf_octets is "\x01\x02", i.e. the concatenation - # of partial1 and partial2 + # The real value of inf_octets is "\x01\x02", i.e. the concatenation of partial1 and partial2 inf_octets.infinite_length = true assert_equal true, inf_octets.infinite_length