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 <code>SEQUENCE</code> and <code>SEQUENCE OF</code> constructs. * <p> * DER form is always definite form length fields, while * BER support uses indefinite form. * <hr> * <p><b>X.690</b></p> * <p><b>8: Basic encoding rules</b></p> * <p><b>8.9 Encoding of a sequence value </b></p> * 8.9.1 The encoding of a sequence value shall be constructed. * <p> * <b>8.9.2</b> The contents octets shall consist of the complete * encoding of one data value from each of the types listed in * the ASN.1 definition of the sequence type, in the order of * their appearance in the definition, unless the type was referenced * with the keyword <b>OPTIONAL</b> or the keyword <b>DEFAULT</b>. * </p><p> * <b>8.9.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>. * If present, it shall appear in the encoding at the point * corresponding to the appearance of the type in the ASN.1 definition. * </p><p> * <b>8.10 Encoding of a sequence-of value </b> * </p><p> * <b>8.10.1</b> The encoding of a sequence-of value shall be constructed. * <p> * <b>8.10.2</b> The contents octets shall consist of zero, * one or more complete encodings of data values from the type listed in * the ASN.1 definition. * <p> * <b>8.10.3</b> The order of the encodings of the data values shall be * the same as the order of the data values in the sequence-of value to * be encoded. * </p> * <p><b>9: Canonical encoding rules</b></p> * <p><b>9.1 Length forms</b></p> * 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).] * * <p><b>11: Restrictions on BER employed by both CER and DER</b></p> * <p><b>11.5 Set and sequence components with default value</b></p> * 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. */ public abstract class ASN1Sequence extends ASN1Primitive implements org.bouncycastle.util.Iterable<ASN1Encodable> { protected Vector seq = new Vector(); /** * Return an ASN1Sequence from the given object. * * @param obj the object we want converted. * @exception IllegalArgumentException if the object cannot be converted. * @return an ASN1Sequence instance, or null. */ public static ASN1Sequence getInstance( Object obj) { if (obj == null || obj instanceof ASN1Sequence) { return (ASN1Sequence)obj; } else if (obj instanceof ASN1SequenceParser) { return ASN1Sequence.getInstance(((ASN1SequenceParser)obj).toASN1Primitive()); } else if (obj instanceof byte[]) { try { return ASN1Sequence.getInstance(fromByteArray((byte[])obj)); } catch (IOException e) { throw new IllegalArgumentException("failed to construct sequence from byte[]: " + e.getMessage()); } } else if (obj instanceof ASN1Encodable) { ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive(); if (primitive instanceof ASN1Sequence) { return (ASN1Sequence)primitive; } } throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName()); } /** * Return an ASN1 sequence 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 * sequence - 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 sequences 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 ASN1Sequence instance. */ public static ASN1Sequence getInstance( ASN1TaggedObject obj, boolean explicit) { if (explicit) { if (!obj.isExplicit()) { throw new IllegalArgumentException("object implicit - explicit expected."); } return ASN1Sequence.getInstance(obj.getObject().toASN1Primitive()); } else { // // constructed object which appears to be explicitly tagged // when it should be implicit means we have to add the // surrounding sequence. // if (obj.isExplicit()) { if (obj instanceof BERTaggedObject) { return new BERSequence(obj.getObject()); } else { return new DLSequence(obj.getObject()); } } else { if (obj.getObject() instanceof ASN1Sequence) { return (ASN1Sequence)obj.getObject(); } } } throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName()); } /** * Create an empty sequence */ protected ASN1Sequence() { } /** * Create a sequence containing one object * @param obj the object to be put in the SEQUENCE. */ protected ASN1Sequence( ASN1Encodable obj) { seq.addElement(obj); } /** * Create a sequence containing a vector of objects. * @param v the vector of objects to be put in the SEQUENCE */ protected ASN1Sequence( ASN1EncodableVector v) { for (int i = 0; i != v.size(); i++) { seq.addElement(v.get(i)); } } /* * Create a sequence containing a vector of objects. */ protected ASN1Sequence( ASN1Encodable[] array) { for (int i = 0; i != array.length; i++) { seq.addElement(array[i]); } } 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 Enumeration getObjects() { return seq.elements(); } public ASN1SequenceParser parser() { final ASN1Sequence outer = this; return new ASN1SequenceParser() { 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; } }; } /** * Return the object at the sequence position indicated by index. * * @param index the sequence number (starting at zero) of the object * @return the object at the sequence position indicated by index. */ public ASN1Encodable getObjectAt( int index) { return (ASN1Encodable)seq.elementAt(index); } /** * Return the number of objects in this sequence. * * @return the number of objects in this sequence. */ public int size() { return seq.size(); } public int hashCode() { Enumeration e = this.getObjects(); int hashCode = size(); while (e.hasMoreElements()) { Object o = getNext(e); hashCode *= 17; hashCode ^= o.hashCode(); } return hashCode; } boolean asn1Equals( ASN1Primitive o) { if (!(o instanceof ASN1Sequence)) { return false; } ASN1Sequence other = (ASN1Sequence)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(); return encObj; } /** * Change current SEQUENCE object to be encoded as {@link DERSequence}. * This is part of Distinguished Encoding Rules form serialization. */ ASN1Primitive toDERObject() { ASN1Sequence derSeq = new DERSequence(); derSeq.seq = this.seq; return derSeq; } /** * Change current SEQUENCE object to be encoded as {@link DLSequence}. * This is part of Direct Length form serialization. */ ASN1Primitive toDLObject() { ASN1Sequence dlSeq = new DLSequence(); dlSeq.seq = this.seq; return dlSeq; } boolean isConstructed() { return true; } abstract void encode(ASN1OutputStream out) throws IOException; public String toString() { return seq.toString(); } public Iterator<ASN1Encodable> iterator() { return new Arrays.Iterator<ASN1Encodable>(toArray()); } }