1package org.bouncycastle.asn1;
2
3import java.io.ByteArrayOutputStream;
4import java.io.IOException;
5
6/**
7 * Class representing the DER-type External
8 */
9public class DERExternal
10    extends ASN1Primitive
11{
12    private ASN1ObjectIdentifier directReference;
13    private ASN1Integer indirectReference;
14    private ASN1Primitive dataValueDescriptor;
15    private int encoding;
16    private ASN1Primitive externalContent;
17
18    public DERExternal(ASN1EncodableVector vector)
19    {
20        int offset = 0;
21
22        ASN1Primitive enc = getObjFromVector(vector, offset);
23        if (enc instanceof ASN1ObjectIdentifier)
24        {
25            directReference = (ASN1ObjectIdentifier)enc;
26            offset++;
27            enc = getObjFromVector(vector, offset);
28        }
29        if (enc instanceof ASN1Integer)
30        {
31            indirectReference = (ASN1Integer) enc;
32            offset++;
33            enc = getObjFromVector(vector, offset);
34        }
35        if (!(enc instanceof ASN1TaggedObject))
36        {
37            dataValueDescriptor = (ASN1Primitive) enc;
38            offset++;
39            enc = getObjFromVector(vector, offset);
40        }
41
42        if (vector.size() != offset + 1)
43        {
44            throw new IllegalArgumentException("input vector too large");
45        }
46
47        if (!(enc instanceof ASN1TaggedObject))
48        {
49            throw new IllegalArgumentException("No tagged object found in vector. Structure doesn't seem to be of type External");
50        }
51        ASN1TaggedObject obj = (ASN1TaggedObject)enc;
52        setEncoding(obj.getTagNo());
53        externalContent = obj.getObject();
54    }
55
56    private ASN1Primitive getObjFromVector(ASN1EncodableVector v, int index)
57    {
58        if (v.size() <= index)
59        {
60            throw new IllegalArgumentException("too few objects in input vector");
61        }
62
63        return v.get(index).toASN1Primitive();
64    }
65    /**
66     * Creates a new instance of DERExternal
67     * See X.690 for more informations about the meaning of these parameters
68     * @param directReference The direct reference or <code>null</code> if not set.
69     * @param indirectReference The indirect reference or <code>null</code> if not set.
70     * @param dataValueDescriptor The data value descriptor or <code>null</code> if not set.
71     * @param externalData The external data in its encoded form.
72     */
73    public DERExternal(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference, ASN1Primitive dataValueDescriptor, DERTaggedObject externalData)
74    {
75        this(directReference, indirectReference, dataValueDescriptor, externalData.getTagNo(), externalData.toASN1Primitive());
76    }
77
78    /**
79     * Creates a new instance of DERExternal.
80     * See X.690 for more informations about the meaning of these parameters
81     * @param directReference The direct reference or <code>null</code> if not set.
82     * @param indirectReference The indirect reference or <code>null</code> if not set.
83     * @param dataValueDescriptor The data value descriptor or <code>null</code> if not set.
84     * @param encoding The encoding to be used for the external data
85     * @param externalData The external data
86     */
87    public DERExternal(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference, ASN1Primitive dataValueDescriptor, int encoding, ASN1Primitive externalData)
88    {
89        setDirectReference(directReference);
90        setIndirectReference(indirectReference);
91        setDataValueDescriptor(dataValueDescriptor);
92        setEncoding(encoding);
93        setExternalContent(externalData.toASN1Primitive());
94    }
95
96    /* (non-Javadoc)
97     * @see java.lang.Object#hashCode()
98     */
99    public int hashCode()
100    {
101        int ret = 0;
102        if (directReference != null)
103        {
104            ret = directReference.hashCode();
105        }
106        if (indirectReference != null)
107        {
108            ret ^= indirectReference.hashCode();
109        }
110        if (dataValueDescriptor != null)
111        {
112            ret ^= dataValueDescriptor.hashCode();
113        }
114        ret ^= externalContent.hashCode();
115        return ret;
116    }
117
118    boolean isConstructed()
119    {
120        return true;
121    }
122
123    int encodedLength()
124        throws IOException
125    {
126        return this.getEncoded().length;
127    }
128
129    /* (non-Javadoc)
130     * @see org.bouncycastle.asn1.ASN1Primitive#encode(org.bouncycastle.asn1.DEROutputStream)
131     */
132    void encode(ASN1OutputStream out)
133        throws IOException
134    {
135        ByteArrayOutputStream baos = new ByteArrayOutputStream();
136        if (directReference != null)
137        {
138            baos.write(directReference.getEncoded(ASN1Encoding.DER));
139        }
140        if (indirectReference != null)
141        {
142            baos.write(indirectReference.getEncoded(ASN1Encoding.DER));
143        }
144        if (dataValueDescriptor != null)
145        {
146            baos.write(dataValueDescriptor.getEncoded(ASN1Encoding.DER));
147        }
148        DERTaggedObject obj = new DERTaggedObject(true, encoding, externalContent);
149        baos.write(obj.getEncoded(ASN1Encoding.DER));
150        out.writeEncoded(BERTags.CONSTRUCTED, BERTags.EXTERNAL, baos.toByteArray());
151    }
152
153    /* (non-Javadoc)
154     * @see org.bouncycastle.asn1.ASN1Primitive#asn1Equals(org.bouncycastle.asn1.ASN1Primitive)
155     */
156    boolean asn1Equals(ASN1Primitive o)
157    {
158        if (!(o instanceof DERExternal))
159        {
160            return false;
161        }
162        if (this == o)
163        {
164            return true;
165        }
166        DERExternal other = (DERExternal)o;
167        if (directReference != null)
168        {
169            if (other.directReference == null || !other.directReference.equals(directReference))
170            {
171                return false;
172            }
173        }
174        if (indirectReference != null)
175        {
176            if (other.indirectReference == null || !other.indirectReference.equals(indirectReference))
177            {
178                return false;
179            }
180        }
181        if (dataValueDescriptor != null)
182        {
183            if (other.dataValueDescriptor == null || !other.dataValueDescriptor.equals(dataValueDescriptor))
184            {
185                return false;
186            }
187        }
188        return externalContent.equals(other.externalContent);
189    }
190
191    /**
192     * Returns the data value descriptor
193     * @return The descriptor
194     */
195    public ASN1Primitive getDataValueDescriptor()
196    {
197        return dataValueDescriptor;
198    }
199
200    /**
201     * Returns the direct reference of the external element
202     * @return The reference
203     */
204    public ASN1ObjectIdentifier getDirectReference()
205    {
206        return directReference;
207    }
208
209    /**
210     * Returns the encoding of the content. Valid values are
211     * <ul>
212     * <li><code>0</code> single-ASN1-type</li>
213     * <li><code>1</code> OCTET STRING</li>
214     * <li><code>2</code> BIT STRING</li>
215     * </ul>
216     * @return The encoding
217     */
218    public int getEncoding()
219    {
220        return encoding;
221    }
222
223    /**
224     * Returns the content of this element
225     * @return The content
226     */
227    public ASN1Primitive getExternalContent()
228    {
229        return externalContent;
230    }
231
232    /**
233     * Returns the indirect reference of this element
234     * @return The reference
235     */
236    public ASN1Integer getIndirectReference()
237    {
238        return indirectReference;
239    }
240
241    /**
242     * Sets the data value descriptor
243     * @param dataValueDescriptor The descriptor
244     */
245    private void setDataValueDescriptor(ASN1Primitive dataValueDescriptor)
246    {
247        this.dataValueDescriptor = dataValueDescriptor;
248    }
249
250    /**
251     * Sets the direct reference of the external element
252     * @param directReferemce The reference
253     */
254    private void setDirectReference(ASN1ObjectIdentifier directReferemce)
255    {
256        this.directReference = directReferemce;
257    }
258
259    /**
260     * Sets the encoding of the content. Valid values are
261     * <ul>
262     * <li><code>0</code> single-ASN1-type</li>
263     * <li><code>1</code> OCTET STRING</li>
264     * <li><code>2</code> BIT STRING</li>
265     * </ul>
266     * @param encoding The encoding
267     */
268    private void setEncoding(int encoding)
269    {
270        if (encoding < 0 || encoding > 2)
271        {
272            throw new IllegalArgumentException("invalid encoding value: " + encoding);
273        }
274        this.encoding = encoding;
275    }
276
277    /**
278     * Sets the content of this element
279     * @param externalContent The content
280     */
281    private void setExternalContent(ASN1Primitive externalContent)
282    {
283        this.externalContent = externalContent;
284    }
285
286    /**
287     * Sets the indirect reference of this element
288     * @param indirectReference The reference
289     */
290    private void setIndirectReference(ASN1Integer indirectReference)
291    {
292        this.indirectReference = indirectReference;
293    }
294}
295