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