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