001/* Oid.java -- Object identifier class.
002   Copyright (C) 2004 Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version.
037
038   The documentation comments of this class are derived from the text
039   of RFC 2853:  Generic Security Service API Version 2: Java Bindings.
040   That document is covered under the following license notice:
041
042Copyright (C) The Internet Society (2000).  All Rights Reserved.
043
044This document and translations of it may be copied and furnished to
045others, and derivative works that comment on or otherwise explain it
046or assist in its implementation may be prepared, copied, published and
047distributed, in whole or in part, without restriction of any kind,
048provided that the above copyright notice and this paragraph are
049included on all such copies and derivative works.  However, this
050document itself may not be modified in any way, such as by removing
051the copyright notice or references to the Internet Society or other
052Internet organizations, except as needed for the purpose of developing
053Internet standards in which case the procedures for copyrights defined
054in the Internet Standards process must be followed, or as required to
055translate it into languages other than English.
056
057The limited permissions granted above are perpetual and will not be
058revoked by the Internet Society or its successors or assigns.
059
060This document and the information contained herein is provided on an
061"AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
062TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT
063NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN
064WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
065MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. */
066
067
068package org.ietf.jgss;
069
070import gnu.java.lang.CPStringBuilder;
071
072import java.io.ByteArrayInputStream;
073import java.io.ByteArrayOutputStream;
074import java.io.DataInputStream;
075import java.io.InputStream;
076import java.io.IOException;
077import java.io.OutputStream;
078
079import java.math.BigInteger;
080
081import java.util.Arrays;
082import java.util.StringTokenizer;
083
084/**
085 * <p>This class represents Universal Object Identifiers (Oids) and their
086 * associated operations.</p>
087 *
088 * <p>Oids are hierarchically globally-interpretable identifiers used
089 * within the GSS-API framework to identify mechanisms and name formats.</p>
090 *
091 * <p>The structure and encoding of Oids is defined in ISOIEC-8824 and
092 * ISOIEC-8825.  For example the Oid representation of Kerberos V5
093 * mechanism is "1.2.840.113554.1.2.2".</p>
094 *
095 * <p>The {@link GSSName} name class contains <code>public static Oid</code>
096 * objects representing the standard name types defined in GSS-API.</p>
097 */
098public class Oid
099{
100
101  // Constants and fields.
102  // -------------------------------------------------------------------------
103
104  private static final int OBJECT_IDENTIFIER = 0x06;
105  private static final int RELATIVE_OID      = 0x0d;
106
107  private final int[] components;
108  private byte[] derOid;
109  private String strOid;
110  private boolean relative;
111
112  // Constructors.
113  // -------------------------------------------------------------------------
114
115  /**
116   * Creates an Oid object from a string representation of its integer
117   * components (e.g. "1.2.840.113554.1.2.2").
118   *
119   * @param strOid The string representation for the oid.
120   * @throws GSSException If the argument is badly formed.
121   */
122  public Oid(String strOid) throws GSSException
123  {
124    if (strOid == null)
125      throw new NullPointerException();
126    this.strOid = strOid;
127    try
128      {
129        StringTokenizer tok = new StringTokenizer(strOid, ".");
130        components = new int[tok.countTokens()];
131        int i = 0;
132        while (tok.hasMoreTokens() && i < components.length)
133          {
134            components[i++] = Integer.parseInt(tok.nextToken());
135          }
136      }
137    catch (Exception x)
138      {
139        throw new GSSException(GSSException.FAILURE);
140      }
141    relative = false;
142  }
143
144  /**
145   * Creates an Oid object from its DER encoding. This refers to the full
146   * encoding including tag and length.  The structure and encoding of
147   * Oids is defined in ISOIEC-8824 and ISOIEC-8825.  This method is
148   * identical in functionality to its byte array counterpart.
149   *
150   * @param derOid Stream containing the DER encoded oid.
151   * @throws GSSException If the DER stream is badly formed, or if the
152   *                      input stream throws an exception.
153   */
154  public Oid(InputStream derOid) throws GSSException
155  {
156    DataInputStream in = new DataInputStream(derOid);
157    try
158      {
159        int tag = in.read() & 0x1F;
160        if (tag != OBJECT_IDENTIFIER && tag != RELATIVE_OID)
161          throw new IOException();
162        int len = in.read();
163        if ((len & ~0x7F) != 0)
164          {
165            byte[] buf = new byte[len & 0x7F];
166            in.readFully(buf);
167            len = new BigInteger(1, buf).intValue();
168          }
169        if (len < 0)
170          throw new IOException();
171        byte[] enc = new byte[len];
172        in.readFully(enc);
173        int[] comp = new int[len + 1];
174        int count = 0;
175        int i = 0;
176        relative = tag == RELATIVE_OID;
177        if (!relative && i < len)
178          {
179            int j = (enc[i] & 0xFF);
180            comp[count++] = j / 40;
181            comp[count++] = j % 40;
182            i++;
183          }
184        while (i < len)
185          {
186            int j = 0;
187            do
188              {
189                j = enc[i++] & 0xFF;
190                comp[count] <<= 7;
191                comp[count]  |= j & 0x7F;
192                if (i >= len && (j & 0x80) != 0)
193                  throw new IOException();
194              }
195            while ((j & 0x80) != 0);
196            count++;
197          }
198        if (count == len)
199          this.components = comp;
200        else
201          {
202            this.components = new int[count];
203            System.arraycopy(comp, 0, components, 0, count);
204          }
205      }
206    catch (IOException ioe)
207      {
208        throw new GSSException(GSSException.FAILURE);
209      }
210  }
211
212  /**
213   * Creates an Oid object from its DER encoding. This refers to the full
214   * encoding including tag and length.  The structure and encoding of
215   * Oids is defined in ISOIEC-8824 and ISOIEC-8825.  This method is
216   * identical in functionality to its streaming counterpart.
217   *
218   * @param derOid Byte array storing a DER encoded oid.
219   * @throws GSSException If the DER bytes are badly formed.
220   */
221  public Oid(byte[] derOid) throws GSSException
222  {
223    this(new ByteArrayInputStream(derOid));
224    this.derOid = (byte[]) derOid.clone();
225  }
226
227  Oid(int[] components)
228  {
229    this.components = components;
230    relative = false;
231  }
232
233  // Instance methods.
234  // -------------------------------------------------------------------------
235
236  /**
237   * Returns a string representation of the oid's integer components in
238   * dot separated notation (e.g. "1.2.840.113554.1.2.2").
239   *
240   * @return The string representation of this oid.
241   */
242  public String toString()
243  {
244    if (strOid == null)
245      {
246        CPStringBuilder buf = new CPStringBuilder();
247        for (int i = 0; i < components.length; i++)
248          {
249            buf.append(components[i]);
250            if (i < components.length - 1)
251              buf.append('.');
252          }
253        strOid = buf.toString();
254      }
255    return strOid;
256  }
257
258  /**
259   * Returns the full ASN.1 DER encoding for this oid object, which
260   * includes the tag and length.
261   *
262   * @return The ASN.1 DER encoding for this oid.
263   * @throws GSSException If encoding fails.
264   */
265  public byte[] getDER() throws GSSException
266  {
267    if (derOid == null)
268      {
269        ByteArrayOutputStream out = new ByteArrayOutputStream(256);
270        try
271          {
272            int i = 0;
273            if (!relative)
274              {
275                int b = components[i++] * 40 + (components.length > 1
276                                                ? components[i++] : 0);
277                encodeSubId(out, b);
278              }
279            for ( ; i < components.length; i++)
280              encodeSubId(out, components[i]);
281            byte[] oid = out.toByteArray();
282            out.reset();
283            if (relative)
284              out.write(RELATIVE_OID);
285            else
286              out.write(OBJECT_IDENTIFIER);
287            if (oid.length < 128)
288              out.write(oid.length);
289            else if (oid.length < 256)
290              {
291                out.write(0x81);
292                out.write(oid.length);
293              }
294            else if (oid.length < 65536)
295              {
296                out.write(0x82);
297                out.write((oid.length >>> 8) & 0xFF);
298                out.write(oid.length & 0xFF);
299              }
300            else if (oid.length < 16777216)
301              {
302                out.write(0x83);
303                out.write((oid.length >>> 16) & 0xFF);
304                out.write((oid.length >>>  8) & 0xFF);
305                out.write(oid.length & 0xFF);
306              }
307            else
308              {
309                out.write(0x84);
310                out.write((oid.length >>> 24) & 0xFF);
311                out.write((oid.length >>> 16) & 0xFF);
312                out.write((oid.length >>>  8) & 0xFF);
313                out.write(oid.length & 0xFF);
314              }
315            out.write(oid);
316          }
317        catch (IOException ioe)
318          {
319            throw new GSSException(GSSException.FAILURE);
320          }
321        derOid = out.toByteArray();
322      }
323    return (byte[]) derOid.clone();
324  }
325
326  /**
327   * A utility method to test if an Oid object is contained within the
328   * supplied Oid object array.
329   *
330   * @param oids An array of oids to search.
331   * @return True if this oid is contained in the given array.
332   */
333  public boolean containedIn(Oid[] oids)
334  {
335    for (int i = 0; i < oids.length; i++)
336      {
337        if (equals(oids[i]))
338          return true;
339      }
340    return false;
341  }
342
343  public boolean equals(Object o)
344  {
345    if (!(o instanceof Oid))
346      return false;
347    Oid that = (Oid) o;
348    return Arrays.equals(components, that.components);
349  }
350
351  public int hashCode()
352  {
353    int code = 0;
354    for (int i = 0; i < components.length; i++)
355      code += components[i];
356    return code;
357  }
358
359  // Own methods.
360  // -------------------------------------------------------------------------
361
362  private static void encodeSubId(OutputStream out, int id) throws IOException
363  {
364    if (id < 128)
365      {
366        out.write(id);
367      }
368    else if (id < 16384)
369      {
370        out.write((id >>> 7) | 0x80);
371        out.write(id & 0x7F);
372      }
373    else if (id < 2097152)
374      {
375        out.write((id >>> 14) | 0x80);
376        out.write(((id >>> 7) | 0x80) & 0xFF);
377        out.write(id & 0x7F);
378      }
379    else if (id < 268435456)
380      {
381        out.write( (id >>> 21) | 0x80);
382        out.write(((id >>> 14) | 0x80) & 0xFF);
383        out.write(((id >>>  7) | 0x80) & 0xFF);
384        out.write(id & 0x7F);
385      }
386  }
387}