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}