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