Import from 1.9a8 tarball
[mozilla-nss.git] / security / nss / lib / pki1 / oid.c
blobc266035370cf5e52addc2961a06021633d1f4927
1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
12 * License.
14 * The Original Code is the Netscape security libraries.
16 * The Initial Developer of the Original Code is
17 * Netscape Communications Corporation.
18 * Portions created by the Initial Developer are Copyright (C) 1994-2000
19 * the Initial Developer. All Rights Reserved.
21 * Contributor(s):
23 * Alternatively, the contents of this file may be used under the terms of
24 * either the GNU General Public License Version 2 or later (the "GPL"), or
25 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 * in which case the provisions of the GPL or the LGPL are applicable instead
27 * of those above. If you wish to allow use of your version of this file only
28 * under the terms of either the GPL or the LGPL, and not to allow others to
29 * use your version of this file under the terms of the MPL, indicate your
30 * decision by deleting the provisions above and replace them with the notice
31 * and other provisions required by the GPL or the LGPL. If you do not delete
32 * the provisions above, a recipient may use your version of this file under
33 * the terms of any one of the MPL, the GPL or the LGPL.
35 * ***** END LICENSE BLOCK ***** */
37 #ifdef DEBUG
38 static const char CVS_ID[] = "@(#) $RCSfile: oid.c,v $ $Revision: 1.6 $ $Date: 2005/01/20 02:25:49 $";
39 #endif /* DEBUG */
42 * oid.c
44 * This file contains the implementation of the basic OID routines.
47 #ifndef BASE_H
48 #include "base.h"
49 #endif /* BASE_H */
51 #ifndef PKI1_H
52 #include "pki1.h"
53 #endif /* PKI1_H */
55 #include "plhash.h"
56 #include "plstr.h"
59 * NSSOID
61 * The public "methods" regarding this "object" are:
63 * NSSOID_CreateFromBER -- constructor
64 * NSSOID_CreateFromUTF8 -- constructor
65 * (there is no explicit destructor)
67 * NSSOID_GetDEREncoding
68 * NSSOID_GetUTF8Encoding
70 * The non-public "methods" regarding this "object" are:
72 * nssOID_CreateFromBER -- constructor
73 * nssOID_CreateFromUTF8 -- constructor
74 * (there is no explicit destructor)
76 * nssOID_GetDEREncoding
77 * nssOID_GetUTF8Encoding
79 * In debug builds, the following non-public calls are also available:
81 * nssOID_verifyPointer
82 * nssOID_getExplanation
83 * nssOID_getTaggedUTF8
86 const NSSOID *NSS_OID_UNKNOWN = (NSSOID *)NULL;
89 * First, the public "wrappers"
93 * NSSOID_CreateFromBER
95 * This routine creates an NSSOID by decoding a BER- or DER-encoded
96 * OID. It may return NULL upon error, in which case it
97 * will have created an error stack.
99 * The error may be one of the following values:
100 * NSS_ERROR_INVALID_BER
101 * NSS_ERROR_NO_MEMORY
103 * Return value:
104 * NULL upon error
105 * An NSSOID upon success
108 NSS_EXTERN NSSOID *
109 NSSOID_CreateFromBER
111 NSSBER *berOid
114 nss_ClearErrorStack();
116 #ifdef DEBUG
118 * NSSBERs can be created by the user,
119 * so no pointer-tracking can be checked.
122 if( (NSSBER *)NULL == berOid ) {
123 nss_SetError(NSS_ERROR_INVALID_BER);
124 return (NSSOID *)NULL;
127 if( (void *)NULL == berOid->data ) {
128 nss_SetError(NSS_ERROR_INVALID_BER);
129 return (NSSOID *)NULL;
131 #endif /* DEBUG */
133 return nssOID_CreateFromBER(berOid);
137 * NSSOID_CreateFromUTF8
139 * This routine creates an NSSOID by decoding a UTF8 string
140 * representation of an OID in dotted-number format. The string may
141 * optionally begin with an octothorpe. It may return NULL
142 * upon error, in which case it will have created an error stack.
144 * The error may be one of the following values:
145 * NSS_ERROR_INVALID_UTF8
146 * NSS_ERROR_NO_MEMORY
148 * Return value:
149 * NULL upon error
150 * An NSSOID upon success
153 NSS_EXTERN NSSOID *
154 NSSOID_CreateFromUTF8
156 NSSUTF8 *stringOid
159 nss_ClearErrorStack();
161 #ifdef DEBUG
163 * NSSUTF8s can be created by the user,
164 * so no pointer-tracking can be checked.
167 if( (NSSUTF8 *)NULL == stringOid ) {
168 nss_SetError(NSS_ERROR_INVALID_UTF8);
169 return (NSSOID *)NULL;
171 #endif /* DEBUG */
173 return nssOID_CreateFromUTF8(stringOid);
177 * NSSOID_GetDEREncoding
179 * This routine returns the DER encoding of the specified NSSOID.
180 * If the optional arena argument is non-null, the memory used will
181 * be obtained from that arena; otherwise, the memory will be obtained
182 * from the heap. This routine may return return null upon error, in
183 * which case it will have created an error stack.
185 * The error may be one of the following values:
186 * NSS_ERROR_INVALID_NSSOID
187 * NSS_ERROR_NO_MEMORY
189 * Return value:
190 * NULL upon error
191 * The DER encoding of this NSSOID
194 NSS_EXTERN NSSDER *
195 NSSOID_GetDEREncoding
197 const NSSOID *oid,
198 NSSDER *rvOpt,
199 NSSArena *arenaOpt
202 nss_ClearErrorStack();
204 #ifdef DEBUG
205 if( PR_SUCCESS != nssOID_verifyPointer(oid) ) {
206 return (NSSDER *)NULL;
209 if( (NSSArena *)NULL != arenaOpt ) {
210 if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) {
211 return (NSSDER *)NULL;
214 #endif /* DEBUG */
216 return nssOID_GetDEREncoding(oid, rvOpt, arenaOpt);
220 * NSSOID_GetUTF8Encoding
222 * This routine returns a UTF8 string containing the dotted-number
223 * encoding of the specified NSSOID. If the optional arena argument
224 * is non-null, the memory used will be obtained from that arena;
225 * otherwise, the memory will be obtained from the heap. This routine
226 * may return null upon error, in which case it will have created an
227 * error stack.
229 * The error may be one of the following values:
230 * NSS_ERROR_INVALID_NSSOID
231 * NSS_ERROR_NO_MEMORY
233 * Return value:
234 * NULL upon error
235 * A pointer to a UTF8 string containing the dotted-digit encoding of
236 * this NSSOID
239 NSS_EXTERN NSSUTF8 *
240 NSSOID_GetUTF8Encoding
242 const NSSOID *oid,
243 NSSArena *arenaOpt
246 nss_ClearErrorStack();
248 #ifdef DEBUG
249 if( PR_SUCCESS != nssOID_verifyPointer(oid) ) {
250 return (NSSUTF8 *)NULL;
253 if( (NSSArena *)NULL != arenaOpt ) {
254 if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) {
255 return (NSSUTF8 *)NULL;
258 #endif /* DEBUG */
260 return nssOID_GetUTF8Encoding(oid, arenaOpt);
264 * Next, some internal bookkeeping; including the OID "tag" table
265 * and the debug-version pointer tracker.
269 * For implementation reasons (so NSSOIDs can be compared with ==),
270 * we hash all NSSOIDs. This is the hash table.
273 static PLHashTable *oid_hash_table;
276 * And this is its lock.
279 static PZLock *oid_hash_lock;
282 * This is the hash function. We simply XOR the encoded form with
283 * itself in sizeof(PLHashNumber)-byte chunks. Improving this
284 * routine is left as an excercise for the more mathematically
285 * inclined student.
288 static PLHashNumber PR_CALLBACK
289 oid_hash
291 const void *key
294 const NSSItem *item = (const NSSItem *)key;
295 PLHashNumber rv = 0;
297 PRUint8 *data = (PRUint8 *)item->data;
298 PRUint32 i;
299 PRUint8 *rvc = (PRUint8 *)&rv;
301 for( i = 0; i < item->size; i++ ) {
302 rvc[ i % sizeof(rv) ] ^= *data;
303 data++;
306 return rv;
310 * This is the key-compare function. It simply does a lexical
311 * comparison on the encoded OID form. This does not result in
312 * quite the same ordering as the "sequence of numbers" order,
313 * but heck it's only used internally by the hash table anyway.
316 static PRIntn PR_CALLBACK
317 oid_hash_compare
319 const void *k1,
320 const void *k2
323 PRIntn rv;
325 const NSSItem *i1 = (const NSSItem *)k1;
326 const NSSItem *i2 = (const NSSItem *)k2;
328 PRUint32 size = (i1->size < i2->size) ? i1->size : i2->size;
330 rv = (PRIntn)nsslibc_memequal(i1->data, i2->data, size, (PRStatus *)NULL);
331 if( 0 == rv ) {
332 rv = i1->size - i2->size;
335 return !rv;
339 * The pointer-tracking code
342 #ifdef DEBUG
343 extern const NSSError NSS_ERROR_INTERNAL_ERROR;
345 static nssPointerTracker oid_pointer_tracker;
347 static PRStatus
348 oid_add_pointer
350 const NSSOID *oid
353 PRStatus rv;
355 rv = nssPointerTracker_initialize(&oid_pointer_tracker);
356 if( PR_SUCCESS != rv ) {
357 return rv;
360 rv = nssPointerTracker_add(&oid_pointer_tracker, oid);
361 if( PR_SUCCESS != rv ) {
362 NSSError e = NSS_GetError();
363 if( NSS_ERROR_NO_MEMORY != e ) {
364 nss_SetError(NSS_ERROR_INTERNAL_ERROR);
367 return rv;
370 return PR_SUCCESS;
373 #if defined(CAN_DELETE_OIDS)
375 * We actually don't define NSSOID deletion, since we keep OIDs
376 * in a hash table for easy comparison. Were we to, this is
377 * what the pointer-removal function would look like.
380 static PRStatus
381 oid_remove_pointer
383 const NSSOID *oid
386 PRStatus rv;
388 rv = nssPointerTracker_remove(&oid_pointer_tracker, oid);
389 if( PR_SUCCESS != rv ) {
390 nss_SetError(NSS_ERROR_INTERNAL_ERROR);
393 return rv;
395 #endif /* CAN_DELETE_OIDS */
397 #endif /* DEBUG */
400 * All dynamically-added OIDs get their memory from one statically-
401 * declared arena here, merely so that any cleanup code will have
402 * an easier time of it.
405 static NSSArena *oid_arena;
408 * This is the call-once function which initializes the hashtable.
409 * It creates it, then prepopulates it with all of the builtin OIDs.
410 * It also creates the aforementioned NSSArena.
413 static PRStatus PR_CALLBACK
414 oid_once_func
416 void
419 PRUint32 i;
421 /* Initialize the arena */
422 oid_arena = nssArena_Create();
423 if( (NSSArena *)NULL == oid_arena ) {
424 goto loser;
427 /* Create the hash table lock */
428 oid_hash_lock = PZ_NewLock(nssILockOID);
429 if( (PZLock *)NULL == oid_hash_lock ) {
430 nss_SetError(NSS_ERROR_NO_MEMORY);
431 goto loser;
434 /* Create the hash table */
435 oid_hash_table = PL_NewHashTable(0, oid_hash, oid_hash_compare,
436 PL_CompareValues,
437 (PLHashAllocOps *)0,
438 (void *)0);
439 if( (PLHashTable *)NULL == oid_hash_table ) {
440 nss_SetError(NSS_ERROR_NO_MEMORY);
441 goto loser;
444 /* And populate it with all the builtins */
445 for( i = 0; i < nss_builtin_oid_count; i++ ) {
446 NSSOID *oid = (NSSOID *)&nss_builtin_oids[i];
447 PLHashEntry *e = PL_HashTableAdd(oid_hash_table, &oid->data, oid);
448 if( (PLHashEntry *)NULL == e ) {
449 nss_SetError(NSS_ERROR_NO_MEMORY);
450 goto loser;
453 #ifdef DEBUG
454 if( PR_SUCCESS != oid_add_pointer(oid) ) {
455 goto loser;
457 #endif /* DEBUG */
460 return PR_SUCCESS;
462 loser:
463 if( (PLHashTable *)NULL != oid_hash_table ) {
464 PL_HashTableDestroy(oid_hash_table);
465 oid_hash_table = (PLHashTable *)NULL;
468 if( (PZLock *)NULL != oid_hash_lock ) {
469 PZ_DestroyLock(oid_hash_lock);
470 oid_hash_lock = (PZLock *)NULL;
473 if( (NSSArena *)NULL != oid_arena ) {
474 (void)nssArena_Destroy(oid_arena);
475 oid_arena = (NSSArena *)NULL;
478 return PR_FAILURE;
482 * This is NSPR's once-block.
485 static PRCallOnceType oid_call_once;
488 * And this is our multiply-callable internal init routine, which
489 * will call-once our call-once function.
492 static PRStatus
493 oid_init
495 void
498 return PR_CallOnce(&oid_call_once, oid_once_func);
501 #ifdef DEBUG
504 * nssOID_verifyPointer
506 * This method is only present in debug builds.
508 * If the specified pointer is a valid pointer to an NSSOID object,
509 * this routine will return PR_SUCCESS. Otherwise, it will put an
510 * error on the error stack and return PR_FAILURE.
512 * The error may be one of the following values:
513 * NSS_ERROR_INVALID_NSSOID
514 * NSS_ERROR_NO_MEMORY
516 * Return value:
517 * PR_SUCCESS if the pointer is valid
518 * PR_FAILURE if it isn't
521 NSS_EXTERN PRStatus
522 nssOID_verifyPointer
524 const NSSOID *oid
527 PRStatus rv;
529 rv = oid_init();
530 if( PR_SUCCESS != rv ) {
531 return PR_FAILURE;
534 rv = nssPointerTracker_initialize(&oid_pointer_tracker);
535 if( PR_SUCCESS != rv ) {
536 return PR_FAILURE;
539 rv = nssPointerTracker_verify(&oid_pointer_tracker, oid);
540 if( PR_SUCCESS != rv ) {
541 nss_SetError(NSS_ERROR_INVALID_NSSOID);
542 return PR_FAILURE;
545 return PR_SUCCESS;
547 #endif /* DEBUG */
550 * oid_sanity_check_ber
552 * This routine merely applies some sanity-checking to the BER-encoded
553 * OID.
556 static PRStatus
557 oid_sanity_check_ber
559 NSSBER *berOid
562 PRUint32 i;
563 PRUint8 *data = (PRUint8 *)berOid->data;
566 * The size must be longer than zero bytes.
569 if( berOid->size <= 0 ) {
570 return PR_FAILURE;
574 * In general, we can't preclude any number from showing up
575 * someday. We could probably guess that top-level numbers
576 * won't get very big (beyond the current ccitt(0), iso(1),
577 * or joint-ccitt-iso(2)). However, keep in mind that the
578 * encoding rules wrap the first two numbers together, as
580 * (first * 40) + second
582 * Also, it is noted in the specs that this implies that the
583 * second number won't go above forty.
585 * 128 encodes 3.8, which seems pretty safe for now. Let's
586 * check that the first byte is less than that.
588 * XXX This is a "soft check" -- we may want to exclude it.
591 if( data[0] >= 0x80 ) {
592 return PR_FAILURE;
596 * In a normalised format, leading 0x80s will never show up.
597 * This means that no 0x80 will be preceeded by the final
598 * byte of a sequence, which would naturaly be less than 0x80.
599 * Our internal encoding for the single-digit OIDs uses 0x80,
600 * but the only places we use them (loading the builtin table,
601 * and adding a UTF8-encoded OID) bypass this check.
604 for( i = 1; i < berOid->size; i++ ) {
605 if( (0x80 == data[i]) && (data[i-1] < 0x80) ) {
606 return PR_FAILURE;
611 * The high bit of each octet indicates that following octets
612 * are included in the current number. Thus the last byte can't
613 * have the high bit set.
616 if( data[ berOid->size-1 ] >= 0x80 ) {
617 return PR_FAILURE;
621 * Other than that, any byte sequence is legit.
623 return PR_SUCCESS;
627 * nssOID_CreateFromBER
629 * This routine creates an NSSOID by decoding a BER- or DER-encoded
630 * OID. It may return NULL upon error, in which case it
631 * will have set an error on the error stack.
633 * The error may be one of the following values:
634 * NSS_ERROR_INVALID_BER
635 * NSS_ERROR_NO_MEMORY
637 * Return value:
638 * NULL upon error
639 * An NSSOID upon success
642 NSS_EXTERN NSSOID *
643 nssOID_CreateFromBER
645 NSSBER *berOid
648 NSSOID *rv;
649 PLHashEntry *e;
651 if( PR_SUCCESS != oid_init() ) {
652 return (NSSOID *)NULL;
655 if( PR_SUCCESS != oid_sanity_check_ber(berOid) ) {
656 nss_SetError(NSS_ERROR_INVALID_BER);
657 return (NSSOID *)NULL;
661 * Does it exist?
663 PZ_Lock(oid_hash_lock);
664 rv = (NSSOID *)PL_HashTableLookup(oid_hash_table, berOid);
665 (void)PZ_Unlock(oid_hash_lock);
666 if( (NSSOID *)NULL != rv ) {
667 /* Found it! */
668 return rv;
672 * Doesn't exist-- create it.
674 rv = nss_ZNEW(oid_arena, NSSOID);
675 if( (NSSOID *)NULL == rv ) {
676 return (NSSOID *)NULL;
679 rv->data.data = nss_ZAlloc(oid_arena, berOid->size);
680 if( (void *)NULL == rv->data.data ) {
681 return (NSSOID *)NULL;
684 rv->data.size = berOid->size;
685 nsslibc_memcpy(rv->data.data, berOid->data, berOid->size);
687 #ifdef DEBUG
688 rv->tag = "<runtime>";
689 rv->expl = "(OID registered at runtime)";
690 #endif /* DEBUG */
692 PZ_Lock(oid_hash_lock);
693 e = PL_HashTableAdd(oid_hash_table, &rv->data, rv);
694 (void)PZ_Unlock(oid_hash_lock);
695 if( (PLHashEntry *)NULL == e ) {
696 nss_ZFreeIf(rv->data.data);
697 nss_ZFreeIf(rv);
698 nss_SetError(NSS_ERROR_NO_MEMORY);
699 return (NSSOID *)NULL;
702 #ifdef DEBUG
704 PRStatus st;
705 st = oid_add_pointer(rv);
706 if( PR_SUCCESS != st ) {
707 PZ_Lock(oid_hash_lock);
708 (void)PL_HashTableRemove(oid_hash_table, &rv->data);
709 (void)PZ_Unlock(oid_hash_lock);
710 (void)nss_ZFreeIf(rv->data.data);
711 (void)nss_ZFreeIf(rv);
712 return (NSSOID *)NULL;
715 #endif /* DEBUG */
717 return rv;
721 * oid_sanity_check_utf8
723 * This routine merely applies some sanity-checking to the
724 * UTF8-encoded OID.
727 static PRStatus
728 oid_sanity_check_utf8
730 NSSUTF8 *s
734 * It may begin with an octothorpe, which we skip.
737 if( '#' == *s ) {
738 s++;
742 * It begins with a number
745 if( (*s < '0') || (*s > '9') ) {
746 return PR_FAILURE;
750 * First number is only one digit long
752 * XXX This is a "soft check" -- we may want to exclude it
755 if( (s[1] != '.') && (s[1] != '\0') ) {
756 return PR_FAILURE;
760 * Every character is either a digit or a period
763 for( ; '\0' != *s; s++ ) {
764 if( ('.' != *s) && ((*s < '0') || (*s > '9')) ) {
765 return PR_FAILURE;
768 /* No two consecutive periods */
769 if( ('.' == *s) && ('.' == s[1]) ) {
770 return PR_FAILURE;
775 * The last character isn't a period
778 if( '.' == *--s ) {
779 return PR_FAILURE;
782 return PR_SUCCESS;
785 static PRUint32
786 oid_encode_number
788 PRUint32 n,
789 PRUint8 *dp,
790 PRUint32 nb
793 PRUint32 a[5];
794 PRUint32 i;
795 PRUint32 rv;
797 a[0] = (n >> 28) & 0x7f;
798 a[1] = (n >> 21) & 0x7f;
799 a[2] = (n >> 14) & 0x7f;
800 a[3] = (n >> 7) & 0x7f;
801 a[4] = n & 0x7f;
803 for( i = 0; i < 5; i++ ) {
804 if( 0 != a[i] ) {
805 break;
809 if( 5 == i ) {
810 i--;
813 rv = 5-i;
814 if( rv > nb ) {
815 return rv;
818 for( ; i < 4; i++ ) {
819 *dp = 0x80 | a[i];
820 dp++;
823 *dp = a[4];
825 return rv;
829 * oid_encode_huge
831 * This routine will convert a huge decimal number into the DER
832 * encoding for oid numbers. It is not limited to numbers that will
833 * fit into some wordsize, like oid_encode_number. But it's not
834 * necessarily very fast, either. This is here in case some joker
835 * throws us an ASCII oid like 1.2.3.99999999999999999999999999.
838 static PRUint32
839 oid_encode_huge
841 NSSUTF8 *s,
842 NSSUTF8 *e,
843 PRUint8 *dp,
844 PRUint32 nb
847 PRUint32 slen = (e-s);
848 PRUint32 blen = (slen+1)/2;
849 PRUint8 *st = (PRUint8 *)NULL;
850 PRUint8 *bd = (PRUint8 *)NULL;
851 PRUint32 i;
852 PRUint32 bitno;
853 PRUint8 *last;
854 PRUint8 *first;
855 PRUint32 byteno;
856 PRUint8 mask;
858 /* We'll be munging the data, so duplicate it */
859 st = (PRUint8 *)nss_ZAlloc((NSSArena *)NULL, slen);
860 if( (PRUint8 *)NULL == st ) {
861 return 0;
864 /* Don't know ahead of time exactly how long we'll need */
865 bd = (PRUint8 *)nss_ZAlloc((NSSArena *)NULL, blen);
866 if( (PRUint8 *)NULL == bd ) {
867 (void)nss_ZFreeIf(st);
868 return 0;
871 /* Copy the original, and convert ASCII to numbers */
872 for( i = 0; i < slen; i++ ) {
873 st[i] = (PRUint8)(s[i] - '0');
876 last = &st[slen-1];
877 first = &st[0];
880 * The way we create the binary version is by looking at it
881 * bit by bit. Start with the least significant bit. If the
882 * number is odd, set that bit. Halve the number (with integer
883 * division), and go to the next least significant bit. Keep
884 * going until the number goes to zero.
886 for( bitno = 0; ; bitno++ ) {
887 PRUint8 *d;
889 byteno = bitno/7;
890 mask = (PRUint8)(1 << (bitno%7));
892 /* Skip leading zeroes */
893 for( ; first < last; first ++ ) {
894 if( 0 != *first ) {
895 break;
899 /* Down to one number and it's a zero? Done. */
900 if( (first == last) && (0 == *last) ) {
901 break;
904 /* Last digit is odd? Set the bit */
905 if( *last & 1 ) {
906 bd[ byteno ] |= mask;
911 * Divide the number in half. This is just a matter
912 * of going from the least significant digit upwards,
913 * halving each one. If any digit is odd (other than
914 * the last, which has already been handled), add five
915 * to the digit to its right.
917 *last /= 2;
919 for( d = &last[-1]; d >= first; d-- ) {
920 if( *d & 1 ) {
921 d[1] += 5;
924 *d /= 2;
928 /* Is there room to write the encoded data? */
929 if( (byteno+1) > nb ) {
930 return (byteno+1);
933 /* Trim any leading zero that crept in there */
934 for( ; byteno > 0; byteno-- ) {
935 if( 0 != bd[ byteno ] ) {
936 break;
940 /* Copy all but the last, marking the "continue" bit */
941 for( i = 0; i < byteno; i++ ) {
942 dp[i] = bd[ byteno-i ] | 0x80;
944 /* And the last with the "continue" bit clear */
945 dp[byteno] = bd[0];
947 (void)nss_ZFreeIf(bd);
948 (void)nss_ZFreeIf(st);
949 return (byteno+1);
953 * oid_encode_string
955 * This routine converts a dotted-number OID into a DER-encoded
956 * one. It assumes we've already sanity-checked the string.
959 extern const NSSError NSS_ERROR_INTERNAL_ERROR;
961 static NSSOID *
962 oid_encode_string
964 NSSUTF8 *s
967 PRUint32 nn = 0; /* number of numbers */
968 PRUint32 nb = 0; /* number of bytes (estimated) */
969 NSSUTF8 *t;
970 PRUint32 nd = 0; /* number of digits */
971 NSSOID *rv;
972 PRUint8 *dp;
973 PRUint32 a, b;
974 PRUint32 inc;
976 /* Dump any octothorpe */
977 if( '#' == *s ) {
978 s++;
981 /* Count up the bytes needed */
982 for( t = s; '\0' != *t; t++ ) {
983 if( '.' == *t ) {
984 nb += (nd+1)/2; /* errs on the big side */
985 nd = 0;
986 nn++;
987 } else {
988 nd++;
991 nb += (nd+1)/2;
992 nn++;
994 if( 1 == nn ) {
996 * We have our own "denormalised" encoding for these,
997 * which is only used internally.
999 nb++;
1003 * Allocate. Note that we don't use the oid_arena here.. this is
1004 * because there really isn't a "free()" for stuff allocated out of
1005 * arenas (at least with the current implementation), so this would
1006 * keep using up memory each time a UTF8-encoded OID were added.
1007 * If need be (if this is the first time this oid has been seen),
1008 * we'll copy it.
1010 rv = nss_ZNEW((NSSArena *)NULL, NSSOID);
1011 if( (NSSOID *)NULL == rv ) {
1012 return (NSSOID *)NULL;
1015 rv->data.data = nss_ZAlloc((NSSArena *)NULL, nb);
1016 if( (void *)NULL == rv->data.data ) {
1017 (void)nss_ZFreeIf(rv);
1018 return (NSSOID *)NULL;
1021 dp = (PRUint8 *)rv->data.data;
1023 a = atoi(s);
1025 if( 1 == nn ) {
1026 dp[0] = '\x80';
1027 inc = oid_encode_number(a, &dp[1], nb-1);
1028 if( inc >= nb ) {
1029 goto loser;
1031 } else {
1032 for( t = s; '.' != *t; t++ ) {
1036 t++;
1037 b = atoi(t);
1038 inc = oid_encode_number(a*40+b, dp, nb);
1039 if( inc > nb ) {
1040 goto loser;
1042 dp += inc;
1043 nb -= inc;
1044 nn -= 2;
1046 while( nn-- > 0 ) {
1047 NSSUTF8 *u;
1049 for( ; '.' != *t; t++ ) {
1053 t++;
1055 for( u = t; ('\0' != *u) && ('.' != *u); u++ ) {
1059 if( (u-t > 9) ) {
1060 /* In the billions. Rats. */
1061 inc = oid_encode_huge(t, u, dp, nb);
1062 } else {
1063 b = atoi(t);
1064 inc = oid_encode_number(b, dp, nb);
1067 if( inc > nb ) {
1068 goto loser;
1070 dp += inc;
1071 nb -= inc;
1075 return rv;
1077 loser:
1078 nss_SetError(NSS_ERROR_INTERNAL_ERROR);
1079 return (NSSOID *)NULL;
1083 * nssOID_CreateFromUTF8
1085 * This routine creates an NSSOID by decoding a UTF8 string
1086 * representation of an OID in dotted-number format. The string may
1087 * optionally begin with an octothorpe. It may return NULL
1088 * upon error, in which case it will have set an error on the error
1089 * stack.
1091 * The error may be one of the following values:
1092 * NSS_ERROR_INVALID_STRING
1093 * NSS_ERROR_NO_MEMORY
1095 * Return value:
1096 * NULL upon error
1097 * An NSSOID upon success
1100 NSS_EXTERN NSSOID *
1101 nssOID_CreateFromUTF8
1103 NSSUTF8 *stringOid
1106 NSSOID *rv = (NSSOID *)NULL;
1107 NSSOID *candidate = (NSSOID *)NULL;
1108 PLHashEntry *e;
1110 if( PR_SUCCESS != oid_init() ) {
1111 return (NSSOID *)NULL;
1114 if( PR_SUCCESS != oid_sanity_check_utf8(stringOid) ) {
1115 nss_SetError(NSS_ERROR_INVALID_STRING);
1116 return (NSSOID *)NULL;
1119 candidate = oid_encode_string(stringOid);
1120 if( (NSSOID *)NULL == candidate ) {
1121 /* Internal error only */
1122 return rv;
1126 * Does it exist?
1128 PZ_Lock(oid_hash_lock);
1129 rv = (NSSOID *)PL_HashTableLookup(oid_hash_table, &candidate->data);
1130 (void)PZ_Unlock(oid_hash_lock);
1131 if( (NSSOID *)NULL != rv ) {
1132 /* Already exists. Delete my copy and return the original. */
1133 (void)nss_ZFreeIf(candidate->data.data);
1134 (void)nss_ZFreeIf(candidate);
1135 return rv;
1139 * Nope. Add it. Remember to allocate it out of the oid arena.
1142 rv = nss_ZNEW(oid_arena, NSSOID);
1143 if( (NSSOID *)NULL == rv ) {
1144 goto loser;
1147 rv->data.data = nss_ZAlloc(oid_arena, candidate->data.size);
1148 if( (void *)NULL == rv->data.data ) {
1149 goto loser;
1152 rv->data.size = candidate->data.size;
1153 nsslibc_memcpy(rv->data.data, candidate->data.data, rv->data.size);
1155 (void)nss_ZFreeIf(candidate->data.data);
1156 (void)nss_ZFreeIf(candidate);
1158 #ifdef DEBUG
1159 rv->tag = "<runtime>";
1160 rv->expl = "(OID registered at runtime)";
1161 #endif /* DEBUG */
1163 PZ_Lock(oid_hash_lock);
1164 e = PL_HashTableAdd(oid_hash_table, &rv->data, rv);
1165 (void)PZ_Unlock(oid_hash_lock);
1166 if( (PLHashEntry *)NULL == e ) {
1167 nss_SetError(NSS_ERROR_NO_MEMORY);
1168 goto loser;
1171 #ifdef DEBUG
1173 PRStatus st;
1174 st = oid_add_pointer(rv);
1175 if( PR_SUCCESS != st ) {
1176 PZ_Lock(oid_hash_lock);
1177 (void)PL_HashTableRemove(oid_hash_table, &rv->data);
1178 (void)PZ_Unlock(oid_hash_lock);
1179 goto loser;
1182 #endif /* DEBUG */
1184 return rv;
1186 loser:
1187 if( (NSSOID *)NULL != candidate ) {
1188 (void)nss_ZFreeIf(candidate->data.data);
1190 (void)nss_ZFreeIf(candidate);
1192 if( (NSSOID *)NULL != rv ) {
1193 (void)nss_ZFreeIf(rv->data.data);
1195 (void)nss_ZFreeIf(rv);
1197 return (NSSOID *)NULL;
1201 * nssOID_GetDEREncoding
1203 * This routine returns the DER encoding of the specified NSSOID.
1204 * If the optional arena argument is non-null, the memory used will
1205 * be obtained from that arena; otherwise, the memory will be obtained
1206 * from the heap. This routine may return return null upon error, in
1207 * which case it will have set an error on the error stack.
1209 * The error may be one of the following values:
1210 * NSS_ERROR_INVALID_OID
1211 * NSS_ERROR_NO_MEMORY
1213 * Return value:
1214 * NULL upon error
1215 * The DER encoding of this NSSOID
1218 NSS_EXTERN NSSDER *
1219 nssOID_GetDEREncoding
1221 const NSSOID *oid,
1222 NSSDER *rvOpt,
1223 NSSArena *arenaOpt
1226 const NSSItem *it;
1227 NSSDER *rv;
1229 if( PR_SUCCESS != oid_init() ) {
1230 return (NSSDER *)NULL;
1233 #ifdef NSSDEBUG
1234 if( PR_SUCCESS != nssOID_verifyPointer(oid) ) {
1235 return (NSSDER *)NULL;
1238 if( (NSSArena *)NULL != arenaOpt ) {
1239 if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) {
1240 return (NSSDER *)NULL;
1243 #endif /* NSSDEBUG */
1245 it = &oid->data;
1247 if( (NSSDER *)NULL == rvOpt ) {
1248 rv = nss_ZNEW(arenaOpt, NSSDER);
1249 if( (NSSDER *)NULL == rv ) {
1250 return (NSSDER *)NULL;
1252 } else {
1253 rv = rvOpt;
1256 rv->data = nss_ZAlloc(arenaOpt, it->size);
1257 if( (void *)NULL == rv->data ) {
1258 if( rv != rvOpt ) {
1259 (void)nss_ZFreeIf(rv);
1261 return (NSSDER *)NULL;
1264 rv->size = it->size;
1265 nsslibc_memcpy(rv->data, it->data, it->size);
1267 return rv;
1271 * nssOID_GetUTF8Encoding
1273 * This routine returns a UTF8 string containing the dotted-number
1274 * encoding of the specified NSSOID. If the optional arena argument
1275 * is non-null, the memory used will be obtained from that arena;
1276 * otherwise, the memory will be obtained from the heap. This routine
1277 * may return null upon error, in which case it will have set an error
1278 * on the error stack.
1280 * The error may be one of the following values:
1281 * NSS_ERROR_INVALID_OID
1282 * NSS_ERROR_NO_MEMORY
1284 * Return value:
1285 * NULL upon error
1286 * A pointer to a UTF8 string containing the dotted-digit encoding of
1287 * this NSSOID
1290 NSS_EXTERN NSSUTF8 *
1291 nssOID_GetUTF8Encoding
1293 const NSSOID *oid,
1294 NSSArena *arenaOpt
1297 NSSUTF8 *rv;
1298 PRUint8 *end;
1299 PRUint8 *d;
1300 PRUint8 *e;
1301 char *a;
1302 char *b;
1303 PRUint32 len;
1305 if( PR_SUCCESS != oid_init() ) {
1306 return (NSSUTF8 *)NULL;
1309 #ifdef NSSDEBUG
1310 if( PR_SUCCESS != nssOID_verifyPointer(oid) ) {
1311 return (NSSUTF8 *)NULL;
1314 if( (NSSArena *)NULL != arenaOpt ) {
1315 if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) {
1316 return (NSSUTF8 *)NULL;
1319 #endif /* NSSDEBUG */
1321 a = (char *)NULL;
1323 /* d will point to the next sequence of bytes to decode */
1324 d = (PRUint8 *)oid->data.data;
1325 /* end points to one past the legitimate data */
1326 end = &d[ oid->data.size ];
1328 #ifdef NSSDEBUG
1330 * Guarantee that the for(e=d;e<end;e++) loop below will
1331 * terminate. Our BER sanity-checking code above will prevent
1332 * such a BER from being registered, so the only other way one
1333 * might show up is if our dotted-decimal encoder above screws
1334 * up or our generated list is wrong. So I'll wrap it with
1335 * #ifdef NSSDEBUG and #endif.
1337 if( end[-1] & 0x80 ) {
1338 nss_SetError(NSS_ERROR_INTERNAL_ERROR);
1339 return (NSSUTF8 *)NULL;
1341 #endif /* NSSDEBUG */
1344 * Check for our pseudo-encoded single-digit OIDs
1346 if( (*d == 0x80) && (2 == oid->data.size) ) {
1347 /* Funky encoding. The second byte is the number */
1348 a = PR_smprintf("%lu", (PRUint32)d[1]);
1349 if( (char *)NULL == a ) {
1350 nss_SetError(NSS_ERROR_NO_MEMORY);
1351 return (NSSUTF8 *)NULL;
1353 goto done;
1356 for( ; d < end; d = &e[1] ) {
1358 for( e = d; e < end; e++ ) {
1359 if( 0 == (*e & 0x80) ) {
1360 break;
1364 if( ((e-d) > 4) || (((e-d) == 4) && (*d & 0x70)) ) {
1365 /* More than a 32-bit number */
1366 } else {
1367 PRUint32 n = 0;
1369 switch( e-d ) {
1370 case 4:
1371 n |= ((PRUint32)(e[-4] & 0x0f)) << 28;
1372 case 3:
1373 n |= ((PRUint32)(e[-3] & 0x7f)) << 21;
1374 case 2:
1375 n |= ((PRUint32)(e[-2] & 0x7f)) << 14;
1376 case 1:
1377 n |= ((PRUint32)(e[-1] & 0x7f)) << 7;
1378 case 0:
1379 n |= ((PRUint32)(e[-0] & 0x7f)) ;
1382 if( (char *)NULL == a ) {
1383 /* This is the first number.. decompose it */
1384 PRUint32 one = (n/40), two = (n%40);
1386 a = PR_smprintf("%lu.%lu", one, two);
1387 if( (char *)NULL == a ) {
1388 nss_SetError(NSS_ERROR_NO_MEMORY);
1389 return (NSSUTF8 *)NULL;
1391 } else {
1392 b = PR_smprintf("%s.%lu", a, n);
1393 if( (char *)NULL == b ) {
1394 PR_smprintf_free(a);
1395 nss_SetError(NSS_ERROR_NO_MEMORY);
1396 return (NSSUTF8 *)NULL;
1399 PR_smprintf_free(a);
1400 a = b;
1405 done:
1407 * Even if arenaOpt is NULL, we have to copy the data so that
1408 * it'll be freed with the right version of free: ours, not
1409 * PR_smprintf_free's.
1411 len = PL_strlen(a);
1412 rv = (NSSUTF8 *)nss_ZAlloc(arenaOpt, len);
1413 if( (NSSUTF8 *)NULL == rv ) {
1414 PR_smprintf_free(a);
1415 return (NSSUTF8 *)NULL;
1418 nsslibc_memcpy(rv, a, len);
1419 PR_smprintf_free(a);
1421 return rv;
1425 * nssOID_getExplanation
1427 * This method is only present in debug builds.
1429 * This routine will return a static pointer to a UTF8-encoded string
1430 * describing (in English) the specified OID. The memory pointed to
1431 * by the return value is not owned by the caller, and should not be
1432 * freed or modified. Note that explanations are only provided for
1433 * the OIDs built into the NSS library; there is no way to specify an
1434 * explanation for dynamically created OIDs. This routine is intended
1435 * only for use in debugging tools such as "derdump." This routine
1436 * may return null upon error, in which case it will have placed an
1437 * error on the error stack.
1439 * The error may be one of the following values:
1440 * NSS_ERROR_INVALID_NSSOID
1442 * Return value:
1443 * NULL upon error
1444 * A static pointer to a readonly, non-caller-owned UTF8-encoded
1445 * string explaining the specified OID.
1448 #ifdef DEBUG
1449 NSS_EXTERN const NSSUTF8 *
1450 nssOID_getExplanation
1452 NSSOID *oid
1455 if( PR_SUCCESS != oid_init() ) {
1456 return (const NSSUTF8 *)NULL;
1459 #ifdef NSSDEBUG
1460 if( PR_SUCCESS != nssOID_verifyPointer(oid) ) {
1461 return (NSSUTF8 *)NULL;
1463 #endif /* NSSDEBUG */
1465 return oid->expl;
1468 extern const NSSError NSS_ERROR_INVALID_NSSOID;
1469 #endif /* DEBUG */
1472 * nssOID_getTaggedUTF8
1474 * This method is only present in debug builds.
1476 * This routine will return a pointer to a caller-owned UTF8-encoded
1477 * string containing a tagged encoding of the specified OID. Note
1478 * that OID (component) tags are only provided for the OIDs built
1479 * into the NSS library; there is no way to specify tags for
1480 * dynamically created OIDs. This routine is intended for use in
1481 * debugging tools such as "derdump." If the optional arena argument
1482 * is non-null, the memory used will be obtained from that arena;
1483 * otherwise, the memory will be obtained from the heap. This routine
1484 * may return return null upon error, in which case it will have set
1485 * an error on the error stack.
1487 * The error may be one of the following values
1488 * NSS_ERROR_INVALID_NSSOID
1489 * NSS_ERROR_NO_MEMORY
1491 * Return value:
1492 * NULL upon error
1493 * A pointer to a UTF8 string containing the tagged encoding of
1494 * this NSSOID
1497 #ifdef DEBUG
1498 NSS_EXTERN NSSUTF8 *
1499 nssOID_getTaggedUTF8
1501 NSSOID *oid,
1502 NSSArena *arenaOpt
1505 NSSUTF8 *rv;
1506 char *raw;
1507 char *c;
1508 char *a = (char *)NULL;
1509 char *b;
1510 PRBool done = PR_FALSE;
1511 PRUint32 len;
1513 if( PR_SUCCESS != oid_init() ) {
1514 return (NSSUTF8 *)NULL;
1517 #ifdef NSSDEBUG
1518 if( PR_SUCCESS != nssOID_verifyPointer(oid) ) {
1519 return (NSSUTF8 *)NULL;
1522 if( (NSSArena *)NULL != arenaOpt ) {
1523 if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) {
1524 return (NSSUTF8 *)NULL;
1527 #endif /* NSSDEBUG */
1529 a = PR_smprintf("{");
1530 if( (char *)NULL == a ) {
1531 nss_SetError(NSS_ERROR_NO_MEMORY);
1532 return (NSSUTF8 *)NULL;
1536 * What I'm doing here is getting the text version of the OID,
1537 * e.g. 1.2.12.92, then looking up each set of leading numbers
1538 * as oids.. e.g. "1," then "1.2," then "1.2.12," etc. Each of
1539 * those will have the leaf tag, and I just build up the string.
1540 * I never said this was the most efficient way of doing it,
1541 * but hey it's a debug-build thing, and I'm getting really tired
1542 * of writing this stupid low-level PKI code.
1545 /* I know it's all ASCII, so I can use char */
1546 raw = (char *)nssOID_GetUTF8Encoding(oid, (NSSArena *)NULL);
1547 if( (char *)NULL == raw ) {
1548 return (NSSUTF8 *)NULL;
1551 for( c = raw; !done; c++ ) {
1552 NSSOID *lead;
1553 char *lastdot;
1555 for( ; '.' != *c; c++ ) {
1556 if( '\0' == *c ) {
1557 done = PR_TRUE;
1558 break;
1562 *c = '\0';
1563 lead = nssOID_CreateFromUTF8((NSSUTF8 *)raw);
1564 if( (NSSOID *)NULL == lead ) {
1565 PR_smprintf_free(a);
1566 nss_ZFreeIf(raw);
1567 nss_SetError(NSS_ERROR_NO_MEMORY);
1568 return (NSSUTF8 *)NULL;
1571 lastdot = PL_strrchr(raw, '.');
1572 if( (char *)NULL == lastdot ) {
1573 lastdot = raw;
1576 b = PR_smprintf("%s %s(%s) ", a, lead->tag, &lastdot[1]);
1577 if( (char *)NULL == b ) {
1578 PR_smprintf_free(a);
1579 nss_ZFreeIf(raw);
1580 /* drop the OID reference on the floor */
1581 nss_SetError(NSS_ERROR_NO_MEMORY);
1582 return (NSSUTF8 *)NULL;
1585 PR_smprintf_free(a);
1586 a = b;
1588 if( !done ) {
1589 *c = '.';
1593 nss_ZFreeIf(raw);
1595 b = PR_smprintf("%s }", a);
1596 if( (char *)NULL == b ) {
1597 PR_smprintf_free(a);
1598 nss_SetError(NSS_ERROR_NO_MEMORY);
1599 return (NSSUTF8 *)NULL;
1602 len = PL_strlen(b);
1604 rv = (NSSUTF8 *)nss_ZAlloc(arenaOpt, len+1);
1605 if( (NSSUTF8 *)NULL == rv ) {
1606 PR_smprintf_free(b);
1607 return (NSSUTF8 *)NULL;
1610 nsslibc_memcpy(rv, b, len);
1611 PR_smprintf_free(b);
1613 return rv;
1616 extern const NSSError NSS_ERROR_INVALID_NSSOID;
1617 extern const NSSError NSS_ERROR_NO_MEMORY;
1618 #endif /* DEBUG */