Back to index

lightning-sunbird  0.9+nobinonly
oid.c
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is the Netscape security libraries.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 1994-2000
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *
00023  * Alternatively, the contents of this file may be used under the terms of
00024  * either the GNU General Public License Version 2 or later (the "GPL"), or
00025  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00026  * in which case the provisions of the GPL or the LGPL are applicable instead
00027  * of those above. If you wish to allow use of your version of this file only
00028  * under the terms of either the GPL or the LGPL, and not to allow others to
00029  * use your version of this file under the terms of the MPL, indicate your
00030  * decision by deleting the provisions above and replace them with the notice
00031  * and other provisions required by the GPL or the LGPL. If you do not delete
00032  * the provisions above, a recipient may use your version of this file under
00033  * the terms of any one of the MPL, the GPL or the LGPL.
00034  *
00035  * ***** END LICENSE BLOCK ***** */
00036 
00037 #ifdef DEBUG
00038 static const char CVS_ID[] = "@(#) $RCSfile: oid.c,v $ $Revision: 1.6 $ $Date: 2005/01/20 02:25:49 $";
00039 #endif /* DEBUG */
00040 
00041 /*
00042  * oid.c
00043  *
00044  * This file contains the implementation of the basic OID routines.
00045  */
00046 
00047 #ifndef BASE_H
00048 #include "base.h"
00049 #endif /* BASE_H */
00050 
00051 #ifndef PKI1_H
00052 #include "pki1.h"
00053 #endif /* PKI1_H */
00054 
00055 #include "plhash.h"
00056 #include "plstr.h"
00057 
00058 /*
00059  * NSSOID
00060  *
00061  * The public "methods" regarding this "object" are:
00062  *
00063  *  NSSOID_CreateFromBER   -- constructor
00064  *  NSSOID_CreateFromUTF8  -- constructor
00065  *  (there is no explicit destructor)
00066  * 
00067  *  NSSOID_GetDEREncoding
00068  *  NSSOID_GetUTF8Encoding
00069 
00070  * The non-public "methods" regarding this "object" are:
00071  *
00072  *  nssOID_CreateFromBER   -- constructor
00073  *  nssOID_CreateFromUTF8  -- constructor
00074  *  (there is no explicit destructor)
00075  * 
00076  *  nssOID_GetDEREncoding
00077  *  nssOID_GetUTF8Encoding
00078  *
00079  * In debug builds, the following non-public calls are also available:
00080  *
00081  *  nssOID_verifyPointer
00082  *  nssOID_getExplanation
00083  *  nssOID_getTaggedUTF8
00084  */
00085 
00086 const NSSOID *NSS_OID_UNKNOWN = (NSSOID *)NULL;
00087 
00088 /*
00089  * First, the public "wrappers"
00090  */
00091 
00092 /*
00093  * NSSOID_CreateFromBER
00094  *
00095  * This routine creates an NSSOID by decoding a BER- or DER-encoded
00096  * OID.  It may return NULL upon error, in which case it 
00097  * will have created an error stack.
00098  *
00099  * The error may be one of the following values:
00100  *  NSS_ERROR_INVALID_BER
00101  *  NSS_ERROR_NO_MEMORY
00102  *
00103  * Return value:
00104  *  NULL upon error
00105  *  An NSSOID upon success
00106  */
00107 
00108 NSS_EXTERN NSSOID *
00109 NSSOID_CreateFromBER
00110 (
00111   NSSBER *berOid
00112 )
00113 {
00114   nss_ClearErrorStack();
00115 
00116 #ifdef DEBUG
00117   /* 
00118    * NSSBERs can be created by the user, 
00119    * so no pointer-tracking can be checked.
00120    */
00121 
00122   if( (NSSBER *)NULL == berOid ) {
00123     nss_SetError(NSS_ERROR_INVALID_BER);
00124     return (NSSOID *)NULL;
00125   }
00126 
00127   if( (void *)NULL == berOid->data ) {
00128     nss_SetError(NSS_ERROR_INVALID_BER);
00129     return (NSSOID *)NULL;
00130   }
00131 #endif /* DEBUG */
00132   
00133   return nssOID_CreateFromBER(berOid);
00134 }
00135 
00136 /*
00137  * NSSOID_CreateFromUTF8
00138  *
00139  * This routine creates an NSSOID by decoding a UTF8 string 
00140  * representation of an OID in dotted-number format.  The string may 
00141  * optionally begin with an octothorpe.  It may return NULL
00142  * upon error, in which case it will have created an error stack.
00143  *
00144  * The error may be one of the following values:
00145  *  NSS_ERROR_INVALID_UTF8
00146  *  NSS_ERROR_NO_MEMORY
00147  *
00148  * Return value:
00149  *  NULL upon error
00150  *  An NSSOID upon success
00151  */
00152 
00153 NSS_EXTERN NSSOID *
00154 NSSOID_CreateFromUTF8
00155 (
00156   NSSUTF8 *stringOid
00157 )
00158 {
00159   nss_ClearErrorStack();
00160 
00161 #ifdef DEBUG
00162   /*
00163    * NSSUTF8s can be created by the user,
00164    * so no pointer-tracking can be checked.
00165    */
00166 
00167   if( (NSSUTF8 *)NULL == stringOid ) {
00168     nss_SetError(NSS_ERROR_INVALID_UTF8);
00169     return (NSSOID *)NULL;
00170   }
00171 #endif /* DEBUG */
00172 
00173   return nssOID_CreateFromUTF8(stringOid);
00174 }
00175 
00176 /*
00177  * NSSOID_GetDEREncoding
00178  *
00179  * This routine returns the DER encoding of the specified NSSOID.
00180  * If the optional arena argument is non-null, the memory used will
00181  * be obtained from that arena; otherwise, the memory will be obtained
00182  * from the heap.  This routine may return return null upon error, in 
00183  * which case it will have created an error stack.
00184  *
00185  * The error may be one of the following values:
00186  *  NSS_ERROR_INVALID_NSSOID
00187  *  NSS_ERROR_NO_MEMORY
00188  *
00189  * Return value:
00190  *  NULL upon error
00191  *  The DER encoding of this NSSOID
00192  */
00193 
00194 NSS_EXTERN NSSDER *
00195 NSSOID_GetDEREncoding
00196 (
00197   const NSSOID *oid,
00198   NSSDER *rvOpt,
00199   NSSArena *arenaOpt
00200 )
00201 {
00202   nss_ClearErrorStack();
00203 
00204 #ifdef DEBUG
00205   if( PR_SUCCESS != nssOID_verifyPointer(oid) ) {
00206     return (NSSDER *)NULL;
00207   }
00208 
00209   if( (NSSArena *)NULL != arenaOpt ) {
00210     if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) {
00211       return (NSSDER *)NULL;
00212     }
00213   }
00214 #endif /* DEBUG */
00215 
00216   return nssOID_GetDEREncoding(oid, rvOpt, arenaOpt);
00217 }
00218 
00219 /*
00220  * NSSOID_GetUTF8Encoding
00221  *
00222  * This routine returns a UTF8 string containing the dotted-number 
00223  * encoding of the specified NSSOID.  If the optional arena argument 
00224  * is non-null, the memory used will be obtained from that arena; 
00225  * otherwise, the memory will be obtained from the heap.  This routine
00226  * may return null upon error, in which case it will have created an
00227  * error stack.
00228  *
00229  * The error may be one of the following values:
00230  *  NSS_ERROR_INVALID_NSSOID
00231  *  NSS_ERROR_NO_MEMORY
00232  *
00233  * Return value:
00234  *  NULL upon error
00235  *  A pointer to a UTF8 string containing the dotted-digit encoding of 
00236  *      this NSSOID
00237  */
00238 
00239 NSS_EXTERN NSSUTF8 *
00240 NSSOID_GetUTF8Encoding
00241 (
00242   const NSSOID *oid,
00243   NSSArena *arenaOpt
00244 )
00245 {
00246   nss_ClearErrorStack();
00247 
00248 #ifdef DEBUG
00249   if( PR_SUCCESS != nssOID_verifyPointer(oid) ) {
00250     return (NSSUTF8 *)NULL;
00251   }
00252 
00253   if( (NSSArena *)NULL != arenaOpt ) {
00254     if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) {
00255       return (NSSUTF8 *)NULL;
00256     }
00257   }
00258 #endif /* DEBUG */
00259 
00260   return nssOID_GetUTF8Encoding(oid, arenaOpt);
00261 }
00262 
00263 /*
00264  * Next, some internal bookkeeping; including the OID "tag" table 
00265  * and the debug-version pointer tracker.
00266  */
00267 
00268 /*
00269  * For implementation reasons (so NSSOIDs can be compared with ==),
00270  * we hash all NSSOIDs.  This is the hash table.
00271  */
00272 
00273 static PLHashTable *oid_hash_table;
00274 
00275 /*
00276  * And this is its lock.
00277  */
00278 
00279 static PZLock *oid_hash_lock;
00280 
00281 /*
00282  * This is the hash function.  We simply XOR the encoded form with
00283  * itself in sizeof(PLHashNumber)-byte chunks.  Improving this
00284  * routine is left as an excercise for the more mathematically
00285  * inclined student.
00286  */
00287 
00288 static PLHashNumber PR_CALLBACK
00289 oid_hash
00290 (
00291   const void *key
00292 )
00293 {
00294   const NSSItem *item = (const NSSItem *)key;
00295   PLHashNumber rv = 0;
00296 
00297   PRUint8 *data = (PRUint8 *)item->data;
00298   PRUint32 i;
00299   PRUint8 *rvc = (PRUint8 *)&rv;
00300 
00301   for( i = 0; i < item->size; i++ ) {
00302     rvc[ i % sizeof(rv) ] ^= *data;
00303     data++;
00304   }
00305 
00306   return rv;
00307 }
00308 
00309 /*
00310  * This is the key-compare function.  It simply does a lexical
00311  * comparison on the encoded OID form.  This does not result in
00312  * quite the same ordering as the "sequence of numbers" order,
00313  * but heck it's only used internally by the hash table anyway.
00314  */
00315 
00316 static PRIntn PR_CALLBACK
00317 oid_hash_compare
00318 (
00319   const void *k1,
00320   const void *k2
00321 )
00322 {
00323   PRIntn rv;
00324 
00325   const NSSItem *i1 = (const NSSItem *)k1;
00326   const NSSItem *i2 = (const NSSItem *)k2;
00327 
00328   PRUint32 size = (i1->size < i2->size) ? i1->size : i2->size;
00329 
00330   rv = (PRIntn)nsslibc_memequal(i1->data, i2->data, size, (PRStatus *)NULL);
00331   if( 0 == rv ) {
00332     rv = i1->size - i2->size;
00333   }
00334 
00335   return !rv;
00336 }
00337 
00338 /*
00339  * The pointer-tracking code
00340  */
00341 
00342 #ifdef DEBUG
00343 extern const NSSError NSS_ERROR_INTERNAL_ERROR;
00344 
00345 static nssPointerTracker oid_pointer_tracker;
00346 
00347 static PRStatus
00348 oid_add_pointer
00349 (
00350   const NSSOID *oid
00351 )
00352 {
00353   PRStatus rv;
00354 
00355   rv = nssPointerTracker_initialize(&oid_pointer_tracker);
00356   if( PR_SUCCESS != rv ) {
00357     return rv;
00358   }
00359 
00360   rv = nssPointerTracker_add(&oid_pointer_tracker, oid);
00361   if( PR_SUCCESS != rv ) {
00362     NSSError e = NSS_GetError();
00363     if( NSS_ERROR_NO_MEMORY != e ) {
00364       nss_SetError(NSS_ERROR_INTERNAL_ERROR);
00365     }
00366 
00367     return rv;
00368   }
00369 
00370   return PR_SUCCESS;
00371 }
00372 
00373 #if defined(CAN_DELETE_OIDS)
00374 /*
00375  * We actually don't define NSSOID deletion, since we keep OIDs
00376  * in a hash table for easy comparison.  Were we to, this is
00377  * what the pointer-removal function would look like.
00378  */
00379 
00380 static PRStatus
00381 oid_remove_pointer
00382 (
00383   const NSSOID *oid
00384 )
00385 {
00386   PRStatus rv;
00387 
00388   rv = nssPointerTracker_remove(&oid_pointer_tracker, oid);
00389   if( PR_SUCCESS != rv ) {
00390     nss_SetError(NSS_ERROR_INTERNAL_ERROR);
00391   }
00392 
00393   return rv;
00394 }
00395 #endif /* CAN_DELETE_OIDS */
00396 
00397 #endif /* DEBUG */
00398 
00399 /*
00400  * All dynamically-added OIDs get their memory from one statically-
00401  * declared arena here, merely so that any cleanup code will have
00402  * an easier time of it.
00403  */
00404 
00405 static NSSArena *oid_arena;
00406 
00407 /*
00408  * This is the call-once function which initializes the hashtable.
00409  * It creates it, then prepopulates it with all of the builtin OIDs.
00410  * It also creates the aforementioned NSSArena.
00411  */
00412 
00413 static PRStatus PR_CALLBACK
00414 oid_once_func
00415 (
00416   void
00417 )
00418 {
00419   PRUint32 i;
00420   
00421   /* Initialize the arena */
00422   oid_arena = nssArena_Create();
00423   if( (NSSArena *)NULL == oid_arena ) {
00424     goto loser;
00425   }
00426 
00427   /* Create the hash table lock */
00428   oid_hash_lock = PZ_NewLock(nssILockOID);
00429   if( (PZLock *)NULL == oid_hash_lock ) {
00430     nss_SetError(NSS_ERROR_NO_MEMORY);
00431     goto loser;
00432   }
00433 
00434   /* Create the hash table */
00435   oid_hash_table = PL_NewHashTable(0, oid_hash, oid_hash_compare,
00436                                    PL_CompareValues, 
00437                                    (PLHashAllocOps *)0,
00438                                    (void *)0);
00439   if( (PLHashTable *)NULL == oid_hash_table ) {
00440     nss_SetError(NSS_ERROR_NO_MEMORY);
00441     goto loser;
00442   }
00443 
00444   /* And populate it with all the builtins */
00445   for( i = 0; i < nss_builtin_oid_count; i++ ) {
00446     NSSOID *oid = (NSSOID *)&nss_builtin_oids[i];
00447     PLHashEntry *e = PL_HashTableAdd(oid_hash_table, &oid->data, oid);
00448     if( (PLHashEntry *)NULL == e ) {
00449       nss_SetError(NSS_ERROR_NO_MEMORY);
00450       goto loser;
00451     }
00452 
00453 #ifdef DEBUG
00454     if( PR_SUCCESS != oid_add_pointer(oid) ) {
00455       goto loser;
00456     }
00457 #endif /* DEBUG */
00458   }
00459 
00460   return PR_SUCCESS;
00461 
00462  loser:
00463   if( (PLHashTable *)NULL != oid_hash_table ) {
00464     PL_HashTableDestroy(oid_hash_table);
00465     oid_hash_table = (PLHashTable *)NULL;
00466   }
00467 
00468   if( (PZLock *)NULL != oid_hash_lock ) {
00469     PZ_DestroyLock(oid_hash_lock);
00470     oid_hash_lock = (PZLock *)NULL;
00471   }
00472 
00473   if( (NSSArena *)NULL != oid_arena ) {
00474     (void)nssArena_Destroy(oid_arena);
00475     oid_arena = (NSSArena *)NULL;
00476   }
00477 
00478   return PR_FAILURE;
00479 }
00480 
00481 /*
00482  * This is NSPR's once-block.
00483  */
00484 
00485 static PRCallOnceType oid_call_once;
00486 
00487 /*
00488  * And this is our multiply-callable internal init routine, which
00489  * will call-once our call-once function.
00490  */
00491 
00492 static PRStatus
00493 oid_init
00494 (
00495   void
00496 )
00497 {
00498   return PR_CallOnce(&oid_call_once, oid_once_func);
00499 }
00500 
00501 #ifdef DEBUG
00502 
00503 /*
00504  * nssOID_verifyPointer
00505  *
00506  * This method is only present in debug builds.
00507  *
00508  * If the specified pointer is a valid pointer to an NSSOID object, 
00509  * this routine will return PR_SUCCESS.  Otherwise, it will put an 
00510  * error on the error stack and return PR_FAILURE.
00511  *
00512  * The error may be one of the following values:
00513  *  NSS_ERROR_INVALID_NSSOID
00514  *  NSS_ERROR_NO_MEMORY
00515  *
00516  * Return value:
00517  *  PR_SUCCESS if the pointer is valid
00518  *  PR_FAILURE if it isn't
00519  */
00520 
00521 NSS_EXTERN PRStatus
00522 nssOID_verifyPointer
00523 (
00524   const NSSOID *oid
00525 )
00526 {
00527   PRStatus rv;
00528 
00529   rv = oid_init();
00530   if( PR_SUCCESS != rv ) {
00531     return PR_FAILURE;
00532   }
00533 
00534   rv = nssPointerTracker_initialize(&oid_pointer_tracker);
00535   if( PR_SUCCESS != rv ) {
00536     return PR_FAILURE;
00537   }
00538 
00539   rv = nssPointerTracker_verify(&oid_pointer_tracker, oid);
00540   if( PR_SUCCESS != rv ) {
00541     nss_SetError(NSS_ERROR_INVALID_NSSOID);
00542     return PR_FAILURE;
00543   }
00544 
00545   return PR_SUCCESS;
00546 }
00547 #endif /* DEBUG */
00548 
00549 /*
00550  * oid_sanity_check_ber
00551  *
00552  * This routine merely applies some sanity-checking to the BER-encoded
00553  * OID.
00554  */
00555 
00556 static PRStatus
00557 oid_sanity_check_ber
00558 (
00559   NSSBER *berOid
00560 )
00561 {
00562   PRUint32 i;
00563   PRUint8 *data = (PRUint8 *)berOid->data;
00564 
00565   /*
00566    * The size must be longer than zero bytes.
00567    */
00568 
00569   if( berOid->size <= 0 ) {
00570     return PR_FAILURE;
00571   }
00572 
00573   /*
00574    * In general, we can't preclude any number from showing up
00575    * someday.  We could probably guess that top-level numbers
00576    * won't get very big (beyond the current ccitt(0), iso(1),
00577    * or joint-ccitt-iso(2)).  However, keep in mind that the
00578    * encoding rules wrap the first two numbers together, as
00579    *
00580    *     (first * 40) + second
00581    *
00582    * Also, it is noted in the specs that this implies that the
00583    * second number won't go above forty.
00584    *
00585    * 128 encodes 3.8, which seems pretty safe for now.  Let's
00586    * check that the first byte is less than that.
00587    *
00588    * XXX This is a "soft check" -- we may want to exclude it.
00589    */
00590 
00591   if( data[0] >= 0x80 ) {
00592     return PR_FAILURE;
00593   }
00594 
00595   /*
00596    * In a normalised format, leading 0x80s will never show up.
00597    * This means that no 0x80 will be preceeded by the final
00598    * byte of a sequence, which would naturaly be less than 0x80.
00599    * Our internal encoding for the single-digit OIDs uses 0x80,
00600    * but the only places we use them (loading the builtin table,
00601    * and adding a UTF8-encoded OID) bypass this check.
00602    */
00603 
00604   for( i = 1; i < berOid->size; i++ ) {
00605     if( (0x80 == data[i]) && (data[i-1] < 0x80) ) {
00606       return PR_FAILURE;
00607     }
00608   }
00609 
00610   /*
00611    * The high bit of each octet indicates that following octets
00612    * are included in the current number.  Thus the last byte can't
00613    * have the high bit set.
00614    */
00615 
00616   if( data[ berOid->size-1 ] >= 0x80 ) {
00617     return PR_FAILURE;
00618   }
00619 
00620   /*
00621    * Other than that, any byte sequence is legit.
00622    */
00623   return PR_SUCCESS;
00624 }
00625 
00626 /*
00627  * nssOID_CreateFromBER
00628  *
00629  * This routine creates an NSSOID by decoding a BER- or DER-encoded
00630  * OID.  It may return NULL upon error, in which case it 
00631  * will have set an error on the error stack.
00632  *
00633  * The error may be one of the following values:
00634  *  NSS_ERROR_INVALID_BER
00635  *  NSS_ERROR_NO_MEMORY
00636  *
00637  * Return value:
00638  *  NULL upon error
00639  *  An NSSOID upon success
00640  */
00641 
00642 NSS_EXTERN NSSOID *
00643 nssOID_CreateFromBER
00644 (
00645   NSSBER *berOid
00646 )
00647 {
00648   NSSOID *rv;
00649   PLHashEntry *e;
00650   
00651   if( PR_SUCCESS != oid_init() ) {
00652     return (NSSOID *)NULL;
00653   }
00654 
00655   if( PR_SUCCESS != oid_sanity_check_ber(berOid) ) {
00656     nss_SetError(NSS_ERROR_INVALID_BER);
00657     return (NSSOID *)NULL;
00658   }
00659 
00660   /*
00661    * Does it exist?
00662    */
00663   PZ_Lock(oid_hash_lock);
00664   rv = (NSSOID *)PL_HashTableLookup(oid_hash_table, berOid);
00665   (void)PZ_Unlock(oid_hash_lock);
00666   if( (NSSOID *)NULL != rv ) {
00667     /* Found it! */
00668     return rv;
00669   }
00670 
00671   /*
00672    * Doesn't exist-- create it.
00673    */
00674   rv = nss_ZNEW(oid_arena, NSSOID);
00675   if( (NSSOID *)NULL == rv ) {
00676     return (NSSOID *)NULL;
00677   }
00678 
00679   rv->data.data = nss_ZAlloc(oid_arena, berOid->size);
00680   if( (void *)NULL == rv->data.data ) {
00681     return (NSSOID *)NULL;
00682   }
00683 
00684   rv->data.size = berOid->size;
00685   nsslibc_memcpy(rv->data.data, berOid->data, berOid->size);
00686 
00687 #ifdef DEBUG
00688   rv->tag = "<runtime>";
00689   rv->expl = "(OID registered at runtime)";
00690 #endif /* DEBUG */
00691 
00692   PZ_Lock(oid_hash_lock);
00693   e = PL_HashTableAdd(oid_hash_table, &rv->data, rv);
00694   (void)PZ_Unlock(oid_hash_lock);
00695   if( (PLHashEntry *)NULL == e ) {
00696     nss_ZFreeIf(rv->data.data);
00697     nss_ZFreeIf(rv);
00698     nss_SetError(NSS_ERROR_NO_MEMORY);
00699     return (NSSOID *)NULL;
00700   }
00701 
00702 #ifdef DEBUG
00703   {
00704     PRStatus st;
00705     st = oid_add_pointer(rv);
00706     if( PR_SUCCESS != st ) {
00707       PZ_Lock(oid_hash_lock);
00708       (void)PL_HashTableRemove(oid_hash_table, &rv->data);
00709       (void)PZ_Unlock(oid_hash_lock);
00710       (void)nss_ZFreeIf(rv->data.data);
00711       (void)nss_ZFreeIf(rv);
00712       return (NSSOID *)NULL;
00713     }
00714   }
00715 #endif /* DEBUG */
00716 
00717   return rv;
00718 }
00719 
00720 /*
00721  * oid_sanity_check_utf8
00722  *
00723  * This routine merely applies some sanity-checking to the 
00724  * UTF8-encoded OID.
00725  */
00726 
00727 static PRStatus
00728 oid_sanity_check_utf8
00729 (
00730   NSSUTF8 *s
00731 )
00732 {
00733   /*
00734    * It may begin with an octothorpe, which we skip.
00735    */
00736 
00737   if( '#' == *s ) {
00738     s++;
00739   }
00740 
00741   /*
00742    * It begins with a number
00743    */
00744 
00745   if( (*s < '0') || (*s > '9') ) {
00746     return PR_FAILURE;
00747   }
00748 
00749   /*
00750    * First number is only one digit long
00751    *
00752    * XXX This is a "soft check" -- we may want to exclude it
00753    */
00754 
00755   if( (s[1] != '.') && (s[1] != '\0') ) {
00756     return PR_FAILURE;
00757   }
00758 
00759   /*
00760    * Every character is either a digit or a period
00761    */
00762 
00763   for( ; '\0' != *s; s++ ) {
00764     if( ('.' != *s) && ((*s < '0') || (*s > '9')) ) {
00765       return PR_FAILURE;
00766     }
00767 
00768     /* No two consecutive periods */
00769     if( ('.' == *s) && ('.' == s[1]) ) {
00770       return PR_FAILURE;
00771     }
00772   }
00773 
00774   /*
00775    * The last character isn't a period
00776    */
00777 
00778   if( '.' == *--s ) {
00779     return PR_FAILURE;
00780   }
00781 
00782   return PR_SUCCESS;
00783 }
00784 
00785 static PRUint32
00786 oid_encode_number
00787 (
00788   PRUint32 n,
00789   PRUint8 *dp,
00790   PRUint32 nb
00791 )
00792 {
00793   PRUint32 a[5];
00794   PRUint32 i;
00795   PRUint32 rv;
00796 
00797   a[0] = (n >> 28) & 0x7f;
00798   a[1] = (n >> 21) & 0x7f;
00799   a[2] = (n >> 14) & 0x7f;
00800   a[3] = (n >>  7) & 0x7f;
00801   a[4] =  n        & 0x7f;
00802 
00803   for( i = 0; i < 5; i++ ) {
00804     if( 0 != a[i] ) {
00805       break;
00806     }
00807   }
00808 
00809   if( 5 == i ) {
00810     i--;
00811   }
00812 
00813   rv = 5-i;
00814   if( rv > nb ) {
00815     return rv;
00816   }
00817 
00818   for( ; i < 4; i++ ) {
00819     *dp = 0x80 | a[i];
00820     dp++;
00821   }
00822 
00823   *dp = a[4];
00824 
00825   return rv;
00826 }
00827 
00828 /*
00829  * oid_encode_huge
00830  *
00831  * This routine will convert a huge decimal number into the DER 
00832  * encoding for oid numbers.  It is not limited to numbers that will
00833  * fit into some wordsize, like oid_encode_number.  But it's not
00834  * necessarily very fast, either.  This is here in case some joker
00835  * throws us an ASCII oid like 1.2.3.99999999999999999999999999.
00836  */
00837 
00838 static PRUint32
00839 oid_encode_huge
00840 (
00841   NSSUTF8 *s,
00842   NSSUTF8 *e,
00843   PRUint8 *dp,
00844   PRUint32 nb
00845 )
00846 {
00847   PRUint32 slen = (e-s);
00848   PRUint32 blen = (slen+1)/2;
00849   PRUint8 *st = (PRUint8 *)NULL;
00850   PRUint8 *bd = (PRUint8 *)NULL;
00851   PRUint32 i;
00852   PRUint32 bitno;
00853   PRUint8 *last;
00854   PRUint8 *first;
00855   PRUint32 byteno;
00856   PRUint8 mask;
00857 
00858   /* We'll be munging the data, so duplicate it */
00859   st = (PRUint8 *)nss_ZAlloc((NSSArena *)NULL, slen);
00860   if( (PRUint8 *)NULL == st ) {
00861     return 0;
00862   }
00863 
00864   /* Don't know ahead of time exactly how long we'll need */
00865   bd = (PRUint8 *)nss_ZAlloc((NSSArena *)NULL, blen);
00866   if( (PRUint8 *)NULL == bd ) {
00867     (void)nss_ZFreeIf(st);
00868     return 0;
00869   }
00870 
00871   /* Copy the original, and convert ASCII to numbers */
00872   for( i = 0; i < slen; i++ ) {
00873     st[i] = (PRUint8)(s[i] - '0');
00874   }
00875 
00876   last = &st[slen-1];
00877   first = &st[0];
00878 
00879   /*
00880    * The way we create the binary version is by looking at it 
00881    * bit by bit.  Start with the least significant bit.  If the
00882    * number is odd, set that bit.  Halve the number (with integer
00883    * division), and go to the next least significant bit.  Keep 
00884    * going until the number goes to zero.
00885    */
00886   for( bitno = 0; ; bitno++ ) {
00887     PRUint8 *d;
00888 
00889     byteno = bitno/7;
00890     mask = (PRUint8)(1 << (bitno%7));
00891 
00892     /* Skip leading zeroes */
00893     for( ; first < last; first ++ ) {
00894       if( 0 != *first ) {
00895         break;
00896       }
00897     }
00898 
00899     /* Down to one number and it's a zero?  Done. */
00900     if( (first == last) && (0 == *last) ) {
00901       break;
00902     }
00903 
00904     /* Last digit is odd?  Set the bit */
00905     if( *last & 1 ) {
00906       bd[ byteno ] |= mask;
00907     }
00908 
00909 
00910     /* 
00911      * Divide the number in half.  This is just a matter
00912      * of going from the least significant digit upwards,
00913      * halving each one.  If any digit is odd (other than
00914      * the last, which has already been handled), add five
00915      * to the digit to its right.
00916      */
00917     *last /= 2;
00918 
00919     for( d = &last[-1]; d >= first; d-- ) {
00920       if( *d & 1 ) {
00921         d[1] += 5;
00922       }
00923 
00924       *d /= 2;
00925     }
00926   }
00927 
00928   /* Is there room to write the encoded data? */
00929   if( (byteno+1) > nb ) {
00930     return (byteno+1);
00931   }
00932 
00933   /* Trim any leading zero that crept in there */
00934   for( ; byteno > 0; byteno-- ) {
00935     if( 0 != bd[ byteno ] ) {
00936       break;
00937     }
00938   }
00939 
00940   /* Copy all but the last, marking the "continue" bit */
00941   for( i = 0; i < byteno; i++ ) {
00942     dp[i] = bd[ byteno-i ] | 0x80;
00943   }
00944   /* And the last with the "continue" bit clear */
00945   dp[byteno] = bd[0];
00946 
00947   (void)nss_ZFreeIf(bd);
00948   (void)nss_ZFreeIf(st);
00949   return (byteno+1);
00950 }
00951 
00952 /*
00953  * oid_encode_string
00954  *
00955  * This routine converts a dotted-number OID into a DER-encoded
00956  * one.  It assumes we've already sanity-checked the string.
00957  */
00958 
00959 extern const NSSError NSS_ERROR_INTERNAL_ERROR;
00960 
00961 static NSSOID *
00962 oid_encode_string
00963 (
00964   NSSUTF8 *s
00965 )
00966 {
00967   PRUint32 nn = 0; /* number of numbers */
00968   PRUint32 nb = 0; /* number of bytes (estimated) */
00969   NSSUTF8 *t;
00970   PRUint32 nd = 0; /* number of digits */
00971   NSSOID *rv;
00972   PRUint8 *dp;
00973   PRUint32 a, b;
00974   PRUint32 inc;
00975 
00976   /* Dump any octothorpe */
00977   if( '#' == *s ) {
00978     s++;
00979   }
00980 
00981   /* Count up the bytes needed */
00982   for( t = s; '\0' != *t; t++ ) {
00983     if( '.' == *t ) {
00984       nb += (nd+1)/2; /* errs on the big side */
00985       nd = 0;
00986       nn++;
00987     } else {
00988       nd++;
00989     }
00990   }
00991   nb += (nd+1)/2;
00992   nn++;
00993 
00994   if( 1 == nn ) {
00995     /*
00996      * We have our own "denormalised" encoding for these,
00997      * which is only used internally.
00998      */
00999     nb++;
01000   }
01001 
01002   /* 
01003    * Allocate.  Note that we don't use the oid_arena here.. this is
01004    * because there really isn't a "free()" for stuff allocated out of
01005    * arenas (at least with the current implementation), so this would
01006    * keep using up memory each time a UTF8-encoded OID were added.
01007    * If need be (if this is the first time this oid has been seen),
01008    * we'll copy it.
01009    */
01010   rv = nss_ZNEW((NSSArena *)NULL, NSSOID);
01011   if( (NSSOID *)NULL == rv ) {
01012     return (NSSOID *)NULL;
01013   }
01014 
01015   rv->data.data = nss_ZAlloc((NSSArena *)NULL, nb);
01016   if( (void *)NULL == rv->data.data ) {
01017     (void)nss_ZFreeIf(rv);
01018     return (NSSOID *)NULL;
01019   }
01020 
01021   dp = (PRUint8 *)rv->data.data;
01022 
01023   a = atoi(s);
01024 
01025   if( 1 == nn ) {
01026     dp[0] = '\x80';
01027     inc = oid_encode_number(a, &dp[1], nb-1);
01028     if( inc >= nb ) {
01029       goto loser;
01030     }
01031   } else {
01032     for( t = s; '.' != *t; t++ ) {
01033       ;
01034     }
01035 
01036     t++;
01037     b = atoi(t);
01038     inc = oid_encode_number(a*40+b, dp, nb);
01039     if( inc > nb ) {
01040       goto loser;
01041     }
01042     dp += inc;
01043     nb -= inc;
01044     nn -= 2;
01045 
01046     while( nn-- > 0 ) {
01047       NSSUTF8 *u;
01048 
01049       for( ; '.' != *t; t++ ) {
01050         ;
01051       }
01052 
01053       t++;
01054 
01055       for( u = t; ('\0' != *u) && ('.' != *u); u++ ) {
01056         ;
01057       }
01058 
01059       if( (u-t > 9) ) {
01060         /* In the billions.  Rats. */
01061         inc = oid_encode_huge(t, u, dp, nb);
01062       } else {
01063         b = atoi(t);
01064         inc = oid_encode_number(b, dp, nb);
01065       }
01066 
01067       if( inc > nb ) {
01068         goto loser;
01069       }
01070       dp += inc;
01071       nb -= inc;
01072     }
01073   }
01074 
01075   return rv;
01076 
01077  loser:
01078   nss_SetError(NSS_ERROR_INTERNAL_ERROR);
01079   return (NSSOID *)NULL;
01080 }
01081 
01082 /*
01083  * nssOID_CreateFromUTF8
01084  *
01085  * This routine creates an NSSOID by decoding a UTF8 string 
01086  * representation of an OID in dotted-number format.  The string may 
01087  * optionally begin with an octothorpe.  It may return NULL
01088  * upon error, in which case it will have set an error on the error 
01089  * stack.
01090  *
01091  * The error may be one of the following values:
01092  *  NSS_ERROR_INVALID_STRING
01093  *  NSS_ERROR_NO_MEMORY
01094  *
01095  * Return value:
01096  *  NULL upon error
01097  *  An NSSOID upon success
01098  */
01099 
01100 NSS_EXTERN NSSOID *
01101 nssOID_CreateFromUTF8
01102 (
01103   NSSUTF8 *stringOid
01104 )
01105 {
01106   NSSOID *rv = (NSSOID *)NULL;
01107   NSSOID *candidate = (NSSOID *)NULL;
01108   PLHashEntry *e;
01109 
01110   if( PR_SUCCESS != oid_init() ) {
01111     return (NSSOID *)NULL;
01112   }
01113 
01114   if( PR_SUCCESS != oid_sanity_check_utf8(stringOid) ) {
01115     nss_SetError(NSS_ERROR_INVALID_STRING);
01116     return (NSSOID *)NULL;
01117   }
01118 
01119   candidate = oid_encode_string(stringOid);
01120   if( (NSSOID *)NULL == candidate ) {
01121     /* Internal error only */
01122     return rv;
01123   }
01124 
01125   /*
01126    * Does it exist?
01127    */
01128   PZ_Lock(oid_hash_lock);
01129   rv = (NSSOID *)PL_HashTableLookup(oid_hash_table, &candidate->data);
01130   (void)PZ_Unlock(oid_hash_lock);
01131   if( (NSSOID *)NULL != rv ) {
01132     /* Already exists.  Delete my copy and return the original. */
01133     (void)nss_ZFreeIf(candidate->data.data);
01134     (void)nss_ZFreeIf(candidate);
01135     return rv;
01136   }
01137 
01138   /* 
01139    * Nope.  Add it.  Remember to allocate it out of the oid arena.
01140    */
01141 
01142   rv = nss_ZNEW(oid_arena, NSSOID);
01143   if( (NSSOID *)NULL == rv ) {
01144     goto loser;
01145   }
01146 
01147   rv->data.data = nss_ZAlloc(oid_arena, candidate->data.size);
01148   if( (void *)NULL == rv->data.data ) {
01149     goto loser;
01150   }
01151 
01152   rv->data.size = candidate->data.size;
01153   nsslibc_memcpy(rv->data.data, candidate->data.data, rv->data.size);
01154 
01155   (void)nss_ZFreeIf(candidate->data.data);
01156   (void)nss_ZFreeIf(candidate);
01157 
01158 #ifdef DEBUG
01159   rv->tag = "<runtime>";
01160   rv->expl = "(OID registered at runtime)";
01161 #endif /* DEBUG */
01162 
01163   PZ_Lock(oid_hash_lock);
01164   e = PL_HashTableAdd(oid_hash_table, &rv->data, rv);
01165   (void)PZ_Unlock(oid_hash_lock);
01166   if( (PLHashEntry *)NULL == e ) {
01167     nss_SetError(NSS_ERROR_NO_MEMORY);
01168     goto loser;
01169   }
01170 
01171 #ifdef DEBUG
01172   {
01173     PRStatus st;
01174     st = oid_add_pointer(rv);
01175     if( PR_SUCCESS != st ) {
01176       PZ_Lock(oid_hash_lock);
01177       (void)PL_HashTableRemove(oid_hash_table, &rv->data);
01178       (void)PZ_Unlock(oid_hash_lock);
01179       goto loser;
01180     }
01181   }
01182 #endif /* DEBUG */
01183 
01184   return rv;
01185 
01186  loser:
01187   if( (NSSOID *)NULL != candidate ) {
01188     (void)nss_ZFreeIf(candidate->data.data);
01189   }
01190   (void)nss_ZFreeIf(candidate);
01191 
01192   if( (NSSOID *)NULL != rv ) {
01193     (void)nss_ZFreeIf(rv->data.data);
01194   }
01195   (void)nss_ZFreeIf(rv);
01196 
01197   return (NSSOID *)NULL;
01198 }
01199 
01200 /*
01201  * nssOID_GetDEREncoding
01202  *
01203  * This routine returns the DER encoding of the specified NSSOID.
01204  * If the optional arena argument is non-null, the memory used will
01205  * be obtained from that arena; otherwise, the memory will be obtained
01206  * from the heap.  This routine may return return null upon error, in 
01207  * which case it will have set an error on the error stack.
01208  *
01209  * The error may be one of the following values:
01210  *  NSS_ERROR_INVALID_OID
01211  *  NSS_ERROR_NO_MEMORY
01212  *
01213  * Return value:
01214  *  NULL upon error
01215  *  The DER encoding of this NSSOID
01216  */
01217 
01218 NSS_EXTERN NSSDER *
01219 nssOID_GetDEREncoding
01220 (
01221   const NSSOID *oid,
01222   NSSDER *rvOpt,
01223   NSSArena *arenaOpt
01224 )
01225 {
01226   const NSSItem *it;
01227   NSSDER *rv;
01228 
01229   if( PR_SUCCESS != oid_init() ) {
01230     return (NSSDER *)NULL;
01231   }
01232 
01233 #ifdef NSSDEBUG
01234   if( PR_SUCCESS != nssOID_verifyPointer(oid) ) {
01235     return (NSSDER *)NULL;
01236   }
01237 
01238   if( (NSSArena *)NULL != arenaOpt ) {
01239     if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) {
01240       return (NSSDER *)NULL;
01241     }
01242   }
01243 #endif /* NSSDEBUG */
01244 
01245   it = &oid->data;
01246 
01247   if( (NSSDER *)NULL == rvOpt ) {
01248     rv = nss_ZNEW(arenaOpt, NSSDER);
01249     if( (NSSDER *)NULL == rv ) {
01250       return (NSSDER *)NULL;
01251     }
01252   } else {
01253     rv = rvOpt;
01254   }
01255 
01256   rv->data = nss_ZAlloc(arenaOpt, it->size);
01257   if( (void *)NULL == rv->data ) {
01258     if( rv != rvOpt ) {
01259       (void)nss_ZFreeIf(rv);
01260     }
01261     return (NSSDER *)NULL;
01262   }
01263 
01264   rv->size = it->size;
01265   nsslibc_memcpy(rv->data, it->data, it->size);
01266 
01267   return rv;
01268 }
01269 
01270 /*
01271  * nssOID_GetUTF8Encoding
01272  *
01273  * This routine returns a UTF8 string containing the dotted-number 
01274  * encoding of the specified NSSOID.  If the optional arena argument 
01275  * is non-null, the memory used will be obtained from that arena; 
01276  * otherwise, the memory will be obtained from the heap.  This routine
01277  * may return null upon error, in which case it will have set an error
01278  * on the error stack.
01279  *
01280  * The error may be one of the following values:
01281  *  NSS_ERROR_INVALID_OID
01282  *  NSS_ERROR_NO_MEMORY
01283  *
01284  * Return value:
01285  *  NULL upon error
01286  *  A pointer to a UTF8 string containing the dotted-digit encoding of 
01287  *      this NSSOID
01288  */
01289 
01290 NSS_EXTERN NSSUTF8 *
01291 nssOID_GetUTF8Encoding
01292 (
01293   const NSSOID *oid,
01294   NSSArena *arenaOpt
01295 )
01296 {
01297   NSSUTF8 *rv;
01298   PRUint8 *end;
01299   PRUint8 *d;
01300   PRUint8 *e;
01301   char *a;
01302   char *b;
01303   PRUint32 len;
01304 
01305   if( PR_SUCCESS != oid_init() ) {
01306     return (NSSUTF8 *)NULL;
01307   }
01308 
01309 #ifdef NSSDEBUG
01310   if( PR_SUCCESS != nssOID_verifyPointer(oid) ) {
01311     return (NSSUTF8 *)NULL;
01312   }
01313 
01314   if( (NSSArena *)NULL != arenaOpt ) {
01315     if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) {
01316       return (NSSUTF8 *)NULL;
01317     }
01318   }
01319 #endif /* NSSDEBUG */
01320 
01321   a = (char *)NULL;
01322 
01323   /* d will point to the next sequence of bytes to decode */
01324   d = (PRUint8 *)oid->data.data;
01325   /* end points to one past the legitimate data */
01326   end = &d[ oid->data.size ];
01327 
01328 #ifdef NSSDEBUG
01329   /*
01330    * Guarantee that the for(e=d;e<end;e++) loop below will
01331    * terminate.  Our BER sanity-checking code above will prevent
01332    * such a BER from being registered, so the only other way one
01333    * might show up is if our dotted-decimal encoder above screws
01334    * up or our generated list is wrong.  So I'll wrap it with
01335    * #ifdef NSSDEBUG and #endif.
01336    */
01337   if( end[-1] & 0x80 ) {
01338     nss_SetError(NSS_ERROR_INTERNAL_ERROR);
01339     return (NSSUTF8 *)NULL;
01340   }
01341 #endif /* NSSDEBUG */
01342 
01343   /*
01344    * Check for our pseudo-encoded single-digit OIDs
01345    */
01346   if( (*d == 0x80) && (2 == oid->data.size) ) {
01347     /* Funky encoding.  The second byte is the number */
01348     a = PR_smprintf("%lu", (PRUint32)d[1]);
01349     if( (char *)NULL == a ) {
01350       nss_SetError(NSS_ERROR_NO_MEMORY);
01351       return (NSSUTF8 *)NULL;
01352     }
01353     goto done;
01354   }
01355 
01356   for( ; d < end; d = &e[1] ) {
01357     
01358     for( e = d; e < end; e++ ) {
01359       if( 0 == (*e & 0x80) ) {
01360         break;
01361       }
01362     }
01363     
01364     if( ((e-d) > 4) || (((e-d) == 4) && (*d & 0x70)) ) {
01365       /* More than a 32-bit number */
01366     } else {
01367       PRUint32 n = 0;
01368       
01369       switch( e-d ) {
01370       case 4:
01371         n |= ((PRUint32)(e[-4] & 0x0f)) << 28;
01372       case 3:
01373         n |= ((PRUint32)(e[-3] & 0x7f)) << 21;
01374       case 2:
01375         n |= ((PRUint32)(e[-2] & 0x7f)) << 14;
01376       case 1:
01377         n |= ((PRUint32)(e[-1] & 0x7f)) <<  7;
01378       case 0:
01379         n |= ((PRUint32)(e[-0] & 0x7f))      ;
01380       }
01381       
01382       if( (char *)NULL == a ) {
01383         /* This is the first number.. decompose it */
01384         PRUint32 one = (n/40), two = (n%40);
01385         
01386         a = PR_smprintf("%lu.%lu", one, two);
01387         if( (char *)NULL == a ) {
01388           nss_SetError(NSS_ERROR_NO_MEMORY);
01389           return (NSSUTF8 *)NULL;
01390         }
01391       } else {
01392         b = PR_smprintf("%s.%lu", a, n);
01393         if( (char *)NULL == b ) {
01394           PR_smprintf_free(a);
01395           nss_SetError(NSS_ERROR_NO_MEMORY);
01396           return (NSSUTF8 *)NULL;
01397         }
01398         
01399         PR_smprintf_free(a);
01400         a = b;
01401       }
01402     }
01403   }
01404 
01405  done:
01406   /*
01407    * Even if arenaOpt is NULL, we have to copy the data so that
01408    * it'll be freed with the right version of free: ours, not
01409    * PR_smprintf_free's.
01410    */
01411   len = PL_strlen(a);
01412   rv = (NSSUTF8 *)nss_ZAlloc(arenaOpt, len);
01413   if( (NSSUTF8 *)NULL == rv ) {
01414     PR_smprintf_free(a);
01415     return (NSSUTF8 *)NULL;
01416   }
01417 
01418   nsslibc_memcpy(rv, a, len);
01419   PR_smprintf_free(a);
01420 
01421   return rv;
01422 }
01423 
01424 /*
01425  * nssOID_getExplanation
01426  *
01427  * This method is only present in debug builds.
01428  *
01429  * This routine will return a static pointer to a UTF8-encoded string
01430  * describing (in English) the specified OID.  The memory pointed to
01431  * by the return value is not owned by the caller, and should not be
01432  * freed or modified.  Note that explanations are only provided for
01433  * the OIDs built into the NSS library; there is no way to specify an
01434  * explanation for dynamically created OIDs.  This routine is intended
01435  * only for use in debugging tools such as "derdump."  This routine
01436  * may return null upon error, in which case it will have placed an
01437  * error on the error stack.
01438  *
01439  * The error may be one of the following values:
01440  *  NSS_ERROR_INVALID_NSSOID
01441  *
01442  * Return value:
01443  *  NULL upon error
01444  *  A static pointer to a readonly, non-caller-owned UTF8-encoded
01445  *    string explaining the specified OID.
01446  */
01447 
01448 #ifdef DEBUG
01449 NSS_EXTERN const NSSUTF8 *
01450 nssOID_getExplanation
01451 (
01452   NSSOID *oid
01453 )
01454 {
01455   if( PR_SUCCESS != oid_init() ) {
01456     return (const NSSUTF8 *)NULL;
01457   }
01458 
01459 #ifdef NSSDEBUG
01460   if( PR_SUCCESS != nssOID_verifyPointer(oid) ) {
01461     return (NSSUTF8 *)NULL;
01462   }
01463 #endif /* NSSDEBUG */
01464 
01465   return oid->expl;
01466 }
01467 
01468 extern const NSSError NSS_ERROR_INVALID_NSSOID;
01469 #endif /* DEBUG */
01470 
01471 /*
01472  * nssOID_getTaggedUTF8
01473  *
01474  * This method is only present in debug builds.
01475  *
01476  * This routine will return a pointer to a caller-owned UTF8-encoded
01477  * string containing a tagged encoding of the specified OID.  Note
01478  * that OID (component) tags are only provided for the OIDs built 
01479  * into the NSS library; there is no way to specify tags for 
01480  * dynamically created OIDs.  This routine is intended for use in
01481  * debugging tools such as "derdump."   If the optional arena argument
01482  * is non-null, the memory used will be obtained from that arena; 
01483  * otherwise, the memory will be obtained from the heap.  This routine 
01484  * may return return null upon error, in which case it will have set 
01485  * an error on the error stack.
01486  *
01487  * The error may be one of the following values
01488  *  NSS_ERROR_INVALID_NSSOID
01489  *  NSS_ERROR_NO_MEMORY
01490  *
01491  * Return value:
01492  *  NULL upon error
01493  *  A pointer to a UTF8 string containing the tagged encoding of
01494  *      this NSSOID
01495  */
01496 
01497 #ifdef DEBUG
01498 NSS_EXTERN NSSUTF8 *
01499 nssOID_getTaggedUTF8
01500 (
01501   NSSOID *oid,
01502   NSSArena *arenaOpt
01503 )
01504 {
01505   NSSUTF8 *rv;
01506   char *raw;
01507   char *c;
01508   char *a = (char *)NULL;
01509   char *b;
01510   PRBool done = PR_FALSE;
01511   PRUint32 len;
01512 
01513   if( PR_SUCCESS != oid_init() ) {
01514     return (NSSUTF8 *)NULL;
01515   }
01516 
01517 #ifdef NSSDEBUG
01518   if( PR_SUCCESS != nssOID_verifyPointer(oid) ) {
01519     return (NSSUTF8 *)NULL;
01520   }
01521 
01522   if( (NSSArena *)NULL != arenaOpt ) {
01523     if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) {
01524       return (NSSUTF8 *)NULL;
01525     }
01526   }
01527 #endif /* NSSDEBUG */
01528 
01529   a = PR_smprintf("{");
01530   if( (char *)NULL == a ) {
01531     nss_SetError(NSS_ERROR_NO_MEMORY);
01532     return (NSSUTF8 *)NULL;
01533   }
01534 
01535   /*
01536    * What I'm doing here is getting the text version of the OID,
01537    * e.g. 1.2.12.92, then looking up each set of leading numbers
01538    * as oids.. e.g. "1," then "1.2," then "1.2.12," etc.  Each of
01539    * those will have the leaf tag, and I just build up the string.
01540    * I never said this was the most efficient way of doing it,
01541    * but hey it's a debug-build thing, and I'm getting really tired
01542    * of writing this stupid low-level PKI code.
01543    */
01544 
01545   /* I know it's all ASCII, so I can use char */
01546   raw = (char *)nssOID_GetUTF8Encoding(oid, (NSSArena *)NULL);
01547   if( (char *)NULL == raw ) {
01548     return (NSSUTF8 *)NULL;
01549   }
01550 
01551   for( c = raw; !done; c++ ) {
01552     NSSOID *lead;
01553     char *lastdot;
01554 
01555     for( ; '.' != *c; c++ ) {
01556       if( '\0' == *c ) {
01557         done = PR_TRUE;
01558         break;
01559       }
01560     }
01561 
01562     *c = '\0';
01563     lead = nssOID_CreateFromUTF8((NSSUTF8 *)raw);
01564     if( (NSSOID *)NULL == lead ) {
01565       PR_smprintf_free(a);
01566       nss_ZFreeIf(raw);
01567       nss_SetError(NSS_ERROR_NO_MEMORY);
01568       return (NSSUTF8 *)NULL;
01569     }
01570 
01571     lastdot = PL_strrchr(raw, '.');
01572     if( (char *)NULL == lastdot ) {
01573       lastdot = raw;
01574     }
01575 
01576     b = PR_smprintf("%s %s(%s) ", a, lead->tag, &lastdot[1]);
01577     if( (char *)NULL == b ) {
01578       PR_smprintf_free(a);
01579       nss_ZFreeIf(raw);
01580       /* drop the OID reference on the floor */
01581       nss_SetError(NSS_ERROR_NO_MEMORY);
01582       return (NSSUTF8 *)NULL;
01583     }
01584 
01585     PR_smprintf_free(a);
01586     a = b;
01587 
01588     if( !done ) {
01589       *c = '.';
01590     }
01591   }
01592 
01593   nss_ZFreeIf(raw);
01594 
01595   b = PR_smprintf("%s }", a);
01596   if( (char *)NULL == b ) {
01597     PR_smprintf_free(a);
01598     nss_SetError(NSS_ERROR_NO_MEMORY);
01599     return (NSSUTF8 *)NULL;
01600   }
01601 
01602   len = PL_strlen(b);
01603   
01604   rv = (NSSUTF8 *)nss_ZAlloc(arenaOpt, len+1);
01605   if( (NSSUTF8 *)NULL == rv ) {
01606     PR_smprintf_free(b);
01607     return (NSSUTF8 *)NULL;
01608   }
01609 
01610   nsslibc_memcpy(rv, b, len);
01611   PR_smprintf_free(b);
01612 
01613   return rv;
01614 }
01615 
01616 extern const NSSError NSS_ERROR_INVALID_NSSOID;
01617 extern const NSSError NSS_ERROR_NO_MEMORY;
01618 #endif /* DEBUG */