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