diff --git a/src/main/java/org/jruby/ext/openssl/ASN1.java b/src/main/java/org/jruby/ext/openssl/ASN1.java index 03a19e81..3e7417e4 100644 --- a/src/main/java/org/jruby/ext/openssl/ASN1.java +++ b/src/main/java/org/jruby/ext/openssl/ASN1.java @@ -41,7 +41,6 @@ import java.util.WeakHashMap; import org.bouncycastle.asn1.*; - import org.jruby.Ruby; import org.jruby.RubyArray; import org.jruby.RubyBignum; @@ -704,6 +703,10 @@ public static void createASN1(final Ruby runtime, final RubyModule OpenSSL, fina Constructive.addReadWriteAttribute(context, "tagging"); Constructive.defineAnnotatedMethods(Constructive.class); + final ObjectAllocator eocAllocator = EndOfContent.ALLOCATOR; + RubyClass EndOfContent = ASN1.defineClassUnder("EndOfContent", _ASN1Data, eocAllocator); + EndOfContent.defineAnnotatedMethods(EndOfContent.class); + ASN1.defineClassUnder("Boolean", Primitive, primitiveAllocator); // OpenSSL::ASN1::Boolean <=> value is a Boolean ASN1.defineClassUnder("Integer", Primitive, primitiveAllocator); // OpenSSL::ASN1::Integer <=> value is a Number ASN1.defineClassUnder("Null", Primitive, primitiveAllocator); // OpenSSL::ASN1::Null <=> value is always nil @@ -728,9 +731,6 @@ public static void createASN1(final Ruby runtime, final RubyModule OpenSSL, fina ASN1.defineClassUnder("UTCTime", Primitive, primitiveAllocator); // OpenSSL::ASN1::UTCTime <=> value is a Time ASN1.defineClassUnder("GeneralizedTime", Primitive, primitiveAllocator); // OpenSSL::ASN1::GeneralizedTime <=> value is a Time - ASN1.defineClassUnder("EndOfContent", _ASN1Data, asn1DataAllocator). // OpenSSL::ASN1::EndOfContent <=> value is always nil - defineAnnotatedMethods(EndOfContent.class); - ASN1.defineClassUnder("ObjectId", Primitive, primitiveAllocator). defineAnnotatedMethods(ObjectId.class); @@ -1166,19 +1166,62 @@ private BytesInputStream(final ByteList bytes) { private static IRubyObject decodeImpl(final ThreadContext context, final RubyModule ASN1, final BytesInputStream in) throws IOException, IllegalArgumentException { + final byte[] asn1 = in.bytes(); + int offset = in.offset(); + final int tag = asn1[offset] & 0xFF; + + if ( ( tag & BERTags.CONSTRUCTED ) == 0 ) { + return decodeObject(context, ASN1, readObject(in)); + } + // NOTE: need to handle OpenSSL::ASN1::Constructive wrapping by hand : - final Integer tag = getConstructiveTag(in.bytes(), in.offset()); - IRubyObject decoded = decodeObject(context, ASN1, readObject( in )); - if ( tag != null ) { // OpenSSL::ASN1::Constructive.new( arg ) : - if ( tag.intValue() == SEQUENCE ) { - //type = "Sequence"; // got a OpenSSL::ASN1::Sequence already : - return Constructive.setInfiniteLength(context, decoded); + int tagNo = tag & 0x1f; + if (tagNo == 0x1f) + { + tagNo = 0; + int b = asn1[ ++offset ]; + + // X.690-0207 8.1.2.4.2 + // "c) bits 7 to 1 of the first subsequent octet shall not all be zero." + if ((b & 0x7f) == 0) // Note: -1 will pass + { + throw new IOException("corrupted stream - invalid high tag number found"); + } + + while ((b >= 0) && ((b & 0x80) != 0)) + { + tagNo |= (b & 0x7f); + tagNo <<= 7; + b = asn1[ ++offset ]; + } + + if (b < 0) + { + throw new IOException("EOF found inside tag value."); } - if ( tag.intValue() == SET ) { - //type = "Set"; // got a OpenSSL::ASN1::Set already : - return Constructive.setInfiniteLength(context, decoded); + + tagNo |= (b & 0x7f); + } + final int length = asn1[ ++offset ] & 0xFF; + final boolean isIndefiniteLength = length == 0x80; + IRubyObject decoded; + + decoded = decodeObject(context, ASN1, readObject(in)); + + final boolean isUniversal = ((ASN1Data) decoded).isUniversal(context); + + if (isIndefiniteLength) { + if (tagNo == BERTags.SEQUENCE || tagNo == BERTags.SET) { + return ASN1Data.setInfiniteLength(context, decoded); + } else if (isUniversal) { + decoded = Constructive.newInfiniteLength(context, context.runtime.newArray(decoded), tagNo); + } else { + if (decoded instanceof ASN1Data) { + return ASN1Data.setInfiniteLength(context, decoded); + } else { + decoded = ASN1Data.newInfiniteLength(context, context.runtime.newArray(decoded), tagNo, ((ASN1Data) decoded).tagClass()); + } } - return Constructive.newInfiniteConstructive(context, "Constructive", context.runtime.newArray(decoded), tag); } return decoded; } @@ -1235,89 +1278,6 @@ private static org.bouncycastle.asn1.ASN1Primitive readObject(final InputStream return new ASN1InputStream(bytes).readObject(); } - // NOTE: BC's ASNInputStream internals "reinvented" a bit : - private static Integer getConstructiveTag(final byte[] asn1, int offset) { - final int tag = asn1[ offset ] & 0xFF; - if ( ( tag & BERTags.CONSTRUCTED ) != 0 ) { // isConstructed - // - // calculate tag number - // - // readTagNumber(asn1, ++offset, tag) : - int tagNo = tag & 0x1f; - // - // with tagged object tag number is bottom 5 bits, or stored at the start of the content - // - if (tagNo == 0x1f) - { - tagNo = 0; - - int b = asn1[ ++offset ]; //s.read(); - - // X.690-0207 8.1.2.4.2 - // "c) bits 7 to 1 of the first subsequent octet shall not all be zero." - if ((b & 0x7f) == 0) // Note: -1 will pass - { - return null; //throw new IOException("corrupted stream - invalid high tag number found"); - } - - while ((b >= 0) && ((b & 0x80) != 0)) - { - tagNo |= (b & 0x7f); - tagNo <<= 7; - b = asn1[ ++offset ]; //s.read(); - } - - if (b < 0) - { - return null; //throw new EOFException("EOF found inside tag value."); - } - - tagNo |= (b & 0x7f); - } - - // - // calculate length - // - final int length = asn1[ ++offset ] & 0xFF; - - if ( length == 0x80 ) { - // return -1; // indefinite-length encoding - } - else { - return null; - } - - if ((tag & BERTags.APPLICATION) != 0) { - //return new BERApplicationSpecificParser(tagNo, sp).getLoadedObject(); - } - - if ((tag & BERTags.TAGGED) != 0) { - //return new BERTaggedObjectParser(true, tagNo, sp).getLoadedObject(); - } - - //System.out.println(" tagNo = 0x" + Integer.toHexString(tagNo)); - // TODO There are other tags that may be constructed (e.g. BIT_STRING) - switch (tagNo) { - case BERTags.SEQUENCE : - //return new BERSequenceParser(sp).getLoadedObject(); - return Integer.valueOf( SEQUENCE ); //return "Sequence"; - case BERTags.SET : - //return new BERSetParser(sp).getLoadedObject(); - return Integer.valueOf( SET ); //return "Set"; - case BERTags.OCTET_STRING : - return Integer.valueOf( OCTET_STRING ); - //return new BEROctetStringParser(sp).getLoadedObject(); - case BERTags.EXTERNAL : - //return new DERExternalParser(sp).getLoadedObject(); - default: - return Integer.valueOf( 0 ); //return "Constructive"; - //throw new IOException("unknown BER object encountered"); - } - } - - return null; - } - public static class ASN1Data extends RubyObject { private static final long serialVersionUID = 6117598347932209839L; @@ -1340,9 +1300,38 @@ public IRubyObject initialize(final ThreadContext context, this.callMethod(context, "tag=", tag); this.callMethod(context, "value=", value); this.callMethod(context, "tag_class=", tag_class); + this.setInstanceVariable("@indefinite_length", context.runtime.getFalse()); return this; } + static ASN1Data newInfiniteLength(final ThreadContext context, + final IRubyObject value, final int defaultTag, final IRubyObject tagClass) { + final Ruby runtime = context.runtime; + + final RubyClass klass = _ASN1(runtime).getClass("ASN1Data"); + final ASN1Data self = new Constructive(runtime, klass); + + ASN1Data.newInfiniteLengthImpl(context, self, value, defaultTag, tagClass); + return self; + } + + static void newInfiniteLengthImpl(final ThreadContext context, final ASN1Data self, final IRubyObject value, final int defaultTag, final IRubyObject tagClass) { + self.setInstanceVariable("@tag", context.runtime.newFixnum(defaultTag)); + self.setInstanceVariable("@value", value); + self.setInstanceVariable("@tag_class", tagClass); + self.setInstanceVariable("@tagging", context.nil); + + setInfiniteLength(context, self); + } + + static ASN1Data setInfiniteLength(final ThreadContext context, final IRubyObject constructive) { + final ASN1Data instance = ((ASN1Data) constructive); + final IRubyObject value = instance.value(context); + value.callMethod(context, "<<", EndOfContent.newInstance(context)); + instance.setInstanceVariable("@indefinite_length", context.runtime.getTrue()); + return instance; + } + private void checkTag(final Ruby runtime, final IRubyObject tag, final IRubyObject tagClass) { if ( ! (tagClass instanceof RubySymbol) ) { throw newASN1Error(runtime, "invalid tag class"); @@ -1352,18 +1341,30 @@ private void checkTag(final Ruby runtime, final IRubyObject tag, final IRubyObje } } - boolean isEOC() { - return "EndOfContent".equals( getClassBaseName() ); + private boolean isConstructive() { + return "Constructive".equals(getMetaClass().getRealClass().getBaseName()); + } + + boolean isInfiniteLength() { + return getInstanceVariable("@indefinite_length").isTrue(); + } + + boolean isEOC(final ThreadContext context) { + return getTag(context) == 0 && isUniversal((context)); } boolean isUniversal(final ThreadContext context) { - return "ASN1Data".equals(getClassBaseName()) && getTagClass(context) == 0; + return getTagClass(context) == BERTags.UNIVERSAL; } IRubyObject tagging() { return getInstanceVariable("@tagging"); } + IRubyObject tagClass() { + return getInstanceVariable("@tag_class"); + } + boolean isExplicitTagging() { return ! isImplicitTagging(); } boolean isImplicitTagging() { return true; } @@ -1420,17 +1421,18 @@ final ASN1TaggedObject toASN1TaggedObject(final ThreadContext context) { } } - if (vec.size() > 0) { - // array of asn1 objects as value - return new DERTaggedObject(isExplicitTagging(), tag, new DERSequence(vec)); + if (values.length() > 0) { + return new DERTaggedObject(isExplicitTagging(), tagClass, tag, new DERGeneralString(values.toString())); + } else { + // array of strings as value (default) + return new DERTaggedObject(isExplicitTagging(), tagClass, tag, new BERSequence(vec)); } - - // array of strings as value (default) - return new DERTaggedObject(isExplicitTagging(), tagClass, tag, - new DERGeneralString(values.toString())); } else if (value instanceof ASN1Data) { return new DERTaggedObject(isExplicitTagging(), tagClass, tag, ((ASN1Data) value).toASN1(context)); } else if (value instanceof RubyObject) { + if (isEOC(context)) { + return null; + } final IRubyObject string = value.checkStringType(); if (string instanceof RubyString) { return new DERTaggedObject(isExplicitTagging(), tagClass, tag, @@ -1457,43 +1459,147 @@ public IRubyObject to_der(final ThreadContext context) { } byte[] toDER(final ThreadContext context) throws IOException { - if ( isEOC() ) return new byte[] { 0x00, 0x00 }; + if ( + ("ASN1Data".equals(getClassBaseName()) && isUniversal(context)) + ) { + return toDERInternal(context, isConstructive(), isInfiniteLength(), value(context)); + } + + final ASN1Primitive prim = toASN1(context).toASN1Primitive(); - if (isUniversal(context)) { - // handstitch conversion + if (isInfiniteLength()) { + final java.io.ByteArrayOutputStream tagOut = new ByteArrayOutputStream(); + final java.io.ByteArrayOutputStream contentOut = new ByteArrayOutputStream(); final java.io.ByteArrayOutputStream out = new ByteArrayOutputStream(); - final IRubyObject value = callMethod(context, "value"); + prim.encodeTo(contentOut, ASN1Encoding.DER); + writeDERIdentifier(getTag(context), getTagClass(context) | BERTags.CONSTRUCTED, tagOut); - final byte[] valueBytes; - if (value instanceof RubyArray) { - final RubyArray arr = (RubyArray) value; - final java.io.ByteArrayOutputStream valueOut = new ByteArrayOutputStream(); + byte[] tagOutArr = tagOut.toByteArray(); + byte[] contentOutArr = contentOut.toByteArray(); - for (final IRubyObject obj : arr.toJavaArray()) { - final IRubyObject string = value.checkStringType(); - if (string instanceof RubyString) { - valueOut.write(((RubyString) string).getBytes()); - } else { - throw context.runtime.newTypeError( - "no implicit conversion of " + obj.getMetaClass().getBaseName() + " into String"); - } - } - valueBytes = valueOut.toByteArray(); - } else { - final IRubyObject string = value.checkStringType(); - if (string instanceof RubyString) { - valueBytes = ((RubyString) string).getBytes(); + out.write(tagOutArr); + out.write(0x80); + out.write(contentOutArr, tagOutArr.length + 1, contentOutArr.length - tagOutArr.length - 1); + out.write(0x00); + out.write(0x00); + + return out.toByteArray(); + } else { + return prim.getEncoded(ASN1Encoding.DER); + } + } + + byte[] toDERInternal(final ThreadContext context, boolean isConstructed, boolean isIndefiniteLength, final IRubyObject value) throws IOException { + // handstitch conversion + final java.io.ByteArrayOutputStream out = new ByteArrayOutputStream(); + + final byte[] valueBytes; + + if (value == null) { + valueBytes = new byte[] {}; + } else if (value instanceof RubyArray) { + final IRubyObject[] arr = ((RubyArray) value).toJavaArray(); + final java.io.ByteArrayOutputStream valueOut = new ByteArrayOutputStream(); + + + for ( int i = 0; i < arr.length; i++ ) { + final IRubyObject obj = arr[i]; + + if (obj instanceof EndOfContent && i != arr.length - 1) { + throw newASN1Error(context.runtime, "illegal EOC octets in value"); + } + + final byte[] objBytes; + + if (obj.respondsTo("to_der")) { + objBytes = ((RubyString) obj.callMethod(context, "to_der")).getBytes(); } else { - throw context.runtime.newTypeError( - "no implicit conversion of " + value.getMetaClass().getBaseName() + " into String"); + objBytes = ((RubyString) obj.convertToString()).getBytes(); } + + valueOut.write(objBytes); + } + + if (isIndefiniteLength) { + if (arr.length != 0 && !(arr[arr.length - 1] instanceof EndOfContent)) { + // indefinite length object with no EOC object in the array. + valueOut.write(0x00); + valueOut.write(0x00); + } + } + + valueBytes = valueOut.toByteArray(); + } else { + if (isIndefiniteLength) { + throw newASN1Error( + context.runtime, + "indefinite length form cannot be used with primitive encoding" + ); + } + + if (value instanceof RubyString) { + valueBytes = ((RubyString) value).getBytes(); + } else { + valueBytes = value.convertToString().getBytes(); } - out.write(getTag(context)); - out.write(valueBytes.length); - out.write(valueBytes); - return out.toByteArray(); } - return toASN1(context).toASN1Primitive().getEncoded(ASN1Encoding.DER); + + int flags = getTagClass(context); + if (isConstructed) { + flags |= BERTags.CONSTRUCTED; + } + // tag + writeDERIdentifier(getTag(context), flags, out); + if (isIndefiniteLength) { + out.write(0x80); + } else { + writeDERLength(valueBytes.length, out); + } + // value + out.write(valueBytes); + + return out.toByteArray(); + } + + void writeDERIdentifier(int tag, int flags, java.io.ByteArrayOutputStream out) { + if (tag > 0x1f) { + byte[] stack = new byte[6]; + int pos = stack.length; + + stack[--pos] = (byte)(tag & 0x7F); + while (tag > 127) + { + tag >>>= 7; + stack[--pos] = (byte)(tag & 0x7F | 0x80); + } + + stack[--pos] = (byte)(flags | 0x1F); + + out.write(stack, pos, stack.length - pos); + } else { + out.write(flags | tag); + } + } + + void writeDERLength(int length, java.io.ByteArrayOutputStream out) { + if (length < 128) { + out.write(length); + } else { + byte[] stack = new byte[5]; + int pos = stack.length; + + do + { + stack[--pos] = (byte)length; + length >>>= 8; + } + while (length != 0); + + int count = stack.length - pos; + stack[--pos] = (byte)(0x80 | count); + + out.write(stack, pos, count - pos); + } } protected IRubyObject defaultTag() { @@ -1545,11 +1651,20 @@ static void printArray(final PrintStream out, final int indent, final RubyArray } - public static class EndOfContent { + public static class EndOfContent extends ASN1Data { - private EndOfContent() {} + static ObjectAllocator ALLOCATOR = new ObjectAllocator() { + public IRubyObject allocate(Ruby runtime, RubyClass klass) { + return new EndOfContent(runtime, klass); + } + }; - @JRubyMethod(visibility = Visibility.PRIVATE) + public EndOfContent(Ruby runtime, RubyClass type) { + super(runtime,type); + } + + + @JRubyMethod(required = 0, optional = 0, visibility = Visibility.PRIVATE) public static IRubyObject initialize(final ThreadContext context, final IRubyObject self) { final Ruby runtime = context.runtime; self.getInstanceVariables().setInstanceVariable("@tag", runtime.newFixnum(0)); @@ -1563,6 +1678,17 @@ static IRubyObject newInstance(final ThreadContext context) { return klass.newInstance(context, Block.NULL_BLOCK); } + @Override + boolean isImplicitTagging() { + IRubyObject tagging = tagging(); + if ( tagging.isNil() ) return true; + return "IMPLICIT".equals( tagging.toString() ); + } + + @Override + byte[] toDER(final ThreadContext context) throws IOException { + return toDERInternal(context, false, false, null); + } } public static class Primitive extends ASN1Data { @@ -1620,12 +1746,20 @@ static void initializeImpl(final ThreadContext context, if ( tag.isNil() ) throw newASN1Error(runtime, "must specify tag number"); - if ( tagging.isNil() ) tagging = runtime.newSymbol("EXPLICIT"); - if ( ! (tagging instanceof RubySymbol) ) { - throw newASN1Error(runtime, "invalid tag default"); + if ( tagging.isNil()) { + if (tag_class.isNil()) { + tag_class = runtime.newSymbol("UNIVERSAL"); + } + } else { + if (!(tagging instanceof RubySymbol)) { + throw newASN1Error(runtime, "invalid tagging method"); + } + + if (tag_class.isNil()) { + tag_class = runtime.newSymbol("CONTEXT_SPECIFIC"); + } } - if ( tag_class.isNil() ) tag_class = runtime.newSymbol("CONTEXT_SPECIFIC"); if ( ! (tag_class instanceof RubySymbol) ) { throw newASN1Error(runtime, "invalid tag class"); } @@ -1682,12 +1816,7 @@ boolean isImplicitTagging() { } @Override - boolean isEOC() { - return false; - } - - @Override - boolean isUniversal(final ThreadContext context) { + boolean isEOC(final ThreadContext context) { return false; } @@ -1697,6 +1826,21 @@ private boolean isNull() { @Override byte[] toDER(final ThreadContext context) throws IOException { + Class type = typeClass( getMetaClass() ); + final IRubyObject value = value(context); + + if ( type == null ) { + RubyString string; + + if (value instanceof RubyString) { + string = (RubyString) value; + } else { + string = value.convertToString(); + } + + return toDERInternal(context, false, false, string); + } + return toASN1(context).toASN1Primitive().getEncoded(ASN1Encoding.DER); } @@ -1711,16 +1855,6 @@ ASN1Encodable toASN1(final ThreadContext context) { private ASN1Encodable toASN1Primitive(final ThreadContext context) { Class type = typeClass( getMetaClass() ); - if ( type == null ) { - final int tag = getTag(context); - if ( tag == 0 ) return null; // TODO pass EOC to BC ? - if ( isExplicitTagging() ) type = typeClass( tag ); - if ( type == null ) { - throw new IllegalArgumentException( - "no type for: " + getMetaClass() + " or tag: " + getTag(context) - ); - } - } final IRubyObject val = value(context); if ( type == ASN1ObjectIdentifier.class ) { @@ -1857,27 +1991,15 @@ public IRubyObject initialize(final ThreadContext context, final IRubyObject[] a return this; } - static Constructive newInfiniteConstructive(final ThreadContext context, - final String type, final IRubyObject value, final int defaultTag) { + static Constructive newInfiniteLength(final ThreadContext context, + final IRubyObject value, final int defaultTag) { final Ruby runtime = context.runtime; - final RubyClass klass = _ASN1(context.runtime).getClass(type); + final RubyClass klass = _ASN1(context.runtime).getClass("Constructive"); final Constructive self = new Constructive(runtime, klass); - self.setInstanceVariable("@tag", runtime.newFixnum(defaultTag)); - self.setInstanceVariable("@value", value); - self.setInstanceVariable("@tag_class", runtime.newSymbol("UNIVERSAL")); - self.setInstanceVariable("@tagging", context.nil); - - return setInfiniteLength(context, self); - } - - static Constructive setInfiniteLength(final ThreadContext context, final IRubyObject constructive) { - final Constructive instance = ((Constructive) constructive); - final IRubyObject value = instance.value(context); - value.callMethod(context, "<<", EndOfContent.newInstance(context)); - instance.setInstanceVariable("@indefinite_length", context.runtime.getTrue()); - return instance; + ASN1Data.newInfiniteLengthImpl(context, self, value, defaultTag, runtime.newSymbol("UNIVERSAL")); + return self; } private boolean rawConstructive() { @@ -1892,10 +2014,6 @@ private boolean isSet() { return "Set".equals( getClassBaseName() ); } - private boolean isInfiniteLength() { - return getInstanceVariable("@indefinite_length").isTrue(); - } - private boolean isTagged() { return !tagging().isNil(); } @@ -1941,22 +2059,19 @@ ASN1Encodable toASN1(final ThreadContext context) { @Override @JRubyMethod public IRubyObject to_der(final ThreadContext context) { - if ( rawConstructive() ) { // MRI compatibility - if ( ! isInfiniteLength() && ! super.value(context).isNil() ) { - final Ruby runtime = context.runtime; - throw newASN1Error(runtime, "Constructive shall only be used with indefinite length"); - } - } return super.to_der(context); } @Override byte[] toDER(final ThreadContext context) throws IOException { - if ( isInfiniteLength() ) { - if ( isSequence() ) { + final int tagNo = getTag(context); + final boolean isIndefiniteLength = isInfiniteLength(); + + if ( isIndefiniteLength ) { + if ( isSequence() || tagNo == SEQUENCE ) { return sequenceToDER(context); } - if ( isSet() ) { + if ( isSet() || tagNo == SET) { return setToDER(context); } // "raw" Constructive @@ -1965,12 +2080,18 @@ byte[] toDER(final ThreadContext context) throws IOException { return octetStringToDER(context); case BIT_STRING: return bitStringToDER(context); - case SEQUENCE: - return sequenceToDER(context); - case SET: - return setToDER(context); } - throw new UnsupportedOperationException( this.inspect().toString() ); + return toDERInternal(context, true, isInfiniteLength(), value(context)); + } + + if (isEOC(context)) { + return toDERInternal(context, true, isIndefiniteLength, null); + } + + Class type = typeClass( getMetaClass() ); + + if ( type == null ) { + return toDERInternal(context, true, isIndefiniteLength, valueAsArray(context)); } return super.toDER(context); @@ -2022,19 +2143,23 @@ private byte[] setToDER(final ThreadContext context) throws IOException { private ASN1EncodableVector toASN1EncodableVector(final ThreadContext context) { final ASN1EncodableVector vec = new ASN1EncodableVector(); final IRubyObject value = value(context); - final RubyArray val; + final RubyArray val = valueAsArray(context); + for ( int i = 0; i < val.size(); i++ ) { + if ( addEntry(context, vec, val.entry(i)) ) break; + } + return vec; + } + + private RubyArray valueAsArray(final ThreadContext context) { + final IRubyObject value = value(context); if (value instanceof RubyArray ) { - val = (RubyArray) value; + return (RubyArray) value; } else { if (!value.respondsTo("to_a")) { throw context.runtime.newTypeError("can't convert " + value.getMetaClass().getName() + " into Array"); } - val = (RubyArray) value.callMethod(context, "to_a"); - } - for ( int i = 0; i < val.size(); i++ ) { - if ( addEntry(context, vec, val.entry(i)) ) break; + return (RubyArray) value.callMethod(context, "to_a"); } - return vec; } public ASN1Primitive toASN1Primitive() { @@ -2067,7 +2192,7 @@ private static boolean addEntry(final ThreadContext context, final ASN1Encodable } else if ( entry instanceof ASN1Data ) { final ASN1Data data = ( (ASN1Data) entry ); - if ( data.isEOC() ) return true; + if ( data.isEOC(context) ) return true; vec.add( data.toASN1(context) ); } else { diff --git a/src/test/ruby/test_asn1.rb b/src/test/ruby/test_asn1.rb index 9544fc73..0cc59e27 100644 --- a/src/test/ruby/test_asn1.rb +++ b/src/test/ruby/test_asn1.rb @@ -509,66 +509,37 @@ def test_generalizedtime end def test_basic_asn1data - # TODO: Import Issue - # Java::JavaLang::ClassCastException: - # class org.jruby.RubyString cannot be cast to class org.jruby.ext.openssl.ASN1$ASN1Data - # org.jruby.ext.openssl.ASN1$ASN1Data.toASN1TaggedObject(ASN1.java:1408) - # org.jruby.ext.openssl.ASN1$ASN1Data.toASN1(ASN1.java:1383) - # org.jruby.ext.openssl.ASN1$ASN1Data.toDER(ASN1.java:1424) - # org.jruby.ext.openssl.ASN1$ASN1Data.to_der(ASN1.java:1414) - #encode_test B(%w{ 00 00 }), OpenSSL::ASN1::ASN1Data.new(B(%w{}), 0, :UNIVERSAL) - #encode_test B(%w{ 01 00 }), OpenSSL::ASN1::ASN1Data.new(B(%w{}), 1, :UNIVERSAL) - #encode_decode_test B(%w{ 41 00 }), OpenSSL::ASN1::ASN1Data.new(B(%w{}), 1, :APPLICATION) - #encode_decode_test B(%w{ 81 00 }), OpenSSL::ASN1::ASN1Data.new(B(%w{}), 1, :CONTEXT_SPECIFIC) - #encode_decode_test B(%w{ C1 00 }), OpenSSL::ASN1::ASN1Data.new(B(%w{}), 1, :PRIVATE) - # TODO: Import Issue - # OpenSSL::ASN1::ASN1Error: tag number for :UNIVERSAL too large - # org/jruby/RubyClass.java:942:in `new' - #encode_decode_test B(%w{ 1F 20 00 }), OpenSSL::ASN1::ASN1Data.new(B(%w{}), 32, :UNIVERSAL) - #encode_decode_test B(%w{ 1F C0 20 00 }), OpenSSL::ASN1::ASN1Data.new(B(%w{}), 8224, :UNIVERSAL) - # TODO: Import Issue (same as start of this test) - # Java::JavaLang::ClassCastException: - # class org.jruby.RubyString cannot be cast to class org.jruby.ext.openssl.ASN1$ASN1Data - # org.jruby.ext.openssl.ASN1$ASN1Data.toASN1TaggedObject(ASN1.java:1408) - # org.jruby.ext.openssl.ASN1$ASN1Data.toASN1(ASN1.java:1383) - # org.jruby.ext.openssl.ASN1$ASN1Data.toDER(ASN1.java:1424) - # org.jruby.ext.openssl.ASN1$ASN1Data.to_der(ASN1.java:1414) - #encode_decode_test B(%w{ 41 02 AB CD }), OpenSSL::ASN1::ASN1Data.new(B(%w{ AB CD }), 1, :APPLICATION) - #encode_decode_test B(%w{ 41 81 80 } + %w{ AB CD } * 64), OpenSSL::ASN1::ASN1Data.new(B(%w{ AB CD } * 64), 1, :APPLICATION) - #encode_decode_test B(%w{ 41 82 01 00 } + %w{ AB CD } * 128), OpenSSL::ASN1::ASN1Data.new(B(%w{ AB CD } * 128), 1, :APPLICATION) - #encode_decode_test B(%w{ 61 00 }), OpenSSL::ASN1::ASN1Data.new([], 1, :APPLICATION) - #obj = OpenSSL::ASN1::ASN1Data.new([OpenSSL::ASN1::ASN1Data.new(B(%w{ AB CD }), 2, :PRIVATE)], 1, :APPLICATION) - #obj.indefinite_length = true - #encode_decode_test B(%w{ 61 80 C2 02 AB CD 00 00 }), obj - #obj = OpenSSL::ASN1::ASN1Data.new([ - # OpenSSL::ASN1::ASN1Data.new(B(%w{ AB CD }), 2, :PRIVATE), - # OpenSSL::ASN1::EndOfContent.new - #], 1, :APPLICATION) - #obj.indefinite_length = true - #encode_test B(%w{ 61 80 C2 02 AB CD 00 00 }), obj - #obj = OpenSSL::ASN1::ASN1Data.new(B(%w{ AB CD }), 1, :UNIVERSAL) - #obj.indefinite_length = true - # TODO: Import Issue - # expected but was <# expected but was <"\x01\x01\xFF"> - #encode_test B(%w{ 01 00 }), OpenSSL::ASN1::Primitive.new(B(%w{}), 1, nil, :UNIVERSAL) - # <"\x81\x00"> expected but was <"\x01\x01\xFF"> - #encode_test B(%w{ 81 00 }), OpenSSL::ASN1::Primitive.new(B(%w{}), 1, nil, :CONTEXT_SPECIFIC) - # <"\x01\x02\xAB\xCD"> expected but was <"\x01\x01\xFF"> - #encode_test B(%w{ 01 02 AB CD }), OpenSSL::ASN1::Primitive.new(B(%w{ AB CD }), 1) - # exception was expected but none was thrown. - #assert_raise(TypeError) { OpenSSL::ASN1::Primitive.new([], 1).to_der } + encode_test B(%w{ 00 00 }), OpenSSL::ASN1::Primitive.new(B(%w{}), 0) + encode_test B(%w{ 01 00 }), OpenSSL::ASN1::Primitive.new(B(%w{}), 1, nil, :UNIVERSAL) + encode_test B(%w{ 81 00 }), OpenSSL::ASN1::Primitive.new(B(%w{}), 1, nil, :CONTEXT_SPECIFIC) + encode_test B(%w{ 01 02 AB CD }), OpenSSL::ASN1::Primitive.new(B(%w{ AB CD }), 1) + assert_raise(TypeError) { OpenSSL::ASN1::Primitive.new([], 1).to_der } prim = OpenSSL::ASN1::Integer.new(50) assert_equal false, prim.indefinite_length @@ -582,27 +553,19 @@ def test_basic_primitive # def test_basic_constructed - #octet_string = OpenSSL::ASN1::OctetString.new(B(%w{ AB CD })) - # TODO: Import Issue - # OpenSSL::ASN1::ASN1Error: Constructive shall only be used with indefinite length - #encode_test B(%w{ 20 00 }), OpenSSL::ASN1::Constructive.new([], 0) - #encode_test B(%w{ 21 00 }), OpenSSL::ASN1::Constructive.new([], 1, nil, :UNIVERSAL) - #encode_test B(%w{ A1 00 }), OpenSSL::ASN1::Constructive.new([], 1, nil, :CONTEXT_SPECIFIC) - #encode_test B(%w{ 21 04 04 02 AB CD }), OpenSSL::ASN1::Constructive.new([octet_string], 1) - # Java::JavaLang::UnsupportedOperationException: - # #], - # @tag_class=:CONTEXT_SPECIFIC, @tagging=:EXPLICIT, @indefinite_length=true> - # org.jruby.ext.openssl.ASN1$Constructive.toDER(ASN1.java:1881) - # org.jruby.ext.openssl.ASN1$ASN1Data.to_der(ASN1.java:1414) - # org.jruby.ext.openssl.ASN1$Constructive.to_der(ASN1.java:1858) - #obj = OpenSSL::ASN1::Constructive.new([octet_string], 1) - #obj.indefinite_length = true - #encode_decode_test B(%w{ 21 80 04 02 AB CD 00 00 }), obj - # (see above) Java::JavaLang::UnsupportedOperationException - #obj = OpenSSL::ASN1::Constructive.new([octet_string, OpenSSL::ASN1::EndOfContent.new], 1) - #obj.indefinite_length = true - #encode_test B(%w{ 21 80 04 02 AB CD 00 00 }), obj + octet_string = OpenSSL::ASN1::OctetString.new(B(%w{ AB CD })) + encode_test B(%w{ 20 00 }), OpenSSL::ASN1::Constructive.new([], 0) + encode_test B(%w{ 21 00 }), OpenSSL::ASN1::Constructive.new([], 1, nil, :UNIVERSAL) + encode_test B(%w{ A1 00 }), OpenSSL::ASN1::Constructive.new([], 1, nil, :CONTEXT_SPECIFIC) + encode_test B(%w{ 21 04 04 02 AB CD }), OpenSSL::ASN1::Constructive.new([octet_string], 1) + obj = OpenSSL::ASN1::Constructive.new([octet_string], 1) + obj.indefinite_length = true + encode_test B(%w{ 21 80 04 02 AB CD 00 00 }), obj + # TODO: BC doesn't support decoding indef constructive asn1s from unsupported tag types. + # encode_decode_test B(%w{ 21 80 04 02 AB CD 00 00 }), obj + obj = OpenSSL::ASN1::Constructive.new([octet_string, OpenSSL::ASN1::EndOfContent.new], 1) + obj.indefinite_length = true + encode_test B(%w{ 21 80 04 02 AB CD 00 00 }), obj end def test_constructive @@ -1295,7 +1258,7 @@ def test_decode_application_specific assert_equal 'o=Telstra', asn1_data.value[1].value assert_equal OpenSSL::ASN1::ASN1Data, asn1_data.value[2].class assert_equal :CONTEXT_SPECIFIC, asn1_data.value[2].tag_class -# assert_equal 'ess', asn1_data.value[2].value + assert_equal 'ess', asn1_data.value[2].value # assert_equal raw, asn1.to_der end