1 package org.bouncycastle.asn1;
2 
3 import java.io.IOException;
4 
5 /**
6  * ASN.1 TaggedObject - in ASN.1 notation this is any object preceded by
7  * a [n] where n is some number - these are assumed to follow the construction
8  * rules (as with sequences).
9  */
10 public abstract class ASN1TaggedObject
11     extends ASN1Primitive
12     implements ASN1TaggedObjectParser
13 {
14     int             tagNo;
15     boolean         empty = false;
16     boolean         explicit = true;
17     ASN1Encodable obj = null;
18 
getInstance( ASN1TaggedObject obj, boolean explicit)19     static public ASN1TaggedObject getInstance(
20         ASN1TaggedObject    obj,
21         boolean             explicit)
22     {
23         if (explicit)
24         {
25             return (ASN1TaggedObject)obj.getObject();
26         }
27 
28         throw new IllegalArgumentException("implicitly tagged tagged object");
29     }
30 
getInstance( Object obj)31     static public ASN1TaggedObject getInstance(
32         Object obj)
33     {
34         if (obj == null || obj instanceof ASN1TaggedObject)
35         {
36                 return (ASN1TaggedObject)obj;
37         }
38         else if (obj instanceof byte[])
39         {
40             try
41             {
42                 return ASN1TaggedObject.getInstance(fromByteArray((byte[])obj));
43             }
44             catch (IOException e)
45             {
46                 throw new IllegalArgumentException("failed to construct tagged object from byte[]: " + e.getMessage());
47             }
48         }
49 
50         throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
51     }
52 
53     /**
54      * Create a tagged object with the style given by the value of explicit.
55      * <p>
56      * If the object implements ASN1Choice the tag style will always be changed
57      * to explicit in accordance with the ASN.1 encoding rules.
58      * </p>
59      * @param explicit true if the object is explicitly tagged.
60      * @param tagNo the tag number for this object.
61      * @param obj the tagged object.
62      */
ASN1TaggedObject( boolean explicit, int tagNo, ASN1Encodable obj)63     public ASN1TaggedObject(
64         boolean         explicit,
65         int             tagNo,
66         ASN1Encodable   obj)
67     {
68         if (obj instanceof ASN1Choice)
69         {
70             this.explicit = true;
71         }
72         else
73         {
74             this.explicit = explicit;
75         }
76 
77         this.tagNo = tagNo;
78 
79         if (this.explicit)
80         {
81             this.obj = obj;
82         }
83         else
84         {
85             ASN1Primitive prim = obj.toASN1Primitive();
86 
87             if (prim instanceof ASN1Set)
88             {
89                 ASN1Set s = null;
90             }
91 
92             this.obj = obj;
93         }
94     }
95 
asn1Equals( ASN1Primitive o)96     boolean asn1Equals(
97         ASN1Primitive o)
98     {
99         if (!(o instanceof ASN1TaggedObject))
100         {
101             return false;
102         }
103 
104         ASN1TaggedObject other = (ASN1TaggedObject)o;
105 
106         if (tagNo != other.tagNo || empty != other.empty || explicit != other.explicit)
107         {
108             return false;
109         }
110 
111         if(obj == null)
112         {
113             if (other.obj != null)
114             {
115                 return false;
116             }
117         }
118         else
119         {
120             if (!(obj.toASN1Primitive().equals(other.obj.toASN1Primitive())))
121             {
122                 return false;
123             }
124         }
125 
126         return true;
127     }
128 
hashCode()129     public int hashCode()
130     {
131         int code = tagNo;
132 
133         // TODO: actually this is wrong - the problem is that a re-encoded
134         // object may end up with a different hashCode due to implicit
135         // tagging. As implicit tagging is ambiguous if a sequence is involved
136         // it seems the only correct method for both equals and hashCode is to
137         // compare the encodings...
138         if (obj != null)
139         {
140             code ^= obj.hashCode();
141         }
142 
143         return code;
144     }
145 
getTagNo()146     public int getTagNo()
147     {
148         return tagNo;
149     }
150 
151     /**
152      * return whether or not the object may be explicitly tagged.
153      * <p>
154      * Note: if the object has been read from an input stream, the only
155      * time you can be sure if isExplicit is returning the true state of
156      * affairs is if it returns false. An implicitly tagged object may appear
157      * to be explicitly tagged, so you need to understand the context under
158      * which the reading was done as well, see getObject below.
159      */
isExplicit()160     public boolean isExplicit()
161     {
162         return explicit;
163     }
164 
isEmpty()165     public boolean isEmpty()
166     {
167         return empty;
168     }
169 
170     /**
171      * return whatever was following the tag.
172      * <p>
173      * Note: tagged objects are generally context dependent if you're
174      * trying to extract a tagged object you should be going via the
175      * appropriate getInstance method.
176      */
getObject()177     public ASN1Primitive getObject()
178     {
179         if (obj != null)
180         {
181             return obj.toASN1Primitive();
182         }
183 
184         return null;
185     }
186 
187     /**
188      * Return the object held in this tagged object as a parser assuming it has
189      * the type of the passed in tag. If the object doesn't have a parser
190      * associated with it, the base object is returned.
191      */
getObjectParser( int tag, boolean isExplicit)192     public ASN1Encodable getObjectParser(
193         int     tag,
194         boolean isExplicit)
195     {
196         switch (tag)
197         {
198         case BERTags.SET:
199             return ASN1Set.getInstance(this, isExplicit).parser();
200         case BERTags.SEQUENCE:
201             return ASN1Sequence.getInstance(this, isExplicit).parser();
202         case BERTags.OCTET_STRING:
203             return ASN1OctetString.getInstance(this, isExplicit).parser();
204         }
205 
206         if (isExplicit)
207         {
208             return getObject();
209         }
210 
211         throw new RuntimeException("implicit tagging not implemented for tag: " + tag);
212     }
213 
getLoadedObject()214     public ASN1Primitive getLoadedObject()
215     {
216         return this.toASN1Primitive();
217     }
218 
toDERObject()219     ASN1Primitive toDERObject()
220     {
221         return new DERTaggedObject(explicit, tagNo, obj);
222     }
223 
toDLObject()224     ASN1Primitive toDLObject()
225     {
226         return new DLTaggedObject(explicit, tagNo, obj);
227     }
228 
encode(ASN1OutputStream out)229     abstract void encode(ASN1OutputStream out)
230         throws IOException;
231 
toString()232     public String toString()
233     {
234         return "[" + tagNo + "]" + obj;
235     }
236 }
237