Skip to content

Commit e3d4460

Browse files
Fixing ASN1Data encoding and decoding
`OpenSSL::ASN1::ASN1Data` encoding and decoding hasn't worked well for some time: * it was decoding the value always as a Sequence (when values can be strings as well); * after decoding, the `.tag_class` was always `:CONTEXT_SPECIFIC` (it can be `:UNIVERSAL` as well); * it wasn't correctly encoding `:APPLICATION`-tagged structures; The tests added verify that the behaviour of these operations now matchws what CRuby's `openssl` gem does.
1 parent 4a33a85 commit e3d4460

File tree

1 file changed

+51
-17
lines changed

1 file changed

+51
-17
lines changed

src/main/java/org/jruby/ext/openssl/ASN1.java

+51-17
Original file line numberDiff line numberDiff line change
@@ -934,7 +934,7 @@ private static RubyString name(final ThreadContext context, IRubyObject value,
934934
} // ObjectId
935935

936936
static IRubyObject decodeObject(final ThreadContext context,
937-
final RubyModule ASN1, final org.bouncycastle.asn1.ASN1Encodable obj)
937+
final RubyModule ASN1, final ASN1Encodable obj)
938938
throws IOException, IllegalArgumentException {
939939
final Ruby runtime = context.runtime;
940940

@@ -1046,20 +1046,34 @@ else if ( obj instanceof ASN1GraphicString ) {
10461046

10471047
if (obj instanceof ASN1TaggedObject) {
10481048
final ASN1TaggedObject taggedObj = (ASN1TaggedObject) obj;
1049-
if (taggedObj.getTagClass() == BERTags.APPLICATION) {
1050-
IRubyObject tag = runtime.newFixnum( taggedObj.getTagNo() );
1051-
IRubyObject tag_class = runtime.newSymbol("APPLICATION");
1052-
final ASN1Sequence sequence = (ASN1Sequence) taggedObj.getBaseUniversal(false, SEQUENCE);
1053-
@SuppressWarnings("unchecked")
1049+
// IRubyObject val = decodeObject(context, ASN1, taggedObj.getBaseObject());
1050+
IRubyObject tag = runtime.newFixnum( taggedObj.getTagNo() );
1051+
IRubyObject tag_class;
1052+
switch(taggedObj.getTagClass()) {
1053+
case BERTags.PRIVATE:
1054+
tag_class = runtime.newSymbol("PRIVATE");
1055+
break;
1056+
case BERTags.APPLICATION:
1057+
tag_class = runtime.newSymbol("APPLICATION");
1058+
break;
1059+
case BERTags.CONTEXT_SPECIFIC:
1060+
tag_class = runtime.newSymbol("CONTEXT_SPECIFIC");
1061+
break;
1062+
default:
1063+
tag_class = runtime.newSymbol("UNIVERSAL");
1064+
break;
1065+
}
1066+
1067+
final ASN1Encodable taggedBaseObj = taggedObj.getBaseObject();
1068+
1069+
// TODO: how to infer that this tagged object is constructed
1070+
if (taggedBaseObj instanceof ASN1Sequence) {
1071+
final ASN1Sequence sequence = (ASN1Sequence) taggedBaseObj;
10541072
final RubyArray valArr = decodeObjects(context, ASN1, sequence.getObjects());
10551073
return ASN1.getClass("ASN1Data").newInstance(context, new IRubyObject[] { valArr, tag, tag_class }, Block.NULL_BLOCK);
1056-
} else {
1057-
IRubyObject val = decodeObject(context, ASN1, taggedObj.getBaseObject());
1058-
IRubyObject tag = runtime.newFixnum( taggedObj.getTagNo() );
1059-
IRubyObject tag_class = runtime.newSymbol("CONTEXT_SPECIFIC");
1060-
final RubyArray valArr = runtime.newArray(val);
1061-
return ASN1.getClass("ASN1Data").newInstance(context, new IRubyObject[] { valArr, tag, tag_class }, Block.NULL_BLOCK);
10621074
}
1075+
1076+
return ASN1.getClass("ASN1Data").newInstance(context, new IRubyObject[] { decodeObject(context, ASN1, taggedBaseObj).callMethod(context, "value"), tag, tag_class }, Block.NULL_BLOCK);
10631077
}
10641078

10651079
if ( obj instanceof ASN1Sequence ) {
@@ -1367,27 +1381,47 @@ ASN1Encodable toASN1(final ThreadContext context) {
13671381

13681382
final ASN1TaggedObject toASN1TaggedObject(final ThreadContext context) {
13691383
final int tag = getTag(context);
1384+
final RubyModule ASN1 = _ASN1(context.runtime);
1385+
1386+
IRubyObject value = callMethod(context, "value");
1387+
final IRubyObject tagClass = callMethod(context, "tag_class");
1388+
int berTag;
1389+
String tagClassName = tagClass.toString();
1390+
1391+
if (tagClassName.equals("PRIVATE")) {
1392+
berTag = BERTags.PRIVATE;
1393+
} else if (tagClassName.equals("APPLICATION")) {
1394+
berTag = BERTags.APPLICATION;
1395+
} else if (tagClassName.equals("CONTEXT_SPECIFIC")) {
1396+
berTag = BERTags.CONTEXT_SPECIFIC;
1397+
} else {
1398+
berTag = BERTags.UNIVERSAL;
1399+
}
13701400

1371-
final IRubyObject value = callMethod(context, "value");
1372-
if (value instanceof RubyArray) {
1401+
if ( value instanceof RubyArray ) {
13731402
final RubyArray arr = (RubyArray) value;
13741403
assert ! arr.isEmpty();
13751404

13761405
ASN1EncodableVector vec = new ASN1EncodableVector();
1377-
for (final IRubyObject obj : arr.toJavaArray()) {
1406+
for ( final IRubyObject obj : arr.toJavaArray() ) {
13781407
ASN1Encodable data = ((ASN1Data) obj).toASN1(context);
13791408
if ( data == null ) break;
13801409
vec.add( data );
13811410
}
1382-
return new DERTaggedObject(isExplicitTagging(), tag, new DERSequence(vec));
1411+
1412+
// TODO: it's not possible to generate a constructed tagged object, bc raises exception
1413+
// berTag = berTag | BERTags.CONSTRUCTED;
1414+
return new DERTaggedObject(isExplicitTagging(), berTag, tag, new DERSequence(vec));
13831415
}
13841416

13851417
if (!(value instanceof ASN1Data)) {
13861418
throw new UnsupportedOperationException("toASN1 " + inspect() + " value: " + value.inspect() + " (" + value.getMetaClass() + ")");
13871419
}
1388-
return new DERTaggedObject(isExplicitTagging(), tag, ((ASN1Data) value).toASN1(context));
1420+
1421+
return new DERTaggedObject(isExplicitTagging(), berTag, tag, ((ASN1Data) value).toASN1(context));
13891422
}
13901423

1424+
13911425
@JRubyMethod
13921426
public IRubyObject to_der(final ThreadContext context) {
13931427
try {

0 commit comments

Comments
 (0)