1 package org.bouncycastle.asn1;
2 
3 import java.io.ByteArrayOutputStream;
4 import java.io.IOException;
5 import java.util.Enumeration;
6 import java.util.Vector;
7 
8 /**
9  * ASN.1 <code>SET</code> and <code>SET OF</code> constructs.
10  * <p>
11  * Note: This does not know which syntax the set is!
12  * (The difference: ordering of SET elements or not ordering.)
13  * <p>
14  * DER form is always definite form length fields, while
15  * BER support uses indefinite form.
16  * <p>
17  * The CER form support does not exist.
18  * <p>
19  * <hr>
20  * <h2>X.690</h2>
21  * <h3>8: Basic encoding rules</h3>
22  * <h4>8.11 Encoding of a set value </h4>
23  * <b>8.11.1</b> The encoding of a set value shall be constructed
24  * <p>
25  * <b>8.11.2</b> The contents octets shall consist of the complete
26  * encoding of a data value from each of the types listed in the
27  * ASN.1 definition of the set type, in an order chosen by the sender,
28  * unless the type was referenced with the keyword
29  * <b>OPTIONAL</b> or the keyword <b>DEFAULT</b>.
30  * <p>
31  * <b>8.11.3</b> The encoding of a data value may, but need not,
32  * be present for a type which was referenced with the keyword
33  * <b>OPTIONAL</b> or the keyword <b>DEFAULT</b>.
34  * <blockquote>
35  * NOTE &mdash; The order of data values in a set value is not significant,
36  * and places no constraints on the order during transfer
37  * </blockquote>
38  * <h4>8.12 Encoding of a set-of value</h4>
39  * <b>8.12.1</b> The encoding of a set-of value shall be constructed.
40  * <p>
41  * <b>8.12.2</b> The text of 8.10.2 applies:
42  * <i>The contents octets shall consist of zero,
43  * one or more complete encodings of data values from the type listed in
44  * the ASN.1 definition.</i>
45  * <p>
46  * <b>8.12.3</b> The order of data values need not be preserved by
47  * the encoding and subsequent decoding.
48  *
49  * <h3>9: Canonical encoding rules</h3>
50  * <h4>9.1 Length forms</h4>
51  * If the encoding is constructed, it shall employ the indefinite length form.
52  * If the encoding is primitive, it shall include the fewest length octets necessary.
53  * [Contrast with 8.1.3.2 b).]
54  * <h4>9.3 Set components</h4>
55  * The encodings of the component values of a set value shall
56  * appear in an order determined by their tags as specified
57  * in 8.6 of ITU-T Rec. X.680 | ISO/IEC 8824-1.
58  * Additionally, for the purposes of determining the order in which
59  * components are encoded when one or more component is an untagged
60  * choice type, each untagged choice type is ordered as though it
61  * has a tag equal to that of the smallest tag in that choice type
62  * or any untagged choice types nested within.
63  *
64  * <h3>10: Distinguished encoding rules</h3>
65  * <h4>10.1 Length forms</h4>
66  * The definite form of length encoding shall be used,
67  * encoded in the minimum number of octets.
68  * [Contrast with 8.1.3.2 b).]
69  * <h4>10.3 Set components</h4>
70  * The encodings of the component values of a set value shall appear
71  * in an order determined by their tags as specified
72  * in 8.6 of ITU-T Rec. X.680 | ISO/IEC 8824-1.
73  * <blockquote>
74  * NOTE &mdash; Where a component of the set is an untagged choice type,
75  * the location of that component in the ordering will depend on
76  * the tag of the choice component being encoded.
77  * </blockquote>
78  *
79  * <h3>11: Restrictions on BER employed by both CER and DER</h3>
80  * <h4>11.5 Set and sequence components with default value </h4>
81  * The encoding of a set value or sequence value shall not include
82  * an encoding for any component value which is equal to
83  * its default value.
84  * <h4>11.6 Set-of components </h4>
85  * <p>
86  * The encodings of the component values of a set-of value
87  * shall appear in ascending order, the encodings being compared
88  * as octet strings with the shorter components being padded at
89  * their trailing end with 0-octets.
90  * <blockquote>
91  * NOTE &mdash; The padding octets are for comparison purposes only
92  * and do not appear in the encodings.
93  * </blockquote>
94  */
95 public abstract class ASN1Set
96     extends ASN1Primitive
97 {
98     private Vector set = new Vector();
99     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      */
getInstance( Object obj)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      */
getInstance( ASN1TaggedObject obj, boolean explicit)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 
ASN1Set()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      */
ASN1Set( ASN1Encodable obj)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      */
ASN1Set( ASN1EncodableVector v, boolean doSort)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      */
ASN1Set( ASN1Encodable[] array, boolean doSort)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 
getObjects()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      */
getObjectAt( int index)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      */
size()295     public int size()
296     {
297         return set.size();
298     }
299 
toArray()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 
parser()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 
hashCode()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      */
toDERObject()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      */
toDLObject()407     ASN1Primitive toDLObject()
408     {
409         ASN1Set derSet = new DLSet();
410 
411         derSet.set = this.set;
412 
413         return derSet;
414     }
415 
asn1Equals( ASN1Primitive o)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 
getNext(Enumeration e)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      */
lessThanOrEqual( byte[] a, byte[] b)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 
getDEREncoded( ASN1Encodable obj)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 
sort()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 
isConstructed()543     boolean isConstructed()
544     {
545         return true;
546     }
547 
encode(ASN1OutputStream out)548     abstract void encode(ASN1OutputStream out)
549             throws IOException;
550 
toString()551     public String toString()
552     {
553         return set.toString();
554     }
555 }
556