package org.bouncycastle.asn1;

import java.io.IOException;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Vector;

import org.bouncycastle.util.Arrays;

ASN.1 SET and SET OF constructs.

Note: This does not know which syntax the set is! (The difference: ordering of SET elements or not ordering.)

DER form is always definite form length fields, while BER support uses indefinite form.

The CER form support does not exist.

X.690

8: Basic encoding rules

8.11 Encoding of a set value

8.11.1 The encoding of a set value shall be constructed

8.11.2 The contents octets shall consist of the complete encoding of a data value from each of the types listed in the ASN.1 definition of the set type, in an order chosen by the sender, unless the type was referenced with the keyword OPTIONAL or the keyword DEFAULT.

8.11.3 The encoding of a data value may, but need not, be present for a type which was referenced with the keyword OPTIONAL or the keyword DEFAULT.

NOTE — The order of data values in a set value is not significant, and places no constraints on the order during transfer

8.12 Encoding of a set-of value

8.12.1 The encoding of a set-of value shall be constructed.

8.12.2 The text of 8.10.2 applies: The contents octets shall consist of zero, one or more complete encodings of data values from the type listed in the ASN.1 definition.

8.12.3 The order of data values need not be preserved by the encoding and subsequent decoding.

9: Canonical encoding rules

9.1 Length forms

If the encoding is constructed, it shall employ the indefinite-length form. If the encoding is primitive, it shall include the fewest length octets necessary. [Contrast with 8.1.3.2 b).]

9.3 Set components

The encodings of the component values of a set value shall appear in an order determined by their tags as specified in 8.6 of ITU-T Rec. X.680 | ISO/IEC 8824-1. Additionally, for the purposes of determining the order in which components are encoded when one or more component is an untagged choice type, each untagged choice type is ordered as though it has a tag equal to that of the smallest tag in that choice type or any untagged choice types nested within.

10: Distinguished encoding rules

10.1 Length forms

The definite form of length encoding shall be used, encoded in the minimum number of octets. [Contrast with 8.1.3.2 b).]

10.3 Set components

The encodings of the component values of a set value shall appear in an order determined by their tags as specified in 8.6 of ITU-T Rec. X.680 | ISO/IEC 8824-1.
NOTE — Where a component of the set is an untagged choice type, the location of that component in the ordering will depend on the tag of the choice component being encoded.

11: Restrictions on BER employed by both CER and DER

11.5 Set and sequence components with default value

The encoding of a set value or sequence value shall not include an encoding for any component value which is equal to its default value.

11.6 Set-of components

The encodings of the component values of a set-of value shall appear in ascending order, the encodings being compared as octet strings with the shorter components being padded at their trailing end with 0-octets.

NOTE — The padding octets are for comparison purposes only and do not appear in the encodings.
/** * ASN.1 <code>SET</code> and <code>SET OF</code> constructs. * <p> * Note: This does not know which syntax the set is! * (The difference: ordering of SET elements or not ordering.) * </p><p> * DER form is always definite form length fields, while * BER support uses indefinite form. * </p><p> * The CER form support does not exist. * </p><p> * <h2>X.690</h2> * <h3>8: Basic encoding rules</h3> * <h4>8.11 Encoding of a set value </h4> * <b>8.11.1</b> The encoding of a set value shall be constructed * <p> * <b>8.11.2</b> The contents octets shall consist of the complete * encoding of a data value from each of the types listed in the * ASN.1 definition of the set type, in an order chosen by the sender, * unless the type was referenced with the keyword * <b>OPTIONAL</b> or the keyword <b>DEFAULT</b>. * </p><p> * <b>8.11.3</b> The encoding of a data value may, but need not, * be present for a type which was referenced with the keyword * <b>OPTIONAL</b> or the keyword <b>DEFAULT</b>. * <blockquote> * NOTE &mdash; The order of data values in a set value is not significant, * and places no constraints on the order during transfer * </blockquote> * <h4>8.12 Encoding of a set-of value</h4> * <p> * <b>8.12.1</b> The encoding of a set-of value shall be constructed. * </p><p> * <b>8.12.2</b> The text of 8.10.2 applies: * <i>The contents octets shall consist of zero, * one or more complete encodings of data values from the type listed in * the ASN.1 definition.</i> * </p><p> * <b>8.12.3</b> The order of data values need not be preserved by * the encoding and subsequent decoding. * * <h3>9: Canonical encoding rules</h3> * <h4>9.1 Length forms</h4> * If the encoding is constructed, it shall employ the indefinite-length form. * If the encoding is primitive, it shall include the fewest length octets necessary. * [Contrast with 8.1.3.2 b).] * <h4>9.3 Set components</h4> * The encodings of the component values of a set value shall * appear in an order determined by their tags as specified * in 8.6 of ITU-T Rec. X.680 | ISO/IEC 8824-1. * Additionally, for the purposes of determining the order in which * components are encoded when one or more component is an untagged * choice type, each untagged choice type is ordered as though it * has a tag equal to that of the smallest tag in that choice type * or any untagged choice types nested within. * * <h3>10: Distinguished encoding rules</h3> * <h4>10.1 Length forms</h4> * The definite form of length encoding shall be used, * encoded in the minimum number of octets. * [Contrast with 8.1.3.2 b).] * <h4>10.3 Set components</h4> * The encodings of the component values of a set value shall appear * in an order determined by their tags as specified * in 8.6 of ITU-T Rec. X.680 | ISO/IEC 8824-1. * <blockquote> * NOTE &mdash; Where a component of the set is an untagged choice type, * the location of that component in the ordering will depend on * the tag of the choice component being encoded. * </blockquote> * * <h3>11: Restrictions on BER employed by both CER and DER</h3> * <h4>11.5 Set and sequence components with default value </h4> * The encoding of a set value or sequence value shall not include * an encoding for any component value which is equal to * its default value. * <h4>11.6 Set-of components </h4> * <p> * The encodings of the component values of a set-of value * shall appear in ascending order, the encodings being compared * as octet strings with the shorter components being padded at * their trailing end with 0-octets. * <blockquote> * NOTE &mdash; The padding octets are for comparison purposes only * and do not appear in the encodings. * </blockquote> */
public abstract class ASN1Set extends ASN1Primitive implements org.bouncycastle.util.Iterable<ASN1Encodable> { private Vector set = new Vector(); private boolean isSorted = false;
return an ASN1Set from the given object.
Params:
  • obj – the object we want converted.
Throws:
Returns:an ASN1Set instance, or null.
/** * return an ASN1Set from the given object. * * @param obj the object we want converted. * @exception IllegalArgumentException if the object cannot be converted. * @return an ASN1Set instance, or null. */
public static ASN1Set getInstance( Object obj) { if (obj == null || obj instanceof ASN1Set) { return (ASN1Set)obj; } else if (obj instanceof ASN1SetParser) { return ASN1Set.getInstance(((ASN1SetParser)obj).toASN1Primitive()); } else if (obj instanceof byte[]) { try { return ASN1Set.getInstance(ASN1Primitive.fromByteArray((byte[])obj)); } catch (IOException e) { throw new IllegalArgumentException("failed to construct set from byte[]: " + e.getMessage()); } } else if (obj instanceof ASN1Encodable) { ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive(); if (primitive instanceof ASN1Set) { return (ASN1Set)primitive; } } throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName()); }
Return an ASN1 set from a tagged object. There is a special case here, if an object appears to have been explicitly tagged on reading but we were expecting it to be implicitly tagged in the normal course of events it indicates that we lost the surrounding set - so we need to add it back (this will happen if the tagged object is a sequence that contains other sequences). If you are dealing with implicitly tagged sets you really should be using this method.
Params:
  • obj – the tagged object.
  • explicit – true if the object is meant to be explicitly tagged false otherwise.
Throws:
Returns:an ASN1Set instance.
/** * Return an ASN1 set from a tagged object. There is a special * case here, if an object appears to have been explicitly tagged on * reading but we were expecting it to be implicitly tagged in the * normal course of events it indicates that we lost the surrounding * set - so we need to add it back (this will happen if the tagged * object is a sequence that contains other sequences). If you are * dealing with implicitly tagged sets you really <b>should</b> * be using this method. * * @param obj the tagged object. * @param explicit true if the object is meant to be explicitly tagged * false otherwise. * @exception IllegalArgumentException if the tagged object cannot * be converted. * @return an ASN1Set instance. */
public static ASN1Set getInstance( ASN1TaggedObject obj, boolean explicit) { if (explicit) { if (!obj.isExplicit()) { throw new IllegalArgumentException("object implicit - explicit expected."); } return (ASN1Set)obj.getObject(); } else { ASN1Primitive o = obj.getObject(); // // constructed object which appears to be explicitly tagged // and it's really implicit means we have to add the // surrounding set. // if (obj.isExplicit()) { if (obj instanceof BERTaggedObject) { return new BERSet(o); } else { return new DLSet(o); } } else { if (o instanceof ASN1Set) { return (ASN1Set)o; } // // in this case the parser returns a sequence, convert it // into a set. // if (o instanceof ASN1Sequence) { ASN1Sequence s = (ASN1Sequence)o; if (obj instanceof BERTaggedObject) { return new BERSet(s.toArray()); } else { return new DLSet(s.toArray()); } } } } throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName()); } protected ASN1Set() { }
Create a SET containing one object
Params:
  • obj – object to be added to the SET.
/** * Create a SET containing one object * @param obj object to be added to the SET. */
protected ASN1Set( ASN1Encodable obj) { set.addElement(obj); }
Create a SET containing a vector of objects.
Params:
  • v – a vector of objects to make up the SET.
  • doSort – true if should be sorted DER style, false otherwise.
/** * Create a SET containing a vector of objects. * @param v a vector of objects to make up the SET. * @param doSort true if should be sorted DER style, false otherwise. */
protected ASN1Set( ASN1EncodableVector v, boolean doSort) { for (int i = 0; i != v.size(); i++) { set.addElement(v.get(i)); } if (doSort) { this.sort(); } }
Create a SET containing an array of objects.
Params:
  • array – an array of objects to make up the SET.
  • doSort – true if should be sorted DER style, false otherwise.
/** * Create a SET containing an array of objects. * @param array an array of objects to make up the SET. * @param doSort true if should be sorted DER style, false otherwise. */
protected ASN1Set( ASN1Encodable[] array, boolean doSort) { for (int i = 0; i != array.length; i++) { set.addElement(array[i]); } if (doSort) { this.sort(); } } public Enumeration getObjects() { return set.elements(); }
return the object at the set position indicated by index.
Params:
  • index – the set number (starting at zero) of the object
Returns:the object at the set position indicated by index.
/** * return the object at the set position indicated by index. * * @param index the set number (starting at zero) of the object * @return the object at the set position indicated by index. */
public ASN1Encodable getObjectAt( int index) { return (ASN1Encodable)set.elementAt(index); }
return the number of objects in this set.
Returns:the number of objects in this set.
/** * return the number of objects in this set. * * @return the number of objects in this set. */
public int size() { return set.size(); } public ASN1Encodable[] toArray() { ASN1Encodable[] values = new ASN1Encodable[this.size()]; for (int i = 0; i != this.size(); i++) { values[i] = this.getObjectAt(i); } return values; } public ASN1SetParser parser() { final ASN1Set outer = this; return new ASN1SetParser() { private final int max = size(); private int index; public ASN1Encodable readObject() throws IOException { if (index == max) { return null; } ASN1Encodable obj = getObjectAt(index++); if (obj instanceof ASN1Sequence) { return ((ASN1Sequence)obj).parser(); } if (obj instanceof ASN1Set) { return ((ASN1Set)obj).parser(); } return obj; } public ASN1Primitive getLoadedObject() { return outer; } public ASN1Primitive toASN1Primitive() { return outer; } }; } public int hashCode() { Enumeration e = this.getObjects(); int hashCode = size(); while (e.hasMoreElements()) { Object o = getNext(e); hashCode *= 17; hashCode ^= o.hashCode(); } return hashCode; }
Change current SET object to be encoded as DERSet. This is part of Distinguished Encoding Rules form serialization.
/** * Change current SET object to be encoded as {@link DERSet}. * This is part of Distinguished Encoding Rules form serialization. */
ASN1Primitive toDERObject() { if (isSorted) { ASN1Set derSet = new DERSet(); derSet.set = this.set; return derSet; } else { Vector v = new Vector(); for (int i = 0; i != set.size(); i++) { v.addElement(set.elementAt(i)); } ASN1Set derSet = new DERSet(); derSet.set = v; derSet.sort(); return derSet; } }
Change current SET object to be encoded as DLSet. This is part of Direct Length form serialization.
/** * Change current SET object to be encoded as {@link DLSet}. * This is part of Direct Length form serialization. */
ASN1Primitive toDLObject() { ASN1Set derSet = new DLSet(); derSet.set = this.set; return derSet; } boolean asn1Equals( ASN1Primitive o) { if (!(o instanceof ASN1Set)) { return false; } ASN1Set other = (ASN1Set)o; if (this.size() != other.size()) { return false; } Enumeration s1 = this.getObjects(); Enumeration s2 = other.getObjects(); while (s1.hasMoreElements()) { ASN1Encodable obj1 = getNext(s1); ASN1Encodable obj2 = getNext(s2); ASN1Primitive o1 = obj1.toASN1Primitive(); ASN1Primitive o2 = obj2.toASN1Primitive(); if (o1 == o2 || o1.equals(o2)) { continue; } return false; } return true; } private ASN1Encodable getNext(Enumeration e) { ASN1Encodable encObj = (ASN1Encodable)e.nextElement(); // unfortunately null was allowed as a substitute for DER null if (encObj == null) { return DERNull.INSTANCE; } return encObj; }
return true if a <= b (arrays are assumed padded with zeros).
/** * return true if a <= b (arrays are assumed padded with zeros). */
private boolean lessThanOrEqual( byte[] a, byte[] b) { int len = Math.min(a.length, b.length); for (int i = 0; i != len; ++i) { if (a[i] != b[i]) { return (a[i] & 0xff) < (b[i] & 0xff); } } return len == a.length; } private byte[] getDEREncoded( ASN1Encodable obj) { try { return obj.toASN1Primitive().getEncoded(ASN1Encoding.DER); } catch (IOException e) { throw new IllegalArgumentException("cannot encode object added to SET"); } } protected void sort() { if (!isSorted) { isSorted = true; if (set.size() > 1) { boolean swapped = true; int lastSwap = set.size() - 1; while (swapped) { int index = 0; int swapIndex = 0; byte[] a = getDEREncoded((ASN1Encodable)set.elementAt(0)); swapped = false; while (index != lastSwap) { byte[] b = getDEREncoded((ASN1Encodable)set.elementAt(index + 1)); if (lessThanOrEqual(a, b)) { a = b; } else { Object o = set.elementAt(index); set.setElementAt(set.elementAt(index + 1), index); set.setElementAt(o, index + 1); swapped = true; swapIndex = index; } index++; } lastSwap = swapIndex; } } } } boolean isConstructed() { return true; } abstract void encode(ASN1OutputStream out) throws IOException; public String toString() { return set.toString(); } public Iterator<ASN1Encodable> iterator() { return new Arrays.Iterator<ASN1Encodable>(toArray()); } }