001package org.apache.commons.ssl.org.bouncycastle.asn1;
002
003import java.io.ByteArrayOutputStream;
004import java.io.IOException;
005import java.util.Enumeration;
006import java.util.Vector;
007
008/**
009 * ASN.1 <code>SET</code> and <code>SET OF</code> constructs.
010 * <p>
011 * Note: This does not know which syntax the set is!
012 * (The difference: ordering of SET elements or not ordering.)
013 * <p>
014 * DER form is always definite form length fields, while
015 * BER support uses indefinite form.
016 * <p>
017 * The CER form support does not exist.
018 * <p>
019 * <hr>
020 * <h2>X.690</h2>
021 * <h3>8: Basic encoding rules</h3>
022 * <h4>8.11 Encoding of a set value </h4>
023 * <b>8.11.1</b> The encoding of a set value shall be constructed
024 * <p>
025 * <b>8.11.2</b> The contents octets shall consist of the complete
026 * encoding of a data value from each of the types listed in the
027 * ASN.1 definition of the set type, in an order chosen by the sender,
028 * unless the type was referenced with the keyword
029 * <b>OPTIONAL</b> or the keyword <b>DEFAULT</b>.
030 * <p>
031 * <b>8.11.3</b> The encoding of a data value may, but need not,
032 * be present for a type which was referenced with the keyword
033 * <b>OPTIONAL</b> or the keyword <b>DEFAULT</b>.
034 * <blockquote>
035 * NOTE &mdash; The order of data values in a set value is not significant,
036 * and places no constraints on the order during transfer
037 * </blockquote>
038 * <h4>8.12 Encoding of a set-of value</h4>
039 * <b>8.12.1</b> The encoding of a set-of value shall be constructed.
040 * <p>
041 * <b>8.12.2</b> The text of 8.10.2 applies:
042 * <i>The contents octets shall consist of zero,
043 * one or more complete encodings of data values from the type listed in
044 * the ASN.1 definition.</i>
045 * <p>
046 * <b>8.12.3</b> The order of data values need not be preserved by
047 * the encoding and subsequent decoding.
048 *
049 * <h3>9: Canonical encoding rules</h3>
050 * <h4>9.1 Length forms</h4>
051 * If the encoding is constructed, it shall employ the indefinite length form.
052 * If the encoding is primitive, it shall include the fewest length octets necessary.
053 * [Contrast with 8.1.3.2 b).]
054 * <h4>9.3 Set components</h4>
055 * The encodings of the component values of a set value shall
056 * appear in an order determined by their tags as specified
057 * in 8.6 of ITU-T Rec. X.680 | ISO/IEC 8824-1.
058 * Additionally, for the purposes of determining the order in which
059 * components are encoded when one or more component is an untagged
060 * choice type, each untagged choice type is ordered as though it
061 * has a tag equal to that of the smallest tag in that choice type
062 * or any untagged choice types nested within.
063 *
064 * <h3>10: Distinguished encoding rules</h3>
065 * <h4>10.1 Length forms</h4>
066 * The definite form of length encoding shall be used,
067 * encoded in the minimum number of octets.
068 * [Contrast with 8.1.3.2 b).]
069 * <h4>10.3 Set components</h4>
070 * The encodings of the component values of a set value shall appear
071 * in an order determined by their tags as specified
072 * in 8.6 of ITU-T Rec. X.680 | ISO/IEC 8824-1.
073 * <blockquote>
074 * NOTE &mdash; Where a component of the set is an untagged choice type,
075 * the location of that component in the ordering will depend on
076 * the tag of the choice component being encoded.
077 * </blockquote>
078 *
079 * <h3>11: Restrictions on BER employed by both CER and DER</h3>
080 * <h4>11.5 Set and sequence components with default value </h4>
081 * The encoding of a set value or sequence value shall not include
082 * an encoding for any component value which is equal to
083 * its default value.
084 * <h4>11.6 Set-of components </h4>
085 * <p>
086 * The encodings of the component values of a set-of value
087 * shall appear in ascending order, the encodings being compared
088 * as octet strings with the shorter components being padded at
089 * their trailing end with 0-octets.
090 * <blockquote>
091 * NOTE &mdash; The padding octets are for comparison purposes only
092 * and do not appear in the encodings.
093 * </blockquote>
094 */
095public abstract class ASN1Set
096    extends ASN1Primitive
097{
098    private Vector set = new Vector();
099    private boolean isSorted = false;
100
101    /**
102     * return an ASN1Set from the given object.
103     *
104     * @param obj the object we want converted.
105     * @exception IllegalArgumentException if the object cannot be converted.
106     * @return an ASN1Set instance, or null.
107     */
108    public static ASN1Set getInstance(
109        Object  obj)
110    {
111        if (obj == null || obj instanceof ASN1Set)
112        {
113            return (ASN1Set)obj;
114        }
115        else if (obj instanceof ASN1SetParser)
116        {
117            return ASN1Set.getInstance(((ASN1SetParser)obj).toASN1Primitive());
118        }
119        else if (obj instanceof byte[])
120        {
121            try
122            {
123                return ASN1Set.getInstance(ASN1Primitive.fromByteArray((byte[])obj));
124            }
125            catch (IOException e)
126            {
127                throw new IllegalArgumentException("failed to construct set from byte[]: " + e.getMessage());
128            }
129        }
130        else if (obj instanceof ASN1Encodable)
131        {
132            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
133
134            if (primitive instanceof ASN1Set)
135            {
136                return (ASN1Set)primitive;
137            }
138        }
139
140        throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
141    }
142
143    /**
144     * Return an ASN1 set from a tagged object. There is a special
145     * case here, if an object appears to have been explicitly tagged on 
146     * reading but we were expecting it to be implicitly tagged in the 
147     * normal course of events it indicates that we lost the surrounding
148     * set - so we need to add it back (this will happen if the tagged
149     * object is a sequence that contains other sequences). If you are
150     * dealing with implicitly tagged sets you really <b>should</b>
151     * be using this method.
152     *
153     * @param obj the tagged object.
154     * @param explicit true if the object is meant to be explicitly tagged
155     *          false otherwise.
156     * @exception IllegalArgumentException if the tagged object cannot
157     *          be converted.
158     * @return an ASN1Set instance.
159     */
160    public static ASN1Set getInstance(
161        ASN1TaggedObject    obj,
162        boolean             explicit)
163    {
164        if (explicit)
165        {
166            if (!obj.isExplicit())
167            {
168                throw new IllegalArgumentException("object implicit - explicit expected.");
169            }
170
171            return (ASN1Set)obj.getObject();
172        }
173        else
174        {
175            //
176            // constructed object which appears to be explicitly tagged
177            // and it's really implicit means we have to add the
178            // surrounding set.
179            //
180            if (obj.isExplicit())
181            {
182                if (obj instanceof BERTaggedObject)
183                {
184                    return new BERSet(obj.getObject());
185                }
186                else
187                {
188                    return new DLSet(obj.getObject());
189                }
190            }
191            else
192            {
193                if (obj.getObject() instanceof ASN1Set)
194                {
195                    return (ASN1Set)obj.getObject();
196                }
197
198                //
199                // in this case the parser returns a sequence, convert it
200                // into a set.
201                //
202                if (obj.getObject() instanceof ASN1Sequence)
203                {
204                    ASN1Sequence s = (ASN1Sequence)obj.getObject();
205
206                    if (obj instanceof BERTaggedObject)
207                    {
208                        return new BERSet(s.toArray());
209                    }
210                    else
211                    {
212                        return new DLSet(s.toArray());
213                    }
214                }
215            }
216        }
217
218        throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
219    }
220
221    protected ASN1Set()
222    {
223    }
224
225    /**
226     * create a sequence containing one object
227     * @param obj object to be added to the SET.
228     */
229    protected ASN1Set(
230        ASN1Encodable obj)
231    {
232        set.addElement(obj);
233    }
234
235    /**
236     * create a sequence containing a vector of objects.
237     * @param v a vector of objects to make up the SET.
238     * @param doSort true if should be sorted DER style, false otherwise.
239     */
240    protected ASN1Set(
241        ASN1EncodableVector v,
242        boolean                  doSort)
243    {
244        for (int i = 0; i != v.size(); i++)
245        {
246            set.addElement(v.get(i));
247        }
248
249        if (doSort)
250        {
251            this.sort();
252        }
253    }
254
255    /**
256     * create a sequence containing a vector of objects.
257     */
258    protected ASN1Set(
259        ASN1Encodable[]   array,
260        boolean doSort)
261    {
262        for (int i = 0; i != array.length; i++)
263        {
264            set.addElement(array[i]);
265        }
266
267        if (doSort)
268        {
269            this.sort();
270        }
271    }
272
273    public Enumeration getObjects()
274    {
275        return set.elements();
276    }
277
278    /**
279     * return the object at the set position indicated by index.
280     *
281     * @param index the set number (starting at zero) of the object
282     * @return the object at the set position indicated by index.
283     */
284    public ASN1Encodable getObjectAt(
285        int index)
286    {
287        return (ASN1Encodable)set.elementAt(index);
288    }
289
290    /**
291     * return the number of objects in this set.
292     *
293     * @return the number of objects in this set.
294     */
295    public int size()
296    {
297        return set.size();
298    }
299
300    public ASN1Encodable[] toArray()
301    {
302        ASN1Encodable[] values = new ASN1Encodable[this.size()];
303
304        for (int i = 0; i != this.size(); i++)
305        {
306            values[i] = this.getObjectAt(i);
307        }
308
309        return values;
310    }
311
312    public ASN1SetParser parser()
313    {
314        final ASN1Set outer = this;
315
316        return new ASN1SetParser()
317        {
318            private final int max = size();
319
320            private int index;
321
322            public ASN1Encodable readObject() throws IOException
323            {
324                if (index == max)
325                {
326                    return null;
327                }
328
329                ASN1Encodable obj = getObjectAt(index++);
330                if (obj instanceof ASN1Sequence)
331                {
332                    return ((ASN1Sequence)obj).parser();
333                }
334                if (obj instanceof ASN1Set)
335                {
336                    return ((ASN1Set)obj).parser();
337                }
338
339                return obj;
340            }
341
342            public ASN1Primitive getLoadedObject()
343            {
344                return outer;
345            }
346
347            public ASN1Primitive toASN1Primitive()
348            {
349                return outer;
350            }
351        };
352    }
353
354    public int hashCode()
355    {
356        Enumeration             e = this.getObjects();
357        int                     hashCode = size();
358
359        while (e.hasMoreElements())
360        {
361            Object o = getNext(e);
362            hashCode *= 17;
363
364            hashCode ^= o.hashCode();
365        }
366
367        return hashCode;
368    }
369
370    /**
371     * Change current SET object to be encoded as {@link DERSet}.
372     * This is part of Distinguished Encoding Rules form serialization.
373     */
374    ASN1Primitive toDERObject()
375    {
376        if (isSorted)
377        {
378            ASN1Set derSet = new DERSet();
379
380            derSet.set = this.set;
381
382            return derSet;
383        }
384        else
385        {
386            Vector v = new Vector();
387
388            for (int i = 0; i != set.size(); i++)
389            {
390                v.addElement(set.elementAt(i));
391            }
392
393            ASN1Set derSet = new DERSet();
394
395            derSet.set = v;
396
397            derSet.sort();
398
399            return derSet;
400        }
401    }
402
403    /**
404     * Change current SET object to be encoded as {@link DLSet}.
405     * This is part of Direct Length form serialization.
406     */
407    ASN1Primitive toDLObject()
408    {
409        ASN1Set derSet = new DLSet();
410
411        derSet.set = this.set;
412
413        return derSet;
414    }
415
416    boolean asn1Equals(
417        ASN1Primitive o)
418    {
419        if (!(o instanceof ASN1Set))
420        {
421            return false;
422        }
423
424        ASN1Set   other = (ASN1Set)o;
425
426        if (this.size() != other.size())
427        {
428            return false;
429        }
430
431        Enumeration s1 = this.getObjects();
432        Enumeration s2 = other.getObjects();
433
434        while (s1.hasMoreElements())
435        {
436            ASN1Encodable obj1 = getNext(s1);
437            ASN1Encodable obj2 = getNext(s2);
438
439            ASN1Primitive o1 = obj1.toASN1Primitive();
440            ASN1Primitive o2 = obj2.toASN1Primitive();
441
442            if (o1 == o2 || o1.equals(o2))
443            {
444                continue;
445            }
446
447            return false;
448        }
449
450        return true;
451    }
452
453    private ASN1Encodable getNext(Enumeration e)
454    {
455        ASN1Encodable encObj = (ASN1Encodable)e.nextElement();
456
457        // unfortunately null was allowed as a substitute for DER null
458        if (encObj == null)
459        {
460            return DERNull.INSTANCE;
461        }
462
463        return encObj;
464    }
465
466    /**
467     * return true if a <= b (arrays are assumed padded with zeros).
468     */
469    private boolean lessThanOrEqual(
470         byte[] a,
471         byte[] b)
472    {
473        int len = Math.min(a.length, b.length);
474        for (int i = 0; i != len; ++i)
475        {
476            if (a[i] != b[i])
477            {
478                return (a[i] & 0xff) < (b[i] & 0xff);
479            }
480        }
481        return len == a.length;
482    }
483
484    private byte[] getDEREncoded(
485        ASN1Encodable obj)
486    {
487        try
488        {
489            return obj.toASN1Primitive().getEncoded(ASN1Encoding.DER);
490        }
491        catch (IOException e)
492        {
493            throw new IllegalArgumentException("cannot encode object added to SET");
494        }
495    }
496
497    protected void sort()
498    {
499        if (!isSorted)
500        {
501            isSorted = true;
502            if (set.size() > 1)
503            {
504                boolean    swapped = true;
505                int        lastSwap = set.size() - 1;
506
507                while (swapped)
508                {
509                    int    index = 0;
510                    int    swapIndex = 0;
511                    byte[] a = getDEREncoded((ASN1Encodable)set.elementAt(0));
512
513                    swapped = false;
514
515                    while (index != lastSwap)
516                    {
517                        byte[] b = getDEREncoded((ASN1Encodable)set.elementAt(index + 1));
518
519                        if (lessThanOrEqual(a, b))
520                        {
521                            a = b;
522                        }
523                        else
524                        {
525                            Object  o = set.elementAt(index);
526
527                            set.setElementAt(set.elementAt(index + 1), index);
528                            set.setElementAt(o, index + 1);
529
530                            swapped = true;
531                            swapIndex = index;
532                        }
533
534                        index++;
535                    }
536
537                    lastSwap = swapIndex;
538                }
539            }
540        }
541    }
542
543    boolean isConstructed()
544    {
545        return true;
546    }
547
548    abstract void encode(ASN1OutputStream out)
549            throws IOException;
550
551    public String toString() 
552    {
553        return set.toString();
554    }
555}