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