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 }