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