001/* 002 * Copyright 2007-2018 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2018 Ping Identity Corporation 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU General Public License (GPLv2 only) 010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 011 * as published by the Free Software Foundation. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Public License for more details. 017 * 018 * You should have received a copy of the GNU General Public License 019 * along with this program; if not, see <http://www.gnu.org/licenses>. 020 */ 021package com.unboundid.asn1; 022 023 024 025import java.util.ArrayList; 026import java.util.Collection; 027 028import com.unboundid.util.ByteStringBuffer; 029import com.unboundid.util.Debug; 030import com.unboundid.util.NotMutable; 031import com.unboundid.util.ThreadSafety; 032import com.unboundid.util.ThreadSafetyLevel; 033 034import static com.unboundid.asn1.ASN1Messages.*; 035 036 037 038/** 039 * This class provides an ASN.1 set element, which is used to hold a set of 040 * zero or more other elements (potentially including additional "envelope" 041 * element types like other sequences and/or sets) in which the order of those 042 * elements should not be considered significant. 043 */ 044@NotMutable() 045@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 046public final class ASN1Set 047 extends ASN1Element 048{ 049 /** 050 * The serial version UID for this serializable class. 051 */ 052 private static final long serialVersionUID = -523497075310394409L; 053 054 055 056 /* 057 * NOTE: This class uses lazy initialization for the encoded value. The 058 * encoded value should only be needed by the getValue() method, which is used 059 * by ASN1Element.encode(). Even though this class is externally immutable, 060 * that does not by itself make it completely threadsafe, because weirdness in 061 * the Java memory model could allow the assignment to be performed out of 062 * order. By passing the value through a volatile variable any time the value 063 * is set other than in the constructor (which will always be safe) we ensure 064 * that this reordering cannot happen. 065 * 066 * In the majority of cases, passing the value through assignments to 067 * valueBytes through a volatile variable is much faster than declaring 068 * valueBytes itself to be volatile because a volatile variable cannot be held 069 * in CPU caches or registers and must only be accessed from memory visible to 070 * all threads. Since the value may be read much more often than it is 071 * written, passing it through a volatile variable rather than making it 072 * volatile directly can help avoid that penalty when possible. 073 */ 074 075 076 077 // The set of ASN.1 elements contained in this set. 078 private final ASN1Element[] elements; 079 080 // The encoded representation of the value, if available. 081 private byte[] encodedValue; 082 083 // A volatile variable used to guard publishing the encodedValue array. See 084 // the note above to explain why this is needed. 085 private volatile byte[] encodedValueGuard; 086 087 088 089 /** 090 * Creates a new ASN.1 set with the default BER type and no encapsulated 091 * elements. 092 */ 093 public ASN1Set() 094 { 095 super(ASN1Constants.UNIVERSAL_SET_TYPE); 096 097 elements = ASN1Constants.NO_ELEMENTS; 098 encodedValue = ASN1Constants.NO_VALUE; 099 } 100 101 102 103 /** 104 * Creates a new ASN.1 set with the specified BER type and no encapsulated 105 * elements. 106 * 107 * @param type The BER type to use for this element. 108 */ 109 public ASN1Set(final byte type) 110 { 111 super(type); 112 113 elements = ASN1Constants.NO_ELEMENTS; 114 encodedValue = ASN1Constants.NO_VALUE; 115 } 116 117 118 119 /** 120 * Creates a new ASN.1 set with the default BER type and the provided set of 121 * elements. 122 * 123 * @param elements The set of elements to include in this set. 124 */ 125 public ASN1Set(final ASN1Element... elements) 126 { 127 super(ASN1Constants.UNIVERSAL_SET_TYPE); 128 129 if (elements == null) 130 { 131 this.elements = ASN1Constants.NO_ELEMENTS; 132 } 133 else 134 { 135 this.elements = elements; 136 } 137 138 encodedValue = null; 139 } 140 141 142 143 /** 144 * Creates a new ASN.1 set with the default BER type and the provided set of 145 * elements. 146 * 147 * @param elements The set of elements to include in this set. 148 */ 149 public ASN1Set(final Collection<? extends ASN1Element> elements) 150 { 151 super(ASN1Constants.UNIVERSAL_SET_TYPE); 152 153 if ((elements == null) || elements.isEmpty()) 154 { 155 this.elements = ASN1Constants.NO_ELEMENTS; 156 } 157 else 158 { 159 this.elements = new ASN1Element[elements.size()]; 160 elements.toArray(this.elements); 161 } 162 163 encodedValue = null; 164 } 165 166 167 168 /** 169 * Creates a new ASN.1 set with the specified BER type and the provided set of 170 * elements. 171 * 172 * @param type The BER type to use for this element. 173 * @param elements The set of elements to include in this set. 174 */ 175 public ASN1Set(final byte type, final ASN1Element... elements) 176 { 177 super(type); 178 179 if (elements == null) 180 { 181 this.elements = ASN1Constants.NO_ELEMENTS; 182 } 183 else 184 { 185 this.elements = elements; 186 } 187 188 encodedValue = null; 189 } 190 191 192 193 /** 194 * Creates a new ASN.1 set with the specified BER type and the provided set of 195 * elements. 196 * 197 * @param type The BER type to use for this element. 198 * @param elements The set of elements to include in this set. 199 */ 200 public ASN1Set(final byte type, 201 final Collection<? extends ASN1Element> elements) 202 { 203 super(type); 204 205 if ((elements == null) || elements.isEmpty()) 206 { 207 this.elements = ASN1Constants.NO_ELEMENTS; 208 } 209 else 210 { 211 this.elements = new ASN1Element[elements.size()]; 212 elements.toArray(this.elements); 213 } 214 215 encodedValue = null; 216 } 217 218 219 220 /** 221 * Creates a new ASN.1 set with the specified type, set of elements, and 222 * encoded value. 223 * 224 * @param type The BER type to use for this element. 225 * @param elements The set of elements to include in this set. 226 * @param value The pre-encoded value for this element. 227 */ 228 private ASN1Set(final byte type, final ASN1Element[] elements, 229 final byte[] value) 230 { 231 super(type); 232 233 this.elements = elements; 234 encodedValue = value; 235 } 236 237 238 239 /** 240 * {@inheritDoc} 241 */ 242 @Override() 243 byte[] getValueArray() 244 { 245 return getValue(); 246 } 247 248 249 250 /** 251 * {@inheritDoc} 252 */ 253 @Override() 254 int getValueOffset() 255 { 256 return 0; 257 } 258 259 260 261 /** 262 * {@inheritDoc} 263 */ 264 @Override() 265 public int getValueLength() 266 { 267 return getValue().length; 268 } 269 270 271 272 /** 273 * {@inheritDoc} 274 */ 275 @Override() 276 public byte[] getValue() 277 { 278 if (encodedValue == null) 279 { 280 encodedValueGuard = ASN1Sequence.encodeElements(elements); 281 encodedValue = encodedValueGuard; 282 } 283 284 return encodedValue; 285 } 286 287 288 289 /** 290 * {@inheritDoc} 291 */ 292 @Override() 293 public void encodeTo(final ByteStringBuffer buffer) 294 { 295 buffer.append(getType()); 296 297 if (elements.length == 0) 298 { 299 buffer.append((byte) 0x00); 300 return; 301 } 302 303 // In this case, it will likely be faster to just go ahead and append 304 // encoded representations of all of the elements and insert the length 305 // later once we know it. 306 final int originalLength = buffer.length(); 307 for (final ASN1Element e : elements) 308 { 309 e.encodeTo(buffer); 310 } 311 312 buffer.insert(originalLength, 313 encodeLength(buffer.length() - originalLength)); 314 } 315 316 317 318 /** 319 * Retrieves the set of encapsulated elements held in this set. 320 * 321 * @return The set of encapsulated elements held in this set. 322 */ 323 public ASN1Element[] elements() 324 { 325 return elements; 326 } 327 328 329 330 /** 331 * Decodes the contents of the provided byte array as a set element. 332 * 333 * @param elementBytes The byte array to decode as an ASN.1 set element. 334 * 335 * @return The decoded ASN.1 set element. 336 * 337 * @throws ASN1Exception If the provided array cannot be decoded as a set 338 * element. 339 */ 340 public static ASN1Set decodeAsSet(final byte[] elementBytes) 341 throws ASN1Exception 342 { 343 try 344 { 345 int valueStartPos = 2; 346 int length = (elementBytes[1] & 0x7F); 347 if (length != elementBytes[1]) 348 { 349 final int numLengthBytes = length; 350 351 length = 0; 352 for (int i=0; i < numLengthBytes; i++) 353 { 354 length <<= 8; 355 length |= (elementBytes[valueStartPos++] & 0xFF); 356 } 357 } 358 359 if ((elementBytes.length - valueStartPos) != length) 360 { 361 throw new ASN1Exception(ERR_ELEMENT_LENGTH_MISMATCH.get(length, 362 (elementBytes.length - valueStartPos))); 363 } 364 365 final byte[] value = new byte[length]; 366 System.arraycopy(elementBytes, valueStartPos, value, 0, length); 367 368 int numElements = 0; 369 final ArrayList<ASN1Element> elementList = new ArrayList<>(5); 370 try 371 { 372 int pos = 0; 373 while (pos < value.length) 374 { 375 final byte type = value[pos++]; 376 377 final byte firstLengthByte = value[pos++]; 378 int l = (firstLengthByte & 0x7F); 379 if (l != firstLengthByte) 380 { 381 final int numLengthBytes = l; 382 l = 0; 383 for (int i=0; i < numLengthBytes; i++) 384 { 385 l <<= 8; 386 l |= (value[pos++] & 0xFF); 387 } 388 } 389 390 final int posPlusLength = pos + l; 391 if ((l < 0) || (posPlusLength < 0) || (posPlusLength > value.length)) 392 { 393 throw new ASN1Exception( 394 ERR_SET_BYTES_DECODE_LENGTH_EXCEEDS_AVAILABLE.get()); 395 } 396 397 elementList.add(new ASN1Element(type, value, pos, l)); 398 pos += l; 399 numElements++; 400 } 401 } 402 catch (final ASN1Exception ae) 403 { 404 throw ae; 405 } 406 catch (final Exception e) 407 { 408 Debug.debugException(e); 409 throw new ASN1Exception(ERR_SET_BYTES_DECODE_EXCEPTION.get(e), e); 410 } 411 412 int i = 0; 413 final ASN1Element[] elements = new ASN1Element[numElements]; 414 for (final ASN1Element e : elementList) 415 { 416 elements[i++] = e; 417 } 418 419 return new ASN1Set(elementBytes[0], elements, value); 420 } 421 catch (final ASN1Exception ae) 422 { 423 Debug.debugException(ae); 424 throw ae; 425 } 426 catch (final Exception e) 427 { 428 Debug.debugException(e); 429 throw new ASN1Exception(ERR_ELEMENT_DECODE_EXCEPTION.get(e), e); 430 } 431 } 432 433 434 435 /** 436 * Decodes the provided ASN.1 element as a set element. 437 * 438 * @param element The ASN.1 element to be decoded. 439 * 440 * @return The decoded ASN.1 set element. 441 * 442 * @throws ASN1Exception If the provided element cannot be decoded as a set 443 * element. 444 */ 445 public static ASN1Set decodeAsSet(final ASN1Element element) 446 throws ASN1Exception 447 { 448 int numElements = 0; 449 final ArrayList<ASN1Element> elementList = new ArrayList<>(5); 450 final byte[] value = element.getValue(); 451 452 try 453 { 454 int pos = 0; 455 while (pos < value.length) 456 { 457 final byte type = value[pos++]; 458 459 final byte firstLengthByte = value[pos++]; 460 int length = (firstLengthByte & 0x7F); 461 if (length != firstLengthByte) 462 { 463 final int numLengthBytes = length; 464 length = 0; 465 for (int i=0; i < numLengthBytes; i++) 466 { 467 length <<= 8; 468 length |= (value[pos++] & 0xFF); 469 } 470 } 471 472 final int posPlusLength = pos + length; 473 if ((length < 0) || (posPlusLength < 0) || 474 (posPlusLength > value.length)) 475 { 476 throw new ASN1Exception( 477 ERR_SET_DECODE_LENGTH_EXCEEDS_AVAILABLE.get( 478 String.valueOf(element))); 479 } 480 481 elementList.add(new ASN1Element(type, value, pos, length)); 482 pos += length; 483 numElements++; 484 } 485 } 486 catch (final ASN1Exception ae) 487 { 488 throw ae; 489 } 490 catch (final Exception e) 491 { 492 Debug.debugException(e); 493 throw new ASN1Exception( 494 ERR_SET_DECODE_EXCEPTION.get(String.valueOf(element), e), e); 495 } 496 497 int i = 0; 498 final ASN1Element[] elements = new ASN1Element[numElements]; 499 for (final ASN1Element e : elementList) 500 { 501 elements[i++] = e; 502 } 503 504 return new ASN1Set(element.getType(), elements, value); 505 } 506 507 508 509 /** 510 * {@inheritDoc} 511 */ 512 @Override() 513 public void toString(final StringBuilder buffer) 514 { 515 buffer.append('['); 516 for (int i=0; i < elements.length; i++) 517 { 518 if (i > 0) 519 { 520 buffer.append(','); 521 } 522 elements[i].toString(buffer); 523 } 524 buffer.append(']'); 525 } 526}