Skip to content

Commit

Permalink
[fix] ASN1 primitive tagging (encoding) part (#122)
Browse files Browse the repository at this point in the history
  • Loading branch information
kares committed May 5, 2024
1 parent eae9f56 commit ce773fb
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 49 deletions.
62 changes: 37 additions & 25 deletions src/main/java/org/jruby/ext/openssl/ASN1.java
Original file line number Diff line number Diff line change
Expand Up @@ -1336,6 +1336,10 @@ boolean isEOC() {
return "EndOfContent".equals( getClassBaseName() );
}

IRubyObject tagging() {
return getInstanceVariable("@tagging");
}

boolean isExplicitTagging() { return ! isImplicitTagging(); }

boolean isImplicitTagging() { return true; }
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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<? extends ASN1Encodable> type = typeClass( getMetaClass() );
if ( type == null ) {
final int tag = getTag(context);
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down
37 changes: 13 additions & 24 deletions src/test/ruby/test_asn1.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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]
# <OpenSSL::ASN1::OctetString> expected but was <String>
#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
Expand Down Expand Up @@ -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

Expand Down

0 comments on commit ce773fb

Please sign in to comment.