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