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