001    /*
002     *  Licensed to the Apache Software Foundation (ASF) under one
003     *  or more contributor license agreements.  See the NOTICE file
004     *  distributed with this work for additional information
005     *  regarding copyright ownership.  The ASF licenses this file
006     *  to you under the Apache License, Version 2.0 (the
007     *  "License"); you may not use this file except in compliance
008     *  with the License.  You may obtain a copy of the License at
009     *  
010     *    http://www.apache.org/licenses/LICENSE-2.0
011     *  
012     *  Unless required by applicable law or agreed to in writing,
013     *  software distributed under the License is distributed on an
014     *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     *  KIND, either express or implied.  See the License for the
016     *  specific language governing permissions and limitations
017     *  under the License. 
018     *  
019     */
020    package org.apache.directory.shared.asn1.primitives;
021    
022    
023    import java.io.Serializable;
024    import java.util.Arrays;
025    
026    import org.apache.directory.shared.asn1.codec.DecoderException;
027    import org.apache.directory.shared.asn1.util.Asn1StringUtils;
028    import org.apache.directory.shared.i18n.I18n;
029    
030    
031    /**
032     * This class implement an OID (Object Identifier). 
033     * 
034     * An OID is encoded as a list of bytes representing integers. 
035     * 
036     * An OID has a numeric representation where number are separated with dots :
037     * SPNEGO Oid = 1.3.6.1.5.5.2
038     * 
039     * Translating from a byte list to a dot separated list of number follows the rules :
040     * - the first number is in [0..2]
041     * - the second number is in [0..39] if the first number is 0 or 1
042     * - the first byte has a value equal to : number 1 * 40 + number two
043     * - the upper bit of a byte is set if the next byte is a part of the number
044     * 
045     * For instance, the SPNEGO Oid (1.3.6.1.5.5.2) will be encoded : 
046     * 1.3 -> 0x2B (1*40 + 3 = 43 = 0x2B) 
047     * .6  -> 0x06 
048     * .1  -> 0x01 
049     * .5  -> 0x05 
050     * .5  -> 0x05 
051     * .2  -> 0x02 
052     * 
053     * The Kerberos V5 Oid (1.2.840.48018.1.2.2)  will be encoded :
054     * 1.2   -> 0x2A (1*40 + 2 = 42 = 0x2A) 
055     * 840   -> 0x86 0x48 (840 = 6 * 128 + 72 = (0x06 | 0x80) 0x48 = 0x86 0x48
056     * 48018 -> 0x82 0xF7 0x12 (2 * 128 * 128 + 119 * 128 + 18 = (0x02 | 0x80) (0x77 | 0x80) 0x12
057     * .1    -> 0x01
058     * .2    -> 0x02
059     * .2    -> 0x02
060     *  
061     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
062     * @version $Rev: 912399 $, $Date: 2010-02-21 21:52:31 +0100 (Sun, 21 Feb 2010) $
063     */
064    public class OID implements Serializable
065    {
066        private static final long serialVersionUID = 1L;
067    
068        // ~ Instance fields
069        // ----------------------------------------------------------------------------
070    
071        /** The OID as a array of int */
072        private long[] oidValues;
073        
074        /** Th hashcode, computed only once */
075        private int hash;
076    
077    
078        // ~ Constructors
079        // -------------------------------------------------------------------------------
080    
081        /**
082         * Creates a new OID object.
083         */
084        public OID()
085        {
086            // We should not create this kind of object directly, it must
087            // be created through the factory.
088            hash = 0;
089        }
090    
091    
092        /**
093         * Create a new OID object from a byte array
094         * 
095         * @param oid the byte array containing the OID
096         * @throws DecoderException if the byte array does not contain a 
097         * valid OID
098         */
099        public OID( byte[] oid ) throws DecoderException
100        {
101            setOID( oid );
102            hash = computeHashCode();
103        }
104    
105    
106        /**
107         * Create a new OID object from a String
108         * 
109         * @param oid The String which is supposed to be an OID
110         * @throws DecoderException if the byte array does not contain a 
111         * valid OID
112         */
113        public OID( String oid ) throws DecoderException
114        {
115            setOID( oid );
116            hash = computeHashCode();
117        }
118    
119    
120        // ~ Methods
121        // ------------------------------------------------------------------------------------
122        /**
123         * Set the OID. It will be translated from a byte array to an internal
124         * representation.
125         * 
126         * @param oid The bytes containing the OID
127         * @throws DecoderException if the byte array does not contains a valid OID 
128         */
129        public void setOID( byte[] oid ) throws DecoderException
130        {
131    
132            if ( oid == null )
133            {
134                throw new DecoderException( I18n.err( I18n.ERR_00032 ) );
135            }
136    
137            if ( oid.length < 1 )
138            {
139                throw new DecoderException( I18n.err( I18n.ERR_00033, Asn1StringUtils.dumpBytes( oid ) ) );
140            }
141    
142            // First, we have to calculate the number of int to allocate
143            int nbValues = 1;
144    
145            int pos = 0;
146    
147            while ( pos < oid.length )
148            {
149    
150                if ( oid[pos] >= 0 )
151                {
152                    nbValues++;
153                }
154    
155                pos++;
156            }
157    
158            oidValues = new long[nbValues];
159    
160            nbValues = 0;
161            pos = 0;
162    
163            int accumulator = 0;
164    
165            if ( ( oid[0] < 0 ) || ( oid[0] >= 80 ) )
166            {
167                oidValues[nbValues++] = 2;
168    
169                while ( pos < oid.length )
170                {
171    
172                    if ( oid[pos] >= 0 )
173                    {
174                        oidValues[nbValues++] = ( ( accumulator << 7 ) + oid[pos] ) - 80;
175                        accumulator = 0;
176                        pos++;
177                        break;
178                    }
179                    else
180                    {
181                        accumulator = ( accumulator << 7 ) + ( oid[pos] & 0x007F );
182                    }
183    
184                    pos++;
185                }
186            }
187            else if ( oid[0] < 40 )
188            {
189                oidValues[nbValues++] = 0;
190                oidValues[nbValues++] = oid[pos++]; // itu-t
191            }
192            else
193            // oid[0] is < 80
194            {
195                oidValues[nbValues++] = 1;
196                oidValues[nbValues++] = oid[pos++] - 40; // iso
197            }
198    
199            while ( pos < oid.length )
200            {
201    
202                if ( oid[pos] >= 0 )
203                {
204                    oidValues[nbValues++] = ( accumulator << 7 ) + oid[pos];
205                    accumulator = 0;
206                }
207                else
208                {
209                    accumulator = ( accumulator << 7 ) + ( oid[pos] & 0x007F );
210                }
211    
212                pos++;
213            }
214            
215            hash = computeHashCode();
216        }
217    
218    
219        /**
220         * Set the OID. It will be translated from a String to an internal
221         * representation. 
222         * 
223         * The syntax will be controled in respect with this rule :
224         * OID = ( [ '0' | '1' ] '.' [ 0 .. 39 ] | '2' '.' int) ( '.' int )*
225         * 
226         * @param oid The String containing the OID
227         * @throws DecoderException if the byte array does not contains a valid OID 
228         */
229        public void setOID( String oid ) throws DecoderException
230        {
231    
232            if ( ( oid == null ) || ( oid.length() == 0 ) )
233            {
234                throw new DecoderException( I18n.err( I18n.ERR_00032 ) );
235            }
236    
237            int nbValues = 1;
238            char[] chars = oid.toCharArray();
239            boolean dotSeen = false;
240    
241            // Count the number of int to allocate.
242            for ( char c:chars )
243            {
244    
245                if ( c == '.' )
246                {
247    
248                    if ( dotSeen )
249                    {
250    
251                        // Two dots, that's an error !
252                        throw new DecoderException( I18n.err( I18n.ERR_00033, oid ) );
253                    }
254    
255                    nbValues++;
256                    dotSeen = true;
257                }
258                else
259                {
260                    dotSeen = false;
261                }
262            }
263    
264            // We must have at least 2 ints
265            if ( nbValues < 2 )
266            {
267                throw new DecoderException( I18n.err( I18n.ERR_00033, oid ) );
268            }
269    
270            oidValues = new long[nbValues];
271    
272            int pos = 0;
273            int intPos = 0;
274    
275            // This flag is used to forbid a second value above 39 if the
276            // first value is 0 or 1 (itu_t or iso arcs)
277            boolean ituOrIso = false;
278    
279            // The first value
280            switch ( chars[pos] )
281            {
282    
283                case '0': // itu-t
284                case '1': // iso
285                    ituOrIso = true;
286                // fallthrough
287    
288                case '2': // joint-iso-itu-t
289                    oidValues[intPos++] = chars[pos++] - '0';
290                    break;
291    
292                default: // error, this value is not allowed
293                    throw new DecoderException( I18n.err( I18n.ERR_00033, oid ) );
294            }
295    
296            // We must have a dot
297            if ( chars[pos++] != '.' )
298            {
299                throw new DecoderException( I18n.err( I18n.ERR_00033, oid ) );
300            }
301    
302            dotSeen = true;
303    
304            int value = 0;
305    
306            for ( int i = pos; i < chars.length; i++ )
307            {
308    
309                if ( chars[i] == '.' )
310                {
311    
312                    if ( dotSeen )
313                    {
314    
315                        // Two dots, that's an error !
316                        throw new DecoderException( I18n.err( I18n.ERR_00033, oid ) );
317                    }
318    
319                    if ( ituOrIso && value > 39 )
320                    {
321                        throw new DecoderException( I18n.err( I18n.ERR_00033, oid ) );
322                    }
323                    else
324                    {
325                        ituOrIso = false;
326                    }
327    
328                    nbValues++;
329                    dotSeen = true;
330                    oidValues[intPos++] = value;
331                    value = 0;
332                }
333                else if ( ( chars[i] >= 0x30 ) && ( chars[i] <= 0x39 ) )
334                {
335                    dotSeen = false;
336                    value = ( ( value * 10 ) + chars[i] ) - '0';
337    
338                }
339                else
340                {
341    
342                    // We don't have a number, this is an error
343                    throw new DecoderException( I18n.err( I18n.ERR_00033, oid ) );
344                }
345            }
346    
347            oidValues[intPos] = value;
348    
349            hash = computeHashCode();
350        }
351    
352    
353        /**
354         * Get an array of long from the OID
355         * 
356         * @return An array of long representing the OID
357         */
358        public long[] getOIDValues()
359        {
360            return oidValues;
361        }
362    
363    
364        /**
365         * Get the number of bytes necessary to store the OID
366         * 
367         * @return An int representing the length of the OID
368         */
369        public int getOIDLength()
370        {
371            long value = oidValues[0] * 40 + oidValues[1];
372            int nbBytes = 0;
373    
374            if ( value < 128 )
375            {
376                nbBytes = 1;
377            }
378            else if ( value < 16384 )
379            {
380                nbBytes = 2;
381            }
382            else if ( value < 2097152 )
383            {
384                nbBytes = 3;
385            }
386            else if ( value < 268435456 )
387            {
388                nbBytes = 4;
389            }
390            else
391            {
392                nbBytes = 5;
393            }
394    
395            for ( int i = 2; i < oidValues.length; i++ )
396            {
397                value = oidValues[i];
398    
399                if ( value < 128 )
400                {
401                    nbBytes += 1;
402                }
403                else if ( value < 16384 )
404                {
405                    nbBytes += 2;
406                }
407                else if ( value < 2097152 )
408                {
409                    nbBytes += 3;
410                }
411                else if ( value < 268435456 )
412                {
413                    nbBytes += 4;
414                }
415                else
416                {
417                    nbBytes += 5;
418                }
419            }
420    
421            return nbBytes;
422        }
423    
424    
425        /**
426         * Get an array of bytes from the OID
427         * 
428         * @return An array of int representing the OID
429         */
430        public byte[] getOID()
431        {
432            long value = oidValues[0] * 40 + oidValues[1];
433            long firstValues = value;
434    
435            byte[] bytes = new byte[getOIDLength()];
436            int pos = 0;
437    
438            if ( oidValues[0] < 2 )
439            {
440                bytes[pos++] = ( byte ) ( oidValues[0] * 40 + oidValues[1] );
441            }
442            else
443            {
444                if ( firstValues < 128 )
445                {
446                    bytes[pos++] = ( byte ) ( firstValues );
447                }
448                else if ( firstValues < 16384 )
449                {
450                    bytes[pos++] = ( byte ) ( ( firstValues >> 7 ) | 0x0080 );
451                    bytes[pos++] = ( byte ) ( firstValues & 0x007F );
452                }
453                else if ( value < 2097152 )
454                {
455                    bytes[pos++] = ( byte ) ( ( firstValues >> 14 ) | 0x0080 );
456                    bytes[pos++] = ( byte ) ( ( ( firstValues >> 7 ) & 0x007F ) | 0x0080 );
457                    bytes[pos++] = ( byte ) ( firstValues & 0x007F );
458                }
459                else if ( value < 268435456 )
460                {
461                    bytes[pos++] = ( byte ) ( ( firstValues >> 21 ) | 0x0080 );
462                    bytes[pos++] = ( byte ) ( ( ( firstValues >> 14 ) & 0x007F ) | 0x0080 );
463                    bytes[pos++] = ( byte ) ( ( ( firstValues >> 7 ) & 0x007F ) | 0x0080 );
464                    bytes[pos++] = ( byte ) ( firstValues & 0x007F );
465                }
466                else
467                {
468                    bytes[pos++] = ( byte ) ( ( firstValues >> 28 ) | 0x0080 );
469                    bytes[pos++] = ( byte ) ( ( ( firstValues >> 21 ) & 0x007F ) | 0x0080 );
470                    bytes[pos++] = ( byte ) ( ( ( firstValues >> 14 ) & 0x007F ) | 0x0080 );
471                    bytes[pos++] = ( byte ) ( ( ( firstValues >> 7 ) & 0x007F ) | 0x0080 );
472                    bytes[pos++] = ( byte ) ( firstValues & 0x007F );
473                }
474            }
475    
476            for ( int i = 2; i < oidValues.length; i++ )
477            {
478                value = oidValues[i];
479    
480                if ( value < 128 )
481                {
482                    bytes[pos++] = ( byte ) ( value );
483                }
484                else if ( value < 16384 )
485                {
486                    bytes[pos++] = ( byte ) ( ( value >> 7 ) | 0x0080 );
487                    bytes[pos++] = ( byte ) ( value & 0x007F );
488                }
489                else if ( value < 2097152 )
490                {
491                    bytes[pos++] = ( byte ) ( ( value >> 14 ) | 0x0080 );
492                    bytes[pos++] = ( byte ) ( ( ( value >> 7 ) & 0x007F ) | 0x0080 );
493                    bytes[pos++] = ( byte ) ( value & 0x007F );
494                }
495                else if ( value < 268435456 )
496                {
497                    bytes[pos++] = ( byte ) ( ( value >> 21 ) | 0x0080 );
498                    bytes[pos++] = ( byte ) ( ( ( value >> 14 ) & 0x007F ) | 0x0080 );
499                    bytes[pos++] = ( byte ) ( ( ( value >> 7 ) & 0x007F ) | 0x0080 );
500                    bytes[pos++] = ( byte ) ( value & 0x007F );
501                }
502                else
503                {
504                    bytes[pos++] = ( byte ) ( ( value >> 28 ) | 0x0080 );
505                    bytes[pos++] = ( byte ) ( ( ( value >> 21 ) & 0x007F ) | 0x0080 );
506                    bytes[pos++] = ( byte ) ( ( ( value >> 14 ) & 0x007F ) | 0x0080 );
507                    bytes[pos++] = ( byte ) ( ( ( value >> 7 ) & 0x007F ) | 0x0080 );
508                    bytes[pos++] = ( byte ) ( value & 0x007F );
509                }
510            }
511    
512            return bytes;
513        }
514    
515    
516        /**
517         * Compute the hash code for this object. No need to compute
518         * it live when calling the hashCode() method, as an OID
519         * never change.
520         * 
521         * @return the OID's hash code
522         */
523        private int computeHashCode()
524        {
525            int h = 37;
526            
527            for ( long val:oidValues )
528            {
529                int low = (int)(val & 0x0000FFFFL);
530                int high = (int)(val >> 32);
531                h = h*17 + high;
532                h = h*17 + low;
533            }
534            
535            return h;
536        }
537        
538        /**
539         * Check that an OID is valid
540         * @param oid The oid to be checked
541         * @return <code>true</code> if the OID is valid
542         */
543        public static boolean isOID( String oid )
544        {
545            if ( ( oid == null ) || ( oid.length() == 0 ) )
546            {
547                return false;
548            }
549    
550            int nbValues = 1;
551            byte[] bytes = oid.getBytes();
552            boolean dotSeen = false;
553    
554            // Count the number of int to allocate.
555            for ( byte b:bytes )
556            {
557    
558                if ( b == '.' )
559                {
560    
561                    if ( dotSeen )
562                    {
563    
564                        // Two dots, that's an error !
565                        return false;
566                    }
567    
568                    nbValues++;
569                    dotSeen = true;
570                }
571                else
572                {
573                    dotSeen = false;
574                }
575            }
576    
577            // We must have at least 2 ints
578            if ( nbValues < 2 )
579            {
580                return false;
581            }
582    
583            int pos = 0;
584    
585            // This flag is used to forbid a second value above 39 if the
586            // first value is 0 or 1 (itu_t or iso arcs)
587            boolean ituOrIso = false;
588    
589            // The first value
590            switch ( bytes[pos++] )
591            {
592    
593                case '0': // itu-t
594                    // fallthrough
595                case '1': // iso
596                    ituOrIso = true;
597                    // fallthrough
598    
599                case '2': // joint-iso-itu-t
600                    break;
601    
602                default: // error, this value is not allowed
603                    return false;
604            }
605    
606            // We must have a dot
607            if ( bytes[pos++] != '.' )
608            {
609                return false;
610            }
611    
612            dotSeen = true;
613    
614            long value = 0;
615    
616            for ( int i = pos; i < bytes.length; i++ )
617            {
618    
619                if ( bytes[i] == '.' )
620                {
621    
622                    if ( dotSeen )
623                    {
624                        // Two dots, that's an error !
625                        return false;
626                    }
627    
628                    if ( ituOrIso && value > 39 )
629                    {
630                        return false;
631                    }
632                    else
633                    {
634                        ituOrIso = false;
635                    }
636    
637                    nbValues++;
638                    dotSeen = true;
639                    value = 0;
640                }
641                else if ( ( bytes[i] >= 0x30 ) && ( bytes[i] <= 0x39 ) )
642                {
643                    dotSeen = false;
644    
645                    value = ( ( value * 10 ) + bytes[i] ) - '0';
646                }
647                else
648                {
649                    // We don't have a number, this is an error
650                    return false;
651                }
652            }
653    
654            return !dotSeen;
655        }
656        
657        /**
658         * Get the OID as a String
659         * 
660         * @return A String representing the OID
661         */
662        public String toString()
663        {
664    
665            StringBuffer sb = new StringBuffer();
666    
667            if ( oidValues != null )
668            {
669                sb.append( oidValues[0] );
670    
671                for ( int i = 1; i < oidValues.length; i++ )
672                {
673                    sb.append( '.' ).append( oidValues[i] );
674                }
675            }
676    
677            return sb.toString();
678        }
679    
680    
681        public int hashCode()
682        {
683            return hash;
684        }
685    
686    
687        public boolean equals( Object oid )
688        {
689            if ( this == oid )
690            {
691                return true;
692            }
693            
694            if ( oid == null )
695            {
696                return false;
697            }
698            
699            if ( oid.getClass() != this.getClass() )
700            {
701                return false;
702            }
703            
704            OID instance = (OID)oid;
705           
706            if ( instance.hash != hash )
707            {
708                return false;
709            }
710            else
711            {
712                return Arrays.equals( instance.oidValues, oidValues );
713            }
714        }
715        
716    }