001/*
002 * Copyright 2017-2018 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2017-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.Iterator;
027import java.util.List;
028
029import com.unboundid.util.ByteStringBuffer;
030import com.unboundid.util.NotMutable;
031import com.unboundid.util.OID;
032import com.unboundid.util.ThreadSafety;
033import com.unboundid.util.ThreadSafetyLevel;
034
035import static com.unboundid.asn1.ASN1Constants.*;
036import static com.unboundid.asn1.ASN1Messages.*;
037import static com.unboundid.util.Debug.*;
038
039
040
041/**
042 * This class provides an ASN.1 object identifier element, whose value
043 * represents a numeric OID.  Note that ASN.1 object identifier elements must
044 * strictly conform to the numeric OID specification, which has the following
045 * requirements:
046 * <UL>
047 *   <LI>All valid OIDs must contain at least two components.</LI>
048 *   <LI>The value of the first component must be 0, 1, or 2.</LI>
049 *   <LI>If the value of the first component is 0 or 1, then the value of the
050 *       second component must not be greater than 39.</LI>
051 * </UL>
052 */
053@NotMutable()
054@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
055public final class ASN1ObjectIdentifier
056       extends ASN1Element
057{
058  /**
059   * The serial version UID for this serializable class.
060   */
061  private static final long serialVersionUID = -777778295086222273L;
062
063
064
065  // The OID represented by this object identifier element.
066  private final OID oid;
067
068
069
070  /**
071   * Creates a new ASN.1 object identifier element with the default BER type and
072   * the provided OID.
073   *
074   * @param  oid  The OID to represent with this element.  It must not be
075   *              {@code null}, and it must represent a valid OID.
076   *
077   * @throws  ASN1Exception  If the provided OID does not strictly adhere to the
078   *                         numeric OID format.
079   */
080  public ASN1ObjectIdentifier(final OID oid)
081         throws ASN1Exception
082  {
083    this(UNIVERSAL_OBJECT_IDENTIFIER_TYPE, oid);
084  }
085
086
087
088  /**
089   * Creates a new ASN.1 object identifier element with the specified BER type
090   * and the provided OID.
091   *
092   * @param  type  The BER type for this element.
093   * @param  oid   The OID to represent with this element.  It must not be
094   *               {@code null}, and it must represent a valid OID.
095   *
096   * @throws  ASN1Exception  If the provided OID does not strictly adhere to the
097   *                         numeric OID format.
098   */
099  public ASN1ObjectIdentifier(final byte type, final OID oid)
100         throws ASN1Exception
101  {
102    this(type, oid, encodeValue(oid));
103  }
104
105
106
107  /**
108   * Creates a new ASN.1 object identifier element with the default BER type and
109   * the provided OID.
110   *
111   * @param  oidString  The string representation of the OID to represent with
112   *                    this element.  It must not be {@code null}, and it must
113   *                    represent a valid OID.
114   *
115   * @throws  ASN1Exception  If the provided OID does not strictly adhere to the
116   *                         numeric OID format.
117   */
118  public ASN1ObjectIdentifier(final String oidString)
119         throws ASN1Exception
120  {
121    this(UNIVERSAL_OBJECT_IDENTIFIER_TYPE, oidString);
122  }
123
124
125
126  /**
127   * Creates a new ASN.1 object identifier element with the specified BER type
128   * and the provided OID.
129   *
130   * @param  type       The BER type for this element.
131   * @param  oidString  The string representation of the OID to represent with
132   *                    this element.  It must not be {@code null}, and it must
133   *                    represent a valid OID.
134   *
135   * @throws  ASN1Exception  If the provided OID does not strictly adhere to the
136   *                         numeric OID format.
137   */
138  public ASN1ObjectIdentifier(final byte type, final String oidString)
139         throws ASN1Exception
140  {
141    this(type, new OID(oidString));
142  }
143
144
145
146  /**
147   * Creates a new ASN.1 object identifier element with the provided
148   * information.
149   *
150   * @param  type          The BER type to use for this element.
151   * @param  oid           The OID to represent with this element.
152   * @param  encodedValue  The encoded value for this element.
153   */
154  private ASN1ObjectIdentifier(final byte type, final OID oid,
155                               final byte[] encodedValue)
156  {
157    super(type, encodedValue);
158
159    this.oid = oid;
160  }
161
162
163
164  /**
165   * Generates an encoded value for an object identifier element with the
166   * provided OID.
167   *
168   * @param  oid  The OID to represent with this element.  It must not be
169   *              {@code null}, and it must represent a valid OID.
170   *
171   * @return  The encoded value.
172   *
173   * @throws  ASN1Exception  If the provided OID does not strictly conform to
174   *                         the requirements for ASN.1 OIDs.
175   */
176  private static byte[] encodeValue(final OID oid)
177          throws ASN1Exception
178  {
179    // Make sure that the provided UID conforms to the necessary constraints.
180    if (! oid.isValidNumericOID())
181    {
182      throw new ASN1Exception(ERR_OID_ENCODE_NOT_NUMERIC.get());
183    }
184
185    final List<Integer> components = oid.getComponents();
186    if (components.size() < 2)
187    {
188      throw new ASN1Exception(ERR_OID_ENCODE_NOT_ENOUGH_COMPONENTS.get(
189           oid.toString()));
190    }
191
192    final Iterator<Integer> componentIterator = components.iterator();
193
194    final int firstComponent = componentIterator.next();
195    if ((firstComponent < 0) || (firstComponent > 2))
196    {
197      throw new ASN1Exception(ERR_OID_ENCODE_INVALID_FIRST_COMPONENT.get(
198           oid.toString(), firstComponent));
199    }
200
201    final int secondComponent = componentIterator.next();
202    if ((secondComponent < 0) ||
203        ((firstComponent != 2) && (secondComponent > 39)))
204    {
205      throw new ASN1Exception(ERR_OID_ENCODE_INVALID_SECOND_COMPONENT.get(
206           oid.toString(), firstComponent, secondComponent));
207    }
208
209
210    // Construct the encoded representation of the OID.  Compute it as follows:
211    // - The first and second components are merged together by multiplying the
212    //   value of the first component by 40 and adding the value of the second
213    //   component.  Every other component is handled individually.
214    // - For components (including the merged first and second components) whose
215    //   value is less than or equal to 127, the encoded representation of that
216    //   component is simply the single-byte encoded representation of that
217    //   number.
218    // - For components (including the merged first and second components) whose
219    //   value is greater than 127, that component must be encoded in multiple
220    //   bytes.  In the encoded representation, only the lower seven bits of
221    //   each byte will be used to convey the value.  The most significant bit
222    //   of each byte will be used to indicate whether there are more bytes in
223    //   the component.
224    final ByteStringBuffer buffer = new ByteStringBuffer();
225    final int mergedFirstComponents = (40 * firstComponent) + secondComponent;
226    encodeComponent(mergedFirstComponents, buffer);
227    while (componentIterator.hasNext())
228    {
229      encodeComponent(componentIterator.next(), buffer);
230    }
231
232    return buffer.toByteArray();
233  }
234
235
236
237  /**
238   * Appends an encoded representation of the provided component value to the
239   * given buffer.
240   *
241   * @param  c  The value of the component to encode.
242   * @param  b  The buffer to which the encoded representation should be
243   *            appended.
244   */
245  private static void encodeComponent(final int c, final ByteStringBuffer b)
246  {
247    final int finalByte = c & 0b1111111;
248    if (finalByte == c)
249    {
250      b.append((byte) finalByte);
251    }
252    else if ((c & 0b1111111_1111111) == c)
253    {
254      b.append((byte) (0b10000000 | ((c >> 7) & 0b1111111)));
255      b.append((byte) finalByte);
256    }
257    else if ((c & 0b1111111_1111111_1111111) == c)
258    {
259      b.append((byte) (0b10000000 | ((c >> 14) & 0b1111111)));
260      b.append((byte) (0b10000000 | ((c >> 7) & 0b1111111)));
261      b.append((byte) finalByte);
262    }
263    else if ((c & 0b1111111_1111111_1111111_1111111) == c)
264    {
265      b.append((byte) (0b10000000 | ((c >> 21) & 0b1111111)));
266      b.append((byte) (0b10000000 | ((c >> 14) & 0b1111111)));
267      b.append((byte) (0b10000000 | ((c >> 7) & 0b1111111)));
268      b.append((byte) finalByte);
269    }
270    else
271    {
272      b.append((byte) (0b10000000 | ((c >> 28) & 0b1111111)));
273      b.append((byte) (0b10000000 | ((c >> 21) & 0b1111111)));
274      b.append((byte) (0b10000000 | ((c >> 14) & 0b1111111)));
275      b.append((byte) (0b10000000 | ((c >> 7) & 0b1111111)));
276      b.append((byte) finalByte);
277    }
278  }
279
280
281
282  /**
283   * Retrieves the OID represented by this object identifier element.
284   *
285   * @return  The OID represented by this object identifier element.
286   */
287  public OID getOID()
288  {
289    return oid;
290  }
291
292
293
294  /**
295   * Decodes the contents of the provided byte array as an object identifier
296   * element.
297   *
298   * @param  elementBytes  The byte array to decode as an ASN.1 object
299   *                       identifier element.
300   *
301   * @return  The decoded ASN.1 object identifier element.
302   *
303   * @throws  ASN1Exception  If the provided array cannot be decoded as an
304   *                         object identifier element.
305   */
306  public static ASN1ObjectIdentifier decodeAsObjectIdentifier(
307                                          final byte[] elementBytes)
308         throws ASN1Exception
309  {
310    try
311    {
312      int valueStartPos = 2;
313      int length = (elementBytes[1] & 0x7F);
314      if (length != elementBytes[1])
315      {
316        final int numLengthBytes = length;
317
318        length = 0;
319        for (int i=0; i < numLengthBytes; i++)
320        {
321          length <<= 8;
322          length |= (elementBytes[valueStartPos++] & 0xFF);
323        }
324      }
325
326      if ((elementBytes.length - valueStartPos) != length)
327      {
328        throw new ASN1Exception(ERR_ELEMENT_LENGTH_MISMATCH.get(length,
329                                     (elementBytes.length - valueStartPos)));
330      }
331
332      final byte[] elementValue = new byte[length];
333      System.arraycopy(elementBytes, valueStartPos, elementValue, 0, length);
334      final OID oid = decodeValue(elementValue);
335      return new ASN1ObjectIdentifier(elementBytes[0], oid, elementValue);
336    }
337    catch (final ASN1Exception ae)
338    {
339      debugException(ae);
340      throw ae;
341    }
342    catch (final Exception e)
343    {
344      debugException(e);
345      throw new ASN1Exception(ERR_ELEMENT_DECODE_EXCEPTION.get(e), e);
346    }
347  }
348
349
350
351  /**
352   * Decodes the provided ASN.1 element as an object identifier element.
353   *
354   * @param  element  The ASN.1 element to be decoded.
355   *
356   * @return  The decoded ASN.1 object identifier element.
357   *
358   * @throws  ASN1Exception  If the provided element cannot be decoded as an
359   *                         object identifier element.
360   */
361  public static ASN1ObjectIdentifier decodeAsObjectIdentifier(
362                                          final ASN1Element element)
363         throws ASN1Exception
364  {
365    final OID oid = decodeValue(element.getValue());
366    return new ASN1ObjectIdentifier(element.getType(), oid, element.getValue());
367  }
368
369
370
371  /**
372   * Decodes the provided value as an OID.
373   *
374   * @param  elementValue  The bytes that comprise the encoded value for an
375   *                       object identifier element.
376   *
377   * @return  The decoded OID.
378   *
379   * @throws  ASN1Exception  If the provided value cannot be decoded as a valid
380   *                         OID.
381   */
382  private static OID decodeValue(final byte[] elementValue)
383          throws ASN1Exception
384  {
385    if (elementValue.length == 0)
386    {
387      throw new ASN1Exception(ERR_OID_DECODE_EMPTY_VALUE.get());
388    }
389
390    final byte lastByte = elementValue[elementValue.length - 1];
391    if ((lastByte & 0x80) == 0x80)
392    {
393      throw new ASN1Exception(ERR_OID_DECODE_INCOMPLETE_VALUE.get());
394    }
395
396    int currentComponent = 0x00;
397    final ArrayList<Integer> components = new ArrayList<>(elementValue.length);
398    for (final byte b : elementValue)
399    {
400      currentComponent <<= 7;
401      currentComponent |= (b & 0x7F);
402      if ((b & 0x80) == 0x00)
403      {
404        if (components.isEmpty())
405        {
406          if (currentComponent < 40)
407          {
408            components.add(0);
409            components.add(currentComponent);
410          }
411          else if (currentComponent < 80)
412          {
413            components.add(1);
414            components.add(currentComponent - 40);
415          }
416          else
417          {
418            components.add(2);
419            components.add(currentComponent - 80);
420          }
421        }
422        else
423        {
424          components.add(currentComponent);
425        }
426
427        currentComponent = 0x00;
428      }
429    }
430
431    return new OID(components);
432  }
433
434
435
436  /**
437   * {@inheritDoc}
438   */
439  @Override()
440  public void toString(final StringBuilder buffer)
441  {
442    buffer.append(oid.toString());
443  }
444}