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