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(BigInteger x)153     public boolean hasValue(BigInteger x)
154     {
155         return null != x
156             // Fast check to avoid allocation
157             && intValue(bytes, start, SIGN_EXT_SIGNED) == x.intValue()
158             && getValue().equals(x);
159     }
160 
intPositiveValueExact()161     public int intPositiveValueExact()
162     {
163         int count = bytes.length - start;
164         if (count > 4 || (count == 4 && 0 != (bytes[start] & 0x80)))
165         {
166             throw new ArithmeticException("ASN.1 Integer out of positive int range");
167         }
168 
169         return intValue(bytes, start, SIGN_EXT_UNSIGNED);
170     }
171 
intValueExact()172     public int intValueExact()
173     {
174         int count = bytes.length - start;
175         if (count > 4)
176         {
177             throw new ArithmeticException("ASN.1 Integer out of int range");
178         }
179 
180         return intValue(bytes, start, SIGN_EXT_SIGNED);
181     }
182 
longValueExact()183     public long longValueExact()
184     {
185         int count = bytes.length - start;
186         if (count > 8)
187         {
188             throw new ArithmeticException("ASN.1 Integer out of long range");
189         }
190 
191         return longValue(bytes, start, SIGN_EXT_SIGNED);
192     }
193 
isConstructed()194     boolean isConstructed()
195     {
196         return false;
197     }
198 
encodedLength()199     int encodedLength()
200     {
201         return 1 + StreamUtil.calculateBodyLength(bytes.length) + bytes.length;
202     }
203 
encode(ASN1OutputStream out, boolean withTag)204     void encode(ASN1OutputStream out, boolean withTag) throws IOException
205     {
206         out.writeEncoded(withTag, BERTags.INTEGER, bytes);
207     }
208 
hashCode()209     public int hashCode()
210     {
211         return Arrays.hashCode(bytes);
212     }
213 
asn1Equals(ASN1Primitive o)214     boolean asn1Equals(ASN1Primitive o)
215     {
216         if (!(o instanceof ASN1Integer))
217         {
218             return false;
219         }
220 
221         ASN1Integer other = (ASN1Integer)o;
222 
223         return Arrays.areEqual(this.bytes, other.bytes);
224     }
225 
toString()226     public String toString()
227     {
228         return getValue().toString();
229     }
230 
intValue(byte[] bytes, int start, int signExt)231     static int intValue(byte[] bytes, int start, int signExt)
232     {
233         int length = bytes.length;
234         int pos = Math.max(start, length - 4);
235 
236         int val = bytes[pos] & signExt;
237         while (++pos < length)
238         {
239             val = (val << 8) | (bytes[pos] & SIGN_EXT_UNSIGNED);
240         }
241         return val;
242     }
243 
longValue(byte[] bytes, int start, int signExt)244     static long longValue(byte[] bytes, int start, int signExt)
245     {
246         int length = bytes.length;
247         int pos = Math.max(start, length - 8);
248 
249         long val = bytes[pos] & signExt;
250         while (++pos < length)
251         {
252             val = (val << 8) | (bytes[pos] & SIGN_EXT_UNSIGNED);
253         }
254         return val;
255     }
256 
257     /**
258      * Apply the correct validation for an INTEGER primitive following the BER rules.
259      *
260      * @param bytes The raw encoding of the integer.
261      * @return true if the (in)put fails this validation.
262      */
isMalformed(byte[] bytes)263     static boolean isMalformed(byte[] bytes)
264     {
265         switch (bytes.length)
266         {
267         case 0:
268             return true;
269         case 1:
270             return false;
271         default:
272             return bytes[0] == (bytes[1] >> 7)
273                 // Apply loose validation, see note in public constructor ASN1Integer(byte[])
274                 && !Properties.isOverrideSet("org.bouncycastle.asn1.allow_unsafe_integer");
275         }
276     }
277 
signBytesToSkip(byte[] bytes)278     static int signBytesToSkip(byte[] bytes)
279     {
280         int pos = 0, last = bytes.length - 1;
281         while (pos < last
282             && bytes[pos] == (bytes[pos + 1] >> 7))
283         {
284             ++pos;
285         }
286         return pos;
287     }
288 }
289