Back to index

lightning-sunbird  0.9+nobinonly
nsCrypto.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is mozilla.org code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 2001
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Javier Delgadillo <javi@netscape.com>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either the GNU General Public License Version 2 or later (the "GPL"), or
00028  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 #include "nsNSSComponent.h"
00040 #include "nsCrypto.h"
00041 #include "nsKeygenHandler.h"
00042 #include "nsKeygenThread.h"
00043 #include "nsNSSCertificate.h"
00044 #include "nsNSSCertificateDB.h"
00045 #include "nsPKCS12Blob.h"
00046 #include "nsPK11TokenDB.h"
00047 #include "nsIServiceManager.h"
00048 #include "nsIMemory.h"
00049 #include "nsCRT.h"
00050 #include "prprf.h"
00051 #include "prmem.h"
00052 #include "nsDOMCID.h"
00053 #include "nsIDOMWindow.h"
00054 #include "nsIDOMClassInfo.h"
00055 #include "nsIDOMDocument.h"
00056 #include "nsIDocument.h"
00057 #include "nsIScriptObjectPrincipal.h"
00058 #include "nsIScriptContext.h"
00059 #include "nsIScriptGlobalObject.h"
00060 #include "nsIXPConnect.h"
00061 #include "nsIRunnable.h"
00062 #include "nsIWindowWatcher.h"
00063 #include "nsIPrompt.h"
00064 #include "nsIFilePicker.h"
00065 #include "nsJSPrincipals.h"
00066 #include "nsIPrincipal.h"
00067 #include "nsXPIDLString.h"
00068 #include "nsIGenKeypairInfoDlg.h"
00069 #include "nsIDOMCryptoDialogs.h"
00070 #include "nsIFormSigningDialog.h"
00071 #include "nsIProxyObjectManager.h"
00072 #include "nsIJSContextStack.h"
00073 #include "jsapi.h"
00074 #include "jsdbgapi.h"
00075 #include <ctype.h>
00076 #include "nsReadableUtils.h"
00077 #include "pk11func.h"
00078 #include "keyhi.h"
00079 #include "cryptohi.h"
00080 #include "seccomon.h"
00081 #include "secerr.h"
00082 #include "sechash.h"
00083 extern "C" {
00084 #include "crmf.h"
00085 #include "pk11pqg.h"
00086 }
00087 #include "cmmf.h"
00088 #include "nssb64.h"
00089 #include "base64.h"
00090 #include "certdb.h"
00091 #include "secmod.h"
00092 #include "nsISaveAsCharset.h"
00093 
00094 #include "ssl.h" // For SSL_ClearSessionCache
00095 
00096 #include "nsNSSCleaner.h"
00097 NSSCleanupAutoPtrClass(SECKEYPrivateKey, SECKEY_DestroyPrivateKey)
00098 NSSCleanupAutoPtrClass(PK11SlotInfo, PK11_FreeSlot)
00099 NSSCleanupAutoPtrClass(CERTCertNicknames, CERT_FreeNicknames)
00100 
00101 #include "nsNSSShutDown.h"
00102 #include "nsNSSCertHelper.h"
00103 
00104 /*
00105  * These are the most common error strings that are returned
00106  * by the JavaScript methods in case of error.
00107  */
00108 
00109 #define JS_ERROR       "error:"
00110 #define JS_ERROR_INTERNAL  JS_ERROR"internalError"
00111 
00112 #undef REPORT_INCORRECT_NUM_ARGS
00113 
00114 #define JS_OK_ADD_MOD                      3
00115 #define JS_OK_DEL_EXTERNAL_MOD             2
00116 #define JS_OK_DEL_INTERNAL_MOD             1
00117 
00118 #define JS_ERR_INTERNAL                   -1
00119 #define JS_ERR_USER_CANCEL_ACTION         -2
00120 #define JS_ERR_INCORRECT_NUM_OF_ARGUMENTS -3
00121 #define JS_ERR_DEL_MOD                    -4
00122 #define JS_ERR_ADD_MOD                    -5
00123 #define JS_ERR_BAD_MODULE_NAME            -6
00124 #define JS_ERR_BAD_DLL_NAME               -7
00125 #define JS_ERR_BAD_MECHANISM_FLAGS        -8
00126 #define JS_ERR_BAD_CIPHER_ENABLE_FLAGS    -9
00127 #define JS_ERR_ADD_DUPLICATE_MOD          -10
00128 
00129 /*
00130  * This structure is used to store information for one key generation.
00131  * The nsCrypto::GenerateCRMFRequest method parses the inputs and then
00132  * stores one of these structures for every key generation that happens.
00133  * The information stored in this structure is then used to set some
00134  * values in the CRMF request.
00135  */
00136 typedef enum {
00137   rsaEnc, rsaDualUse, rsaSign, rsaNonrepudiation, rsaSignNonrepudiation,
00138   dhEx, dsaSignNonrepudiation, dsaSign, dsaNonrepudiation, invalidKeyGen
00139 } nsKeyGenType;
00140 
00141 typedef struct nsKeyPairInfoStr {
00142   SECKEYPublicKey  *pubKey;     /* The putlic key associated with gen'd 
00143                                    priv key. */
00144   SECKEYPrivateKey *privKey;    /* The private key we generated */ 
00145   nsKeyGenType      keyGenType; /* What type of key gen are we doing.*/
00146 } nsKeyPairInfo;
00147 
00148 
00149 //This class is just used to pass arguments
00150 //to the nsCryptoRunnable event.
00151 class nsCryptoRunArgs : public nsISupports {
00152 public:
00153   nsCryptoRunArgs();
00154   virtual ~nsCryptoRunArgs();
00155   nsCOMPtr<nsISupports> m_kungFuDeathGrip;
00156   JSContext *m_cx;
00157   JSObject  *m_scope;
00158   nsCOMPtr<nsIPrincipal> m_principals;
00159   nsXPIDLCString m_jsCallback;
00160   NS_DECL_ISUPPORTS
00161 };
00162 
00163 //This class is used to run the callback code
00164 //passed to crypto.generateCRMFRequest
00165 //We have to do that for backwards compatibility
00166 //reasons w/ PSM 1.x and Communciator 4.x
00167 class nsCryptoRunnable : public nsIRunnable {
00168 public:
00169   nsCryptoRunnable(nsCryptoRunArgs *args);
00170   virtual ~nsCryptoRunnable();
00171 
00172   NS_IMETHOD Run ();
00173   NS_DECL_ISUPPORTS
00174 private:
00175   nsCryptoRunArgs *m_args;
00176 };
00177 
00178 
00179 //We're going to inherit the memory passed
00180 //into us.
00181 //This class backs up an array of certificates
00182 //as an event.
00183 class nsP12Runnable : public nsIRunnable {
00184 public:
00185   nsP12Runnable(nsIX509Cert **certArr, PRInt32 numCerts, nsIPK11Token *token);
00186   virtual ~nsP12Runnable();
00187 
00188   NS_IMETHOD Run();
00189   NS_DECL_ISUPPORTS
00190 private:
00191   nsCOMPtr<nsIPK11Token> mToken;
00192   nsIX509Cert **mCertArr;
00193   PRInt32       mNumCerts;
00194 };
00195 
00196 // QueryInterface implementation for nsCrypto
00197 NS_INTERFACE_MAP_BEGIN(nsCrypto)
00198   NS_INTERFACE_MAP_ENTRY(nsIDOMCrypto)
00199   NS_INTERFACE_MAP_ENTRY(nsISupports)
00200   NS_INTERFACE_MAP_ENTRY_DOM_CLASSINFO(Crypto)
00201 NS_INTERFACE_MAP_END
00202 
00203 NS_IMPL_ADDREF(nsCrypto)
00204 NS_IMPL_RELEASE(nsCrypto)
00205 
00206 // QueryInterface implementation for nsCRMFObject
00207 NS_INTERFACE_MAP_BEGIN(nsCRMFObject)
00208   NS_INTERFACE_MAP_ENTRY(nsIDOMCRMFObject)
00209   NS_INTERFACE_MAP_ENTRY(nsISupports)
00210   NS_INTERFACE_MAP_ENTRY_DOM_CLASSINFO(CRMFObject)
00211 NS_INTERFACE_MAP_END
00212 
00213 NS_IMPL_ADDREF(nsCRMFObject)
00214 NS_IMPL_RELEASE(nsCRMFObject)
00215 
00216 // QueryInterface implementation for nsPkcs11
00217 NS_INTERFACE_MAP_BEGIN(nsPkcs11)
00218   NS_INTERFACE_MAP_ENTRY(nsIDOMPkcs11)
00219   NS_INTERFACE_MAP_ENTRY(nsISupports)
00220   NS_INTERFACE_MAP_ENTRY_DOM_CLASSINFO(Pkcs11)
00221 NS_INTERFACE_MAP_END
00222 
00223 NS_IMPL_ADDREF(nsPkcs11)
00224 NS_IMPL_RELEASE(nsPkcs11)
00225 
00226 // ISupports implementation for nsCryptoRunnable
00227 NS_IMPL_ISUPPORTS1(nsCryptoRunnable, nsIRunnable)
00228 
00229 // ISupports implementation for nsP12Runnable
00230 NS_IMPL_ISUPPORTS1(nsP12Runnable, nsIRunnable)
00231 
00232 // ISupports implementation for nsCryptoRunArgs
00233 NS_IMPL_ISUPPORTS0(nsCryptoRunArgs)
00234 
00235 static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
00236 
00237 nsCrypto::nsCrypto() :
00238   mEnableSmartCardEvents(PR_FALSE)
00239 {
00240 }
00241 
00242 nsCrypto::~nsCrypto()
00243 {
00244 }
00245 
00246 NS_IMETHODIMP
00247 nsCrypto::SetEnableSmartCardEvents(PRBool aEnable)
00248 {
00249   nsresult rv = NS_OK;
00250 
00251   // this has the side effect of starting the nssComponent (and initializing
00252   // NSS) even if it isn't already going. Starting the nssComponent is a 
00253   // prerequisite for getting smartCard events.
00254   if (aEnable) {
00255     nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv));
00256   }
00257 
00258   if (NS_FAILED(rv)) {
00259     return rv;
00260   }
00261 
00262   mEnableSmartCardEvents = aEnable;
00263   return NS_OK;
00264 }
00265 
00266 NS_IMETHODIMP
00267 nsCrypto::GetEnableSmartCardEvents(PRBool *aEnable)
00268 {
00269   *aEnable = mEnableSmartCardEvents;
00270   return NS_OK;
00271 }
00272 
00273 //These next few functions are based on implementation in
00274 //the script security manager to get the principals from
00275 //a JSContext.  We need that to successfully run the 
00276 //callback paramter passed to crypto.generateCRMFRequest
00277 static nsresult
00278 cryptojs_GetScriptPrincipal(JSContext *cx, JSScript *script,
00279                             nsIPrincipal **result)
00280 {
00281   if (!script) {
00282     *result = nsnull;
00283     return NS_OK;
00284   }
00285   JSPrincipals *jsp = JS_GetScriptPrincipals(cx, script);
00286   if (!jsp) {
00287     return NS_ERROR_FAILURE;
00288   }
00289   nsJSPrincipals *nsJSPrin = NS_STATIC_CAST(nsJSPrincipals *, jsp);
00290   *result = nsJSPrin->nsIPrincipalPtr;
00291   if (!*result) {
00292     return NS_ERROR_FAILURE;
00293   }
00294   NS_ADDREF(*result);
00295   return NS_OK;
00296 }
00297 
00298 static nsresult
00299 cryptojs_GetObjectPrincipal(JSContext *aCx, JSObject *aObj,
00300                             nsIPrincipal **result)
00301 {
00302   JSObject *parent = aObj;
00303   do
00304   {
00305     JSClass *jsClass = JS_GetClass(aCx, parent);
00306     const uint32 privateNsISupports = JSCLASS_HAS_PRIVATE | 
00307                                       JSCLASS_PRIVATE_IS_NSISUPPORTS;
00308     if (jsClass && (jsClass->flags & (privateNsISupports)) == 
00309                     privateNsISupports)
00310     {
00311       nsISupports *supports = (nsISupports *) JS_GetPrivate(aCx, parent);
00312       nsCOMPtr<nsIScriptObjectPrincipal> objPrin = do_QueryInterface(supports);
00313               
00314       if (!objPrin)
00315       {
00316         /*
00317          * If it's a wrapped native, check the underlying native
00318          * instead.
00319          */
00320         nsCOMPtr<nsIXPConnectWrappedNative> xpcNative = 
00321                                             do_QueryInterface(supports);
00322 
00323         if (xpcNative) {
00324           objPrin = do_QueryWrappedNative(xpcNative);
00325         }
00326       }
00327 
00328       if (objPrin && ((*result = objPrin->GetPrincipal()))) {
00329         NS_ADDREF(*result);
00330         return NS_OK;
00331       }
00332     }
00333     parent = JS_GetParent(aCx, parent);
00334   } while (parent);
00335 
00336   // Couldn't find a principal for this object.
00337   return NS_ERROR_FAILURE;
00338 }
00339 
00340 static nsresult
00341 cryptojs_GetFunctionObjectPrincipal(JSContext *cx, JSObject *obj,
00342                                     nsIPrincipal **result)
00343 {
00344   JSFunction *fun = (JSFunction *) JS_GetPrivate(cx, obj);
00345 
00346   JSScript *script = JS_GetFunctionScript(cx, fun);
00347   if (script && JS_GetFunctionObject(fun) != obj)
00348   {
00349     // Scripted function has been cloned; get principals from obj's
00350     // parent-linked scope chain.  We do not get object principals for a
00351     // cloned *native* function, because the subject in that case is a
00352     // script or function further down the stack who is calling us.
00353     return cryptojs_GetObjectPrincipal(cx, obj, result);
00354   }
00355   return cryptojs_GetScriptPrincipal(cx, script, result);
00356 }
00357 
00358 static nsresult
00359 cryptojs_GetFramePrincipal(JSContext *cx, JSStackFrame *fp,
00360                            nsIPrincipal **principal)
00361 {
00362   JSObject *obj = JS_GetFrameFunctionObject(cx, fp);
00363   if (!obj) {
00364     JSScript *script = JS_GetFrameScript(cx, fp);
00365     return cryptojs_GetScriptPrincipal(cx, script, principal);
00366   }
00367   return cryptojs_GetFunctionObjectPrincipal(cx, obj, principal);
00368 }
00369 
00370 already_AddRefed<nsIPrincipal>
00371 nsCrypto::GetScriptPrincipal(JSContext *cx)
00372 {
00373   JSStackFrame *fp = nsnull;
00374   nsIPrincipal *principal=nsnull;
00375 
00376   for (fp = JS_FrameIterator(cx, &fp); fp; fp = JS_FrameIterator(cx, &fp)) {
00377     cryptojs_GetFramePrincipal(cx, fp, &principal);
00378     if (principal != nsnull) {
00379       break;
00380     }
00381   }
00382 
00383   if (principal)
00384     return principal;
00385 
00386   nsIScriptContext *scriptContext = GetScriptContextFromJSContext(cx);
00387 
00388   if (scriptContext)
00389   {
00390     nsCOMPtr<nsIScriptObjectPrincipal> globalData =
00391       do_QueryInterface(scriptContext->GetGlobalObject());
00392     NS_ENSURE_TRUE(globalData, nsnull);
00393     NS_IF_ADDREF(principal = globalData->GetPrincipal());
00394   }
00395 
00396   return principal;
00397 }
00398 
00399 //A quick function to let us know if the key we're trying to generate
00400 //can be escrowed.
00401 static PRBool
00402 ns_can_escrow(nsKeyGenType keyGenType)
00403 {
00404   /* For now, we only escrow rsa-encryption keys. */
00405   return (PRBool)(keyGenType == rsaEnc);
00406 }
00407 
00408 //Retrieve crypto.version so that callers know what
00409 //version of PSM this is.
00410 NS_IMETHODIMP
00411 nsCrypto::GetVersion(nsAString& aVersion)
00412 {
00413   aVersion.Assign(NS_LITERAL_STRING(PSM_VERSION_STRING).get());
00414   return NS_OK;
00415 }
00416 
00417 /*
00418  * Given an nsKeyGenType, return the PKCS11 mechanism that will
00419  * perform the correct key generation.
00420  */
00421 static PRUint32
00422 cryptojs_convert_to_mechanism(nsKeyGenType keyGenType)
00423 {
00424   PRUint32 retMech;
00425 
00426   switch (keyGenType) {
00427   case rsaEnc:
00428   case rsaDualUse:
00429   case rsaSign:
00430   case rsaNonrepudiation:
00431   case rsaSignNonrepudiation:
00432     retMech = CKM_RSA_PKCS_KEY_PAIR_GEN;
00433     break;
00434   case dhEx:
00435     retMech = CKM_DH_PKCS_KEY_PAIR_GEN;
00436     break;
00437   case dsaSign:
00438   case dsaSignNonrepudiation:
00439   case dsaNonrepudiation:
00440     retMech = CKM_DSA_KEY_PAIR_GEN;
00441     break;
00442   default:
00443     retMech = CKM_INVALID_MECHANISM;
00444   }
00445   return retMech;
00446 }
00447 
00448 /*
00449  * This function converts a string read through JavaScript parameters
00450  * and translates it to the internal enumeration representing the
00451  * key gen type.
00452  */
00453 static nsKeyGenType
00454 cryptojs_interpret_key_gen_type(char *keyAlg)
00455 {
00456   char *end;
00457   if (keyAlg == nsnull) {
00458     return invalidKeyGen;
00459   }
00460   /* First let's remove all leading and trailing white space */
00461   while (isspace(keyAlg[0])) keyAlg++;
00462   end = strchr(keyAlg, '\0');
00463   if (end == nsnull) {
00464     return invalidKeyGen;
00465   }
00466   end--;
00467   while (isspace(*end)) end--;
00468   end[1] = '\0';
00469   if (strcmp(keyAlg, "rsa-ex") == 0) {
00470     return rsaEnc;
00471   } else if (strcmp(keyAlg, "rsa-dual-use") == 0) {
00472     return rsaDualUse;
00473   } else if (strcmp(keyAlg, "rsa-sign") == 0) {
00474     return rsaSign;
00475   } else if (strcmp(keyAlg, "rsa-sign-nonrepudiation") == 0) {
00476     return rsaSignNonrepudiation;
00477   } else if (strcmp(keyAlg, "rsa-nonrepudiation") == 0) {
00478     return rsaNonrepudiation;
00479   } else if (strcmp(keyAlg, "dsa-sign-nonrepudiation") == 0) {
00480     return dsaSignNonrepudiation;
00481   } else if (strcmp(keyAlg, "dsa-sign") ==0 ){
00482     return dsaSign;
00483   } else if (strcmp(keyAlg, "dsa-nonrepudiation") == 0) {
00484     return dsaNonrepudiation;
00485   } else if (strcmp(keyAlg, "dh-ex") == 0) {
00486     return dhEx;
00487   }
00488   return invalidKeyGen;
00489 }
00490 
00491 //Take the string passed into us via crypto.generateCRMFRequest
00492 //as the keygen type parameter and convert it to parameters 
00493 //we can actually pass to the PKCS#11 layer.
00494 static void*
00495 nsConvertToActualKeyGenParams(PRUint32 keyGenMech, char *params,
00496                               PRUint32 paramLen, PRInt32 keySize)
00497 {
00498   void *returnParams = nsnull;
00499 
00500   // We don't support passing in key generation arguments from
00501   // the JS code just yet.
00502   if (!params) {
00503     /* In this case we provide the parameters ourselves. */
00504     switch (keyGenMech) {
00505     case CKM_RSA_PKCS_KEY_PAIR_GEN:
00506     {
00507        PK11RSAGenParams *rsaParams;
00508        rsaParams = NS_STATIC_CAST(PK11RSAGenParams*,
00509                                   nsMemory::Alloc(sizeof(PK11RSAGenParams)));
00510                                  
00511        if (rsaParams == nsnull) {
00512          return nsnull;
00513        }
00514        /* I'm just taking the same parameters used in 
00515         * certdlgs.c:GenKey
00516         */
00517        if (keySize > 0) {
00518          rsaParams->keySizeInBits = keySize;
00519        } else {
00520          rsaParams->keySizeInBits = 1024;
00521        }
00522        rsaParams->pe = DEFAULT_RSA_KEYGEN_PE;
00523        returnParams = rsaParams;
00524        break;
00525     }
00526     case CKM_DSA_KEY_PAIR_GEN:
00527     {
00528        PQGParams *pqgParams = nsnull;
00529        PQGVerify *vfy = nsnull;
00530        SECStatus  rv;
00531        int        index;
00532           
00533        index = PQG_PBITS_TO_INDEX(keySize);
00534        if (index == -1) {
00535          returnParams = nsnull;
00536          break;
00537        }
00538        rv = PK11_PQG_ParamGen(0, &pqgParams, &vfy);
00539        if (vfy) {
00540          PK11_PQG_DestroyVerify(vfy);
00541        }
00542        if (rv != SECSuccess) {
00543          if (pqgParams) {
00544            PK11_PQG_DestroyParams(pqgParams);
00545          }
00546          return nsnull;
00547        }
00548        returnParams = pqgParams;
00549        break;
00550      }
00551      default:
00552        returnParams = nsnull;
00553      }
00554   }
00555   return returnParams;
00556 }
00557 
00558 //We need to choose which PKCS11 slot we're going to generate
00559 //the key on.  Calls the default implementation provided by
00560 //nsKeygenHandler.cpp
00561 static PK11SlotInfo*
00562 nsGetSlotForKeyGen(nsKeyGenType keyGenType, nsIInterfaceRequestor *ctx)
00563 {
00564   nsNSSShutDownPreventionLock locker;
00565   PRUint32 mechanism = cryptojs_convert_to_mechanism(keyGenType);
00566   PK11SlotInfo *slot = nsnull;
00567   nsresult rv = GetSlotWithMechanism(mechanism,ctx, &slot);
00568   if (NS_FAILED(rv)) {
00569     if (slot)
00570       PK11_FreeSlot(slot);
00571     slot = nsnull;
00572   }
00573   return slot;
00574 }
00575 
00576 //Free the parameters that were passed into PK11_GenerateKeyPair
00577 //depending on the mechanism type used.
00578 static void
00579 nsFreeKeyGenParams(CK_MECHANISM_TYPE keyGenMechanism, void *params)
00580 {
00581   switch (keyGenMechanism) {
00582   case CKM_RSA_PKCS_KEY_PAIR_GEN:
00583     nsMemory::Free(params);
00584     break;
00585   case CKM_DSA_KEY_PAIR_GEN:
00586     PK11_PQG_DestroyParams(NS_STATIC_CAST(PQGParams*,params));
00587     break;
00588   }
00589 }
00590 
00591 //Function that is used to generate a single key pair.
00592 //Once all the arguments have been parsed and processed, this
00593 //function gets called and takes care of actually generating
00594 //the key pair passing the appopriate parameters to the NSS
00595 //functions.
00596 static nsresult
00597 cryptojs_generateOneKeyPair(JSContext *cx, nsKeyPairInfo *keyPairInfo, 
00598                             PRInt32 keySize, char *params, 
00599                             nsIInterfaceRequestor *uiCxt,
00600                             PK11SlotInfo *slot, PRBool willEscrow)
00601                             
00602 {
00603   nsIGeneratingKeypairInfoDialogs * dialogs;
00604   nsKeygenThread *KeygenRunnable = 0;
00605   nsCOMPtr<nsIKeygenThread> runnable;
00606 
00607   PRUint32 mechanism = cryptojs_convert_to_mechanism(keyPairInfo->keyGenType);
00608   void *keyGenParams = nsConvertToActualKeyGenParams(mechanism, params, 
00609                                                      (params) ? strlen(params):0, 
00610                                                      keySize);
00611 
00612   // Make sure the token has password already set on it before trying
00613   // to generate the key.
00614 
00615   nsresult rv = setPassword(slot, uiCxt);
00616   if (NS_FAILED(rv))
00617     return rv;
00618 
00619   if (PK11_Authenticate(slot, PR_TRUE, uiCxt) != SECSuccess)
00620     return NS_ERROR_FAILURE;
00621  
00622 
00623   // Smart cards will not let you extract a private key once 
00624   // it is on the smart card.  If we've been told to escrow
00625   // a private key that will ultimately wind up on a smart card,
00626   // then we'll generate the private key on the internal slot
00627   // as a temporary key, then move it to the destination slot. 
00628   // NOTE: We call PK11_GetInternalSlot instead of PK11_GetInternalKeySlot
00629   //       so that the key has zero chance of being store in the
00630   //       user's key3.db file.  Which the slot returned by
00631   //       PK11_GetInternalKeySlot has access to and PK11_GetInternalSlot
00632   //       does not.
00633   PK11SlotInfo *intSlot = nsnull;
00634   PK11SlotInfoCleaner siCleaner(intSlot);
00635   
00636   PK11SlotInfo *origSlot = nsnull;
00637   PRBool isPerm;
00638 
00639   if (willEscrow && !PK11_IsInternal(slot)) {
00640     intSlot = PK11_GetInternalSlot();
00641     NS_ASSERTION(intSlot,"Couldn't get the internal slot");
00642     isPerm = PR_FALSE;
00643     origSlot = slot;
00644     slot = intSlot;
00645   } else {
00646     isPerm = PR_TRUE;
00647   }
00648 
00649   rv = getNSSDialogs((void**)&dialogs,
00650                      NS_GET_IID(nsIGeneratingKeypairInfoDialogs),
00651                      NS_GENERATINGKEYPAIRINFODIALOGS_CONTRACTID);
00652 
00653   if (NS_SUCCEEDED(rv)) {
00654     KeygenRunnable = new nsKeygenThread();
00655     if (KeygenRunnable) {
00656       NS_ADDREF(KeygenRunnable);
00657     }
00658   }
00659 
00660   if (NS_FAILED(rv) || !KeygenRunnable) {
00661     rv = NS_OK;
00662     keyPairInfo->privKey = PK11_GenerateKeyPair(slot, mechanism, keyGenParams,
00663                                                 &keyPairInfo->pubKey, isPerm, 
00664                                                 isPerm, uiCxt);
00665   } else {
00666     KeygenRunnable->SetParams( slot, mechanism, keyGenParams, isPerm, isPerm, uiCxt );
00667 
00668     runnable = do_QueryInterface(KeygenRunnable);
00669 
00670     if (runnable) {
00671       {
00672         nsPSMUITracker tracker;
00673         if (tracker.isUIForbidden()) {
00674           rv = NS_ERROR_NOT_AVAILABLE;
00675         }
00676         else {
00677           rv = dialogs->DisplayGeneratingKeypairInfo(uiCxt, runnable);
00678           // We call join on the thread, 
00679           // so we can be sure that no simultaneous access to the passed parameters will happen.
00680           KeygenRunnable->Join();
00681         }
00682       }
00683 
00684       NS_RELEASE(dialogs);
00685       if (NS_SUCCEEDED(rv)) {
00686         rv = KeygenRunnable->GetParams(&keyPairInfo->privKey, &keyPairInfo->pubKey);
00687       }
00688     }
00689   }
00690 
00691   nsFreeKeyGenParams(mechanism, keyGenParams);
00692 
00693   if (KeygenRunnable) {
00694     NS_RELEASE(KeygenRunnable);
00695   }
00696 
00697   if (!keyPairInfo->privKey || !keyPairInfo->pubKey) {
00698     return NS_ERROR_FAILURE;
00699   }
00700  
00701 
00702   //If we generated the key pair on the internal slot because the
00703   // keys were going to be escrowed, move the keys over right now.
00704   if (willEscrow && intSlot) {
00705     SECKEYPrivateKey *newPrivKey = PK11_LoadPrivKey(origSlot, 
00706                                                     keyPairInfo->privKey,
00707                                                     keyPairInfo->pubKey,
00708                                                     PR_TRUE, PR_TRUE);
00709     SECKEYPrivateKeyCleaner pkCleaner(newPrivKey);
00710 
00711     if (!newPrivKey)
00712       return NS_ERROR_FAILURE;
00713 
00714     // The private key is stored on the selected slot now, and the copy we
00715     // ultimately use for escrowing when the time comes lives 
00716     // in the internal slot.  We will delete it from that slot
00717     // after the requests are made.  This call only gives up
00718     // our reference to the key object and does not actually 
00719     // physically remove it from the card itself.
00720     // The actual delete calls are being made in the destructors
00721     // of the cleaner helper instances.
00722   }  
00723 
00724   return NS_OK;
00725 }
00726 
00727 /*
00728  * FUNCTION: cryptojs_ReadArgsAndGenerateKey
00729  * -------------------------------------
00730  * INPUTS:
00731  *  cx
00732  *    The JSContext associated with the execution of the corresponging
00733  *    crypto.generateCRMFRequest call
00734  *  argv
00735  *    A pointer to an array of JavaScript parameters passed to the
00736  *    method crypto.generateCRMFRequest.  The array should have the
00737  *    3 arguments keySize, "keyParams", and "keyGenAlg" mentioned in
00738  *    the definition of crypto.generateCRMFRequest at the following
00739  *    document http://docs.iplanet.com/docs/manuals/psm/11/cmcjavascriptapi.html 
00740  *  keyGenType
00741  *    A structure used to store the information about the newly created
00742  *    key pair.
00743  *  uiCxt
00744  *    An interface requestor that would be used to get an nsIPrompt
00745  *    if we need to ask the user for a password.
00746  *  slotToUse
00747  *    The PKCS11 slot to use for generating the key pair. If nsnull, then
00748  *    this function should select a slot that can do the key generation 
00749  *    from the keytype associted with the keyPairInfo, and pass it back to
00750  *    the caller so that subsequence key generations can use the same slot. 
00751  *  willEscrow
00752  *    If PR_TRUE, then that means we will try to escrow the generated
00753  *    private key when building the CRMF request.  If PR_FALSE, then
00754  *    we will not try to escrow the private key.
00755  *
00756  * NOTES:
00757  * This function takes care of reading a set of 3 parameters that define
00758  * one key generation.  The argv pointer should be one that originates
00759  * from the argv parameter passed in to the method nsCrypto::GenerateCRMFRequest.
00760  * The function interprets the argument in the first index as an integer and
00761  * passes that as the key size for the key generation-this parameter is
00762  * mandatory.  The second parameter is read in as a string.  This value can
00763  * be null in JavaScript world and everything will still work.  The third
00764  * parameter is a mandatory string that indicates what kind of key to generate.
00765  * There should always be 1-to-1 correspondence between the strings compared
00766  * in the function cryptojs_interpret_key_gen_type and the strings listed in
00767  * document at http://docs.iplanet.com/docs/manuals/psm/11/cmcjavascriptapi.html 
00768  * under the definition of the method generateCRMFRequest, for the parameter
00769  * "keyGenAlgN".  After reading the parameters, the function then 
00770  * generates the key pairs passing the parameters parsed from the JavaScript i
00771  * routine.  
00772  *
00773  * RETURN:
00774  * NS_OK if creating the Key was successful.  Any other return value
00775  * indicates an error.
00776  */
00777 
00778 static nsresult
00779 cryptojs_ReadArgsAndGenerateKey(JSContext *cx,
00780                                 jsval *argv,
00781                                 nsKeyPairInfo *keyGenType,
00782                                 nsIInterfaceRequestor *uiCxt,
00783                                 PK11SlotInfo **slot, PRBool willEscrow)
00784 {
00785   JSString  *jsString;
00786   char    *params, *keyGenAlg; //Never free these strings cause
00787                                //they are owned by the JS layer.
00788   int    keySize;
00789   nsresult  rv;
00790 
00791   if (!JSVAL_IS_INT(argv[0])) {
00792     JS_ReportError(cx, "%s%s\n", JS_ERROR,
00793                    "passed in non-integer for key size");
00794     return NS_ERROR_FAILURE;
00795   }
00796   keySize = JSVAL_TO_INT(argv[0]);
00797   if (JSVAL_IS_NULL(argv[1])) {
00798     params = nsnull;
00799   } else {
00800     jsString = JS_ValueToString(cx,argv[1]);
00801     params   = JS_GetStringBytes(jsString);
00802   }
00803 
00804   if (JSVAL_IS_NULL(argv[2])) {
00805     JS_ReportError(cx,"%s%s\n", JS_ERROR,
00806              "key generation type not specified");
00807     return NS_ERROR_FAILURE;
00808   }
00809   jsString = JS_ValueToString(cx, argv[2]);
00810   keyGenAlg = JS_GetStringBytes(jsString);
00811   keyGenType->keyGenType = cryptojs_interpret_key_gen_type(keyGenAlg);
00812   if (keyGenType->keyGenType == invalidKeyGen) {
00813     JS_ReportError(cx, "%s%s%s", JS_ERROR,
00814                    "invalid key generation argument:",
00815                    keyGenAlg);
00816     goto loser;
00817   }
00818   if (*slot == nsnull) {
00819     *slot = nsGetSlotForKeyGen(keyGenType->keyGenType, uiCxt);
00820     if (*slot == nsnull)
00821       goto loser;
00822   }
00823 
00824   rv = cryptojs_generateOneKeyPair(cx,keyGenType,keySize,params,uiCxt,*slot,
00825                                    willEscrow);
00826 
00827   if (rv != NS_OK) {
00828     JS_ReportError(cx,"%s%s%s", JS_ERROR,
00829                    "could not generate the key for algorithm ",
00830                    keyGenAlg);
00831     goto loser;
00832   }
00833   return NS_OK;
00834 loser:
00835   return NS_ERROR_FAILURE;
00836 }
00837 
00838 //Utility funciton to free up the memory used by nsKeyPairInfo
00839 //arrays.
00840 static void
00841 nsFreeKeyPairInfo(nsKeyPairInfo *keyids, int numIDs)
00842 {
00843   NS_ASSERTION(keyids, "NULL pointer passed to nsFreeKeyPairInfo");
00844   if (!keyids)
00845     return;
00846   int i;
00847   for (i=0; i<numIDs; i++) {
00848     if (keyids[i].pubKey)
00849       SECKEY_DestroyPublicKey(keyids[i].pubKey);
00850     if (keyids[i].privKey)
00851       SECKEY_DestroyPrivateKey(keyids[i].privKey);
00852   }
00853   delete []keyids;
00854 }
00855 
00856 //Utility funciton used to free the genertaed cert request messages
00857 static void
00858 nsFreeCertReqMessages(CRMFCertReqMsg **certReqMsgs, PRInt32 numMessages)
00859 {
00860   PRInt32 i;
00861   for (i=0; i<numMessages && certReqMsgs[i]; i++) {
00862     CRMF_DestroyCertReqMsg(certReqMsgs[i]);
00863   }
00864   delete []certReqMsgs;
00865 }
00866 
00867 //If the form called for escrowing the private key we just generated,
00868 //this function adds all the correct elements to the request.
00869 //That consists of adding CRMFEncryptedKey to the reques as part
00870 //of the CRMFPKIArchiveOptions Control.
00871 static nsresult
00872 nsSetEscrowAuthority(CRMFCertRequest *certReq, nsKeyPairInfo *keyInfo,
00873                      nsNSSCertificate *wrappingCert)
00874 {
00875   if (!wrappingCert ||
00876       CRMF_CertRequestIsControlPresent(certReq, crmfPKIArchiveOptionsControl)){
00877     return NS_ERROR_FAILURE;
00878   }
00879   CERTCertificate *cert = wrappingCert->GetCert();
00880   if (!cert)
00881     return NS_ERROR_FAILURE;
00882 
00883   CRMFEncryptedKey *encrKey = 
00884       CRMF_CreateEncryptedKeyWithEncryptedValue(keyInfo->privKey, cert);
00885   CERT_DestroyCertificate(cert);
00886   if (!encrKey)
00887     return NS_ERROR_FAILURE;
00888 
00889   CRMFPKIArchiveOptions *archOpt = 
00890       CRMF_CreatePKIArchiveOptions(crmfEncryptedPrivateKey, encrKey);
00891   if (!archOpt) {
00892     CRMF_DestroyEncryptedKey(encrKey);
00893     return NS_ERROR_FAILURE;
00894   }
00895   SECStatus srv = CRMF_CertRequestSetPKIArchiveOptions(certReq, archOpt);
00896   CRMF_DestroyEncryptedKey(encrKey);
00897   CRMF_DestroyPKIArchiveOptions(archOpt);
00898   if (srv != SECSuccess)
00899     return NS_ERROR_FAILURE;
00900 
00901   return NS_OK;
00902 }
00903 
00904 //Set the Distinguished Name (Subject Name) for the cert
00905 //being requested.
00906 static nsresult
00907 nsSetDNForRequest(CRMFCertRequest *certReq, char *reqDN)
00908 {
00909   if (!reqDN || CRMF_CertRequestIsFieldPresent(certReq, crmfSubject)) {
00910     return NS_ERROR_FAILURE;
00911   }
00912   CERTName *subjectName = CERT_AsciiToName(reqDN);
00913   if (!subjectName) {
00914     return NS_ERROR_FAILURE;
00915   }
00916   SECStatus srv = CRMF_CertRequestSetTemplateField(certReq, crmfSubject,
00917                                                    NS_STATIC_CAST(void*,
00918                                                                   subjectName));
00919   CERT_DestroyName(subjectName);
00920   return (srv == SECSuccess) ? NS_OK : NS_ERROR_FAILURE;
00921 }
00922 
00923 //Set Registration Token Control on the request.
00924 static nsresult
00925 nsSetRegToken(CRMFCertRequest *certReq, char *regToken)
00926 {
00927   // this should never happen, but might as well add this.
00928   NS_ASSERTION(certReq, "A bogus certReq passed to nsSetRegToken");
00929   if (regToken){
00930     if (CRMF_CertRequestIsControlPresent(certReq, crmfRegTokenControl))
00931       return NS_ERROR_FAILURE;
00932   
00933     SECItem src;
00934     src.data = (unsigned char*)regToken;
00935     src.len  = strlen(regToken);
00936     SECItem *derEncoded = SEC_ASN1EncodeItem(nsnull, nsnull, &src, 
00937                                         SEC_ASN1_GET(SEC_UTF8StringTemplate));
00938 
00939     if (!derEncoded)
00940       return NS_ERROR_FAILURE;
00941 
00942     SECStatus srv = CRMF_CertRequestSetRegTokenControl(certReq, derEncoded);
00943     SECITEM_FreeItem(derEncoded,PR_TRUE);
00944     if (srv != SECSuccess)
00945       return NS_ERROR_FAILURE;
00946   }
00947   return NS_OK;
00948 }
00949 
00950 //Set the Authenticator control on the cert reuest.  It's just
00951 //a string that gets passed along.
00952 static nsresult
00953 nsSetAuthenticator(CRMFCertRequest *certReq, char *authenticator)
00954 {
00955   //This should never happen, but might as well check.
00956   NS_ASSERTION(certReq, "Bogus certReq passed to nsSetAuthenticator");
00957   if (authenticator) {
00958     if (CRMF_CertRequestIsControlPresent(certReq, crmfAuthenticatorControl))
00959       return NS_ERROR_FAILURE;
00960     
00961     SECItem src;
00962     src.data = (unsigned char*)authenticator;
00963     src.len  = strlen(authenticator);
00964     SECItem *derEncoded = SEC_ASN1EncodeItem(nsnull, nsnull, &src,
00965                                      SEC_ASN1_GET(SEC_UTF8StringTemplate));
00966     if (!derEncoded)
00967       return NS_ERROR_FAILURE;
00968 
00969     SECStatus srv = CRMF_CertRequestSetAuthenticatorControl(certReq, 
00970                                                             derEncoded);
00971     SECITEM_FreeItem(derEncoded, PR_TRUE);
00972     if (srv != SECSuccess)
00973       return NS_ERROR_FAILURE;
00974   }
00975   return NS_OK;
00976 }
00977 
00978 // ASN1 DER encoding rules say that when encoding a BIT string,
00979 // the length in the header for the bit string is the number 
00980 // of "useful" bits in the BIT STRING.  So the function finds
00981 // it and sets accordingly for the returned item.
00982 static void
00983 nsPrepareBitStringForEncoding (SECItem *bitsmap, SECItem *value)
00984 {
00985   unsigned char onebyte;
00986   unsigned int i, len = 0;
00987 
00988   /* to prevent warning on some platform at compile time */
00989   onebyte = '\0';
00990   /* Get the position of the right-most turn-on bit */
00991   for (i = 0; i < (value->len ) * 8; ++i) {
00992     if (i % 8 == 0)
00993       onebyte = value->data[i/8];
00994     if (onebyte & 0x80)
00995       len = i;
00996     onebyte <<= 1;
00997   }
00998 
00999   bitsmap->data = value->data;
01000   /* Add one here since we work with base 1 */
01001   bitsmap->len = len + 1;
01002 }
01003 
01004 //This next section defines all the functions that sets the 
01005 //keyUsageExtension for all the different types of key gens
01006 //we handle.  The keyUsageExtension is just a bit flag extension
01007 //that we set in wrapper functions that call straight into
01008 //nsSetKeyUsageExtension.  There is one wrapper funciton for each
01009 //keyGenType.  The correct function will eventually be called 
01010 //by going through a switch statement based on the nsKeyGenType
01011 //in the nsKeyPairInfo struct.
01012 static nsresult
01013 nsSetKeyUsageExtension(CRMFCertRequest *crmfReq,
01014                        unsigned char   keyUsage)
01015 {
01016   SECItem                 *encodedExt= nsnull;
01017   SECItem                  keyUsageValue = { (SECItemType) 0, nsnull, 0 };
01018   SECItem                  bitsmap = { (SECItemType) 0, nsnull, 0 };
01019   SECStatus                srv;
01020   CRMFCertExtension       *ext = nsnull;
01021   CRMFCertExtCreationInfo  extAddParams;
01022   SEC_ASN1Template         bitStrTemplate = {SEC_ASN1_BIT_STRING, 0, nsnull,
01023                                              sizeof(SECItem)};
01024 
01025   keyUsageValue.data = &keyUsage;
01026   keyUsageValue.len  = 1;
01027   nsPrepareBitStringForEncoding(&bitsmap, &keyUsageValue);
01028 
01029   encodedExt = SEC_ASN1EncodeItem(nsnull, nsnull, &bitsmap,&bitStrTemplate);
01030   if (encodedExt == nsnull) {
01031     goto loser;
01032   }
01033   ext = CRMF_CreateCertExtension(SEC_OID_X509_KEY_USAGE, PR_TRUE, encodedExt);
01034   if (ext == nsnull) {
01035       goto loser;
01036   }
01037   extAddParams.numExtensions = 1;
01038   extAddParams.extensions = &ext;
01039   srv = CRMF_CertRequestSetTemplateField(crmfReq, crmfExtension,
01040                                          &extAddParams);
01041   if (srv != SECSuccess) {
01042       goto loser;
01043   }
01044   CRMF_DestroyCertExtension(ext);
01045   SECITEM_FreeItem(encodedExt, PR_TRUE);
01046   return NS_OK;
01047  loser:
01048   if (ext) {
01049     CRMF_DestroyCertExtension(ext);
01050   }
01051   if (encodedExt) {
01052       SECITEM_FreeItem(encodedExt, PR_TRUE);
01053   }
01054   return NS_ERROR_FAILURE;
01055 }
01056 
01057 static nsresult
01058 nsSetRSADualUse(CRMFCertRequest *crmfReq)
01059 {
01060   unsigned char keyUsage =   KU_DIGITAL_SIGNATURE
01061                            | KU_NON_REPUDIATION
01062                            | KU_KEY_ENCIPHERMENT;
01063 
01064   return nsSetKeyUsageExtension(crmfReq, keyUsage);
01065 }
01066 
01067 static nsresult
01068 nsSetRSAKeyEx(CRMFCertRequest *crmfReq)
01069 {
01070   unsigned char keyUsage = KU_KEY_ENCIPHERMENT;
01071 
01072   return nsSetKeyUsageExtension(crmfReq, keyUsage);
01073 }
01074 
01075 static nsresult
01076 nsSetRSASign(CRMFCertRequest *crmfReq)
01077 {
01078   unsigned char keyUsage = KU_DIGITAL_SIGNATURE;
01079 
01080 
01081   return nsSetKeyUsageExtension(crmfReq, keyUsage);
01082 }
01083 
01084 static nsresult
01085 nsSetRSANonRepudiation(CRMFCertRequest *crmfReq)
01086 {
01087   unsigned char keyUsage = KU_NON_REPUDIATION;
01088 
01089   return nsSetKeyUsageExtension(crmfReq, keyUsage);
01090 }
01091 
01092 static nsresult
01093 nsSetRSASignNonRepudiation(CRMFCertRequest *crmfReq)
01094 {
01095   unsigned char keyUsage = KU_DIGITAL_SIGNATURE |
01096                            KU_NON_REPUDIATION;
01097 
01098   return nsSetKeyUsageExtension(crmfReq, keyUsage);
01099 }
01100 
01101 static nsresult
01102 nsSetDH(CRMFCertRequest *crmfReq)
01103 {
01104   unsigned char keyUsage = KU_KEY_AGREEMENT;
01105 
01106   return nsSetKeyUsageExtension(crmfReq, keyUsage);
01107 }
01108 
01109 static nsresult
01110 nsSetDSASign(CRMFCertRequest *crmfReq)
01111 {
01112   unsigned char keyUsage = KU_DIGITAL_SIGNATURE;
01113 
01114   return nsSetKeyUsageExtension(crmfReq, keyUsage);
01115 }
01116 
01117 static nsresult
01118 nsSetDSANonRepudiation(CRMFCertRequest *crmfReq)
01119 {
01120   unsigned char keyUsage = KU_NON_REPUDIATION;
01121 
01122   return nsSetKeyUsageExtension(crmfReq, keyUsage);
01123 }
01124 
01125 static nsresult
01126 nsSetDSASignNonRepudiation(CRMFCertRequest *crmfReq)
01127 {
01128   unsigned char keyUsage = KU_DIGITAL_SIGNATURE |
01129                            KU_NON_REPUDIATION;
01130 
01131   return nsSetKeyUsageExtension(crmfReq, keyUsage);
01132 }
01133 
01134 static nsresult
01135 nsSetKeyUsageExtension(CRMFCertRequest *crmfReq, nsKeyGenType keyGenType)
01136 {
01137   nsresult rv;
01138 
01139   switch (keyGenType) {
01140   case rsaDualUse:
01141     rv = nsSetRSADualUse(crmfReq);
01142     break;
01143   case rsaEnc:
01144     rv = nsSetRSAKeyEx(crmfReq);
01145     break;
01146   case rsaSign:
01147     rv = nsSetRSASign(crmfReq);
01148     break;
01149   case rsaNonrepudiation:
01150     rv = nsSetRSANonRepudiation(crmfReq);
01151     break;
01152   case rsaSignNonrepudiation:
01153     rv = nsSetRSASignNonRepudiation(crmfReq);
01154     break;
01155   case dhEx:
01156     rv = nsSetDH(crmfReq);
01157     break;
01158   case dsaSign:
01159     rv = nsSetDSASign(crmfReq);
01160     break;
01161   case dsaNonrepudiation:
01162     rv = nsSetDSANonRepudiation(crmfReq);
01163     break;
01164   case dsaSignNonrepudiation:
01165     rv = nsSetDSASignNonRepudiation(crmfReq);
01166     break;
01167   default:
01168     rv = NS_ERROR_FAILURE;
01169     break;
01170   }
01171   return rv;
01172 }
01173 
01174 //Create a single CRMFCertRequest with all of the necessary parts 
01175 //already installed.  The request returned by this function will
01176 //have all the parts necessary and can just be added to a 
01177 //Certificate Request Message.
01178 static CRMFCertRequest*
01179 nsCreateSingleCertReq(nsKeyPairInfo *keyInfo, char *reqDN, char *regToken, 
01180                       char *authenticator, nsNSSCertificate *wrappingCert)
01181 {
01182   PRInt32 reqID;
01183   nsresult rv;
01184 
01185   //The draft says the ID of the request should be a random
01186   //number.  We don't have a way of tracking this number
01187   //to compare when the reply actually comes back,though.
01188   PK11_GenerateRandom((unsigned char*)&reqID, sizeof(reqID));
01189   CRMFCertRequest *certReq = CRMF_CreateCertRequest(reqID);
01190   if (!certReq)
01191     return nsnull;
01192 
01193   long version = SEC_CERTIFICATE_VERSION_3;
01194   SECStatus srv;
01195   CERTSubjectPublicKeyInfo *spki = nsnull;
01196   srv = CRMF_CertRequestSetTemplateField(certReq, crmfVersion, &version);
01197   if (srv != SECSuccess)
01198     goto loser;
01199   
01200   spki = SECKEY_CreateSubjectPublicKeyInfo(keyInfo->pubKey);
01201   if (!spki)
01202     goto loser;
01203 
01204   srv = CRMF_CertRequestSetTemplateField(certReq, crmfPublicKey, spki);
01205   SECKEY_DestroySubjectPublicKeyInfo(spki);
01206   if (srv != SECSuccess)
01207     goto loser;
01208 
01209   if (wrappingCert && ns_can_escrow(keyInfo->keyGenType)) {
01210     rv = nsSetEscrowAuthority(certReq, keyInfo, wrappingCert);
01211     if (NS_FAILED(rv))
01212       goto loser;
01213   }
01214   rv = nsSetDNForRequest(certReq, reqDN);
01215   if (NS_FAILED(rv))
01216     goto loser;
01217 
01218   rv = nsSetRegToken(certReq, regToken);
01219   if (NS_FAILED(rv))
01220     goto loser;
01221 
01222   rv = nsSetAuthenticator(certReq, authenticator);
01223   if (NS_FAILED(rv))
01224     goto loser;
01225 
01226  rv = nsSetKeyUsageExtension(certReq, keyInfo->keyGenType); 
01227   if (NS_FAILED(rv))
01228     goto loser;
01229 
01230   return certReq;
01231 loser:
01232   if (certReq) {
01233     CRMF_DestroyCertRequest(certReq);
01234   }
01235   return nsnull;
01236 }
01237 
01238 /*
01239  * This function will set the Proof Of Possession (POP) for a request
01240  * associated with a key pair intended to do Key Encipherment.  Currently
01241  * this means encryption only keys.
01242  */
01243 static nsresult
01244 nsSetKeyEnciphermentPOP(CRMFCertReqMsg  *certReqMsg)
01245 {
01246   SECItem       bitString;
01247   unsigned char der[2];
01248   SECStatus     srv;
01249   CRMFCertRequest *certReq =  CRMF_CertReqMsgGetCertRequest(certReqMsg);
01250   NS_ASSERTION(certReq,"Error getting the certRequest from the message");
01251   if (!certReq)
01252     return NS_ERROR_FAILURE;
01253 
01254   if (CRMF_CertRequestIsControlPresent(certReq,crmfPKIArchiveOptionsControl)) {
01255     /* For proof of possession on escrowed keys, we use the
01256      * this Message option of POPOPrivKey and include a zero
01257      * length bit string in the POP field.  This is OK because the encrypted
01258      * private key already exists as part of the PKIArchiveOptions
01259      * Control and that for all intents and purposes proves that
01260      * we do own the private key.
01261      */
01262     der[0] = 0x03; /*We've got a bit string          */
01263     der[1] = 0x00; /*We've got a 0 length bit string */
01264     bitString.data = der;
01265     bitString.len  = 2;
01266     srv = CRMF_CertReqMsgSetKeyEnciphermentPOP(certReqMsg, crmfThisMessage,
01267                                               crmfNoSubseqMess, &bitString);
01268   } else {
01269     /* If the encryption key is not being escrowed, then we set the 
01270      * Proof Of Possession to be a Challenge Response mechanism.
01271      */
01272     srv = CRMF_CertReqMsgSetKeyEnciphermentPOP(certReqMsg,
01273                                               crmfSubsequentMessage,
01274                                               crmfChallengeResp, nsnull);
01275   }
01276   CRMF_DestroyCertRequest(certReq);
01277   return (srv == SECSuccess) ? NS_OK : NS_ERROR_FAILURE;
01278 }
01279 
01280 //CRMF require ProofOfPossession to be set on a Certificate
01281 //Request message.  Switch on the keyGenType here and add
01282 //the appropriate POP.
01283 static nsresult
01284 nsSetProofOfPossession(CRMFCertReqMsg *certReqMsg, 
01285                        nsKeyPairInfo  *keyInfo)
01286 {
01287   nsresult rv;
01288   switch (keyInfo->keyGenType) {
01289   case rsaSign:
01290   case rsaDualUse:
01291   case rsaNonrepudiation:
01292   case rsaSignNonrepudiation:
01293   case dsaSign:
01294   case dsaNonrepudiation:
01295   case dsaSignNonrepudiation:
01296     {
01297       SECStatus srv = CRMF_CertReqMsgSetSignaturePOP(certReqMsg,
01298                                                      keyInfo->privKey,
01299                                                      keyInfo->pubKey, nsnull,
01300                                                      nsnull, nsnull);
01301       rv = (srv == SECSuccess) ? NS_OK : NS_ERROR_FAILURE;
01302     }
01303     break;
01304   case rsaEnc:
01305     rv = nsSetKeyEnciphermentPOP(certReqMsg);
01306     break;
01307   case dhEx:
01308     /* This case may be supported in the future, but for now, we just fall 
01309      * though to the default case and return an error for diffie-hellman keys.
01310      */
01311   default:
01312     rv = NS_ERROR_FAILURE;
01313     break;
01314   }
01315   return rv;
01316 
01317 }
01318 
01319 static void PR_CALLBACK
01320 nsCRMFEncoderItemCount(void *arg, const char *buf, unsigned long len)
01321 {
01322   unsigned long *count = (unsigned long *)arg;
01323   *count += len;
01324 }
01325 
01326 static void PR_CALLBACK
01327 nsCRMFEncoderItemStore(void *arg, const char *buf, unsigned long len)
01328 {
01329   SECItem *dest = (SECItem *)arg;
01330   memcpy(dest->data + dest->len, buf, len);
01331   dest->len += len;
01332 }
01333 
01334 static SECItem*
01335 nsEncodeCertReqMessages(CRMFCertReqMsg **certReqMsgs)
01336 {
01337   unsigned long len = 0;
01338   if (CRMF_EncodeCertReqMessages(certReqMsgs, nsCRMFEncoderItemCount, &len)
01339       != SECSuccess) {
01340     return nsnull;
01341   }
01342   SECItem *dest = (SECItem *)PORT_Alloc(sizeof(SECItem));
01343   if (dest == nsnull) {
01344     return nsnull;
01345   }
01346   dest->type = siBuffer;
01347   dest->data = (unsigned char *)PORT_Alloc(len);
01348   if (dest->data == nsnull) {
01349     PORT_Free(dest);
01350     return nsnull;
01351   }
01352   dest->len = 0;
01353 
01354   if (CRMF_EncodeCertReqMessages(certReqMsgs, nsCRMFEncoderItemStore, dest)
01355       != SECSuccess) {
01356     SECITEM_FreeItem(dest, PR_TRUE);
01357     return nsnull;
01358   }
01359   return dest;
01360 }
01361 
01362 //Create a Base64 encoded CRMFCertReqMsg that can be sent to a CA
01363 //requesting one or more certificates to be issued.  This function
01364 //creates a single cert request per key pair and then appends it to
01365 //a message that is ultimately sent off to a CA.
01366 static char*
01367 nsCreateReqFromKeyPairs(nsKeyPairInfo *keyids, PRInt32 numRequests,
01368                         char *reqDN, char *regToken, char *authenticator,
01369                         nsNSSCertificate *wrappingCert) 
01370 {
01371   // We'use the goto notation for clean-up purposes in this function
01372   // that calls the C API of NSS.
01373   PRInt32 i;
01374   // The ASN1 encoder in NSS wants the last entry in the array to be
01375   // NULL so that it knows when the last element is.
01376   CRMFCertReqMsg **certReqMsgs = new CRMFCertReqMsg*[numRequests+1];
01377   CRMFCertRequest *certReq;
01378   if (!certReqMsgs)
01379     return nsnull;
01380   memset(certReqMsgs, 0, sizeof(CRMFCertReqMsg*)*(1+numRequests));
01381   SECStatus srv;
01382   nsresult rv;
01383   SECItem *encodedReq;
01384   char *retString;
01385   for (i=0; i<numRequests; i++) {
01386     certReq = nsCreateSingleCertReq(&keyids[i], reqDN, regToken, authenticator,
01387                                     wrappingCert);
01388     if (!certReq)
01389       goto loser;
01390 
01391     certReqMsgs[i] = CRMF_CreateCertReqMsg();
01392     if (!certReqMsgs[i])
01393       goto loser;
01394     srv = CRMF_CertReqMsgSetCertRequest(certReqMsgs[i], certReq);
01395     if (srv != SECSuccess)
01396       goto loser;
01397 
01398     rv = nsSetProofOfPossession(certReqMsgs[i], &keyids[i]);
01399     if (NS_FAILED(rv))
01400       goto loser;
01401     CRMF_DestroyCertRequest(certReq);
01402   }
01403   encodedReq = nsEncodeCertReqMessages(certReqMsgs);
01404   nsFreeCertReqMessages(certReqMsgs, numRequests);
01405 
01406   retString = NSSBase64_EncodeItem (nsnull, nsnull, 0, encodedReq);
01407   SECITEM_FreeItem(encodedReq, PR_TRUE);
01408   return retString;
01409 loser:
01410   nsFreeCertReqMessages(certReqMsgs,numRequests);
01411   return nsnull;;
01412 }
01413 
01414 static nsISupports *
01415 GetISupportsFromContext(JSContext *cx)
01416 {
01417     if (JS_GetOptions(cx) & JSOPTION_PRIVATE_IS_NSISUPPORTS)
01418         return NS_STATIC_CAST(nsISupports *, JS_GetContextPrivate(cx));
01419 
01420     return nsnull;
01421 }
01422 
01423 //The top level method which is a member of nsIDOMCrypto
01424 //for generate a base64 encoded CRMF request.
01425 NS_IMETHODIMP
01426 nsCrypto::GenerateCRMFRequest(nsIDOMCRMFObject** aReturn)
01427 {
01428   nsNSSShutDownPreventionLock locker;
01429   *aReturn = nsnull;
01430   nsresult nrv;
01431   nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID(), &nrv));
01432   NS_ENSURE_SUCCESS(nrv, nrv);
01433 
01434   nsCOMPtr<nsIXPCNativeCallContext> ncc;
01435 
01436   nrv = xpc->GetCurrentNativeCallContext(getter_AddRefs(ncc));
01437   NS_ENSURE_SUCCESS(nrv, nrv);
01438 
01439   if (!ncc)
01440     return NS_ERROR_NOT_AVAILABLE;
01441 
01442   PRUint32 argc;
01443 
01444   ncc->GetArgc(&argc);
01445 
01446   jsval *argv = nsnull;
01447 
01448   ncc->GetArgvPtr(&argv);
01449 
01450   JSContext *cx;
01451 
01452   ncc->GetJSContext(&cx);
01453 
01454   JSObject* script_obj = nsnull;
01455   nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
01456 
01457 
01458   /*
01459    * Get all of the parameters.
01460    */
01461   if (argc < 5 || ((argc-5) % 3) != 0) {
01462     JS_ReportError(cx, "%s", "%s%s\n", JS_ERROR,
01463                   "incorrect number of parameters");
01464     return NS_ERROR_FAILURE;
01465   }
01466   
01467   if (JSVAL_IS_NULL(argv[0])) {
01468     JS_ReportError(cx, "%s%s\n", JS_ERROR, "no DN specified");
01469     return NS_ERROR_FAILURE;
01470   }
01471   
01472   JSString *jsString = JS_ValueToString(cx,argv[0]);
01473   
01474   char * reqDN = JS_GetStringBytes(jsString);
01475   char *regToken;
01476   if (JSVAL_IS_NULL(argv[1])) {
01477     regToken           = nsnull;
01478   } else {
01479     jsString = JS_ValueToString(cx, argv[1]);
01480     regToken = JS_GetStringBytes(jsString);
01481   }
01482   char *authenticator;
01483   if (JSVAL_IS_NULL(argv[2])) {
01484     authenticator           = nsnull;
01485   } else {
01486     jsString      = JS_ValueToString(cx, argv[2]);
01487     authenticator = JS_GetStringBytes(jsString);
01488   }
01489   char *eaCert;
01490   if (JSVAL_IS_NULL(argv[3])) {
01491     eaCert           = nsnull;
01492   } else {
01493     jsString     = JS_ValueToString(cx, argv[3]);
01494     eaCert       = JS_GetStringBytes(jsString);
01495   }
01496   if (JSVAL_IS_NULL(argv[4])) {
01497     JS_ReportError(cx, "%s%s\n", JS_ERROR, "no completion "
01498                    "function specified");
01499     return NS_ERROR_FAILURE;
01500   }
01501   jsString = JS_ValueToString(cx, argv[4]);
01502   char *jsCallback = JS_GetStringBytes(jsString);
01503 
01504 
01505   nrv = xpc->WrapNative(cx, ::JS_GetGlobalObject(cx),
01506                         NS_STATIC_CAST(nsIDOMCrypto *, this),
01507                         NS_GET_IID(nsIDOMCrypto), getter_AddRefs(holder));
01508   NS_ENSURE_SUCCESS(nrv, nrv);
01509 
01510   nrv = holder->GetJSObject(&script_obj);
01511   NS_ENSURE_SUCCESS(nrv, nrv);
01512 
01513   //Put up some UI warning that someone is trying to 
01514   //escrow the private key.
01515   //Don't addref this copy.  That way ths reference goes away
01516   //at the same the nsIX09Cert ref goes away.
01517   nsNSSCertificate *escrowCert = nsnull;
01518   nsCOMPtr<nsIX509Cert> nssCert;
01519   PRBool willEscrow = PR_FALSE;
01520   if (eaCert) {
01521     SECItem certDer = {siBuffer, nsnull, 0};
01522     SECStatus srv = ATOB_ConvertAsciiToItem(&certDer, eaCert);
01523     if (srv != SECSuccess) {
01524       return NS_ERROR_FAILURE;
01525     }
01526     CERTCertificate *cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
01527                                                     &certDer, nsnull, PR_FALSE,
01528                                                     PR_TRUE);
01529     if (!cert)
01530       return NS_ERROR_FAILURE;
01531 
01532     escrowCert = new nsNSSCertificate(cert);
01533     CERT_DestroyCertificate(cert);
01534     nssCert = escrowCert;
01535     if (!nssCert)
01536       return NS_ERROR_OUT_OF_MEMORY;
01537 
01538     nsCOMPtr<nsIDOMCryptoDialogs> dialogs;
01539     nsresult rv = getNSSDialogs(getter_AddRefs(dialogs),
01540                                 NS_GET_IID(nsIDOMCryptoDialogs),
01541                                 NS_DOMCRYPTODIALOGS_CONTRACTID);
01542     if (NS_FAILED(rv))
01543       return rv;
01544 
01545     PRBool okay=PR_FALSE;
01546     {
01547       nsPSMUITracker tracker;
01548       if (tracker.isUIForbidden()) {
01549         okay = PR_FALSE;
01550       }
01551       else {
01552         dialogs->ConfirmKeyEscrow(nssCert, &okay);
01553       }
01554     }
01555     if (!okay)
01556       return NS_OK;
01557     willEscrow = PR_TRUE;
01558   }
01559   nsCOMPtr<nsIInterfaceRequestor> uiCxt = new PipUIContext;
01560   PRInt32 numRequests = (argc - 5)/3;
01561   nsKeyPairInfo *keyids = new nsKeyPairInfo[numRequests];
01562   if (keyids == nsnull) {
01563     JS_ReportError(cx, "%s\n", JS_ERROR_INTERNAL);
01564     return NS_ERROR_OUT_OF_MEMORY;
01565   }
01566   memset(keyids, 0, sizeof(nsKeyPairInfo)*numRequests);
01567   int keyInfoIndex;
01568   PRUint32 i;
01569   PK11SlotInfo *slot = nsnull;
01570   // Go through all of the arguments and generate the appropriate key pairs.
01571   for (i=5,keyInfoIndex=0; i<argc; i+=3,keyInfoIndex++) {
01572     nrv = cryptojs_ReadArgsAndGenerateKey(cx, &argv[i], &keyids[keyInfoIndex],
01573                                          uiCxt, &slot, willEscrow);
01574                                        
01575     if (NS_FAILED(nrv)) {
01576       if (slot)
01577         PK11_FreeSlot(slot);
01578       nsFreeKeyPairInfo(keyids,numRequests);
01579       return nrv;
01580     }
01581   }
01582   // By this time we'd better have a slot for the key gen.
01583   NS_ASSERTION(slot, "There was no slot selected for key generation");
01584   if (slot) 
01585     PK11_FreeSlot(slot);
01586 
01587   char *encodedRequest = nsCreateReqFromKeyPairs(keyids, numRequests,
01588                                                  reqDN, regToken, 
01589                                                  authenticator,escrowCert);
01590 #ifdef DEBUG_javi
01591   printf ("Created the folloing CRMF request:\n%s\n", encodedRequest);
01592 #endif
01593   if (!encodedRequest) {
01594     nsFreeKeyPairInfo(keyids, numRequests);
01595     return NS_ERROR_FAILURE;
01596   }                                                    
01597   nsCRMFObject *newObject = new nsCRMFObject();
01598   if (newObject == nsnull) {
01599     JS_ReportError(cx, "%s%s\n", JS_ERROR, "could not create crmf JS object");
01600 
01601     nsFreeKeyPairInfo(keyids,numRequests);
01602     return NS_ERROR_OUT_OF_MEMORY;
01603   }
01604   newObject->SetCRMFRequest(encodedRequest);
01605   *aReturn = newObject;
01606   //Give a reference to the returnee.
01607   NS_ADDREF(*aReturn);
01608   nsFreeKeyPairInfo(keyids, numRequests);
01609 
01610   // 
01611   // Post an event on the UI queue so that the JS gets called after
01612   // we return control to the JS layer.  Why do we have to this?
01613   // Because when this API was implemented for PSM 1.x w/ Communicator,
01614   // the only way to make this method work was to have a callback
01615   // in the JS layer that got called after key generation had happened.
01616   // So for backwards compatibility, we return control and then just post
01617   // an event to call the JS the script provides as the code to execute
01618   // when the request has been generated.
01619   //
01620 
01621 
01622   nsCOMPtr<nsIPrincipal>principals;
01623   principals = GetScriptPrincipal(cx);
01624   NS_ASSERTION(principals, "Couldn't get the principals");
01625   nsCryptoRunArgs *args = new nsCryptoRunArgs();
01626   if (!args)
01627     return NS_ERROR_OUT_OF_MEMORY;
01628 
01629   args->m_cx         = cx;
01630   args->m_kungFuDeathGrip = GetISupportsFromContext(cx);
01631   args->m_scope      = JS_GetParent(cx, script_obj);
01632   args->m_jsCallback.Adopt(jsCallback ? nsCRT::strdup(jsCallback) : 0);
01633   args->m_principals = principals;
01634   
01635   nsCryptoRunnable *cryptoRunnable = new nsCryptoRunnable(args);
01636   if (!cryptoRunnable)
01637     return NS_ERROR_OUT_OF_MEMORY;
01638 
01639   nsresult rv = nsNSSEventPostToUIEventQueue(cryptoRunnable);
01640   if (NS_FAILED(rv))
01641     delete cryptoRunnable;
01642 
01643   return rv;
01644 }
01645 
01646 
01647 // Reminder that we inherit the memory passed into us here.
01648 // An implementation to let us back up certs as an event.
01649 nsP12Runnable::nsP12Runnable(nsIX509Cert **certArr, PRInt32 numCerts,
01650                              nsIPK11Token *token)
01651 {
01652   mCertArr  = certArr;
01653   mNumCerts = numCerts;
01654   mToken = token;
01655 }
01656 
01657 nsP12Runnable::~nsP12Runnable()
01658 {
01659   PRInt32 i;
01660   for (i=0; i<mNumCerts; i++) {
01661       NS_IF_RELEASE(mCertArr[i]);
01662   }
01663   delete []mCertArr;
01664 }
01665 
01666 
01667 //Quick helper function to alert users.
01668 void
01669 alertUser(const PRUnichar *message)
01670 {
01671   nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
01672   nsCOMPtr<nsIPrompt> prompter;
01673   if (wwatch)
01674     wwatch->GetNewPrompter(0, getter_AddRefs(prompter));
01675 
01676   if (prompter) {
01677     nsPSMUITracker tracker;
01678     if (!tracker.isUIForbidden()) {
01679       prompter->Alert(0, message);
01680     }
01681   }
01682 }
01683 
01684 //Implementation that backs cert(s) into a PKCS12 file
01685 NS_IMETHODIMP
01686 nsP12Runnable::Run()
01687 {
01688   nsNSSShutDownPreventionLock locker;
01689   NS_ASSERTION(mCertArr, "certArr is NULL while trying to back up");
01690 
01691   nsString final;
01692   nsString temp;
01693   nsresult rv;
01694 
01695   nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv));
01696   if (NS_FAILED(rv))
01697     return rv;
01698 
01699   //Build up the message that let's the user know we're trying to 
01700   //make PKCS12 backups of the new certs.
01701   nssComponent->GetPIPNSSBundleString("ForcedBackup1", final);
01702   final.Append(NS_LITERAL_STRING("\n\n").get());
01703   nssComponent->GetPIPNSSBundleString("ForcedBackup2", temp);
01704   final.Append(temp.get());
01705   final.Append(NS_LITERAL_STRING("\n\n").get());
01706 
01707   nssComponent->GetPIPNSSBundleString("ForcedBackup3", temp);
01708 
01709   final.Append(temp.get());
01710   alertUser(final.get());
01711 
01712   nsCOMPtr<nsIFilePicker> filePicker = 
01713                         do_CreateInstance("@mozilla.org/filepicker;1", &rv);
01714   if (!filePicker) {
01715     NS_ASSERTION(0, "Could not create a file picker when backing up certs.");
01716     return rv;
01717   }
01718 
01719   nsCOMPtr<nsIWindowWatcher> wwatch =
01720     (do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv));
01721   NS_ENSURE_SUCCESS(rv, rv);
01722 
01723   nsCOMPtr<nsIDOMWindow> window;
01724   wwatch->GetActiveWindow(getter_AddRefs(window));
01725 
01726   nsString filePickMessage;
01727   nssComponent->GetPIPNSSBundleString("chooseP12BackupFileDialog",
01728                                       filePickMessage);
01729   rv = filePicker->Init(window, filePickMessage, nsIFilePicker::modeSave);
01730   NS_ENSURE_SUCCESS(rv, rv);
01731 
01732   filePicker->AppendFilter(NS_LITERAL_STRING("PKCS12"),
01733                            NS_LITERAL_STRING("*.p12"));
01734   filePicker->AppendFilters(nsIFilePicker::filterAll);
01735 
01736   PRInt16 dialogReturn;
01737   filePicker->Show(&dialogReturn);
01738   if (dialogReturn == nsIFilePicker::returnCancel)
01739     return NS_OK;  //User canceled.  It'd be nice if they couldn't, 
01740                    //but oh well.
01741 
01742   nsCOMPtr<nsILocalFile> localFile;
01743   rv = filePicker->GetFile(getter_AddRefs(localFile));
01744   if (NS_FAILED(rv))
01745     return NS_ERROR_FAILURE;
01746 
01747   nsPKCS12Blob p12Cxt;
01748   
01749   p12Cxt.SetToken(mToken);
01750   p12Cxt.ExportToFile(localFile, mCertArr, mNumCerts);
01751   return NS_OK;
01752 }
01753 
01754 nsCryptoRunArgs::nsCryptoRunArgs() 
01755 {
01756 }
01757 nsCryptoRunArgs::~nsCryptoRunArgs() {}
01758 
01759 
01760 nsCryptoRunnable::nsCryptoRunnable(nsCryptoRunArgs *args)
01761 {
01762   nsNSSShutDownPreventionLock locker;
01763   NS_ASSERTION(args,"Passed nsnull to nsCryptoRunnable constructor.");
01764   m_args = args;
01765   NS_IF_ADDREF(m_args);
01766   JS_AddNamedRoot(args->m_cx, &args->m_scope,"nsCryptoRunnable::mScope");
01767 }
01768 
01769 nsCryptoRunnable::~nsCryptoRunnable()
01770 {
01771   nsNSSShutDownPreventionLock locker;
01772   JS_RemoveRoot(m_args->m_cx, &m_args->m_scope);
01773   NS_IF_RELEASE(m_args);
01774 }
01775 
01776 //Implementation that runs the callback passed to 
01777 //crypto.generateCRMFRequest as an event.
01778 NS_IMETHODIMP
01779 nsCryptoRunnable::Run()
01780 {
01781   nsNSSShutDownPreventionLock locker;
01782   JSPrincipals *principals;
01783   JSContext *cx = m_args->m_cx;
01784 
01785   nsresult rv = m_args->m_principals->GetJSPrincipals(cx, &principals);
01786   if (NS_FAILED(rv))
01787     return NS_ERROR_FAILURE;
01788 
01789   // make sure the right context is on the stack. must not return w/out popping
01790   nsCOMPtr<nsIJSContextStack> stack(do_GetService("@mozilla.org/js/xpc/ContextStack;1"));
01791   if (!stack || NS_FAILED(stack->Push(cx))) {
01792     return NS_ERROR_FAILURE;
01793   }
01794 
01795   jsval retval;
01796   if (JS_EvaluateScriptForPrincipals(cx, m_args->m_scope, principals,
01797                                      m_args->m_jsCallback, 
01798                                      strlen(m_args->m_jsCallback),
01799                                      nsnull, 0,
01800                                      &retval) != JS_TRUE) {
01801     rv = NS_ERROR_FAILURE;
01802   }
01803 
01804   stack->Pop(nsnull);
01805   return rv;
01806 }
01807 
01808 //Quick helper function to check if a newly issued cert
01809 //already exists in the user's database.
01810 static PRBool
01811 nsCertAlreadyExists(SECItem *derCert)
01812 {
01813   CERTCertDBHandle *handle = CERT_GetDefaultCertDB();
01814   PRArenaPool *arena;
01815   CERTCertificate *cert;
01816   PRBool retVal = PR_FALSE;
01817 
01818   arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
01819   NS_ASSERTION(arena, "Couldn't allocate an arena!");
01820   if (!arena)
01821     return PR_FALSE; //What else could we return?
01822 
01823   cert = CERT_FindCertByDERCert(handle, derCert);
01824   if (cert) {
01825     if (cert->isperm && !cert->nickname && !cert->emailAddr) {
01826       //If the cert doesn't have a nickname or email addr, it is
01827       //bogus cruft, so delete it.
01828       SEC_DeletePermCertificate(cert);
01829     } else if (cert->isperm) {
01830       retVal = PR_TRUE;
01831     }
01832     CERT_DestroyCertificate(cert);
01833   }
01834   return retVal;
01835 }
01836 
01837 static PRInt32
01838 nsCertListCount(CERTCertList *certList)
01839 {
01840   PRInt32 numCerts = 0;
01841   CERTCertListNode *node;
01842 
01843   node = CERT_LIST_HEAD(certList);
01844   while (!CERT_LIST_END(node, certList)) {
01845     numCerts++;
01846     node = CERT_LIST_NEXT(node);
01847   }
01848   return numCerts;
01849 }
01850 
01851 
01852 //Import user certificates that arrive as a CMMF base64 encoded
01853 //string.
01854 NS_IMETHODIMP
01855 nsCrypto::ImportUserCertificates(const nsAString& aNickname, 
01856                                  const nsAString& aCmmfResponse, 
01857                                  PRBool aDoForcedBackup, 
01858                                  nsAString& aReturn)
01859 {
01860   nsNSSShutDownPreventionLock locker;
01861   char *nickname=nsnull, *cmmfResponse=nsnull;
01862   char *retString=nsnull;
01863   CMMFCertRepContent *certRepContent = nsnull;
01864   int numResponses = 0;
01865   nsIX509Cert **certArr = nsnull;
01866   int i;
01867   CMMFCertResponse *currResponse;
01868   CMMFPKIStatus reqStatus;
01869   CERTCertificate *currCert;
01870   PK11SlotInfo *slot;
01871   PRBool freeLocalNickname = PR_FALSE;
01872   char *localNick;
01873   nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
01874   nsresult rv = NS_OK;
01875   CERTCertList *caPubs = nsnull;
01876   nsCOMPtr<nsIPK11Token> token;
01877 
01878   nickname = ToNewCString(aNickname);
01879   cmmfResponse = ToNewCString(aCmmfResponse);
01880   if (nsCRT::strcmp("null", nickname) == 0) {
01881     nsMemory::Free(nickname);
01882     nickname = nsnull;
01883   }
01884 
01885   SECItem cmmfDer = {siBuffer, nsnull, 0};
01886   SECStatus srv = ATOB_ConvertAsciiToItem(&cmmfDer, cmmfResponse);
01887 
01888   if (srv != SECSuccess) {
01889     rv = NS_ERROR_FAILURE;
01890     goto loser;
01891   }
01892 
01893   certRepContent = CMMF_CreateCertRepContentFromDER(CERT_GetDefaultCertDB(),
01894                                                     (const char*)cmmfDer.data,
01895                                                     cmmfDer.len);
01896   if (!certRepContent) {
01897     rv = NS_ERROR_FAILURE;
01898     goto loser;
01899   }
01900 
01901   numResponses = CMMF_CertRepContentGetNumResponses(certRepContent);
01902 
01903   if (aDoForcedBackup) {
01904     //We've been asked to force the user to back up these
01905     //certificates.  Let's keep an array of them around which
01906     //we pass along to the nsP12Runnable to use.
01907     certArr = new nsIX509Cert*[numResponses];
01908     // If this is NULL, chances are we're gonna fail really soon,
01909     // but let's try to keep going just in case.
01910     if (!certArr)
01911       aDoForcedBackup = PR_FALSE;
01912 
01913     memset(certArr, 0, sizeof(nsIX509Cert*)*numResponses);
01914   }
01915   for (i=0; i<numResponses; i++) {
01916     currResponse = CMMF_CertRepContentGetResponseAtIndex(certRepContent,i);
01917     if (!currResponse) {
01918       rv = NS_ERROR_FAILURE;
01919       goto loser;
01920     }
01921     reqStatus = CMMF_CertResponseGetPKIStatusInfoStatus(currResponse);
01922     if (!(reqStatus == cmmfGranted || reqStatus == cmmfGrantedWithMods)) {
01923       // The CA didn't give us the cert we requested.
01924       rv = NS_ERROR_FAILURE;
01925       goto loser;
01926     }
01927     currCert = CMMF_CertResponseGetCertificate(currResponse, 
01928                                                CERT_GetDefaultCertDB());
01929     if (!currCert) {
01930       rv = NS_ERROR_FAILURE;
01931       goto loser;
01932     }
01933 
01934     if (nsCertAlreadyExists(&currCert->derCert)) {
01935       if (aDoForcedBackup) {
01936         certArr[i] = new nsNSSCertificate(currCert);
01937         NS_ADDREF(certArr[i]);
01938       }
01939       CERT_DestroyCertificate(currCert);
01940       CMMF_DestroyCertResponse(currResponse);
01941       continue;
01942     }
01943     // Let's figure out which nickname to give the cert.  If 
01944     // a certificate with the same subject name already exists,
01945     // then just use that one, otherwise, get the default nickname.
01946     if (currCert->nickname) {
01947       localNick = currCert->nickname;
01948     }
01949     else if (nickname == nsnull || nickname[0] == '\0') {
01950       localNick = nsNSSCertificateDB::default_nickname(currCert, ctx);
01951       freeLocalNickname = PR_TRUE;
01952     } else {
01953       //This is the case where we're getting a brand new
01954       //cert that doesn't have the same subjectName as a cert
01955       //that already exists in our db and the CA page has 
01956       //designated a nickname to use for the newly issued cert.
01957       localNick = nickname;
01958     }
01959     slot = PK11_ImportCertForKey(currCert, localNick, ctx);
01960     if (freeLocalNickname) {
01961       nsMemory::Free(localNick);
01962       freeLocalNickname = PR_FALSE;
01963     }
01964     if (slot == nsnull) {
01965       rv = NS_ERROR_FAILURE;
01966       goto loser;
01967     }
01968     if (aDoForcedBackup) {
01969       certArr[i] = new nsNSSCertificate(currCert);
01970       NS_ADDREF(certArr[i]);
01971     }
01972     CERT_DestroyCertificate(currCert);
01973 
01974     if (!token)
01975       token = new nsPK11Token(slot);
01976 
01977     PK11_FreeSlot(slot);
01978     CMMF_DestroyCertResponse(currResponse);
01979   }
01980   //Let the loser: label take care of freeing up our reference to
01981   //nickname (This way we don't free it twice and avoid crashing.
01982   //That would be a good thing.
01983 
01984   retString = "";
01985 
01986   //Import the root chain into the cert db.
01987   caPubs = CMMF_CertRepContentGetCAPubs(certRepContent);
01988   if (caPubs) {
01989     PRInt32 numCAs = nsCertListCount(caPubs);
01990     
01991     NS_ASSERTION(numCAs > 0, "Invalid number of CA's");
01992     if (numCAs > 0) {
01993       CERTCertListNode *node;
01994       SECItem *derCerts;
01995 
01996       derCerts = NS_STATIC_CAST(SECItem*,
01997                                 nsMemory::Alloc(sizeof(SECItem)*numCAs));
01998       if (!derCerts) {
01999         rv = NS_ERROR_OUT_OF_MEMORY;
02000         goto loser;
02001       }
02002       for (node = CERT_LIST_HEAD(caPubs), i=0; 
02003            !CERT_LIST_END(node, caPubs);
02004            node = CERT_LIST_NEXT(node), i++) {
02005         derCerts[i] = node->cert->derCert;
02006       }
02007       nsNSSCertificateDB::ImportValidCACerts(numCAs, derCerts, ctx);
02008       nsMemory::Free(derCerts);
02009     }
02010     
02011     CERT_DestroyCertList(caPubs);
02012   }
02013 
02014   if (aDoForcedBackup) {
02015     // I can't pop up a file picker from the depths of JavaScript,
02016     // so I'll just post an event on the UI queue to do the backups
02017     // later.
02018     nsCOMPtr<nsIRunnable> p12Runnable = new nsP12Runnable(certArr, numResponses,
02019                                                           token);
02020     if (!p12Runnable) {
02021       rv = NS_ERROR_FAILURE;
02022       goto loser;
02023     }
02024 
02025     // null out the certArr pointer which has now been inherited by
02026     // the nsP12Runnable instance so that we don't free up the
02027     // memory on the way out.
02028     certArr = nsnull;
02029 
02030     rv = nsNSSEventPostToUIEventQueue(p12Runnable);
02031     if (NS_FAILED(rv))
02032       goto loser;
02033   }
02034 
02035  loser:
02036   if (certArr) {
02037     for (i=0; i<numResponses; i++) {
02038       NS_IF_RELEASE(certArr[i]);
02039     }
02040     delete []certArr;
02041   }
02042   aReturn.Assign(NS_ConvertASCIItoUCS2(retString));
02043   if (nickname) {
02044     NS_Free(nickname);
02045   }
02046   if (cmmfResponse) {
02047     NS_Free(cmmfResponse);
02048   }
02049   if (certRepContent) {
02050     CMMF_DestroyCertRepContent(certRepContent);
02051   }
02052   return rv;
02053 }
02054 
02055 NS_IMETHODIMP
02056 nsCrypto::PopChallengeResponse(const nsAString& aChallenge, 
02057                                nsAString& aReturn)
02058 {
02059   return NS_ERROR_NOT_IMPLEMENTED;
02060 }
02061 
02062 NS_IMETHODIMP
02063 nsCrypto::Random(PRInt32 aNumBytes, nsAString& aReturn)
02064 {
02065   return NS_ERROR_NOT_IMPLEMENTED;
02066 }
02067 
02068 static void
02069 GetDocumentFromContext(JSContext *cx, nsIDocument **aDocument)
02070 {
02071   // Get the script context.
02072   nsIScriptContext* scriptContext = GetScriptContextFromJSContext(cx);
02073   if (!scriptContext) {
02074     return;
02075   }
02076 
02077   nsCOMPtr<nsIDOMWindow> domWindow = 
02078     do_QueryInterface(scriptContext->GetGlobalObject());
02079   if (!domWindow) {
02080     return;
02081   }
02082 
02083   nsCOMPtr<nsIDOMDocument> domDocument;
02084   domWindow->GetDocument(getter_AddRefs(domDocument));
02085   if (!domDocument) {
02086     return;
02087   }
02088 
02089   CallQueryInterface(domDocument, aDocument);
02090 
02091   return;
02092 }
02093 
02094 void signTextOutputCallback(void *arg, const char *buf, unsigned long len)
02095 {
02096   ((nsCString*)arg)->Append(buf, len);
02097 }
02098 
02099 NS_IMETHODIMP
02100 nsCrypto::SignText(const nsAString& aStringToSign, const nsAString& aCaOption,
02101                    nsAString& aResult)
02102 {
02103   // XXX This code should return error codes, but we're keeping this
02104   //     backwards compatible with NS4.x and so we can't throw exceptions.
02105   NS_NAMED_LITERAL_STRING(internalError, "error:internalError");
02106 
02107   aResult.Truncate();
02108 
02109   nsCOMPtr<nsIXPCNativeCallContext> ncc;
02110   nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID()));
02111   if (xpc) {
02112     xpc->GetCurrentNativeCallContext(getter_AddRefs(ncc));
02113   }
02114 
02115   if (!ncc) {
02116     aResult.Append(internalError);
02117 
02118     return NS_OK;
02119   }
02120 
02121   PRUint32 argc;
02122   ncc->GetArgc(&argc);
02123 
02124   JSContext *cx;
02125   ncc->GetJSContext(&cx);
02126   if (!cx) {
02127     aResult.Append(internalError);
02128 
02129     return NS_OK;
02130   }
02131 
02132   if (!aCaOption.EqualsLiteral("auto") &&
02133       !aCaOption.EqualsLiteral("ask")) {
02134     JS_ReportError(cx, "%s%s\n", JS_ERROR, "caOption argument must be ask or auto");
02135 
02136     aResult.Append(internalError);
02137 
02138     return NS_OK;
02139   }
02140 
02141   // It was decided to always behave as if "ask" were specified.
02142   // XXX Should we warn in the JS Console for auto?
02143 
02144   nsCOMPtr<nsIInterfaceRequestor> uiContext = new PipUIContext;
02145   if (!uiContext) {
02146     aResult.Append(internalError);
02147 
02148     return NS_OK;
02149   }
02150 
02151   PRBool bestOnly = PR_TRUE;
02152   PRBool validOnly = PR_TRUE;
02153   CERTCertList* certList =
02154     CERT_FindUserCertsByUsage(CERT_GetDefaultCertDB(), certUsageEmailSigner,
02155                               bestOnly, validOnly, uiContext);
02156 
02157   PRUint32 numCAs = argc - 2;
02158   if (numCAs > 0) {
02159     nsAutoArrayPtr<char*> caNames(new char*[numCAs]);
02160     if (!caNames) {
02161       aResult.Append(internalError);
02162 
02163       return NS_OK;
02164     }
02165 
02166     jsval *argv = nsnull;
02167     ncc->GetArgvPtr(&argv);
02168 
02169     PRUint32 i;
02170     for (i = 2; i < argc; ++i) {
02171       JSString *caName = JS_ValueToString(cx, argv[i]);
02172       if (!caName) {
02173         aResult.Append(internalError);
02174 
02175         return NS_OK;
02176       }
02177       caNames[i - 2] = JS_GetStringBytes(caName);
02178     }
02179 
02180     if (certList &&
02181         CERT_FilterCertListByCANames(certList, numCAs, caNames,
02182                                      certUsageEmailSigner) != SECSuccess) {
02183       aResult.Append(internalError);
02184 
02185       return NS_OK;
02186     }
02187   }
02188 
02189   if (!certList || CERT_LIST_EMPTY(certList)) {
02190     aResult.AppendLiteral("error:noMatchingCert");
02191 
02192     return NS_OK;
02193   }
02194 
02195   nsCOMPtr<nsIFormSigningDialog> fsd =
02196     do_CreateInstance(NS_FORMSIGNINGDIALOG_CONTRACTID);
02197   if (!fsd) {
02198     aResult.Append(internalError);
02199 
02200     return NS_OK;
02201   }
02202 
02203   nsCOMPtr<nsIProxyObjectManager> proxyman =
02204     do_GetService(NS_XPCOMPROXY_CONTRACTID);
02205   if (!proxyman) {
02206     aResult.Append(internalError);
02207 
02208     return NS_OK;
02209   }
02210 
02211   nsCOMPtr<nsIFormSigningDialog> proxied_fsd;
02212   nsresult rv = proxyman->GetProxyForObject(NS_UI_THREAD_EVENTQ,
02213                                             NS_GET_IID(nsIFormSigningDialog), 
02214                                             fsd, PROXY_SYNC,
02215                                             getter_AddRefs(proxied_fsd));
02216   if (NS_FAILED(rv)) {
02217     aResult.Append(internalError);
02218 
02219     return NS_OK;
02220   }
02221 
02222   nsCOMPtr<nsIDocument> document;
02223   GetDocumentFromContext(cx, getter_AddRefs(document));
02224   if (!document) {
02225     aResult.Append(internalError);
02226 
02227     return NS_OK;
02228   }
02229 
02230   // Get the hostname from the URL of the document.
02231   nsIURI* uri = document->GetDocumentURI();
02232   if (!uri) {
02233     aResult.Append(internalError);
02234 
02235     return NS_OK;
02236   }
02237 
02238   nsCString host;
02239   rv = uri->GetHost(host);
02240   if (NS_FAILED(rv)) {
02241     aResult.Append(internalError);
02242 
02243     return NS_OK;
02244   }
02245 
02246   PRInt32 numberOfCerts = 0;
02247   CERTCertListNode* node;
02248   for (node = CERT_LIST_HEAD(certList); !CERT_LIST_END(node, certList);
02249        node = CERT_LIST_NEXT(node)) {
02250     ++numberOfCerts;
02251   }
02252 
02253   CERTCertNicknames* nicknames = getNSSCertNicknamesFromCertList(certList);
02254 
02255   if (!nicknames) {
02256     aResult.Append(internalError);
02257 
02258     return NS_OK;
02259   }
02260 
02261   CERTCertNicknamesCleaner cnc(nicknames);
02262 
02263   NS_ASSERTION(nicknames->numnicknames == numberOfCerts,
02264                "nicknames->numnicknames != numberOfCerts");
02265 
02266   nsAutoArrayPtr<PRUnichar*> certNicknameList(new PRUnichar*[nicknames->numnicknames * 2]);
02267   if (!certNicknameList) {
02268     aResult.Append(internalError);
02269 
02270     return NS_OK;
02271   }
02272 
02273   PRUnichar** certDetailsList = certNicknameList.get() + nicknames->numnicknames;
02274 
02275   PRInt32 certsToUse;
02276   for (node = CERT_LIST_HEAD(certList), certsToUse = 0;
02277        !CERT_LIST_END(node, certList) && certsToUse < nicknames->numnicknames;
02278        node = CERT_LIST_NEXT(node)) {
02279     nsRefPtr<nsNSSCertificate> tempCert = new nsNSSCertificate(node->cert);
02280     if (tempCert) {
02281       nsAutoString nickWithSerial, details;
02282       rv = tempCert->FormatUIStrings(NS_ConvertUTF8toUTF16(nicknames->nicknames[certsToUse]),
02283                                      nickWithSerial, details);
02284       if (NS_SUCCEEDED(rv)) {
02285         certNicknameList[certsToUse] = ToNewUnicode(nickWithSerial);
02286         if (certNicknameList[certsToUse]) {
02287           certDetailsList[certsToUse] = ToNewUnicode(details);
02288           if (!certDetailsList[certsToUse]) {
02289             nsMemory::Free(certNicknameList[certsToUse]);
02290             continue;
02291           }
02292           ++certsToUse;
02293         }
02294       }
02295     }
02296   }
02297 
02298   if (certsToUse == 0) {
02299     aResult.Append(internalError);
02300 
02301     return NS_OK;
02302   }
02303 
02304   NS_ConvertUTF8toUTF16 utf16Host(host);
02305 
02306   CERTCertificate *signingCert = nsnull;
02307   PRBool tryAgain, canceled;
02308   nsAutoString password;
02309   do {
02310     // Throw up the form signing confirmation dialog and get back the index
02311     // of the selected cert.
02312     PRInt32 selectedIndex = -1;
02313     rv = proxied_fsd->ConfirmSignText(uiContext, utf16Host, aStringToSign,
02314                                       NS_CONST_CAST(const PRUnichar**, certNicknameList.get()),
02315                                       NS_CONST_CAST(const PRUnichar**, certDetailsList),
02316                                       certsToUse, &selectedIndex, password,
02317                                       &canceled);
02318     if (NS_FAILED(rv) || canceled) {
02319       break; // out of tryAgain loop
02320     }
02321 
02322     PRInt32 j = 0;
02323     for (node = CERT_LIST_HEAD(certList); !CERT_LIST_END(node, certList);
02324          node = CERT_LIST_NEXT(node)) {
02325       if (j == selectedIndex) {
02326         signingCert = CERT_DupCertificate(node->cert);
02327         break; // out of cert list iteration loop
02328       }
02329       ++j;
02330     }
02331 
02332     if (!signingCert) {
02333       rv = NS_ERROR_FAILURE;
02334       break; // out of tryAgain loop
02335     }
02336 
02337     NS_ConvertUTF16toUTF8 pwUtf8(password);
02338 
02339     tryAgain =
02340       PK11_CheckUserPassword(signingCert->slot,
02341                              NS_CONST_CAST(char *, pwUtf8.get())) != SECSuccess;
02342     // XXX we should show an error dialog before retrying
02343   } while (tryAgain);
02344 
02345   PRInt32 k;
02346   for (k = 0; k < certsToUse; ++k) {
02347     nsMemory::Free(certNicknameList[k]);
02348     nsMemory::Free(certDetailsList[k]);
02349   }
02350 
02351   if (NS_FAILED(rv)) { // something went wrong inside the tryAgain loop
02352     aResult.Append(internalError);
02353 
02354     return NS_OK;
02355   }
02356 
02357   if (canceled) {
02358     aResult.AppendLiteral("error:userCancel");
02359 
02360     return NS_OK;
02361   }
02362 
02363   SECKEYPrivateKey* privKey = PK11_FindKeyByAnyCert(signingCert, uiContext);
02364   if (!privKey) {
02365     aResult.Append(internalError);
02366 
02367     return NS_OK;
02368   }
02369 
02370   nsCAutoString charset(document->GetDocumentCharacterSet());
02371 
02372   // XXX Doing what nsFormSubmission::GetEncoder does (see
02373   //     http://bugzilla.mozilla.org/show_bug.cgi?id=81203).
02374   if (charset.EqualsLiteral("ISO-8859-1")) {
02375     charset.AssignLiteral("windows-1252");
02376   }
02377 
02378   nsCOMPtr<nsISaveAsCharset> encoder =
02379     do_CreateInstance(NS_SAVEASCHARSET_CONTRACTID);
02380   if (encoder) {
02381     rv = encoder->Init(charset.get(),
02382                        (nsISaveAsCharset::attr_EntityAfterCharsetConv + 
02383                        nsISaveAsCharset::attr_FallbackDecimalNCR),
02384                        0);
02385   }
02386 
02387   nsXPIDLCString buffer;
02388   if (aStringToSign.Length() > 0) {
02389     if (encoder && NS_SUCCEEDED(rv)) {
02390       rv = encoder->Convert(PromiseFlatString(aStringToSign).get(),
02391                             getter_Copies(buffer));
02392       if (NS_FAILED(rv)) {
02393         aResult.Append(internalError);
02394 
02395         return NS_OK;
02396       }
02397     }
02398     else {
02399       AppendUTF16toUTF8(aStringToSign, buffer);
02400     }
02401   }
02402 
02403   HASHContext *hc = HASH_Create(HASH_AlgSHA1);
02404   if (!hc) {
02405     aResult.Append(internalError);
02406 
02407     return NS_OK;
02408   }
02409 
02410   unsigned char hash[SHA1_LENGTH];
02411 
02412   SECItem digest;
02413   digest.data = hash;
02414 
02415   HASH_Begin(hc);
02416   HASH_Update(hc, NS_REINTERPRET_CAST(const unsigned char*, buffer.get()),
02417               buffer.Length());
02418   HASH_End(hc, digest.data, &digest.len, SHA1_LENGTH);
02419   HASH_Destroy(hc);
02420 
02421   nsCString p7;
02422   SECStatus srv = SECFailure;
02423 
02424   SEC_PKCS7ContentInfo *ci = SEC_PKCS7CreateSignedData(signingCert,
02425                                                        certUsageEmailSigner,
02426                                                        nsnull, SEC_OID_SHA1,
02427                                                        &digest, nsnull, uiContext);
02428   if (ci) {
02429     srv = SEC_PKCS7IncludeCertChain(ci, nsnull);
02430     if (srv == SECSuccess) {
02431       srv = SEC_PKCS7AddSigningTime(ci);
02432       if (srv == SECSuccess) {
02433         srv = SEC_PKCS7Encode(ci, signTextOutputCallback, &p7, nsnull, nsnull,
02434                               uiContext);
02435       }
02436     }
02437 
02438     SEC_PKCS7DestroyContentInfo(ci);
02439   }
02440 
02441   if (srv != SECSuccess) {
02442     aResult.Append(internalError);
02443 
02444     return NS_OK;
02445   }
02446 
02447   SECItem binary_item;
02448   binary_item.data = NS_REINTERPRET_CAST(unsigned char*,
02449                                          NS_CONST_CAST(char*, p7.get()));
02450   binary_item.len = p7.Length();
02451 
02452   char *result = NSSBase64_EncodeItem(nsnull, nsnull, 0, &binary_item);
02453   if (result) {
02454     AppendASCIItoUTF16(result, aResult);
02455   }
02456   else {
02457     aResult.Append(internalError);
02458   }
02459 
02460   PORT_Free(result);
02461 
02462   return NS_OK;
02463 }
02464 
02465 NS_IMETHODIMP
02466 nsCrypto::Alert(const nsAString& aMessage)
02467 {
02468   PRUnichar *message = ToNewUnicode(aMessage);
02469   alertUser(message);
02470   nsMemory::Free(message);
02471   return NS_OK;
02472 }
02473 
02474 //Logout out of all installed PKCS11 tokens.
02475 NS_IMETHODIMP
02476 nsCrypto::Logout()
02477 {
02478   nsresult rv;
02479   nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv));
02480   if (NS_FAILED(rv))
02481     return rv;
02482 
02483   {
02484     nsNSSShutDownPreventionLock locker;
02485     PK11_LogoutAll();
02486     SSL_ClearSessionCache();
02487   }
02488 
02489   return nssComponent->LogoutAuthenticatedPK11();
02490 }
02491 
02492 NS_IMETHODIMP
02493 nsCrypto::DisableRightClick()
02494 {
02495   return NS_ERROR_NOT_IMPLEMENTED;
02496 }
02497 
02498 nsCRMFObject::nsCRMFObject()
02499 {
02500 }
02501 
02502 nsCRMFObject::~nsCRMFObject()
02503 {
02504 }
02505 
02506 nsresult
02507 nsCRMFObject::init()
02508 {
02509   return NS_OK;
02510 }
02511 
02512 NS_IMETHODIMP
02513 nsCRMFObject::GetRequest(nsAString& aRequest)
02514 {
02515   aRequest.Assign(mBase64Request);
02516   return NS_OK;
02517 }
02518 
02519 nsresult
02520 nsCRMFObject::SetCRMFRequest(char *inRequest)
02521 {
02522   mBase64Request.AssignWithConversion(inRequest);  
02523   return NS_OK;
02524 }
02525 
02526 nsPkcs11::nsPkcs11()
02527 {
02528 }
02529 
02530 nsPkcs11::~nsPkcs11()
02531 {
02532 }
02533 
02534 //Quick function to confirm with the user.
02535 PRBool
02536 confirm_user(const PRUnichar *message)
02537 {
02538   PRInt32 buttonPressed = 1; // If the user exits by clicking the close box, assume No (button 1)
02539 
02540   nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
02541   nsCOMPtr<nsIPrompt> prompter;
02542   if (wwatch)
02543     wwatch->GetNewPrompter(0, getter_AddRefs(prompter));
02544 
02545   if (prompter) {
02546     nsPSMUITracker tracker;
02547     if (!tracker.isUIForbidden()) {
02548       prompter->ConfirmEx(0, message,
02549                           (nsIPrompt::BUTTON_DELAY_ENABLE) +
02550                           (nsIPrompt::BUTTON_POS_1_DEFAULT) +
02551                           (nsIPrompt::BUTTON_TITLE_OK * nsIPrompt::BUTTON_POS_0) +
02552                           (nsIPrompt::BUTTON_TITLE_CANCEL * nsIPrompt::BUTTON_POS_1),
02553                           nsnull, nsnull, nsnull, nsnull, nsnull, &buttonPressed);
02554     }
02555   }
02556 
02557   return (buttonPressed == 0);
02558 }
02559 
02560 //Delete a PKCS11 module from the user's profile.
02561 NS_IMETHODIMP
02562 nsPkcs11::Deletemodule(const nsAString& aModuleName, PRInt32* aReturn)
02563 {
02564   nsNSSShutDownPreventionLock locker;
02565   nsresult rv;
02566   nsString errorMessage;
02567 
02568   nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv));
02569   if (aModuleName.IsEmpty()) {
02570     *aReturn = JS_ERR_BAD_MODULE_NAME;
02571     nssComponent->GetPIPNSSBundleString("DelModuleBadName", errorMessage);
02572     alertUser(errorMessage.get());
02573     return NS_OK;
02574   }
02575   nsString final;
02576   nsAutoString temp;
02577   //Make sure the user knows we're trying to do this.
02578   nssComponent->GetPIPNSSBundleString("DelModuleWarning", final);
02579   final.Append(NS_LITERAL_STRING("\n").get());
02580   PRUnichar *tempUni = ToNewUnicode(aModuleName);
02581   const PRUnichar *formatStrings[1] = { tempUni };
02582   rv = nssComponent->PIPBundleFormatStringFromName("AddModuleName",
02583                                                    formatStrings, 1, temp);
02584   nsMemory::Free(tempUni);
02585   final.Append(temp);
02586   if (!confirm_user(final.get())) {
02587     *aReturn = JS_ERR_USER_CANCEL_ACTION;
02588     return NS_OK;
02589   }
02590   
02591   char *modName = ToNewCString(aModuleName);
02592   PRInt32 modType;
02593   SECStatus srv = SECMOD_DeleteModule(modName, &modType);
02594   if (srv == SECSuccess) {
02595     SECMODModule *module = SECMOD_FindModule(modName);
02596     if (module) {
02597       nssComponent->ShutdownSmartCardThread(module);
02598       SECMOD_DestroyModule(module);
02599     }
02600     if (modType == SECMOD_EXTERNAL) {
02601       nssComponent->GetPIPNSSBundleString("DelModuleExtSuccess", errorMessage);
02602       *aReturn = JS_OK_DEL_EXTERNAL_MOD;
02603     } else {
02604       nssComponent->GetPIPNSSBundleString("DelModuleIntSuccess", errorMessage);
02605       *aReturn = JS_OK_DEL_INTERNAL_MOD;
02606     }
02607   } else {
02608     *aReturn = JS_ERR_DEL_MOD;
02609     nssComponent->GetPIPNSSBundleString("DelModuleError", errorMessage);
02610   }
02611   alertUser(errorMessage.get());
02612   return NS_OK;
02613 }
02614 
02615 //Add a new PKCS11 module to the user's profile.
02616 NS_IMETHODIMP
02617 nsPkcs11::Addmodule(const nsAString& aModuleName, 
02618                     const nsAString& aLibraryFullPath, 
02619                     PRInt32 aCryptoMechanismFlags, 
02620                     PRInt32 aCipherFlags, PRInt32* aReturn)
02621 {
02622   nsNSSShutDownPreventionLock locker;
02623   nsresult rv;
02624   nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv));
02625   nsString final;
02626   nsAutoString temp;
02627 
02628   rv = nssComponent->GetPIPNSSBundleString("AddModulePrompt", final);
02629   if (NS_FAILED(rv))
02630     return rv;
02631 
02632   final.Append(NS_LITERAL_STRING("\n").get());
02633   
02634   PRUnichar *tempUni = ToNewUnicode(aModuleName); 
02635   const PRUnichar *formatStrings[1] = { tempUni };
02636   rv = nssComponent->PIPBundleFormatStringFromName("AddModuleName",
02637                                                    formatStrings, 1, temp);
02638   nsMemory::Free(tempUni);
02639 
02640   if (NS_FAILED(rv))
02641     return rv;
02642 
02643   final.Append(temp);
02644   final.Append(NS_LITERAL_STRING("\n").get());
02645 
02646   tempUni = ToNewUnicode(aLibraryFullPath);
02647   formatStrings[0] = tempUni;
02648   rv = nssComponent->PIPBundleFormatStringFromName("AddModulePath",
02649                                                    formatStrings, 1, temp);
02650   nsMemory::Free(tempUni);
02651   if (NS_FAILED(rv))
02652     return rv;
02653 
02654   final.Append(temp);
02655   final.Append(NS_LITERAL_STRING("\n").get());
02656  
02657   if (!confirm_user(final.get())) {
02658     // The user has canceled. So let's return now.
02659     *aReturn = JS_ERR_USER_CANCEL_ACTION;
02660     return NS_OK;
02661   }
02662   
02663   char *moduleName = ToNewCString(aModuleName);
02664   char *fullPath   = ToNewCString(aLibraryFullPath);
02665   PRUint32 mechFlags = SECMOD_PubMechFlagstoInternal(aCryptoMechanismFlags);
02666   PRUint32 cipherFlags = SECMOD_PubCipherFlagstoInternal(aCipherFlags);
02667   SECStatus srv = SECMOD_AddNewModule(moduleName, fullPath, 
02668                                       mechFlags, cipherFlags);
02669   if (srv == SECSuccess) {
02670     SECMODModule *module = SECMOD_FindModule(moduleName);
02671     if (module) {
02672       nssComponent->LaunchSmartCardThread(module);
02673       SECMOD_DestroyModule(module);
02674     }
02675   }
02676 
02677   nsMemory::Free(moduleName);
02678   nsMemory::Free(fullPath);
02679 
02680   // The error message we report to the user depends directly on 
02681   // what the return value for SEDMOD_AddNewModule is
02682   switch (srv) {
02683   case SECSuccess:
02684     nssComponent->GetPIPNSSBundleString("AddModuleSuccess", final);
02685     *aReturn = JS_OK_ADD_MOD;
02686     break;
02687   case SECFailure:
02688     nssComponent->GetPIPNSSBundleString("AddModuleFailure", final);
02689     *aReturn = JS_ERR_ADD_MOD;
02690     break;
02691   case -2:
02692     nssComponent->GetPIPNSSBundleString("AddModuleDup", final);
02693     *aReturn = JS_ERR_ADD_DUPLICATE_MOD;
02694     break;
02695   default:
02696     NS_ASSERTION(0,"Bogus return value, this should never happen");
02697     return NS_ERROR_FAILURE;
02698   }
02699   alertUser(final.get());
02700   return NS_OK;
02701 }
02702