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