1 package org.bouncycastle.asn1;
2 
3 import java.io.IOException;
4 
5 /**
6  * A DER encoded SET object
7  * <p>
8  * For X.690 syntax rules, see {@link ASN1Set}.
9  * </p><p>
10  * For short: Constructing this form does sort the supplied elements,
11  * and the sorting happens also before serialization (if necesssary).
12  * This is different from the way {@link BERSet},{@link DLSet} does things.
13  * </p>
14  */
15 public class DERSet
16     extends ASN1Set
17 {
convert(ASN1Set set)18     public static DERSet convert(ASN1Set set)
19     {
20         return (DERSet)set.toDERObject();
21     }
22 
23     private int bodyLength = -1;
24 
25     /**
26      * create an empty set
27      */
DERSet()28     public DERSet()
29     {
30     }
31 
32     /**
33      * create a set containing one object
34      * @param element the object to go in the set
35      */
DERSet(ASN1Encodable element)36     public DERSet(ASN1Encodable element)
37     {
38         super(element);
39     }
40 
41     /**
42      * create a set containing a vector of objects.
43      * @param elementVector the vector of objects to make up the set.
44      */
DERSet(ASN1EncodableVector elementVector)45     public DERSet(ASN1EncodableVector elementVector)
46     {
47         super(elementVector, true);
48     }
49 
50     /**
51      * create a set containing an array of objects.
52      * @param elements the array of objects to make up the set.
53      */
DERSet(ASN1Encodable[] elements)54     public DERSet(ASN1Encodable[] elements)
55     {
56         super(elements, true);
57     }
58 
DERSet(boolean isSorted, ASN1Encodable[] elements)59     DERSet(boolean isSorted, ASN1Encodable[] elements)
60     {
61         super(checkSorted(isSorted), elements);
62     }
63 
getBodyLength()64     private int getBodyLength() throws IOException
65     {
66         if (bodyLength < 0)
67         {
68             int count = elements.length;
69             int totalLength = 0;
70 
71             for (int i = 0; i < count; ++i)
72             {
73                 ASN1Primitive derObject = elements[i].toASN1Primitive().toDERObject();
74                 totalLength += derObject.encodedLength();
75             }
76 
77             this.bodyLength = totalLength;
78         }
79 
80         return bodyLength;
81     }
82 
encodedLength()83     int encodedLength() throws IOException
84     {
85         int length = getBodyLength();
86 
87         return 1 + StreamUtil.calculateBodyLength(length) + length;
88     }
89 
90     /*
91      * A note on the implementation:
92      * <p>
93      * As DER requires the constructed, definite-length model to
94      * be used for structured types, this varies slightly from the
95      * ASN.1 descriptions given. Rather than just outputting SET,
96      * we also have to specify CONSTRUCTED, and the objects length.
97      */
encode(ASN1OutputStream out, boolean withTag)98     void encode(ASN1OutputStream out, boolean withTag) throws IOException
99     {
100         if (withTag)
101         {
102             out.write(BERTags.SET | BERTags.CONSTRUCTED);
103         }
104 
105         DEROutputStream derOut = out.getDERSubStream();
106 
107         int count = elements.length;
108         if (bodyLength >= 0 || count > 16)
109         {
110             out.writeLength(getBodyLength());
111 
112             for (int i = 0; i < count; ++i)
113             {
114                 ASN1Primitive derObject = elements[i].toASN1Primitive().toDERObject();
115                 derObject.encode(derOut, true);
116             }
117         }
118         else
119         {
120             int totalLength = 0;
121 
122             ASN1Primitive[] derObjects = new ASN1Primitive[count];
123             for (int i = 0; i < count; ++i)
124             {
125                 ASN1Primitive derObject = elements[i].toASN1Primitive().toDERObject();
126                 derObjects[i] = derObject;
127                 totalLength += derObject.encodedLength();
128             }
129 
130             this.bodyLength = totalLength;
131             out.writeLength(totalLength);
132 
133             for (int i = 0; i < count; ++i)
134             {
135                 derObjects[i].encode(derOut, true);
136             }
137         }
138     }
139 
toDERObject()140     ASN1Primitive toDERObject()
141     {
142         return isSorted ? this : super.toDERObject();
143     }
144 
toDLObject()145     ASN1Primitive toDLObject()
146     {
147         return this;
148     }
149 
checkSorted(boolean isSorted)150     private static boolean checkSorted(boolean isSorted)
151     {
152         if (!isSorted)
153         {
154             throw new IllegalStateException("DERSet elements should always be in sorted order");
155         }
156         return isSorted;
157     }
158 }
159