1 package org.bouncycastle.asn1;
2 
3 import java.io.IOException;
4 import java.math.BigInteger;
5 
6 import org.bouncycastle.util.Arrays;
7 import org.bouncycastle.util.Properties;
8 
9 /**
10  * Class representing the ASN.1 INTEGER type.
11  */
12 public class ASN1Integer
13     extends ASN1Primitive
14 {
15     static final int SIGN_EXT_SIGNED = 0xFFFFFFFF;
16     static final int SIGN_EXT_UNSIGNED = 0xFF;
17 
18     private final byte[] bytes;
19     private final int start;
20 
21     /**
22      * Return an integer from the passed in object.
23      *
24      * @param obj an ASN1Integer or an object that can be converted into one.
25      * @return an ASN1Integer instance.
26      * @throws IllegalArgumentException if the object cannot be converted.
27      */
getInstance( Object obj)28     public static ASN1Integer getInstance(
29         Object obj)
30     {
31         if (obj == null || obj instanceof ASN1Integer)
32         {
33             return (ASN1Integer)obj;
34         }
35 
36         if (obj instanceof byte[])
37         {
38             try
39             {
40                 return (ASN1Integer)fromByteArray((byte[])obj);
41             }
42             catch (Exception e)
43             {
44                 throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
45             }
46         }
47 
48         throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
49     }
50 
51     /**
52      * Return an Integer from a tagged object.
53      *
54      * @param obj      the tagged object holding the object we want
55      * @param explicit true if the object is meant to be explicitly
56      *                 tagged false otherwise.
57      * @return an ASN1Integer instance.
58      * @throws IllegalArgumentException if the tagged object cannot
59      * be converted.
60      */
getInstance( ASN1TaggedObject obj, boolean explicit)61     public static ASN1Integer getInstance(
62         ASN1TaggedObject obj,
63         boolean explicit)
64     {
65         ASN1Primitive o = obj.getObject();
66 
67         if (explicit || o instanceof ASN1Integer)
68         {
69             return getInstance(o);
70         }
71         else
72         {
73             return new ASN1Integer(ASN1OctetString.getInstance(o).getOctets());
74         }
75     }
76 
77     /**
78      * Construct an INTEGER from the passed in long value.
79      *
80      * @param value the long representing the value desired.
81      */
ASN1Integer(long value)82     public ASN1Integer(long value)
83     {
84         this.bytes = BigInteger.valueOf(value).toByteArray();
85         this.start = 0;
86     }
87 
88     /**
89      * Construct an INTEGER from the passed in BigInteger value.
90      *
91      * @param value the BigInteger representing the value desired.
92      */
ASN1Integer(BigInteger value)93     public ASN1Integer(BigInteger value)
94     {
95         this.bytes = value.toByteArray();
96         this.start = 0;
97     }
98 
99     /**
100      * Construct an INTEGER from the passed in byte array.
101      *
102      * <p>
103      * <b>NB: Strict Validation applied by default.</b>
104      * </p>
105      * <p>
106      * It has turned out that there are still a few applications that struggle with
107      * the ASN.1 BER encoding rules for an INTEGER as described in:
108      *
109      * https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf
110      * Section 8.3.2.
111      * </p>
112      * <p>
113      * Users can set the 'org.bouncycastle.asn1.allow_unsafe_integer' to 'true'
114      * and a looser validation will be applied. Users must recognise that this is
115      * not ideal and may pave the way for an exploit based around a faulty encoding
116      * in the future.
117      * </p>
118      *
119      * @param bytes the byte array representing a 2's complement encoding of a BigInteger.
120      */
ASN1Integer(byte[] bytes)121     public ASN1Integer(byte[] bytes)
122     {
123         this(bytes, true);
124     }
125 
ASN1Integer(byte[] bytes, boolean clone)126     ASN1Integer(byte[] bytes, boolean clone)
127     {
128         if (isMalformed(bytes))
129         {
130             throw new IllegalArgumentException("malformed integer");
131         }
132 
133         this.bytes = clone ? Arrays.clone(bytes) : bytes;
134         this.start = signBytesToSkip(bytes);
135     }
136 
137     /**
138      * in some cases positive values get crammed into a space,
139      * that's not quite big enough...
140      *
141      * @return the BigInteger that results from treating this ASN.1 INTEGER as unsigned.
142      */
getPositiveValue()143     public BigInteger getPositiveValue()
144     {
145         return new BigInteger(1, bytes);
146     }
147 
getValue()148     public BigInteger getValue()
149     {
150         return new BigInteger(bytes);
151     }
152 
hasValue(int x)153     public boolean hasValue(int x)
154     {
155         return (bytes.length - start) <= 4
156             && intValue(bytes, start, SIGN_EXT_SIGNED) == x;
157     }
158 
hasValue(long x)159     public boolean hasValue(long x)
160     {
161         return (bytes.length - start) <= 8
162             && longValue(bytes, start, SIGN_EXT_SIGNED) == x;
163     }
164 
hasValue(BigInteger x)165     public boolean hasValue(BigInteger x)
166     {
167         return null != x
168             // Fast check to avoid allocation
169             && intValue(bytes, start, SIGN_EXT_SIGNED) == x.intValue()
170             && getValue().equals(x);
171     }
172 
intPositiveValueExact()173     public int intPositiveValueExact()
174     {
175         int count = bytes.length - start;
176         if (count > 4 || (count == 4 && 0 != (bytes[start] & 0x80)))
177         {
178             throw new ArithmeticException("ASN.1 Integer out of positive int range");
179         }
180 
181         return intValue(bytes, start, SIGN_EXT_UNSIGNED);
182     }
183 
intValueExact()184     public int intValueExact()
185     {
186         int count = bytes.length - start;
187         if (count > 4)
188         {
189             throw new ArithmeticException("ASN.1 Integer out of int range");
190         }
191 
192         return intValue(bytes, start, SIGN_EXT_SIGNED);
193     }
194 
longValueExact()195     public long longValueExact()
196     {
197         int count = bytes.length - start;
198         if (count > 8)
199         {
200             throw new ArithmeticException("ASN.1 Integer out of long range");
201         }
202 
203         return longValue(bytes, start, SIGN_EXT_SIGNED);
204     }
205 
isConstructed()206     boolean isConstructed()
207     {
208         return false;
209     }
210 
encodedLength()211     int encodedLength()
212     {
213         return 1 + StreamUtil.calculateBodyLength(bytes.length) + bytes.length;
214     }
215 
encode(ASN1OutputStream out, boolean withTag)216     void encode(ASN1OutputStream out, boolean withTag) throws IOException
217     {
218         out.writeEncoded(withTag, BERTags.INTEGER, bytes);
219     }
220 
hashCode()221     public int hashCode()
222     {
223         return Arrays.hashCode(bytes);
224     }
225 
asn1Equals(ASN1Primitive o)226     boolean asn1Equals(ASN1Primitive o)
227     {
228         if (!(o instanceof ASN1Integer))
229         {
230             return false;
231         }
232 
233         ASN1Integer other = (ASN1Integer)o;
234 
235         return Arrays.areEqual(this.bytes, other.bytes);
236     }
237 
toString()238     public String toString()
239     {
240         return getValue().toString();
241     }
242 
intValue(byte[] bytes, int start, int signExt)243     static int intValue(byte[] bytes, int start, int signExt)
244     {
245         int length = bytes.length;
246         int pos = Math.max(start, length - 4);
247 
248         int val = bytes[pos] & signExt;
249         while (++pos < length)
250         {
251             val = (val << 8) | (bytes[pos] & SIGN_EXT_UNSIGNED);
252         }
253         return val;
254     }
255 
longValue(byte[] bytes, int start, int signExt)256     static long longValue(byte[] bytes, int start, int signExt)
257     {
258         int length = bytes.length;
259         int pos = Math.max(start, length - 8);
260 
261         long val = bytes[pos] & signExt;
262         while (++pos < length)
263         {
264             val = (val << 8) | (bytes[pos] & SIGN_EXT_UNSIGNED);
265         }
266         return val;
267     }
268 
269     /**
270      * Apply the correct validation for an INTEGER primitive following the BER rules.
271      *
272      * @param bytes The raw encoding of the integer.
273      * @return true if the (in)put fails this validation.
274      */
isMalformed(byte[] bytes)275     static boolean isMalformed(byte[] bytes)
276     {
277         switch (bytes.length)
278         {
279         case 0:
280             return true;
281         case 1:
282             return false;
283         default:
284             return bytes[0] == (bytes[1] >> 7)
285                 // Apply loose validation, see note in public constructor ASN1Integer(byte[])
286                 && !Properties.isOverrideSet("org.bouncycastle.asn1.allow_unsafe_integer");
287         }
288     }
289 
signBytesToSkip(byte[] bytes)290     static int signBytesToSkip(byte[] bytes)
291     {
292         int pos = 0, last = bytes.length - 1;
293         while (pos < last
294             && bytes[pos] == (bytes[pos + 1] >> 7))
295         {
296             ++pos;
297         }
298         return pos;
299     }
300 }
301