001/* 002 * Copyright 2007-2024 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2007-2024 Ping Identity Corporation 007 * 008 * Licensed under the Apache License, Version 2.0 (the "License"); 009 * you may not use this file except in compliance with the License. 010 * You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, software 015 * distributed under the License is distributed on an "AS IS" BASIS, 016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 017 * See the License for the specific language governing permissions and 018 * limitations under the License. 019 */ 020/* 021 * Copyright (C) 2007-2024 Ping Identity Corporation 022 * 023 * This program is free software; you can redistribute it and/or modify 024 * it under the terms of the GNU General Public License (GPLv2 only) 025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 026 * as published by the Free Software Foundation. 027 * 028 * This program is distributed in the hope that it will be useful, 029 * but WITHOUT ANY WARRANTY; without even the implied warranty of 030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 031 * GNU General Public License for more details. 032 * 033 * You should have received a copy of the GNU General Public License 034 * along with this program; if not, see <http://www.gnu.org/licenses>. 035 */ 036package com.unboundid.asn1; 037 038 039 040import com.unboundid.util.Debug; 041import com.unboundid.util.NotMutable; 042import com.unboundid.util.NotNull; 043import com.unboundid.util.ThreadSafety; 044import com.unboundid.util.ThreadSafetyLevel; 045 046import static com.unboundid.asn1.ASN1Messages.*; 047 048 049 050/** 051 * This class provides an ASN.1 integer element that is backed by a Java 052 * {@code int}, which is a signed 32-bit value and can represent any integer 053 * between -2147483648 and 2147483647. If you need support for integer values 054 * in the signed 64-bit range, see the {@link ASN1Long} class as an alternative. 055 * If you need support for integer values of arbitrary size, see 056 * {@link ASN1BigInteger}. 057 */ 058@NotMutable() 059@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 060public final class ASN1Integer 061 extends ASN1Element 062{ 063 /** 064 * The serial version UID for this serializable class. 065 */ 066 private static final long serialVersionUID = -733929804601994372L; 067 068 069 070 // The int value for this element. 071 private final int intValue; 072 073 074 075 /** 076 * Creates a new ASN.1 integer element with the default BER type and the 077 * provided int value. 078 * 079 * @param intValue The int value to use for this element. 080 */ 081 public ASN1Integer(final int intValue) 082 { 083 super(ASN1Constants.UNIVERSAL_INTEGER_TYPE, encodeIntValue(intValue)); 084 085 this.intValue = intValue; 086 } 087 088 089 090 /** 091 * Creates a new ASN.1 integer element with the specified BER type and the 092 * provided int value. 093 * 094 * @param type The BER type to use for this element. 095 * @param intValue The int value to use for this element. 096 */ 097 public ASN1Integer(final byte type, final int intValue) 098 { 099 super(type, encodeIntValue(intValue)); 100 101 this.intValue = intValue; 102 } 103 104 105 106 /** 107 * Creates a new ASN.1 integer element with the specified BER type and the 108 * provided int and pre-encoded values. 109 * 110 * @param type The BER type to use for this element. 111 * @param intValue The int value to use for this element. 112 * @param value The pre-encoded value to use for this element. 113 */ 114 private ASN1Integer(final byte type, final int intValue, 115 @NotNull final byte[] value) 116 { 117 super(type, value); 118 119 this.intValue = intValue; 120 } 121 122 123 124 /** 125 * Encodes the provided int value to a byte array suitable for use as the 126 * value of an integer element. 127 * 128 * @param intValue The int value to be encoded. 129 * 130 * @return A byte array containing the encoded value. 131 */ 132 @NotNull() 133 static byte[] encodeIntValue(final int intValue) 134 { 135 if (intValue < 0) 136 { 137 if ((intValue & 0xFFFF_FF80) == 0xFFFF_FF80) 138 { 139 return new byte[] 140 { 141 (byte) (intValue & 0xFF) 142 }; 143 } 144 else if ((intValue & 0xFFFF_8000) == 0xFFFF_8000) 145 { 146 return new byte[] 147 { 148 (byte) ((intValue >> 8) & 0xFF), 149 (byte) (intValue & 0xFF) 150 }; 151 } 152 else if ((intValue & 0xFF80_0000) == 0xFF80_0000) 153 { 154 return new byte[] 155 { 156 (byte) ((intValue >> 16) & 0xFF), 157 (byte) ((intValue >> 8) & 0xFF), 158 (byte) (intValue & 0xFF) 159 }; 160 } 161 else 162 { 163 return new byte[] 164 { 165 (byte) ((intValue >> 24) & 0xFF), 166 (byte) ((intValue >> 16) & 0xFF), 167 (byte) ((intValue >> 8) & 0xFF), 168 (byte) (intValue & 0xFF) 169 }; 170 } 171 } 172 else 173 { 174 if ((intValue & 0x0000_007F) == intValue) 175 { 176 return new byte[] 177 { 178 (byte) (intValue & 0x7F) 179 }; 180 } 181 else if ((intValue & 0x0000_7FFF) == intValue) 182 { 183 return new byte[] 184 { 185 (byte) ((intValue >> 8) & 0x7F), 186 (byte) (intValue & 0xFF) 187 }; 188 } 189 else if ((intValue & 0x007F_FFFF) == intValue) 190 { 191 return new byte[] 192 { 193 (byte) ((intValue >> 16) & 0x7F), 194 (byte) ((intValue >> 8) & 0xFF), 195 (byte) (intValue & 0xFF) 196 }; 197 } 198 else 199 { 200 return new byte[] 201 { 202 (byte) ((intValue >> 24) & 0x7F), 203 (byte) ((intValue >> 16) & 0xFF), 204 (byte) ((intValue >> 8) & 0xFF), 205 (byte) (intValue & 0xFF) 206 }; 207 } 208 } 209 } 210 211 212 213 /** 214 * Retrieves the int value for this element. 215 * 216 * @return The int value for this element. 217 */ 218 public int intValue() 219 { 220 return intValue; 221 } 222 223 224 225 /** 226 * Decodes the contents of the provided byte array as an integer element. 227 * 228 * @param elementBytes The byte array to decode as an ASN.1 integer element. 229 * 230 * @return The decoded ASN.1 integer element. 231 * 232 * @throws ASN1Exception If the provided array cannot be decoded as an 233 * integer element. 234 */ 235 @NotNull() 236 public static ASN1Integer decodeAsInteger(@NotNull final byte[] elementBytes) 237 throws ASN1Exception 238 { 239 try 240 { 241 int valueStartPos = 2; 242 int length = (elementBytes[1] & 0x7F); 243 if (length != elementBytes[1]) 244 { 245 final int numLengthBytes = length; 246 247 length = 0; 248 for (int i=0; i < numLengthBytes; i++) 249 { 250 length <<= 8; 251 length |= (elementBytes[valueStartPos++] & 0xFF); 252 } 253 } 254 255 if ((elementBytes.length - valueStartPos) != length) 256 { 257 throw new ASN1Exception(ERR_ELEMENT_LENGTH_MISMATCH.get(length, 258 (elementBytes.length - valueStartPos))); 259 } 260 261 final byte[] value = new byte[length]; 262 System.arraycopy(elementBytes, valueStartPos, value, 0, length); 263 264 int intValue; 265 switch (value.length) 266 { 267 case 1: 268 intValue = (value[0] & 0xFF); 269 if ((value[0] & 0x80) != 0x00) 270 { 271 intValue |= 0xFFFF_FF00; 272 } 273 break; 274 275 case 2: 276 intValue = ((value[0] & 0xFF) << 8) | (value[1] & 0xFF); 277 if ((value[0] & 0x80) != 0x00) 278 { 279 intValue |= 0xFFFF_0000; 280 } 281 break; 282 283 case 3: 284 intValue = ((value[0] & 0xFF) << 16) | ((value[1] & 0xFF) << 8) | 285 (value[2] & 0xFF); 286 if ((value[0] & 0x80) != 0x00) 287 { 288 intValue |= 0xFF00_0000; 289 } 290 break; 291 292 case 4: 293 intValue = ((value[0] & 0xFF) << 24) | ((value[1] & 0xFF) << 16) | 294 ((value[2] & 0xFF) << 8) | (value[3] & 0xFF); 295 break; 296 297 default: 298 throw new ASN1Exception(ERR_ENUMERATED_INVALID_LENGTH.get( 299 value.length)); 300 } 301 302 return new ASN1Integer(elementBytes[0], intValue, value); 303 } 304 catch (final ASN1Exception ae) 305 { 306 Debug.debugException(ae); 307 throw ae; 308 } 309 catch (final Exception e) 310 { 311 Debug.debugException(e); 312 throw new ASN1Exception(ERR_ELEMENT_DECODE_EXCEPTION.get(e), e); 313 } 314 } 315 316 317 318 /** 319 * Decodes the provided ASN.1 element as an integer element. 320 * 321 * @param element The ASN.1 element to be decoded. 322 * 323 * @return The decoded ASN.1 integer element. 324 * 325 * @throws ASN1Exception If the provided element cannot be decoded as an 326 * integer element. 327 */ 328 @NotNull() 329 public static ASN1Integer decodeAsInteger(@NotNull final ASN1Element element) 330 throws ASN1Exception 331 { 332 int intValue; 333 final byte[] value = element.getValue(); 334 switch (value.length) 335 { 336 case 1: 337 intValue = (value[0] & 0xFF); 338 if ((value[0] & 0x80) != 0x00) 339 { 340 intValue |= 0xFFFF_FF00; 341 } 342 break; 343 344 case 2: 345 intValue = ((value[0] & 0xFF) << 8) | (value[1] & 0xFF); 346 if ((value[0] & 0x80) != 0x00) 347 { 348 intValue |= 0xFFFF_0000; 349 } 350 break; 351 352 case 3: 353 intValue = ((value[0] & 0xFF) << 16) | ((value[1] & 0xFF) << 8) | 354 (value[2] & 0xFF); 355 if ((value[0] & 0x80) != 0x00) 356 { 357 intValue |= 0xFF00_0000; 358 } 359 break; 360 361 case 4: 362 intValue = ((value[0] & 0xFF) << 24) | ((value[1] & 0xFF) << 16) | 363 ((value[2] & 0xFF) << 8) | (value[3] & 0xFF); 364 break; 365 366 default: 367 throw new ASN1Exception(ERR_INTEGER_INVALID_LENGTH.get(value.length)); 368 } 369 370 return new ASN1Integer(element.getType(), intValue, value); 371 } 372 373 374 375 /** 376 * {@inheritDoc} 377 */ 378 @Override() 379 public void toString(@NotNull final StringBuilder buffer) 380 { 381 buffer.append(intValue); 382 } 383}