michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: #include "nsCrypto.h" michael@0: #include "nsNSSComponent.h" michael@0: #include "secmod.h" michael@0: michael@0: #include "nsReadableUtils.h" michael@0: #include "nsCRT.h" michael@0: #include "nsXPIDLString.h" michael@0: #include "nsISaveAsCharset.h" michael@0: #include "nsNativeCharsetUtils.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: michael@0: #ifndef MOZ_DISABLE_CRYPTOLEGACY michael@0: #include "nsKeygenHandler.h" michael@0: #include "nsKeygenThread.h" michael@0: #include "nsNSSCertificate.h" michael@0: #include "nsNSSCertificateDB.h" michael@0: #include "nsPKCS12Blob.h" michael@0: #include "nsPK11TokenDB.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsIServiceManager.h" michael@0: #include "nsIMemory.h" michael@0: #include "nsAlgorithm.h" michael@0: #include "prprf.h" michael@0: #include "nsDOMCID.h" michael@0: #include "nsIDOMWindow.h" michael@0: #include "nsIDOMClassInfo.h" michael@0: #include "nsIDOMDocument.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsIScriptObjectPrincipal.h" michael@0: #include "nsIScriptContext.h" michael@0: #include "nsIScriptGlobalObject.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsCxPusher.h" michael@0: #include "nsDOMJSUtils.h" michael@0: #include "nsJSUtils.h" michael@0: #include "nsIXPConnect.h" michael@0: #include "nsIRunnable.h" michael@0: #include "nsIWindowWatcher.h" michael@0: #include "nsIPrompt.h" michael@0: #include "nsIFilePicker.h" michael@0: #include "nsJSPrincipals.h" michael@0: #include "nsJSUtils.h" michael@0: #include "nsIPrincipal.h" michael@0: #include "nsIScriptSecurityManager.h" michael@0: #include "nsIGenKeypairInfoDlg.h" michael@0: #include "nsIDOMCryptoDialogs.h" michael@0: #include "nsIFormSigningDialog.h" michael@0: #include "nsIContentSecurityPolicy.h" michael@0: #include "nsIURI.h" michael@0: #include "jsapi.h" michael@0: #include "js/OldDebugAPI.h" michael@0: #include michael@0: #include "pk11func.h" michael@0: #include "keyhi.h" michael@0: #include "cryptohi.h" michael@0: #include "seccomon.h" michael@0: #include "secerr.h" michael@0: #include "sechash.h" michael@0: #include "crmf.h" michael@0: #include "pk11pqg.h" michael@0: #include "cmmf.h" michael@0: #include "nssb64.h" michael@0: #include "base64.h" michael@0: #include "cert.h" michael@0: #include "certdb.h" michael@0: #include "secmod.h" michael@0: #include "ScopedNSSTypes.h" michael@0: #include "pkix/pkixtypes.h" michael@0: michael@0: #include "ssl.h" // For SSL_ClearSessionCache michael@0: michael@0: #include "nsNSSCleaner.h" michael@0: michael@0: #include "nsNSSCertHelper.h" michael@0: #include michael@0: #include "nsWrapperCacheInlines.h" michael@0: #endif michael@0: #ifndef MOZ_DISABLE_CRYPTOLEGACY michael@0: #include "mozilla/dom/CRMFObjectBinding.h" michael@0: #endif michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: michael@0: /* michael@0: * These are the most common error strings that are returned michael@0: * by the JavaScript methods in case of error. michael@0: */ michael@0: michael@0: #define JS_ERROR "error:" michael@0: #define JS_ERROR_INTERNAL JS_ERROR"internalError" michael@0: michael@0: #undef REPORT_INCORRECT_NUM_ARGS michael@0: michael@0: #define JS_OK_ADD_MOD 3 michael@0: #define JS_OK_DEL_EXTERNAL_MOD 2 michael@0: #define JS_OK_DEL_INTERNAL_MOD 1 michael@0: michael@0: #define JS_ERR_INTERNAL -1 michael@0: #define JS_ERR_USER_CANCEL_ACTION -2 michael@0: #define JS_ERR_INCORRECT_NUM_OF_ARGUMENTS -3 michael@0: #define JS_ERR_DEL_MOD -4 michael@0: #define JS_ERR_ADD_MOD -5 michael@0: #define JS_ERR_BAD_MODULE_NAME -6 michael@0: #define JS_ERR_BAD_DLL_NAME -7 michael@0: #define JS_ERR_BAD_MECHANISM_FLAGS -8 michael@0: #define JS_ERR_BAD_CIPHER_ENABLE_FLAGS -9 michael@0: #define JS_ERR_ADD_DUPLICATE_MOD -10 michael@0: michael@0: #ifndef MOZ_DISABLE_CRYPTOLEGACY michael@0: michael@0: NSSCleanupAutoPtrClass_WithParam(PK11Context, PK11_DestroyContext, TrueParam, true) michael@0: michael@0: /* michael@0: * This structure is used to store information for one key generation. michael@0: * The nsCrypto::GenerateCRMFRequest method parses the inputs and then michael@0: * stores one of these structures for every key generation that happens. michael@0: * The information stored in this structure is then used to set some michael@0: * values in the CRMF request. michael@0: */ michael@0: typedef enum { michael@0: rsaEnc, rsaDualUse, rsaSign, rsaNonrepudiation, rsaSignNonrepudiation, michael@0: ecEnc, ecDualUse, ecSign, ecNonrepudiation, ecSignNonrepudiation, michael@0: dhEx, dsaSignNonrepudiation, dsaSign, dsaNonrepudiation, invalidKeyGen michael@0: } nsKeyGenType; michael@0: michael@0: bool isECKeyGenType(nsKeyGenType kgt) michael@0: { michael@0: switch (kgt) michael@0: { michael@0: case ecEnc: michael@0: case ecDualUse: michael@0: case ecSign: michael@0: case ecNonrepudiation: michael@0: case ecSignNonrepudiation: michael@0: return true; michael@0: michael@0: default: michael@0: break; michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: typedef struct nsKeyPairInfoStr { michael@0: SECKEYPublicKey *pubKey; /* The putlic key associated with gen'd michael@0: priv key. */ michael@0: SECKEYPrivateKey *privKey; /* The private key we generated */ michael@0: nsKeyGenType keyGenType; /* What type of key gen are we doing.*/ michael@0: michael@0: CERTCertificate *ecPopCert; michael@0: /* null: use signing for pop michael@0: other than null: a cert that defines EC keygen params michael@0: and will be used for dhMac PoP. */ michael@0: michael@0: SECKEYPublicKey *ecPopPubKey; michael@0: /* extracted public key from ecPopCert */ michael@0: } nsKeyPairInfo; michael@0: michael@0: michael@0: //This class is just used to pass arguments michael@0: //to the nsCryptoRunnable event. michael@0: class nsCryptoRunArgs : public nsISupports { michael@0: public: michael@0: nsCryptoRunArgs(JSContext *aCx); michael@0: virtual ~nsCryptoRunArgs(); michael@0: nsCOMPtr m_kungFuDeathGrip; michael@0: JSContext *m_cx; michael@0: JS::PersistentRooted m_scope; michael@0: nsCOMPtr m_principals; michael@0: nsXPIDLCString m_jsCallback; michael@0: NS_DECL_ISUPPORTS michael@0: }; michael@0: michael@0: //This class is used to run the callback code michael@0: //passed to crypto.generateCRMFRequest michael@0: //We have to do that for backwards compatibility michael@0: //reasons w/ PSM 1.x and Communciator 4.x michael@0: class nsCryptoRunnable : public nsIRunnable { michael@0: public: michael@0: nsCryptoRunnable(nsCryptoRunArgs *args); michael@0: virtual ~nsCryptoRunnable(); michael@0: michael@0: NS_IMETHOD Run (); michael@0: NS_DECL_ISUPPORTS michael@0: private: michael@0: nsCryptoRunArgs *m_args; michael@0: }; michael@0: michael@0: michael@0: //We're going to inherit the memory passed michael@0: //into us. michael@0: //This class backs up an array of certificates michael@0: //as an event. michael@0: class nsP12Runnable : public nsIRunnable { michael@0: public: michael@0: nsP12Runnable(nsIX509Cert **certArr, int32_t numCerts, nsIPK11Token *token); michael@0: virtual ~nsP12Runnable(); michael@0: michael@0: NS_IMETHOD Run(); michael@0: NS_DECL_ISUPPORTS michael@0: private: michael@0: nsCOMPtr mToken; michael@0: nsIX509Cert **mCertArr; michael@0: int32_t mNumCerts; michael@0: }; michael@0: michael@0: // QueryInterface implementation for nsCrypto michael@0: NS_INTERFACE_MAP_BEGIN(nsCrypto) michael@0: NS_INTERFACE_MAP_ENTRY(nsIDOMCrypto) michael@0: NS_INTERFACE_MAP_END_INHERITING(mozilla::dom::Crypto) michael@0: michael@0: NS_IMPL_ADDREF_INHERITED(nsCrypto, mozilla::dom::Crypto) michael@0: NS_IMPL_RELEASE_INHERITED(nsCrypto, mozilla::dom::Crypto) michael@0: michael@0: // QueryInterface implementation for nsPkcs11 michael@0: #endif // MOZ_DISABLE_CRYPTOLEGACY michael@0: michael@0: NS_INTERFACE_MAP_BEGIN(nsPkcs11) michael@0: NS_INTERFACE_MAP_ENTRY(nsIPKCS11) michael@0: NS_INTERFACE_MAP_ENTRY(nsISupports) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: NS_IMPL_ADDREF(nsPkcs11) michael@0: NS_IMPL_RELEASE(nsPkcs11) michael@0: michael@0: #ifndef MOZ_DISABLE_CRYPTOLEGACY michael@0: michael@0: // ISupports implementation for nsCryptoRunnable michael@0: NS_IMPL_ISUPPORTS(nsCryptoRunnable, nsIRunnable) michael@0: michael@0: // ISupports implementation for nsP12Runnable michael@0: NS_IMPL_ISUPPORTS(nsP12Runnable, nsIRunnable) michael@0: michael@0: // ISupports implementation for nsCryptoRunArgs michael@0: NS_IMPL_ISUPPORTS0(nsCryptoRunArgs) michael@0: michael@0: nsCrypto::nsCrypto() : michael@0: mEnableSmartCardEvents(false) michael@0: { michael@0: } michael@0: michael@0: nsCrypto::~nsCrypto() michael@0: { michael@0: } michael@0: michael@0: void michael@0: nsCrypto::Init(nsIDOMWindow* aWindow) michael@0: { michael@0: mozilla::dom::Crypto::Init(aWindow); michael@0: } michael@0: michael@0: void michael@0: nsCrypto::SetEnableSmartCardEvents(bool aEnable, ErrorResult& aRv) michael@0: { michael@0: NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID); michael@0: michael@0: nsresult rv = NS_OK; michael@0: michael@0: // this has the side effect of starting the nssComponent (and initializing michael@0: // NSS) even if it isn't already going. Starting the nssComponent is a michael@0: // prerequisite for getting smartCard events. michael@0: if (aEnable) { michael@0: nsCOMPtr nssComponent(do_GetService(kNSSComponentCID, &rv)); michael@0: } michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: aRv.Throw(rv); michael@0: return; michael@0: } michael@0: michael@0: mEnableSmartCardEvents = aEnable; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsCrypto::SetEnableSmartCardEvents(bool aEnable) michael@0: { michael@0: ErrorResult rv; michael@0: SetEnableSmartCardEvents(aEnable, rv); michael@0: return rv.ErrorCode(); michael@0: } michael@0: michael@0: bool michael@0: nsCrypto::EnableSmartCardEvents() michael@0: { michael@0: return mEnableSmartCardEvents; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsCrypto::GetEnableSmartCardEvents(bool *aEnable) michael@0: { michael@0: *aEnable = EnableSmartCardEvents(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: //A quick function to let us know if the key we're trying to generate michael@0: //can be escrowed. michael@0: static bool michael@0: ns_can_escrow(nsKeyGenType keyGenType) michael@0: { michael@0: /* For now, we only escrow rsa-encryption and ec-encryption keys. */ michael@0: return (bool)(keyGenType == rsaEnc || keyGenType == ecEnc); michael@0: } michael@0: michael@0: //Retrieve crypto.version so that callers know what michael@0: //version of PSM this is. michael@0: void michael@0: nsCrypto::GetVersion(nsString& aVersion) michael@0: { michael@0: aVersion.Assign(NS_LITERAL_STRING(PSM_VERSION_STRING)); michael@0: } michael@0: michael@0: /* michael@0: * Given an nsKeyGenType, return the PKCS11 mechanism that will michael@0: * perform the correct key generation. michael@0: */ michael@0: static uint32_t michael@0: cryptojs_convert_to_mechanism(nsKeyGenType keyGenType) michael@0: { michael@0: uint32_t retMech; michael@0: michael@0: switch (keyGenType) { michael@0: case rsaEnc: michael@0: case rsaDualUse: michael@0: case rsaSign: michael@0: case rsaNonrepudiation: michael@0: case rsaSignNonrepudiation: michael@0: retMech = CKM_RSA_PKCS_KEY_PAIR_GEN; michael@0: break; michael@0: case ecEnc: michael@0: case ecDualUse: michael@0: case ecSign: michael@0: case ecNonrepudiation: michael@0: case ecSignNonrepudiation: michael@0: retMech = CKM_EC_KEY_PAIR_GEN; michael@0: break; michael@0: case dhEx: michael@0: retMech = CKM_DH_PKCS_KEY_PAIR_GEN; michael@0: break; michael@0: case dsaSign: michael@0: case dsaSignNonrepudiation: michael@0: case dsaNonrepudiation: michael@0: retMech = CKM_DSA_KEY_PAIR_GEN; michael@0: break; michael@0: default: michael@0: retMech = CKM_INVALID_MECHANISM; michael@0: } michael@0: return retMech; michael@0: } michael@0: michael@0: /* michael@0: * This function takes a string read through JavaScript parameters michael@0: * and translates it to the internal enumeration representing the michael@0: * key gen type. Leading and trailing whitespace must be already removed. michael@0: */ michael@0: static nsKeyGenType michael@0: cryptojs_interpret_key_gen_type(const nsAString& keyAlg) michael@0: { michael@0: if (keyAlg.EqualsLiteral("rsa-ex")) { michael@0: return rsaEnc; michael@0: } michael@0: if (keyAlg.EqualsLiteral("rsa-dual-use")) { michael@0: return rsaDualUse; michael@0: } michael@0: if (keyAlg.EqualsLiteral("rsa-sign")) { michael@0: return rsaSign; michael@0: } michael@0: if (keyAlg.EqualsLiteral("rsa-sign-nonrepudiation")) { michael@0: return rsaSignNonrepudiation; michael@0: } michael@0: if (keyAlg.EqualsLiteral("rsa-nonrepudiation")) { michael@0: return rsaNonrepudiation; michael@0: } michael@0: if (keyAlg.EqualsLiteral("ec-ex")) { michael@0: return ecEnc; michael@0: } michael@0: if (keyAlg.EqualsLiteral("ec-dual-use")) { michael@0: return ecDualUse; michael@0: } michael@0: if (keyAlg.EqualsLiteral("ec-sign")) { michael@0: return ecSign; michael@0: } michael@0: if (keyAlg.EqualsLiteral("ec-sign-nonrepudiation")) { michael@0: return ecSignNonrepudiation; michael@0: } michael@0: if (keyAlg.EqualsLiteral("ec-nonrepudiation")) { michael@0: return ecNonrepudiation; michael@0: } michael@0: if (keyAlg.EqualsLiteral("dsa-sign-nonrepudiation")) { michael@0: return dsaSignNonrepudiation; michael@0: } michael@0: if (keyAlg.EqualsLiteral("dsa-sign")) { michael@0: return dsaSign; michael@0: } michael@0: if (keyAlg.EqualsLiteral("dsa-nonrepudiation")) { michael@0: return dsaNonrepudiation; michael@0: } michael@0: if (keyAlg.EqualsLiteral("dh-ex")) { michael@0: return dhEx; michael@0: } michael@0: return invalidKeyGen; michael@0: } michael@0: michael@0: /* michael@0: * input: null terminated char* pointing to (the remainder of) an michael@0: * EC key param string. michael@0: * michael@0: * bool return value, false means "no more name=value pair found", michael@0: * true means "found, see out params" michael@0: * michael@0: * out param name: char * pointing to name (not zero terminated) michael@0: * out param name_len: length of found name michael@0: * out param value: char * pointing to value (not zero terminated) michael@0: * out param value_len: length of found value michael@0: * out param next_pair: to be used for a follow up call to this function michael@0: */ michael@0: michael@0: bool getNextNameValueFromECKeygenParamString(char *input, michael@0: char *&name, michael@0: int &name_len, michael@0: char *&value, michael@0: int &value_len, michael@0: char *&next_call) michael@0: { michael@0: if (!input || !*input) michael@0: return false; michael@0: michael@0: // we allow leading ; and leading space in front of each name value pair michael@0: michael@0: while (*input && *input == ';') michael@0: ++input; michael@0: michael@0: while (*input && *input == ' ') michael@0: ++input; michael@0: michael@0: name = input; michael@0: michael@0: while (*input && *input != '=') michael@0: ++input; michael@0: michael@0: if (*input != '=') michael@0: return false; michael@0: michael@0: name_len = input - name; michael@0: ++input; michael@0: michael@0: value = input; michael@0: michael@0: while (*input && *input != ';') michael@0: ++input; michael@0: michael@0: value_len = input - value; michael@0: next_call = input; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: //Take the string passed into us via crypto.generateCRMFRequest michael@0: //as the keygen type parameter and convert it to parameters michael@0: //we can actually pass to the PKCS#11 layer. michael@0: static void* michael@0: nsConvertToActualKeyGenParams(uint32_t keyGenMech, char *params, michael@0: uint32_t paramLen, int32_t keySize, michael@0: nsKeyPairInfo *keyPairInfo) michael@0: { michael@0: void *returnParams = nullptr; michael@0: michael@0: michael@0: switch (keyGenMech) { michael@0: case CKM_RSA_PKCS_KEY_PAIR_GEN: michael@0: { michael@0: // For RSA, we don't support passing in key generation arguments from michael@0: // the JS code just yet. michael@0: if (params) michael@0: return nullptr; michael@0: michael@0: PK11RSAGenParams *rsaParams; michael@0: rsaParams = static_cast michael@0: (nsMemory::Alloc(sizeof(PK11RSAGenParams))); michael@0: michael@0: if (!rsaParams) { michael@0: return nullptr; michael@0: } michael@0: /* I'm just taking the same parameters used in michael@0: * certdlgs.c:GenKey michael@0: */ michael@0: if (keySize > 0) { michael@0: rsaParams->keySizeInBits = keySize; michael@0: } else { michael@0: rsaParams->keySizeInBits = 1024; michael@0: } michael@0: rsaParams->pe = DEFAULT_RSA_KEYGEN_PE; michael@0: returnParams = rsaParams; michael@0: break; michael@0: } michael@0: case CKM_EC_KEY_PAIR_GEN: michael@0: { michael@0: /* michael@0: * keygen params for generating EC keys must be composed of name=value pairs, michael@0: * multiple pairs allowed, separated using semicolon ; michael@0: * michael@0: * Either param "curve" or param "popcert" must be specified. michael@0: * curve=name-of-curve michael@0: * popcert=base64-encoded-cert michael@0: * michael@0: * When both params are specified, popcert will be used. michael@0: * If no popcert param is given, or if popcert can not be decoded, michael@0: * we will fall back to the curve param. michael@0: * michael@0: * Additional name=value pairs may be defined in the future. michael@0: * michael@0: * If param popcert is present and valid, the given certificate will be used michael@0: * to determine the key generation params. In addition the certificate michael@0: * will be used to produce a dhMac based Proof of Posession, michael@0: * using the cert's public key, subject and issuer names, michael@0: * as specified in RFC 2511 section 4.3 paragraph 2 and Appendix A. michael@0: * michael@0: * If neither param popcert nor param curve could be used, michael@0: * tse a curve based on the keysize param. michael@0: * NOTE: Here keysize is used only as an indication of michael@0: * High/Medium/Low strength; elliptic curve michael@0: * cryptography uses smaller keys than RSA to provide michael@0: * equivalent security. michael@0: */ michael@0: michael@0: char *curve = nullptr; michael@0: michael@0: { michael@0: // extract components of name=value list michael@0: michael@0: char *next_input = params; michael@0: char *name = nullptr; michael@0: char *value = nullptr; michael@0: int name_len = 0; michael@0: int value_len = 0; michael@0: michael@0: while (getNextNameValueFromECKeygenParamString( michael@0: next_input, name, name_len, value, value_len, michael@0: next_input)) michael@0: { michael@0: // use only the first specified curve michael@0: if (!curve && PL_strncmp(name, "curve", std::min(name_len, 5)) == 0) michael@0: { michael@0: curve = PL_strndup(value, value_len); michael@0: } michael@0: // use only the first specified popcert michael@0: else if (!keyPairInfo->ecPopCert && michael@0: PL_strncmp(name, "popcert", std::min(name_len, 7)) == 0) michael@0: { michael@0: char *certstr = PL_strndup(value, value_len); michael@0: if (certstr) { michael@0: keyPairInfo->ecPopCert = CERT_ConvertAndDecodeCertificate(certstr); michael@0: PL_strfree(certstr); michael@0: michael@0: if (keyPairInfo->ecPopCert) michael@0: { michael@0: keyPairInfo->ecPopPubKey = CERT_ExtractPublicKey(keyPairInfo->ecPopCert); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: // first try to use the params of the provided CA cert michael@0: if (keyPairInfo->ecPopPubKey && keyPairInfo->ecPopPubKey->keyType == ecKey) michael@0: { michael@0: returnParams = SECITEM_DupItem(&keyPairInfo->ecPopPubKey->u.ec.DEREncodedParams); michael@0: } michael@0: michael@0: // if we did not yet find good params, do we have a curve name? michael@0: if (!returnParams && curve) michael@0: { michael@0: returnParams = decode_ec_params(curve); michael@0: } michael@0: michael@0: // if we did not yet find good params, do something based on keysize michael@0: if (!returnParams) michael@0: { michael@0: switch (keySize) { michael@0: case 512: michael@0: case 1024: michael@0: returnParams = decode_ec_params("secp256r1"); michael@0: break; michael@0: case 2048: michael@0: default: michael@0: returnParams = decode_ec_params("secp384r1"); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (curve) michael@0: PL_strfree(curve); michael@0: michael@0: break; michael@0: } michael@0: case CKM_DSA_KEY_PAIR_GEN: michael@0: { michael@0: // For DSA, we don't support passing in key generation arguments from michael@0: // the JS code just yet. michael@0: if (params) michael@0: return nullptr; michael@0: michael@0: PQGParams *pqgParams = nullptr; michael@0: PQGVerify *vfy = nullptr; michael@0: SECStatus rv; michael@0: int index; michael@0: michael@0: index = PQG_PBITS_TO_INDEX(keySize); michael@0: if (index == -1) { michael@0: returnParams = nullptr; michael@0: break; michael@0: } michael@0: rv = PK11_PQG_ParamGen(0, &pqgParams, &vfy); michael@0: if (vfy) { michael@0: PK11_PQG_DestroyVerify(vfy); michael@0: } michael@0: if (rv != SECSuccess) { michael@0: if (pqgParams) { michael@0: PK11_PQG_DestroyParams(pqgParams); michael@0: } michael@0: return nullptr; michael@0: } michael@0: returnParams = pqgParams; michael@0: break; michael@0: } michael@0: default: michael@0: returnParams = nullptr; michael@0: } michael@0: return returnParams; michael@0: } michael@0: michael@0: //We need to choose which PKCS11 slot we're going to generate michael@0: //the key on. Calls the default implementation provided by michael@0: //nsKeygenHandler.cpp michael@0: static PK11SlotInfo* michael@0: nsGetSlotForKeyGen(nsKeyGenType keyGenType, nsIInterfaceRequestor *ctx) michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: uint32_t mechanism = cryptojs_convert_to_mechanism(keyGenType); michael@0: PK11SlotInfo *slot = nullptr; michael@0: nsresult rv = GetSlotWithMechanism(mechanism,ctx, &slot); michael@0: if (NS_FAILED(rv)) { michael@0: if (slot) michael@0: PK11_FreeSlot(slot); michael@0: slot = nullptr; michael@0: } michael@0: return slot; michael@0: } michael@0: michael@0: //Free the parameters that were passed into PK11_GenerateKeyPair michael@0: //depending on the mechanism type used. michael@0: static void michael@0: nsFreeKeyGenParams(CK_MECHANISM_TYPE keyGenMechanism, void *params) michael@0: { michael@0: switch (keyGenMechanism) { michael@0: case CKM_RSA_PKCS_KEY_PAIR_GEN: michael@0: nsMemory::Free(params); michael@0: break; michael@0: case CKM_EC_KEY_PAIR_GEN: michael@0: SECITEM_FreeItem(reinterpret_cast(params), true); michael@0: break; michael@0: case CKM_DSA_KEY_PAIR_GEN: michael@0: PK11_PQG_DestroyParams(static_cast(params)); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: //Function that is used to generate a single key pair. michael@0: //Once all the arguments have been parsed and processed, this michael@0: //function gets called and takes care of actually generating michael@0: //the key pair passing the appopriate parameters to the NSS michael@0: //functions. michael@0: static nsresult michael@0: cryptojs_generateOneKeyPair(JSContext *cx, nsKeyPairInfo *keyPairInfo, michael@0: int32_t keySize, char *params, michael@0: nsIInterfaceRequestor *uiCxt, michael@0: PK11SlotInfo *slot, bool willEscrow) michael@0: michael@0: { michael@0: const PK11AttrFlags sensitiveFlags = (PK11_ATTR_SENSITIVE | PK11_ATTR_PRIVATE); michael@0: const PK11AttrFlags temporarySessionFlags = PK11_ATTR_SESSION; michael@0: const PK11AttrFlags permanentTokenFlags = PK11_ATTR_TOKEN; michael@0: const PK11AttrFlags extractableFlags = PK11_ATTR_EXTRACTABLE; michael@0: michael@0: nsIGeneratingKeypairInfoDialogs * dialogs; michael@0: nsKeygenThread *KeygenRunnable = 0; michael@0: nsCOMPtr runnable; michael@0: michael@0: uint32_t mechanism = cryptojs_convert_to_mechanism(keyPairInfo->keyGenType); michael@0: void *keyGenParams = nsConvertToActualKeyGenParams(mechanism, params, michael@0: (params) ? strlen(params):0, michael@0: keySize, keyPairInfo); michael@0: michael@0: if (!keyGenParams || !slot) { michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: // Make sure the token has password already set on it before trying michael@0: // to generate the key. michael@0: michael@0: nsresult rv = setPassword(slot, uiCxt); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: if (PK11_Authenticate(slot, true, uiCxt) != SECSuccess) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: // Smart cards will not let you extract a private key once michael@0: // it is on the smart card. If we've been told to escrow michael@0: // a private key that will be stored on a smart card, michael@0: // then we'll use the following strategy to ensure we can escrow it. michael@0: // We'll attempt to generate the key on our internal token, michael@0: // because this is expected to avoid some problems. michael@0: // If it works, we'll escrow, move the key to the smartcard, done. michael@0: // If it didn't work (or the internal key doesn't support the desired michael@0: // mechanism), then we'll attempt to generate the key on michael@0: // the destination token, with the EXTRACTABLE flag set. michael@0: // If it works, we'll extract, escrow, done. michael@0: // If it failed, then we're unable to escrow and return failure. michael@0: // NOTE: We call PK11_GetInternalSlot instead of PK11_GetInternalKeySlot michael@0: // so that the key has zero chance of being store in the michael@0: // user's key3.db file. Which the slot returned by michael@0: // PK11_GetInternalKeySlot has access to and PK11_GetInternalSlot michael@0: // does not. michael@0: ScopedPK11SlotInfo intSlot; michael@0: michael@0: if (willEscrow && !PK11_IsInternal(slot)) { michael@0: intSlot = PK11_GetInternalSlot(); michael@0: NS_ASSERTION(intSlot,"Couldn't get the internal slot"); michael@0: michael@0: if (!PK11_DoesMechanism(intSlot, mechanism)) { michael@0: // Set to null, and the subsequent code will not attempt to use it. michael@0: intSlot = nullptr; michael@0: } michael@0: } michael@0: michael@0: rv = getNSSDialogs((void**)&dialogs, michael@0: NS_GET_IID(nsIGeneratingKeypairInfoDialogs), michael@0: NS_GENERATINGKEYPAIRINFODIALOGS_CONTRACTID); michael@0: michael@0: if (NS_SUCCEEDED(rv)) { michael@0: KeygenRunnable = new nsKeygenThread(); michael@0: if (KeygenRunnable) { michael@0: NS_ADDREF(KeygenRunnable); michael@0: } michael@0: } michael@0: michael@0: // "firstAttemptSlot" and "secondAttemptSlot" are alternative names michael@0: // for better code readability, we don't increase the reference counts. michael@0: michael@0: PK11SlotInfo *firstAttemptSlot = nullptr; michael@0: PK11AttrFlags firstAttemptFlags = 0; michael@0: michael@0: PK11SlotInfo *secondAttemptSlot = slot; michael@0: PK11AttrFlags secondAttemptFlags = sensitiveFlags | permanentTokenFlags; michael@0: michael@0: if (willEscrow) { michael@0: secondAttemptFlags |= extractableFlags; michael@0: } michael@0: michael@0: if (!intSlot || PK11_IsInternal(slot)) { michael@0: // if we cannot use the internal slot, then there is only one attempt michael@0: // if the destination slot is the internal slot, then there is only one attempt michael@0: firstAttemptSlot = secondAttemptSlot; michael@0: firstAttemptFlags = secondAttemptFlags; michael@0: secondAttemptSlot = nullptr; michael@0: secondAttemptFlags = 0; michael@0: } michael@0: else { michael@0: firstAttemptSlot = intSlot; michael@0: firstAttemptFlags = sensitiveFlags | temporarySessionFlags; michael@0: michael@0: // We always need the extractable flag on the first attempt, michael@0: // because we want to move the key to another slot - ### is this correct? michael@0: firstAttemptFlags |= extractableFlags; michael@0: } michael@0: michael@0: bool mustMoveKey = false; michael@0: michael@0: if (NS_FAILED(rv) || !KeygenRunnable) { michael@0: /* execute key generation on this thread */ michael@0: rv = NS_OK; michael@0: michael@0: keyPairInfo->privKey = michael@0: PK11_GenerateKeyPairWithFlags(firstAttemptSlot, mechanism, michael@0: keyGenParams, &keyPairInfo->pubKey, michael@0: firstAttemptFlags, uiCxt); michael@0: michael@0: if (keyPairInfo->privKey) { michael@0: // success on first attempt michael@0: if (secondAttemptSlot) { michael@0: mustMoveKey = true; michael@0: } michael@0: } michael@0: else { michael@0: keyPairInfo->privKey = michael@0: PK11_GenerateKeyPairWithFlags(secondAttemptSlot, mechanism, michael@0: keyGenParams, &keyPairInfo->pubKey, michael@0: secondAttemptFlags, uiCxt); michael@0: } michael@0: michael@0: } else { michael@0: /* execute key generation on separate thread */ michael@0: KeygenRunnable->SetParams( firstAttemptSlot, firstAttemptFlags, michael@0: secondAttemptSlot, secondAttemptFlags, michael@0: mechanism, keyGenParams, uiCxt ); michael@0: michael@0: runnable = do_QueryInterface(KeygenRunnable); michael@0: michael@0: if (runnable) { michael@0: { michael@0: nsPSMUITracker tracker; michael@0: if (tracker.isUIForbidden()) { michael@0: rv = NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: else { michael@0: rv = dialogs->DisplayGeneratingKeypairInfo(uiCxt, runnable); michael@0: // We call join on the thread, michael@0: // so we can be sure that no simultaneous access to the passed parameters will happen. michael@0: KeygenRunnable->Join(); michael@0: } michael@0: } michael@0: michael@0: NS_RELEASE(dialogs); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: PK11SlotInfo *used_slot = nullptr; michael@0: rv = KeygenRunnable->ConsumeResult(&used_slot, michael@0: &keyPairInfo->privKey, &keyPairInfo->pubKey); michael@0: michael@0: if (NS_SUCCEEDED(rv)) { michael@0: if ((used_slot == firstAttemptSlot) && secondAttemptSlot) { michael@0: mustMoveKey = true; michael@0: } michael@0: michael@0: if (used_slot) { michael@0: PK11_FreeSlot(used_slot); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: firstAttemptSlot = nullptr; michael@0: secondAttemptSlot = nullptr; michael@0: michael@0: nsFreeKeyGenParams(mechanism, keyGenParams); michael@0: michael@0: if (KeygenRunnable) { michael@0: NS_RELEASE(KeygenRunnable); michael@0: } michael@0: michael@0: if (!keyPairInfo->privKey || !keyPairInfo->pubKey) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: //If we generated the key pair on the internal slot because the michael@0: // keys were going to be escrowed, move the keys over right now. michael@0: if (mustMoveKey) { michael@0: ScopedSECKEYPrivateKey newPrivKey(PK11_LoadPrivKey(slot, michael@0: keyPairInfo->privKey, michael@0: keyPairInfo->pubKey, michael@0: true, true)); michael@0: if (!newPrivKey) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: // The private key is stored on the selected slot now, and the copy we michael@0: // ultimately use for escrowing when the time comes lives michael@0: // in the internal slot. We will delete it from that slot michael@0: // after the requests are made. michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* michael@0: * FUNCTION: cryptojs_ReadArgsAndGenerateKey michael@0: * ------------------------------------- michael@0: * INPUTS: michael@0: * cx michael@0: * The JSContext associated with the execution of the corresponging michael@0: * crypto.generateCRMFRequest call michael@0: * argv michael@0: * A pointer to an array of JavaScript parameters passed to the michael@0: * method crypto.generateCRMFRequest. The array should have the michael@0: * 3 arguments keySize, "keyParams", and "keyGenAlg" mentioned in michael@0: * the definition of crypto.generateCRMFRequest at the following michael@0: * document http://docs.iplanet.com/docs/manuals/psm/11/cmcjavascriptapi.html michael@0: * keyGenType michael@0: * A structure used to store the information about the newly created michael@0: * key pair. michael@0: * uiCxt michael@0: * An interface requestor that would be used to get an nsIPrompt michael@0: * if we need to ask the user for a password. michael@0: * slotToUse michael@0: * The PKCS11 slot to use for generating the key pair. If nullptr, then michael@0: * this function should select a slot that can do the key generation michael@0: * from the keytype associted with the keyPairInfo, and pass it back to michael@0: * the caller so that subsequence key generations can use the same slot. michael@0: * willEscrow michael@0: * If true, then that means we will try to escrow the generated michael@0: * private key when building the CRMF request. If false, then michael@0: * we will not try to escrow the private key. michael@0: * michael@0: * NOTES: michael@0: * This function takes care of reading a set of 3 parameters that define michael@0: * one key generation. The argv pointer should be one that originates michael@0: * from the argv parameter passed in to the method nsCrypto::GenerateCRMFRequest. michael@0: * The function interprets the argument in the first index as an integer and michael@0: * passes that as the key size for the key generation-this parameter is michael@0: * mandatory. The second parameter is read in as a string. This value can michael@0: * be null in JavaScript world and everything will still work. The third michael@0: * parameter is a mandatory string that indicates what kind of key to generate. michael@0: * There should always be 1-to-1 correspondence between the strings compared michael@0: * in the function cryptojs_interpret_key_gen_type and the strings listed in michael@0: * document at http://docs.iplanet.com/docs/manuals/psm/11/cmcjavascriptapi.html michael@0: * under the definition of the method generateCRMFRequest, for the parameter michael@0: * "keyGenAlgN". After reading the parameters, the function then michael@0: * generates the key pairs passing the parameters parsed from the JavaScript i michael@0: * routine. michael@0: * michael@0: * RETURN: michael@0: * NS_OK if creating the Key was successful. Any other return value michael@0: * indicates an error. michael@0: */ michael@0: michael@0: static nsresult michael@0: cryptojs_ReadArgsAndGenerateKey(JSContext *cx, michael@0: JS::Value *argv, michael@0: nsKeyPairInfo *keyGenType, michael@0: nsIInterfaceRequestor *uiCxt, michael@0: PK11SlotInfo **slot, bool willEscrow) michael@0: { michael@0: JSString *jsString; michael@0: JSAutoByteString params; michael@0: int keySize; michael@0: nsresult rv; michael@0: michael@0: if (!JSVAL_IS_INT(argv[0])) { michael@0: JS_ReportError(cx, "%s%s", JS_ERROR, michael@0: "passed in non-integer for key size"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: keySize = JSVAL_TO_INT(argv[0]); michael@0: if (!JSVAL_IS_NULL(argv[1])) { michael@0: JS::Rooted v(cx, argv[1]); michael@0: jsString = JS::ToString(cx, v); michael@0: NS_ENSURE_TRUE(jsString, NS_ERROR_OUT_OF_MEMORY); michael@0: argv[1] = STRING_TO_JSVAL(jsString); michael@0: params.encodeLatin1(cx, jsString); michael@0: NS_ENSURE_TRUE(!!params, NS_ERROR_OUT_OF_MEMORY); michael@0: } michael@0: michael@0: if (JSVAL_IS_NULL(argv[2])) { michael@0: JS_ReportError(cx,"%s%s", JS_ERROR, michael@0: "key generation type not specified"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: JS::Rooted v(cx, argv[2]); michael@0: jsString = JS::ToString(cx, v); michael@0: NS_ENSURE_TRUE(jsString, NS_ERROR_OUT_OF_MEMORY); michael@0: argv[2] = STRING_TO_JSVAL(jsString); michael@0: nsDependentJSString dependentKeyGenAlg; michael@0: NS_ENSURE_TRUE(dependentKeyGenAlg.init(cx, jsString), NS_ERROR_UNEXPECTED); michael@0: nsAutoString keyGenAlg(dependentKeyGenAlg); michael@0: keyGenAlg.Trim("\r\n\t "); michael@0: keyGenType->keyGenType = cryptojs_interpret_key_gen_type(keyGenAlg); michael@0: if (keyGenType->keyGenType == invalidKeyGen) { michael@0: NS_LossyConvertUTF16toASCII keyGenAlgNarrow(dependentKeyGenAlg); michael@0: JS_ReportError(cx, "%s%s%s", JS_ERROR, michael@0: "invalid key generation argument:", michael@0: keyGenAlgNarrow.get()); michael@0: goto loser; michael@0: } michael@0: if (!*slot) { michael@0: *slot = nsGetSlotForKeyGen(keyGenType->keyGenType, uiCxt); michael@0: if (!*slot) michael@0: goto loser; michael@0: } michael@0: michael@0: rv = cryptojs_generateOneKeyPair(cx,keyGenType,keySize,params.ptr(),uiCxt, michael@0: *slot,willEscrow); michael@0: michael@0: if (rv != NS_OK) { michael@0: NS_LossyConvertUTF16toASCII keyGenAlgNarrow(dependentKeyGenAlg); michael@0: JS_ReportError(cx,"%s%s%s", JS_ERROR, michael@0: "could not generate the key for algorithm ", michael@0: keyGenAlgNarrow.get()); michael@0: goto loser; michael@0: } michael@0: return NS_OK; michael@0: loser: michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: //Utility funciton to free up the memory used by nsKeyPairInfo michael@0: //arrays. michael@0: static void michael@0: nsFreeKeyPairInfo(nsKeyPairInfo *keyids, int numIDs) michael@0: { michael@0: NS_ASSERTION(keyids, "NULL pointer passed to nsFreeKeyPairInfo"); michael@0: if (!keyids) michael@0: return; michael@0: int i; michael@0: for (i=0; iGetCert()); michael@0: if (!cert) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: CRMFEncryptedKey *encrKey = michael@0: CRMF_CreateEncryptedKeyWithEncryptedValue(keyInfo->privKey, cert.get()); michael@0: if (!encrKey) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: CRMFPKIArchiveOptions *archOpt = michael@0: CRMF_CreatePKIArchiveOptions(crmfEncryptedPrivateKey, encrKey); michael@0: if (!archOpt) { michael@0: CRMF_DestroyEncryptedKey(encrKey); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: SECStatus srv = CRMF_CertRequestSetPKIArchiveOptions(certReq, archOpt); michael@0: CRMF_DestroyEncryptedKey(encrKey); michael@0: CRMF_DestroyPKIArchiveOptions(archOpt); michael@0: if (srv != SECSuccess) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: //Set the Distinguished Name (Subject Name) for the cert michael@0: //being requested. michael@0: static nsresult michael@0: nsSetDNForRequest(CRMFCertRequest *certReq, char *reqDN) michael@0: { michael@0: if (!reqDN || CRMF_CertRequestIsFieldPresent(certReq, crmfSubject)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: ScopedCERTName subjectName(CERT_AsciiToName(reqDN)); michael@0: if (!subjectName) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: SECStatus srv = CRMF_CertRequestSetTemplateField(certReq, crmfSubject, michael@0: static_cast michael@0: (subjectName)); michael@0: return (srv == SECSuccess) ? NS_OK : NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: //Set Registration Token Control on the request. michael@0: static nsresult michael@0: nsSetRegToken(CRMFCertRequest *certReq, char *regToken) michael@0: { michael@0: // this should never happen, but might as well add this. michael@0: NS_ASSERTION(certReq, "A bogus certReq passed to nsSetRegToken"); michael@0: if (regToken){ michael@0: if (CRMF_CertRequestIsControlPresent(certReq, crmfRegTokenControl)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: SECItem src; michael@0: src.data = (unsigned char*)regToken; michael@0: src.len = strlen(regToken); michael@0: SECItem *derEncoded = SEC_ASN1EncodeItem(nullptr, nullptr, &src, michael@0: SEC_ASN1_GET(SEC_UTF8StringTemplate)); michael@0: michael@0: if (!derEncoded) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: SECStatus srv = CRMF_CertRequestSetRegTokenControl(certReq, derEncoded); michael@0: SECITEM_FreeItem(derEncoded,true); michael@0: if (srv != SECSuccess) michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: //Set the Authenticator control on the cert reuest. It's just michael@0: //a string that gets passed along. michael@0: static nsresult michael@0: nsSetAuthenticator(CRMFCertRequest *certReq, char *authenticator) michael@0: { michael@0: //This should never happen, but might as well check. michael@0: NS_ASSERTION(certReq, "Bogus certReq passed to nsSetAuthenticator"); michael@0: if (authenticator) { michael@0: if (CRMF_CertRequestIsControlPresent(certReq, crmfAuthenticatorControl)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: SECItem src; michael@0: src.data = (unsigned char*)authenticator; michael@0: src.len = strlen(authenticator); michael@0: SECItem *derEncoded = SEC_ASN1EncodeItem(nullptr, nullptr, &src, michael@0: SEC_ASN1_GET(SEC_UTF8StringTemplate)); michael@0: if (!derEncoded) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: SECStatus srv = CRMF_CertRequestSetAuthenticatorControl(certReq, michael@0: derEncoded); michael@0: SECITEM_FreeItem(derEncoded, true); michael@0: if (srv != SECSuccess) michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: // ASN1 DER encoding rules say that when encoding a BIT string, michael@0: // the length in the header for the bit string is the number michael@0: // of "useful" bits in the BIT STRING. So the function finds michael@0: // it and sets accordingly for the returned item. michael@0: static void michael@0: nsPrepareBitStringForEncoding (SECItem *bitsmap, SECItem *value) michael@0: { michael@0: unsigned char onebyte; michael@0: unsigned int i, len = 0; michael@0: michael@0: /* to prevent warning on some platform at compile time */ michael@0: onebyte = '\0'; michael@0: /* Get the position of the right-most turn-on bit */ michael@0: for (i = 0; i < (value->len ) * 8; ++i) { michael@0: if (i % 8 == 0) michael@0: onebyte = value->data[i/8]; michael@0: if (onebyte & 0x80) michael@0: len = i; michael@0: onebyte <<= 1; michael@0: } michael@0: michael@0: bitsmap->data = value->data; michael@0: /* Add one here since we work with base 1 */ michael@0: bitsmap->len = len + 1; michael@0: } michael@0: michael@0: //This next section defines all the functions that sets the michael@0: //keyUsageExtension for all the different types of key gens michael@0: //we handle. The keyUsageExtension is just a bit flag extension michael@0: //that we set in wrapper functions that call straight into michael@0: //nsSetKeyUsageExtension. There is one wrapper funciton for each michael@0: //keyGenType. The correct function will eventually be called michael@0: //by going through a switch statement based on the nsKeyGenType michael@0: //in the nsKeyPairInfo struct. michael@0: static nsresult michael@0: nsSetKeyUsageExtension(CRMFCertRequest *crmfReq, michael@0: unsigned char keyUsage) michael@0: { michael@0: SECItem *encodedExt= nullptr; michael@0: SECItem keyUsageValue = { (SECItemType) 0, nullptr, 0 }; michael@0: SECItem bitsmap = { (SECItemType) 0, nullptr, 0 }; michael@0: SECStatus srv; michael@0: CRMFCertExtension *ext = nullptr; michael@0: CRMFCertExtCreationInfo extAddParams; michael@0: SEC_ASN1Template bitStrTemplate = {SEC_ASN1_BIT_STRING, 0, nullptr, michael@0: sizeof(SECItem)}; michael@0: michael@0: keyUsageValue.data = &keyUsage; michael@0: keyUsageValue.len = 1; michael@0: nsPrepareBitStringForEncoding(&bitsmap, &keyUsageValue); michael@0: michael@0: encodedExt = SEC_ASN1EncodeItem(nullptr, nullptr, &bitsmap,&bitStrTemplate); michael@0: if (!encodedExt) { michael@0: goto loser; michael@0: } michael@0: ext = CRMF_CreateCertExtension(SEC_OID_X509_KEY_USAGE, true, encodedExt); michael@0: if (!ext) { michael@0: goto loser; michael@0: } michael@0: extAddParams.numExtensions = 1; michael@0: extAddParams.extensions = &ext; michael@0: srv = CRMF_CertRequestSetTemplateField(crmfReq, crmfExtension, michael@0: &extAddParams); michael@0: if (srv != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: CRMF_DestroyCertExtension(ext); michael@0: SECITEM_FreeItem(encodedExt, true); michael@0: return NS_OK; michael@0: loser: michael@0: if (ext) { michael@0: CRMF_DestroyCertExtension(ext); michael@0: } michael@0: if (encodedExt) { michael@0: SECITEM_FreeItem(encodedExt, true); michael@0: } michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: static nsresult michael@0: nsSetRSADualUse(CRMFCertRequest *crmfReq) michael@0: { michael@0: unsigned char keyUsage = KU_DIGITAL_SIGNATURE michael@0: | KU_NON_REPUDIATION michael@0: | KU_KEY_ENCIPHERMENT; michael@0: michael@0: return nsSetKeyUsageExtension(crmfReq, keyUsage); michael@0: } michael@0: michael@0: static nsresult michael@0: nsSetRSAKeyEx(CRMFCertRequest *crmfReq) michael@0: { michael@0: unsigned char keyUsage = KU_KEY_ENCIPHERMENT; michael@0: michael@0: return nsSetKeyUsageExtension(crmfReq, keyUsage); michael@0: } michael@0: michael@0: static nsresult michael@0: nsSetRSASign(CRMFCertRequest *crmfReq) michael@0: { michael@0: unsigned char keyUsage = KU_DIGITAL_SIGNATURE; michael@0: michael@0: michael@0: return nsSetKeyUsageExtension(crmfReq, keyUsage); michael@0: } michael@0: michael@0: static nsresult michael@0: nsSetRSANonRepudiation(CRMFCertRequest *crmfReq) michael@0: { michael@0: unsigned char keyUsage = KU_NON_REPUDIATION; michael@0: michael@0: return nsSetKeyUsageExtension(crmfReq, keyUsage); michael@0: } michael@0: michael@0: static nsresult michael@0: nsSetRSASignNonRepudiation(CRMFCertRequest *crmfReq) michael@0: { michael@0: unsigned char keyUsage = KU_DIGITAL_SIGNATURE | michael@0: KU_NON_REPUDIATION; michael@0: michael@0: return nsSetKeyUsageExtension(crmfReq, keyUsage); michael@0: } michael@0: michael@0: static nsresult michael@0: nsSetECDualUse(CRMFCertRequest *crmfReq) michael@0: { michael@0: unsigned char keyUsage = KU_DIGITAL_SIGNATURE michael@0: | KU_NON_REPUDIATION michael@0: | KU_KEY_AGREEMENT; michael@0: michael@0: return nsSetKeyUsageExtension(crmfReq, keyUsage); michael@0: } michael@0: michael@0: static nsresult michael@0: nsSetECKeyEx(CRMFCertRequest *crmfReq) michael@0: { michael@0: unsigned char keyUsage = KU_KEY_AGREEMENT; michael@0: michael@0: return nsSetKeyUsageExtension(crmfReq, keyUsage); michael@0: } michael@0: michael@0: static nsresult michael@0: nsSetECSign(CRMFCertRequest *crmfReq) michael@0: { michael@0: unsigned char keyUsage = KU_DIGITAL_SIGNATURE; michael@0: michael@0: michael@0: return nsSetKeyUsageExtension(crmfReq, keyUsage); michael@0: } michael@0: michael@0: static nsresult michael@0: nsSetECNonRepudiation(CRMFCertRequest *crmfReq) michael@0: { michael@0: unsigned char keyUsage = KU_NON_REPUDIATION; michael@0: michael@0: return nsSetKeyUsageExtension(crmfReq, keyUsage); michael@0: } michael@0: michael@0: static nsresult michael@0: nsSetECSignNonRepudiation(CRMFCertRequest *crmfReq) michael@0: { michael@0: unsigned char keyUsage = KU_DIGITAL_SIGNATURE | michael@0: KU_NON_REPUDIATION; michael@0: michael@0: return nsSetKeyUsageExtension(crmfReq, keyUsage); michael@0: } michael@0: michael@0: static nsresult michael@0: nsSetDH(CRMFCertRequest *crmfReq) michael@0: { michael@0: unsigned char keyUsage = KU_KEY_AGREEMENT; michael@0: michael@0: return nsSetKeyUsageExtension(crmfReq, keyUsage); michael@0: } michael@0: michael@0: static nsresult michael@0: nsSetDSASign(CRMFCertRequest *crmfReq) michael@0: { michael@0: unsigned char keyUsage = KU_DIGITAL_SIGNATURE; michael@0: michael@0: return nsSetKeyUsageExtension(crmfReq, keyUsage); michael@0: } michael@0: michael@0: static nsresult michael@0: nsSetDSANonRepudiation(CRMFCertRequest *crmfReq) michael@0: { michael@0: unsigned char keyUsage = KU_NON_REPUDIATION; michael@0: michael@0: return nsSetKeyUsageExtension(crmfReq, keyUsage); michael@0: } michael@0: michael@0: static nsresult michael@0: nsSetDSASignNonRepudiation(CRMFCertRequest *crmfReq) michael@0: { michael@0: unsigned char keyUsage = KU_DIGITAL_SIGNATURE | michael@0: KU_NON_REPUDIATION; michael@0: michael@0: return nsSetKeyUsageExtension(crmfReq, keyUsage); michael@0: } michael@0: michael@0: static nsresult michael@0: nsSetKeyUsageExtension(CRMFCertRequest *crmfReq, nsKeyGenType keyGenType) michael@0: { michael@0: nsresult rv; michael@0: michael@0: switch (keyGenType) { michael@0: case rsaDualUse: michael@0: rv = nsSetRSADualUse(crmfReq); michael@0: break; michael@0: case rsaEnc: michael@0: rv = nsSetRSAKeyEx(crmfReq); michael@0: break; michael@0: case rsaSign: michael@0: rv = nsSetRSASign(crmfReq); michael@0: break; michael@0: case rsaNonrepudiation: michael@0: rv = nsSetRSANonRepudiation(crmfReq); michael@0: break; michael@0: case rsaSignNonrepudiation: michael@0: rv = nsSetRSASignNonRepudiation(crmfReq); michael@0: break; michael@0: case ecDualUse: michael@0: rv = nsSetECDualUse(crmfReq); michael@0: break; michael@0: case ecEnc: michael@0: rv = nsSetECKeyEx(crmfReq); michael@0: break; michael@0: case ecSign: michael@0: rv = nsSetECSign(crmfReq); michael@0: break; michael@0: case ecNonrepudiation: michael@0: rv = nsSetECNonRepudiation(crmfReq); michael@0: break; michael@0: case ecSignNonrepudiation: michael@0: rv = nsSetECSignNonRepudiation(crmfReq); michael@0: break; michael@0: case dhEx: michael@0: rv = nsSetDH(crmfReq); michael@0: break; michael@0: case dsaSign: michael@0: rv = nsSetDSASign(crmfReq); michael@0: break; michael@0: case dsaNonrepudiation: michael@0: rv = nsSetDSANonRepudiation(crmfReq); michael@0: break; michael@0: case dsaSignNonrepudiation: michael@0: rv = nsSetDSASignNonRepudiation(crmfReq); michael@0: break; michael@0: default: michael@0: rv = NS_ERROR_FAILURE; michael@0: break; michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: //Create a single CRMFCertRequest with all of the necessary parts michael@0: //already installed. The request returned by this function will michael@0: //have all the parts necessary and can just be added to a michael@0: //Certificate Request Message. michael@0: static CRMFCertRequest* michael@0: nsCreateSingleCertReq(nsKeyPairInfo *keyInfo, char *reqDN, char *regToken, michael@0: char *authenticator, nsNSSCertificate *wrappingCert) michael@0: { michael@0: uint32_t reqID; michael@0: nsresult rv; michael@0: michael@0: //The draft says the ID of the request should be a random michael@0: //number. We don't have a way of tracking this number michael@0: //to compare when the reply actually comes back,though. michael@0: PK11_GenerateRandom((unsigned char*)&reqID, sizeof(reqID)); michael@0: CRMFCertRequest *certReq = CRMF_CreateCertRequest(reqID); michael@0: if (!certReq) michael@0: return nullptr; michael@0: michael@0: long version = SEC_CERTIFICATE_VERSION_3; michael@0: SECStatus srv; michael@0: CERTSubjectPublicKeyInfo *spki = nullptr; michael@0: srv = CRMF_CertRequestSetTemplateField(certReq, crmfVersion, &version); michael@0: if (srv != SECSuccess) michael@0: goto loser; michael@0: michael@0: spki = SECKEY_CreateSubjectPublicKeyInfo(keyInfo->pubKey); michael@0: if (!spki) michael@0: goto loser; michael@0: michael@0: srv = CRMF_CertRequestSetTemplateField(certReq, crmfPublicKey, spki); michael@0: SECKEY_DestroySubjectPublicKeyInfo(spki); michael@0: if (srv != SECSuccess) michael@0: goto loser; michael@0: michael@0: if (wrappingCert && ns_can_escrow(keyInfo->keyGenType)) { michael@0: rv = nsSetEscrowAuthority(certReq, keyInfo, wrappingCert); michael@0: if (NS_FAILED(rv)) michael@0: goto loser; michael@0: } michael@0: rv = nsSetDNForRequest(certReq, reqDN); michael@0: if (NS_FAILED(rv)) michael@0: goto loser; michael@0: michael@0: rv = nsSetRegToken(certReq, regToken); michael@0: if (NS_FAILED(rv)) michael@0: goto loser; michael@0: michael@0: rv = nsSetAuthenticator(certReq, authenticator); michael@0: if (NS_FAILED(rv)) michael@0: goto loser; michael@0: michael@0: rv = nsSetKeyUsageExtension(certReq, keyInfo->keyGenType); michael@0: if (NS_FAILED(rv)) michael@0: goto loser; michael@0: michael@0: return certReq; michael@0: loser: michael@0: if (certReq) { michael@0: CRMF_DestroyCertRequest(certReq); michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: /* michael@0: * This function will set the Proof Of Possession (POP) for a request michael@0: * associated with a key pair intended to do Key Encipherment. Currently michael@0: * this means encryption only keys. michael@0: */ michael@0: static nsresult michael@0: nsSetKeyEnciphermentPOP(CRMFCertReqMsg *certReqMsg, bool isEscrowed) michael@0: { michael@0: SECItem bitString; michael@0: unsigned char der[2]; michael@0: SECStatus srv; michael@0: michael@0: if (isEscrowed) { michael@0: /* For proof of possession on escrowed keys, we use the michael@0: * this Message option of POPOPrivKey and include a zero michael@0: * length bit string in the POP field. This is OK because the encrypted michael@0: * private key already exists as part of the PKIArchiveOptions michael@0: * Control and that for all intents and purposes proves that michael@0: * we do own the private key. michael@0: */ michael@0: der[0] = 0x03; /*We've got a bit string */ michael@0: der[1] = 0x00; /*We've got a 0 length bit string */ michael@0: bitString.data = der; michael@0: bitString.len = 2; michael@0: srv = CRMF_CertReqMsgSetKeyEnciphermentPOP(certReqMsg, crmfThisMessage, michael@0: crmfNoSubseqMess, &bitString); michael@0: } else { michael@0: /* If the encryption key is not being escrowed, then we set the michael@0: * Proof Of Possession to be a Challenge Response mechanism. michael@0: */ michael@0: srv = CRMF_CertReqMsgSetKeyEnciphermentPOP(certReqMsg, michael@0: crmfSubsequentMessage, michael@0: crmfChallengeResp, nullptr); michael@0: } michael@0: return (srv == SECSuccess) ? NS_OK : NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: static void michael@0: nsCRMFEncoderItemCount(void *arg, const char *buf, unsigned long len); michael@0: michael@0: static void michael@0: nsCRMFEncoderItemStore(void *arg, const char *buf, unsigned long len); michael@0: michael@0: static nsresult michael@0: nsSet_EC_DHMAC_ProofOfPossession(CRMFCertReqMsg *certReqMsg, michael@0: nsKeyPairInfo *keyInfo, michael@0: CRMFCertRequest *certReq) michael@0: { michael@0: // RFC 2511 Appendix A section 2 a) defines, michael@0: // the "text" input for HMAC shall be the DER encoded version of michael@0: // of the single cert request. michael@0: // We'll produce that encoding and destroy it afterwards, michael@0: // because when sending the complete package to the CA, michael@0: // we'll use a different encoding, one that includes POP and michael@0: // allows multiple requests to be sent in one step. michael@0: michael@0: unsigned long der_request_len = 0; michael@0: ScopedSECItem der_request; michael@0: michael@0: if (SECSuccess != CRMF_EncodeCertRequest(certReq, michael@0: nsCRMFEncoderItemCount, michael@0: &der_request_len)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: der_request = SECITEM_AllocItem(nullptr, nullptr, der_request_len); michael@0: if (!der_request) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: // set len in returned SECItem back to zero, because it will michael@0: // be used as the destination offset inside the michael@0: // nsCRMFEncoderItemStore callback. michael@0: michael@0: der_request->len = 0; michael@0: michael@0: if (SECSuccess != CRMF_EncodeCertRequest(certReq, michael@0: nsCRMFEncoderItemStore, michael@0: der_request)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: // RFC 2511 Appendix A section 2 c): michael@0: // "A key K is derived from the shared secret Kec and the subject and michael@0: // issuer names in the CA's certificate as follows: michael@0: // K = SHA1(DER-encoded-subjectName | Kec | DER-encoded-issuerName)" michael@0: michael@0: ScopedPK11SymKey shared_secret; michael@0: ScopedPK11SymKey subject_and_secret; michael@0: ScopedPK11SymKey subject_and_secret_and_issuer; michael@0: ScopedPK11SymKey sha1_of_subject_and_secret_and_issuer; michael@0: michael@0: shared_secret = michael@0: PK11_PubDeriveWithKDF(keyInfo->privKey, // SECKEYPrivateKey *privKey michael@0: keyInfo->ecPopPubKey, // SECKEYPublicKey *pubKey michael@0: false, // bool isSender michael@0: nullptr, // SECItem *randomA michael@0: nullptr, // SECItem *randomB michael@0: CKM_ECDH1_DERIVE, // CK_MECHANISM_TYPE derive michael@0: CKM_CONCATENATE_DATA_AND_BASE, // CK_MECHANISM_TYPE target michael@0: CKA_DERIVE, // CK_ATTRIBUTE_TYPE operation michael@0: 0, // int keySize michael@0: CKD_NULL, // CK_ULONG kdf michael@0: nullptr, // SECItem *sharedData michael@0: nullptr); // void *wincx michael@0: michael@0: if (!shared_secret) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: CK_KEY_DERIVATION_STRING_DATA concat_data_base; michael@0: concat_data_base.pData = keyInfo->ecPopCert->derSubject.data; michael@0: concat_data_base.ulLen = keyInfo->ecPopCert->derSubject.len; michael@0: SECItem concat_data_base_item; michael@0: concat_data_base_item.data = (unsigned char*)&concat_data_base; michael@0: concat_data_base_item.len = sizeof(CK_KEY_DERIVATION_STRING_DATA); michael@0: michael@0: subject_and_secret = michael@0: PK11_Derive(shared_secret, // PK11SymKey *baseKey michael@0: CKM_CONCATENATE_DATA_AND_BASE, // CK_MECHANISM_TYPE mechanism michael@0: &concat_data_base_item, // SECItem *param michael@0: CKM_CONCATENATE_BASE_AND_DATA, // CK_MECHANISM_TYPE target michael@0: CKA_DERIVE, // CK_ATTRIBUTE_TYPE operation michael@0: 0); // int keySize michael@0: michael@0: if (!subject_and_secret) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: CK_KEY_DERIVATION_STRING_DATA concat_base_data; michael@0: concat_base_data.pData = keyInfo->ecPopCert->derSubject.data; michael@0: concat_base_data.ulLen = keyInfo->ecPopCert->derSubject.len; michael@0: SECItem concat_base_data_item; michael@0: concat_base_data_item.data = (unsigned char*)&concat_base_data; michael@0: concat_base_data_item.len = sizeof(CK_KEY_DERIVATION_STRING_DATA); michael@0: michael@0: subject_and_secret_and_issuer = michael@0: PK11_Derive(subject_and_secret, // PK11SymKey *baseKey michael@0: CKM_CONCATENATE_BASE_AND_DATA, // CK_MECHANISM_TYPE mechanism michael@0: &concat_base_data_item, // SECItem *param michael@0: CKM_SHA1_KEY_DERIVATION, // CK_MECHANISM_TYPE target michael@0: CKA_DERIVE, // CK_ATTRIBUTE_TYPE operation michael@0: 0); // int keySize michael@0: michael@0: if (!subject_and_secret_and_issuer) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: sha1_of_subject_and_secret_and_issuer = michael@0: PK11_Derive(subject_and_secret_and_issuer, // PK11SymKey *baseKey michael@0: CKM_SHA1_KEY_DERIVATION, // CK_MECHANISM_TYPE mechanism michael@0: nullptr, // SECItem *param michael@0: CKM_SHA_1_HMAC, // CK_MECHANISM_TYPE target michael@0: CKA_SIGN, // CK_ATTRIBUTE_TYPE operation michael@0: 0); // int keySize michael@0: michael@0: if (!sha1_of_subject_and_secret_and_issuer) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: PK11Context *context = nullptr; michael@0: PK11ContextCleanerTrueParam context_cleaner(context); michael@0: michael@0: SECItem ignore; michael@0: ignore.data = 0; michael@0: ignore.len = 0; michael@0: michael@0: context = michael@0: PK11_CreateContextBySymKey(CKM_SHA_1_HMAC, // CK_MECHANISM_TYPE type michael@0: CKA_SIGN, // CK_ATTRIBUTE_TYPE operation michael@0: sha1_of_subject_and_secret_and_issuer, // PK11SymKey *symKey michael@0: &ignore); // SECItem *param michael@0: michael@0: if (!context) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: if (SECSuccess != PK11_DigestBegin(context)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: if (SECSuccess != michael@0: PK11_DigestOp(context, der_request->data, der_request->len)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: ScopedAutoSECItem result_hmac_sha1_item(SHA1_LENGTH); michael@0: michael@0: if (SECSuccess != michael@0: PK11_DigestFinal(context, michael@0: result_hmac_sha1_item.data, michael@0: &result_hmac_sha1_item.len, michael@0: SHA1_LENGTH)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: if (SECSuccess != michael@0: CRMF_CertReqMsgSetKeyAgreementPOP(certReqMsg, crmfDHMAC, michael@0: crmfNoSubseqMess, &result_hmac_sha1_item)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: static nsresult michael@0: nsSetProofOfPossession(CRMFCertReqMsg *certReqMsg, michael@0: nsKeyPairInfo *keyInfo, michael@0: CRMFCertRequest *certReq) michael@0: { michael@0: // Depending on the type of cert request we'll try michael@0: // POP mechanisms in different order, michael@0: // and add the result to the cert request message. michael@0: // michael@0: // For any signing or dual use cert, michael@0: // try signing first, michael@0: // fall back to DHMAC if we can michael@0: // (EC cert requests that provide keygen param "popcert"), michael@0: // otherwise fail. michael@0: // michael@0: // For encryption only certs that get escrowed, this is sufficient. michael@0: // michael@0: // For encryption only certs, that are not being escrowed, michael@0: // try DHMAC if we can michael@0: // (EC cert requests that provide keygen param "popcert"), michael@0: // otherwise we'll indicate challenge response should be used. michael@0: michael@0: bool isEncryptionOnlyCertRequest = false; michael@0: bool escrowEncryptionOnlyCert = false; michael@0: michael@0: switch (keyInfo->keyGenType) michael@0: { michael@0: case rsaEnc: michael@0: case ecEnc: michael@0: isEncryptionOnlyCertRequest = true; michael@0: break; michael@0: michael@0: case rsaSign: michael@0: case rsaDualUse: michael@0: case rsaNonrepudiation: michael@0: case rsaSignNonrepudiation: michael@0: case ecSign: michael@0: case ecDualUse: michael@0: case ecNonrepudiation: michael@0: case ecSignNonrepudiation: michael@0: case dsaSign: michael@0: case dsaNonrepudiation: michael@0: case dsaSignNonrepudiation: michael@0: break; michael@0: michael@0: case dhEx: michael@0: /* This case may be supported in the future, but for now, we just fall michael@0: * though to the default case and return an error for diffie-hellman keys. michael@0: */ michael@0: default: michael@0: return NS_ERROR_FAILURE; michael@0: }; michael@0: michael@0: if (isEncryptionOnlyCertRequest) michael@0: { michael@0: escrowEncryptionOnlyCert = michael@0: CRMF_CertRequestIsControlPresent(certReq,crmfPKIArchiveOptionsControl); michael@0: } michael@0: michael@0: bool gotDHMACParameters = false; michael@0: michael@0: if (isECKeyGenType(keyInfo->keyGenType) && michael@0: keyInfo->ecPopCert && michael@0: keyInfo->ecPopPubKey) michael@0: { michael@0: gotDHMACParameters = true; michael@0: } michael@0: michael@0: if (isEncryptionOnlyCertRequest) michael@0: { michael@0: if (escrowEncryptionOnlyCert) michael@0: return nsSetKeyEnciphermentPOP(certReqMsg, true); // escrowed michael@0: michael@0: if (gotDHMACParameters) michael@0: return nsSet_EC_DHMAC_ProofOfPossession(certReqMsg, keyInfo, certReq); michael@0: michael@0: return nsSetKeyEnciphermentPOP(certReqMsg, false); // not escrowed michael@0: } michael@0: michael@0: // !isEncryptionOnlyCertRequest michael@0: michael@0: SECStatus srv = CRMF_CertReqMsgSetSignaturePOP(certReqMsg, michael@0: keyInfo->privKey, michael@0: keyInfo->pubKey, nullptr, michael@0: nullptr, nullptr); michael@0: michael@0: if (srv == SECSuccess) michael@0: return NS_OK; michael@0: michael@0: if (!gotDHMACParameters) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: return nsSet_EC_DHMAC_ProofOfPossession(certReqMsg, keyInfo, certReq); michael@0: } michael@0: michael@0: static void michael@0: nsCRMFEncoderItemCount(void *arg, const char *buf, unsigned long len) michael@0: { michael@0: unsigned long *count = (unsigned long *)arg; michael@0: *count += len; michael@0: } michael@0: michael@0: static void michael@0: nsCRMFEncoderItemStore(void *arg, const char *buf, unsigned long len) michael@0: { michael@0: SECItem *dest = (SECItem *)arg; michael@0: memcpy(dest->data + dest->len, buf, len); michael@0: dest->len += len; michael@0: } michael@0: michael@0: static SECItem* michael@0: nsEncodeCertReqMessages(CRMFCertReqMsg **certReqMsgs) michael@0: { michael@0: unsigned long len = 0; michael@0: if (CRMF_EncodeCertReqMessages(certReqMsgs, nsCRMFEncoderItemCount, &len) michael@0: != SECSuccess) { michael@0: return nullptr; michael@0: } michael@0: SECItem *dest = (SECItem *)PORT_Alloc(sizeof(SECItem)); michael@0: if (!dest) { michael@0: return nullptr; michael@0: } michael@0: dest->type = siBuffer; michael@0: dest->data = (unsigned char *)PORT_Alloc(len); michael@0: if (!dest->data) { michael@0: PORT_Free(dest); michael@0: return nullptr; michael@0: } michael@0: dest->len = 0; michael@0: michael@0: if (CRMF_EncodeCertReqMessages(certReqMsgs, nsCRMFEncoderItemStore, dest) michael@0: != SECSuccess) { michael@0: SECITEM_FreeItem(dest, true); michael@0: return nullptr; michael@0: } michael@0: return dest; michael@0: } michael@0: michael@0: //Create a Base64 encoded CRMFCertReqMsg that can be sent to a CA michael@0: //requesting one or more certificates to be issued. This function michael@0: //creates a single cert request per key pair and then appends it to michael@0: //a message that is ultimately sent off to a CA. michael@0: static char* michael@0: nsCreateReqFromKeyPairs(nsKeyPairInfo *keyids, int32_t numRequests, michael@0: char *reqDN, char *regToken, char *authenticator, michael@0: nsNSSCertificate *wrappingCert) michael@0: { michael@0: // We'use the goto notation for clean-up purposes in this function michael@0: // that calls the C API of NSS. michael@0: int32_t i; michael@0: // The ASN1 encoder in NSS wants the last entry in the array to be michael@0: // nullptr so that it knows when the last element is. michael@0: CRMFCertReqMsg **certReqMsgs = new CRMFCertReqMsg*[numRequests+1]; michael@0: CRMFCertRequest *certReq; michael@0: if (!certReqMsgs) michael@0: return nullptr; michael@0: memset(certReqMsgs, 0, sizeof(CRMFCertReqMsg*)*(1+numRequests)); michael@0: SECStatus srv; michael@0: nsresult rv; michael@0: SECItem *encodedReq; michael@0: char *retString; michael@0: for (i=0; i(JS_GetContextPrivate(cx)); michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: //The top level method which is a member of nsIDOMCrypto michael@0: //for generate a base64 encoded CRMF request. michael@0: CRMFObject* michael@0: nsCrypto::GenerateCRMFRequest(JSContext* aContext, michael@0: const nsCString& aReqDN, michael@0: const nsCString& aRegToken, michael@0: const nsCString& aAuthenticator, michael@0: const nsCString& aEaCert, michael@0: const nsCString& aJsCallback, michael@0: const Sequence& aArgs, michael@0: ErrorResult& aRv) michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: nsresult nrv; michael@0: michael@0: uint32_t argc = aArgs.Length(); michael@0: michael@0: /* michael@0: * Get all of the parameters. michael@0: */ michael@0: if (argc % 3 != 0) { michael@0: aRv.ThrowNotEnoughArgsError(); michael@0: return nullptr; michael@0: } michael@0: michael@0: if (aReqDN.IsVoid()) { michael@0: NS_WARNING("no DN specified"); michael@0: aRv.Throw(NS_ERROR_FAILURE); michael@0: return nullptr; michael@0: } michael@0: michael@0: if (aJsCallback.IsVoid()) { michael@0: NS_WARNING("no completion function specified"); michael@0: aRv.Throw(NS_ERROR_FAILURE); michael@0: return nullptr; michael@0: } michael@0: michael@0: JS::RootedObject script_obj(aContext, GetWrapper()); michael@0: if (MOZ_UNLIKELY(!script_obj)) { michael@0: aRv.Throw(NS_ERROR_UNEXPECTED); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsCOMPtr csp; michael@0: if (!nsContentUtils::GetContentSecurityPolicy(aContext, getter_AddRefs(csp))) { michael@0: NS_ERROR("Error: failed to get CSP"); michael@0: aRv.Throw(NS_ERROR_FAILURE); michael@0: return nullptr; michael@0: } michael@0: michael@0: bool evalAllowed = true; michael@0: bool reportEvalViolations = false; michael@0: if (csp && NS_FAILED(csp->GetAllowsEval(&reportEvalViolations, &evalAllowed))) { michael@0: NS_WARNING("CSP: failed to get allowsEval"); michael@0: aRv.Throw(NS_ERROR_FAILURE); michael@0: return nullptr; michael@0: } michael@0: michael@0: if (reportEvalViolations) { michael@0: NS_NAMED_LITERAL_STRING(scriptSample, "window.crypto.generateCRMFRequest: call to eval() or related function blocked by CSP"); michael@0: michael@0: const char *fileName; michael@0: uint32_t lineNum; michael@0: nsJSUtils::GetCallingLocation(aContext, &fileName, &lineNum); michael@0: csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL, michael@0: NS_ConvertASCIItoUTF16(fileName), michael@0: scriptSample, michael@0: lineNum, michael@0: EmptyString(), michael@0: EmptyString()); michael@0: } michael@0: michael@0: if (!evalAllowed) { michael@0: NS_WARNING("eval() not allowed by Content Security Policy"); michael@0: aRv.Throw(NS_ERROR_FAILURE); michael@0: return nullptr; michael@0: } michael@0: michael@0: //Put up some UI warning that someone is trying to michael@0: //escrow the private key. michael@0: //Don't addref this copy. That way ths reference goes away michael@0: //at the same the nsIX09Cert ref goes away. michael@0: nsNSSCertificate *escrowCert = nullptr; michael@0: nsCOMPtr nssCert; michael@0: bool willEscrow = false; michael@0: if (!aEaCert.IsVoid()) { michael@0: SECItem certDer = {siBuffer, nullptr, 0}; michael@0: SECStatus srv = ATOB_ConvertAsciiToItem(&certDer, aEaCert.get()); michael@0: if (srv != SECSuccess) { michael@0: aRv.Throw(NS_ERROR_FAILURE); michael@0: return nullptr; michael@0: } michael@0: mozilla::pkix::ScopedCERTCertificate cert( michael@0: CERT_NewTempCertificate(CERT_GetDefaultCertDB(), michael@0: &certDer, nullptr, false, true)); michael@0: if (!cert) { michael@0: aRv.Throw(NS_ERROR_FAILURE); michael@0: return nullptr; michael@0: } michael@0: michael@0: escrowCert = nsNSSCertificate::Create(cert.get()); michael@0: nssCert = escrowCert; michael@0: if (!nssCert) { michael@0: aRv.Throw(NS_ERROR_OUT_OF_MEMORY); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsCOMPtr dialogs; michael@0: nsresult rv = getNSSDialogs(getter_AddRefs(dialogs), michael@0: NS_GET_IID(nsIDOMCryptoDialogs), michael@0: NS_DOMCRYPTODIALOGS_CONTRACTID); michael@0: if (NS_FAILED(rv)) { michael@0: aRv.Throw(rv); michael@0: return nullptr; michael@0: } michael@0: michael@0: bool okay=false; michael@0: { michael@0: nsPSMUITracker tracker; michael@0: if (tracker.isUIForbidden()) { michael@0: okay = false; michael@0: } michael@0: else { michael@0: dialogs->ConfirmKeyEscrow(nssCert, &okay); michael@0: } michael@0: } michael@0: if (!okay) { michael@0: aRv.Throw(NS_ERROR_FAILURE); michael@0: return nullptr; michael@0: } michael@0: willEscrow = true; michael@0: } michael@0: nsCOMPtr uiCxt = new PipUIContext; michael@0: int32_t numRequests = argc / 3; michael@0: nsKeyPairInfo *keyids = new nsKeyPairInfo[numRequests]; michael@0: memset(keyids, 0, sizeof(nsKeyPairInfo)*numRequests); michael@0: int keyInfoIndex; michael@0: uint32_t i; michael@0: PK11SlotInfo *slot = nullptr; michael@0: // Go through all of the arguments and generate the appropriate key pairs. michael@0: for (i=0,keyInfoIndex=0; i(&aArgs[i]), michael@0: &keyids[keyInfoIndex], michael@0: uiCxt, &slot, willEscrow); michael@0: michael@0: if (NS_FAILED(nrv)) { michael@0: if (slot) michael@0: PK11_FreeSlot(slot); michael@0: nsFreeKeyPairInfo(keyids,numRequests); michael@0: aRv.Throw(nrv); michael@0: return nullptr; michael@0: } michael@0: } michael@0: // By this time we'd better have a slot for the key gen. michael@0: NS_ASSERTION(slot, "There was no slot selected for key generation"); michael@0: if (slot) michael@0: PK11_FreeSlot(slot); michael@0: michael@0: char *encodedRequest = nsCreateReqFromKeyPairs(keyids,numRequests, michael@0: const_cast(aReqDN.get()), michael@0: const_cast(aRegToken.get()), michael@0: const_cast(aAuthenticator.get()), michael@0: escrowCert); michael@0: #ifdef DEBUG_javi michael@0: printf ("Created the folloing CRMF request:\n%s\n", encodedRequest); michael@0: #endif michael@0: if (!encodedRequest) { michael@0: nsFreeKeyPairInfo(keyids, numRequests); michael@0: aRv.Throw(NS_ERROR_FAILURE); michael@0: return nullptr; michael@0: } michael@0: CRMFObject* newObject = new CRMFObject(); michael@0: newObject->SetCRMFRequest(encodedRequest); michael@0: PORT_Free(encodedRequest); michael@0: nsFreeKeyPairInfo(keyids, numRequests); michael@0: michael@0: // Post an event on the UI queue so that the JS gets called after michael@0: // we return control to the JS layer. Why do we have to this? michael@0: // Because when this API was implemented for PSM 1.x w/ Communicator, michael@0: // the only way to make this method work was to have a callback michael@0: // in the JS layer that got called after key generation had happened. michael@0: // So for backwards compatibility, we return control and then just post michael@0: // an event to call the JS the script provides as the code to execute michael@0: // when the request has been generated. michael@0: // michael@0: michael@0: nsCOMPtr secMan = michael@0: do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID); michael@0: if (MOZ_UNLIKELY(!secMan)) { michael@0: aRv.Throw(NS_ERROR_UNEXPECTED); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsCOMPtr principals; michael@0: nsresult rv = secMan->GetSubjectPrincipal(getter_AddRefs(principals)); michael@0: if (NS_FAILED(nrv)) { michael@0: aRv.Throw(nrv); michael@0: return nullptr; michael@0: } michael@0: if (MOZ_UNLIKELY(!principals)) { michael@0: aRv.Throw(NS_ERROR_UNEXPECTED); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsCryptoRunArgs *args = new nsCryptoRunArgs(aContext); michael@0: michael@0: args->m_kungFuDeathGrip = GetISupportsFromContext(aContext); michael@0: args->m_scope = JS_GetParent(script_obj); michael@0: if (!aJsCallback.IsVoid()) { michael@0: args->m_jsCallback = aJsCallback; michael@0: } michael@0: args->m_principals = principals; michael@0: michael@0: nsCryptoRunnable *cryptoRunnable = new nsCryptoRunnable(args); michael@0: michael@0: rv = NS_DispatchToMainThread(cryptoRunnable); michael@0: if (NS_FAILED(rv)) { michael@0: aRv.Throw(rv); michael@0: delete cryptoRunnable; michael@0: } michael@0: michael@0: return newObject; michael@0: } michael@0: michael@0: // Reminder that we inherit the memory passed into us here. michael@0: // An implementation to let us back up certs as an event. michael@0: nsP12Runnable::nsP12Runnable(nsIX509Cert **certArr, int32_t numCerts, michael@0: nsIPK11Token *token) michael@0: { michael@0: mCertArr = certArr; michael@0: mNumCerts = numCerts; michael@0: mToken = token; michael@0: } michael@0: michael@0: nsP12Runnable::~nsP12Runnable() michael@0: { michael@0: int32_t i; michael@0: for (i=0; i nssComponent(do_GetService(kNSSComponentCID, &rv)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: //Build up the message that let's the user know we're trying to michael@0: //make PKCS12 backups of the new certs. michael@0: nssComponent->GetPIPNSSBundleString("ForcedBackup1", final); michael@0: final.Append(MOZ_UTF16("\n\n")); michael@0: nssComponent->GetPIPNSSBundleString("ForcedBackup2", temp); michael@0: final.Append(temp.get()); michael@0: final.Append(MOZ_UTF16("\n\n")); michael@0: michael@0: nssComponent->GetPIPNSSBundleString("ForcedBackup3", temp); michael@0: michael@0: final.Append(temp.get()); michael@0: nsNSSComponent::ShowAlertWithConstructedString(final); michael@0: michael@0: nsCOMPtr filePicker = michael@0: do_CreateInstance("@mozilla.org/filepicker;1", &rv); michael@0: if (!filePicker) { michael@0: NS_ERROR("Could not create a file picker when backing up certs."); michael@0: return rv; michael@0: } michael@0: michael@0: nsCOMPtr wwatch = michael@0: (do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr window; michael@0: wwatch->GetActiveWindow(getter_AddRefs(window)); michael@0: michael@0: nsString filePickMessage; michael@0: nssComponent->GetPIPNSSBundleString("chooseP12BackupFileDialog", michael@0: filePickMessage); michael@0: rv = filePicker->Init(window, filePickMessage, nsIFilePicker::modeSave); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: filePicker->AppendFilter(NS_LITERAL_STRING("PKCS12"), michael@0: NS_LITERAL_STRING("*.p12")); michael@0: filePicker->AppendFilters(nsIFilePicker::filterAll); michael@0: michael@0: int16_t dialogReturn; michael@0: filePicker->Show(&dialogReturn); michael@0: if (dialogReturn == nsIFilePicker::returnCancel) michael@0: return NS_OK; //User canceled. It'd be nice if they couldn't, michael@0: //but oh well. michael@0: michael@0: nsCOMPtr localFile; michael@0: rv = filePicker->GetFile(getter_AddRefs(localFile)); michael@0: if (NS_FAILED(rv)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsPKCS12Blob p12Cxt; michael@0: michael@0: p12Cxt.SetToken(mToken); michael@0: p12Cxt.ExportToFile(localFile, mCertArr, mNumCerts); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsCryptoRunArgs::nsCryptoRunArgs(JSContext *cx) : m_cx(cx), m_scope(cx) {} michael@0: michael@0: nsCryptoRunArgs::~nsCryptoRunArgs() {} michael@0: michael@0: nsCryptoRunnable::nsCryptoRunnable(nsCryptoRunArgs *args) michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: NS_ASSERTION(args,"Passed nullptr to nsCryptoRunnable constructor."); michael@0: m_args = args; michael@0: NS_IF_ADDREF(m_args); michael@0: } michael@0: michael@0: nsCryptoRunnable::~nsCryptoRunnable() michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: NS_IF_RELEASE(m_args); michael@0: } michael@0: michael@0: //Implementation that runs the callback passed to michael@0: //crypto.generateCRMFRequest as an event. michael@0: NS_IMETHODIMP michael@0: nsCryptoRunnable::Run() michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: AutoPushJSContext cx(m_args->m_cx); michael@0: JSAutoRequest ar(cx); michael@0: JS::Rooted scope(cx, m_args->m_scope); michael@0: JSAutoCompartment ac(cx, scope); michael@0: michael@0: bool ok = michael@0: JS_EvaluateScript(cx, scope, m_args->m_jsCallback, michael@0: strlen(m_args->m_jsCallback), nullptr, 0); michael@0: return ok ? NS_OK : NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: //Quick helper function to check if a newly issued cert michael@0: //already exists in the user's database. michael@0: static bool michael@0: nsCertAlreadyExists(SECItem *derCert) michael@0: { michael@0: CERTCertDBHandle *handle = CERT_GetDefaultCertDB(); michael@0: bool retVal = false; michael@0: michael@0: mozilla::pkix::ScopedCERTCertificate cert( michael@0: CERT_FindCertByDERCert(handle, derCert)); michael@0: if (cert) { michael@0: if (cert->isperm && !cert->nickname && !cert->emailAddr) { michael@0: //If the cert doesn't have a nickname or email addr, it is michael@0: //bogus cruft, so delete it. michael@0: SEC_DeletePermCertificate(cert.get()); michael@0: } else if (cert->isperm) { michael@0: retVal = true; michael@0: } michael@0: } michael@0: return retVal; michael@0: } michael@0: michael@0: static int32_t michael@0: nsCertListCount(CERTCertList *certList) michael@0: { michael@0: int32_t numCerts = 0; michael@0: CERTCertListNode *node; michael@0: michael@0: node = CERT_LIST_HEAD(certList); michael@0: while (!CERT_LIST_END(node, certList)) { michael@0: numCerts++; michael@0: node = CERT_LIST_NEXT(node); michael@0: } michael@0: return numCerts; michael@0: } michael@0: michael@0: //Import user certificates that arrive as a CMMF base64 encoded michael@0: //string. michael@0: void michael@0: nsCrypto::ImportUserCertificates(const nsAString& aNickname, michael@0: const nsAString& aCmmfResponse, michael@0: bool aDoForcedBackup, michael@0: nsAString& aReturn, michael@0: ErrorResult& aRv) michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: char *nickname=nullptr, *cmmfResponse=nullptr; michael@0: CMMFCertRepContent *certRepContent = nullptr; michael@0: int numResponses = 0; michael@0: nsIX509Cert **certArr = nullptr; michael@0: int i; michael@0: CMMFCertResponse *currResponse; michael@0: CMMFPKIStatus reqStatus; michael@0: CERTCertificate *currCert; michael@0: PK11SlotInfo *slot; michael@0: nsAutoCString localNick; michael@0: nsCOMPtr ctx = new PipUIContext(); michael@0: nsresult rv = NS_OK; michael@0: nsCOMPtr token; michael@0: michael@0: nickname = ToNewCString(aNickname); michael@0: cmmfResponse = ToNewCString(aCmmfResponse); michael@0: if (nsCRT::strcmp("null", nickname) == 0) { michael@0: nsMemory::Free(nickname); michael@0: nickname = nullptr; michael@0: } michael@0: michael@0: SECItem cmmfDer = {siBuffer, nullptr, 0}; michael@0: SECStatus srv = ATOB_ConvertAsciiToItem(&cmmfDer, cmmfResponse); michael@0: michael@0: if (srv != SECSuccess) { michael@0: rv = NS_ERROR_FAILURE; michael@0: goto loser; michael@0: } michael@0: michael@0: certRepContent = CMMF_CreateCertRepContentFromDER(CERT_GetDefaultCertDB(), michael@0: (const char*)cmmfDer.data, michael@0: cmmfDer.len); michael@0: if (!certRepContent) { michael@0: rv = NS_ERROR_FAILURE; michael@0: goto loser; michael@0: } michael@0: michael@0: numResponses = CMMF_CertRepContentGetNumResponses(certRepContent); michael@0: michael@0: if (aDoForcedBackup) { michael@0: //We've been asked to force the user to back up these michael@0: //certificates. Let's keep an array of them around which michael@0: //we pass along to the nsP12Runnable to use. michael@0: certArr = new nsIX509Cert*[numResponses]; michael@0: // If this is nullptr, chances are we're gonna fail really michael@0: // soon, but let's try to keep going just in case. michael@0: if (!certArr) michael@0: aDoForcedBackup = false; michael@0: michael@0: memset(certArr, 0, sizeof(nsIX509Cert*)*numResponses); michael@0: } michael@0: for (i=0; iderCert)) { michael@0: if (aDoForcedBackup) { michael@0: certArr[i] = nsNSSCertificate::Create(currCert); michael@0: if (!certArr[i]) michael@0: goto loser; michael@0: NS_ADDREF(certArr[i]); michael@0: } michael@0: CERT_DestroyCertificate(currCert); michael@0: CMMF_DestroyCertResponse(currResponse); michael@0: continue; michael@0: } michael@0: // Let's figure out which nickname to give the cert. If michael@0: // a certificate with the same subject name already exists, michael@0: // then just use that one, otherwise, get the default nickname. michael@0: if (currCert->nickname) { michael@0: localNick = currCert->nickname; michael@0: } michael@0: else if (!nickname || nickname[0] == '\0') { michael@0: nsNSSCertificateDB::get_default_nickname(currCert, ctx, localNick, locker); michael@0: } else { michael@0: //This is the case where we're getting a brand new michael@0: //cert that doesn't have the same subjectName as a cert michael@0: //that already exists in our db and the CA page has michael@0: //designated a nickname to use for the newly issued cert. michael@0: localNick = nickname; michael@0: } michael@0: { michael@0: char *cast_const_away = const_cast(localNick.get()); michael@0: slot = PK11_ImportCertForKey(currCert, cast_const_away, ctx); michael@0: } michael@0: if (!slot) { michael@0: rv = NS_ERROR_FAILURE; michael@0: goto loser; michael@0: } michael@0: if (aDoForcedBackup) { michael@0: certArr[i] = nsNSSCertificate::Create(currCert); michael@0: if (!certArr[i]) michael@0: goto loser; michael@0: NS_ADDREF(certArr[i]); michael@0: } michael@0: CERT_DestroyCertificate(currCert); michael@0: michael@0: if (!token) michael@0: token = new nsPK11Token(slot); michael@0: michael@0: PK11_FreeSlot(slot); michael@0: CMMF_DestroyCertResponse(currResponse); michael@0: } michael@0: //Let the loser: label take care of freeing up our reference to michael@0: //nickname (This way we don't free it twice and avoid crashing. michael@0: //That would be a good thing. michael@0: michael@0: //Import the root chain into the cert db. michael@0: { michael@0: mozilla::pkix::ScopedCERTCertList michael@0: caPubs(CMMF_CertRepContentGetCAPubs(certRepContent)); michael@0: if (caPubs) { michael@0: int32_t numCAs = nsCertListCount(caPubs.get()); michael@0: michael@0: NS_ASSERTION(numCAs > 0, "Invalid number of CA's"); michael@0: if (numCAs > 0) { michael@0: CERTCertListNode *node; michael@0: SECItem *derCerts; michael@0: michael@0: derCerts = static_cast michael@0: (nsMemory::Alloc(sizeof(SECItem)*numCAs)); michael@0: if (!derCerts) { michael@0: rv = NS_ERROR_OUT_OF_MEMORY; michael@0: goto loser; michael@0: } michael@0: for (node = CERT_LIST_HEAD(caPubs), i=0; michael@0: !CERT_LIST_END(node, caPubs); michael@0: node = CERT_LIST_NEXT(node), i++) { michael@0: derCerts[i] = node->cert->derCert; michael@0: } michael@0: nsNSSCertificateDB::ImportValidCACerts(numCAs, derCerts, ctx, locker); michael@0: nsMemory::Free(derCerts); michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (aDoForcedBackup) { michael@0: // I can't pop up a file picker from the depths of JavaScript, michael@0: // so I'll just post an event on the UI queue to do the backups michael@0: // later. michael@0: nsCOMPtr p12Runnable = new nsP12Runnable(certArr, numResponses, michael@0: token); michael@0: if (!p12Runnable) { michael@0: rv = NS_ERROR_FAILURE; michael@0: goto loser; michael@0: } michael@0: michael@0: // null out the certArr pointer which has now been inherited by michael@0: // the nsP12Runnable instance so that we don't free up the michael@0: // memory on the way out. michael@0: certArr = nullptr; michael@0: michael@0: rv = NS_DispatchToMainThread(p12Runnable); michael@0: if (NS_FAILED(rv)) michael@0: goto loser; michael@0: } michael@0: michael@0: loser: michael@0: if (certArr) { michael@0: for (i=0; i domWindow = michael@0: do_QueryInterface(scriptContext->GetGlobalObject()); michael@0: if (!domWindow) { michael@0: return; michael@0: } michael@0: michael@0: nsCOMPtr domDocument; michael@0: domWindow->GetDocument(getter_AddRefs(domDocument)); michael@0: if (!domDocument) { michael@0: return; michael@0: } michael@0: michael@0: CallQueryInterface(domDocument, aDocument); michael@0: michael@0: return; michael@0: } michael@0: michael@0: void signTextOutputCallback(void *arg, const char *buf, unsigned long len) michael@0: { michael@0: ((nsCString*)arg)->Append(buf, len); michael@0: } michael@0: michael@0: void michael@0: nsCrypto::SignText(JSContext* aContext, michael@0: const nsAString& aStringToSign, michael@0: const nsAString& aCaOption, michael@0: const Sequence& aArgs, michael@0: nsAString& aReturn) michael@0: { michael@0: // XXX This code should return error codes, but we're keeping this michael@0: // backwards compatible with NS4.x and so we can't throw exceptions. michael@0: NS_NAMED_LITERAL_STRING(internalError, "error:internalError"); michael@0: michael@0: aReturn.Truncate(); michael@0: michael@0: uint32_t argc = aArgs.Length(); michael@0: michael@0: if (!aCaOption.EqualsLiteral("auto") && michael@0: !aCaOption.EqualsLiteral("ask")) { michael@0: NS_WARNING("caOption argument must be ask or auto"); michael@0: aReturn.Append(internalError); michael@0: michael@0: return; michael@0: } michael@0: michael@0: // It was decided to always behave as if "ask" were specified. michael@0: // XXX Should we warn in the JS Console for auto? michael@0: michael@0: nsCOMPtr uiContext = new PipUIContext; michael@0: if (!uiContext) { michael@0: aReturn.Append(internalError); michael@0: michael@0: return; michael@0: } michael@0: michael@0: bool bestOnly = true; michael@0: bool validOnly = true; michael@0: CERTCertList* certList = michael@0: CERT_FindUserCertsByUsage(CERT_GetDefaultCertDB(), certUsageEmailSigner, michael@0: bestOnly, validOnly, uiContext); michael@0: michael@0: uint32_t numCAs = argc; michael@0: if (numCAs > 0) { michael@0: nsAutoArrayPtr caNames(new char*[numCAs]); michael@0: if (!caNames) { michael@0: aReturn.Append(internalError); michael@0: return; michael@0: } michael@0: michael@0: uint32_t i; michael@0: for (i = 0; i < numCAs; ++i) michael@0: caNames[i] = const_cast(aArgs[i].get()); michael@0: michael@0: if (certList && michael@0: CERT_FilterCertListByCANames(certList, numCAs, caNames, michael@0: certUsageEmailSigner) != SECSuccess) { michael@0: aReturn.Append(internalError); michael@0: michael@0: return; michael@0: } michael@0: } michael@0: michael@0: if (!certList || CERT_LIST_EMPTY(certList)) { michael@0: aReturn.AppendLiteral("error:noMatchingCert"); michael@0: michael@0: return; michael@0: } michael@0: michael@0: nsCOMPtr fsd = michael@0: do_CreateInstance(NS_FORMSIGNINGDIALOG_CONTRACTID); michael@0: if (!fsd) { michael@0: aReturn.Append(internalError); michael@0: michael@0: return; michael@0: } michael@0: michael@0: nsCOMPtr document; michael@0: GetDocumentFromContext(aContext, getter_AddRefs(document)); michael@0: if (!document) { michael@0: aReturn.Append(internalError); michael@0: michael@0: return; michael@0: } michael@0: michael@0: // Get the hostname from the URL of the document. michael@0: nsIURI* uri = document->GetDocumentURI(); michael@0: if (!uri) { michael@0: aReturn.Append(internalError); michael@0: michael@0: return; michael@0: } michael@0: michael@0: nsresult rv; michael@0: michael@0: nsCString host; michael@0: rv = uri->GetHost(host); michael@0: if (NS_FAILED(rv)) { michael@0: aReturn.Append(internalError); michael@0: michael@0: return; michael@0: } michael@0: michael@0: int32_t numberOfCerts = 0; michael@0: CERTCertListNode* node; michael@0: for (node = CERT_LIST_HEAD(certList); !CERT_LIST_END(node, certList); michael@0: node = CERT_LIST_NEXT(node)) { michael@0: ++numberOfCerts; michael@0: } michael@0: michael@0: ScopedCERTCertNicknames nicknames(getNSSCertNicknamesFromCertList(certList)); michael@0: michael@0: if (!nicknames) { michael@0: aReturn.Append(internalError); michael@0: michael@0: return; michael@0: } michael@0: michael@0: NS_ASSERTION(nicknames->numnicknames == numberOfCerts, michael@0: "nicknames->numnicknames != numberOfCerts"); michael@0: michael@0: nsAutoArrayPtr certNicknameList(new char16_t*[nicknames->numnicknames * 2]); michael@0: if (!certNicknameList) { michael@0: aReturn.Append(internalError); michael@0: michael@0: return; michael@0: } michael@0: michael@0: char16_t** certDetailsList = certNicknameList.get() + nicknames->numnicknames; michael@0: michael@0: int32_t certsToUse; michael@0: for (node = CERT_LIST_HEAD(certList), certsToUse = 0; michael@0: !CERT_LIST_END(node, certList) && certsToUse < nicknames->numnicknames; michael@0: node = CERT_LIST_NEXT(node)) { michael@0: RefPtr tempCert(nsNSSCertificate::Create(node->cert)); michael@0: if (tempCert) { michael@0: nsAutoString nickWithSerial, details; michael@0: rv = tempCert->FormatUIStrings(NS_ConvertUTF8toUTF16(nicknames->nicknames[certsToUse]), michael@0: nickWithSerial, details); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: certNicknameList[certsToUse] = ToNewUnicode(nickWithSerial); michael@0: if (certNicknameList[certsToUse]) { michael@0: certDetailsList[certsToUse] = ToNewUnicode(details); michael@0: if (!certDetailsList[certsToUse]) { michael@0: nsMemory::Free(certNicknameList[certsToUse]); michael@0: continue; michael@0: } michael@0: ++certsToUse; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (certsToUse == 0) { michael@0: aReturn.Append(internalError); michael@0: michael@0: return; michael@0: } michael@0: michael@0: NS_ConvertUTF8toUTF16 utf16Host(host); michael@0: michael@0: CERTCertificate *signingCert = nullptr; michael@0: bool tryAgain, canceled; michael@0: nsAutoString password; michael@0: do { michael@0: // Throw up the form signing confirmation dialog and get back the index michael@0: // of the selected cert. michael@0: int32_t selectedIndex = -1; michael@0: rv = fsd->ConfirmSignText(uiContext, utf16Host, aStringToSign, michael@0: const_cast(certNicknameList.get()), michael@0: const_cast(certDetailsList), michael@0: certsToUse, &selectedIndex, password, michael@0: &canceled); michael@0: if (NS_FAILED(rv) || canceled) { michael@0: break; // out of tryAgain loop michael@0: } michael@0: michael@0: int32_t j = 0; michael@0: for (node = CERT_LIST_HEAD(certList); !CERT_LIST_END(node, certList); michael@0: node = CERT_LIST_NEXT(node)) { michael@0: if (j == selectedIndex) { michael@0: signingCert = CERT_DupCertificate(node->cert); michael@0: break; // out of cert list iteration loop michael@0: } michael@0: ++j; michael@0: } michael@0: michael@0: if (!signingCert) { michael@0: rv = NS_ERROR_FAILURE; michael@0: break; // out of tryAgain loop michael@0: } michael@0: michael@0: NS_ConvertUTF16toUTF8 pwUtf8(password); michael@0: michael@0: tryAgain = michael@0: PK11_CheckUserPassword(signingCert->slot, michael@0: const_cast(pwUtf8.get())) != SECSuccess; michael@0: // XXX we should show an error dialog before retrying michael@0: } while (tryAgain); michael@0: michael@0: int32_t k; michael@0: for (k = 0; k < certsToUse; ++k) { michael@0: nsMemory::Free(certNicknameList[k]); michael@0: nsMemory::Free(certDetailsList[k]); michael@0: } michael@0: michael@0: if (NS_FAILED(rv)) { // something went wrong inside the tryAgain loop michael@0: aReturn.Append(internalError); michael@0: michael@0: return; michael@0: } michael@0: michael@0: if (canceled) { michael@0: aReturn.AppendLiteral("error:userCancel"); michael@0: michael@0: return; michael@0: } michael@0: michael@0: SECKEYPrivateKey* privKey = PK11_FindKeyByAnyCert(signingCert, uiContext); michael@0: if (!privKey) { michael@0: aReturn.Append(internalError); michael@0: michael@0: return; michael@0: } michael@0: michael@0: nsAutoCString charset(document->GetDocumentCharacterSet()); michael@0: michael@0: // XXX Doing what nsFormSubmission::GetEncoder does (see michael@0: // http://bugzilla.mozilla.org/show_bug.cgi?id=81203). michael@0: if (charset.EqualsLiteral("ISO-8859-1")) { michael@0: charset.AssignLiteral("windows-1252"); michael@0: } michael@0: michael@0: nsCOMPtr encoder = michael@0: do_CreateInstance(NS_SAVEASCHARSET_CONTRACTID); michael@0: if (encoder) { michael@0: rv = encoder->Init(charset.get(), michael@0: (nsISaveAsCharset::attr_EntityAfterCharsetConv + michael@0: nsISaveAsCharset::attr_FallbackDecimalNCR), michael@0: 0); michael@0: } michael@0: michael@0: nsXPIDLCString buffer; michael@0: if (aStringToSign.Length() > 0) { michael@0: if (encoder && NS_SUCCEEDED(rv)) { michael@0: rv = encoder->Convert(PromiseFlatString(aStringToSign).get(), michael@0: getter_Copies(buffer)); michael@0: if (NS_FAILED(rv)) { michael@0: aReturn.Append(internalError); michael@0: michael@0: return; michael@0: } michael@0: } michael@0: else { michael@0: AppendUTF16toUTF8(aStringToSign, buffer); michael@0: } michael@0: } michael@0: michael@0: HASHContext *hc = HASH_Create(HASH_AlgSHA1); michael@0: if (!hc) { michael@0: aReturn.Append(internalError); michael@0: michael@0: return; michael@0: } michael@0: michael@0: unsigned char hash[SHA1_LENGTH]; michael@0: michael@0: SECItem digest; michael@0: digest.data = hash; michael@0: michael@0: HASH_Begin(hc); michael@0: HASH_Update(hc, reinterpret_cast(buffer.get()), michael@0: buffer.Length()); michael@0: HASH_End(hc, digest.data, &digest.len, SHA1_LENGTH); michael@0: HASH_Destroy(hc); michael@0: michael@0: nsCString p7; michael@0: SECStatus srv = SECFailure; michael@0: michael@0: SEC_PKCS7ContentInfo *ci = SEC_PKCS7CreateSignedData(signingCert, michael@0: certUsageEmailSigner, michael@0: nullptr, SEC_OID_SHA1, michael@0: &digest, nullptr, uiContext); michael@0: if (ci) { michael@0: srv = SEC_PKCS7IncludeCertChain(ci, nullptr); michael@0: if (srv == SECSuccess) { michael@0: srv = SEC_PKCS7AddSigningTime(ci); michael@0: if (srv == SECSuccess) { michael@0: srv = SEC_PKCS7Encode(ci, signTextOutputCallback, &p7, nullptr, nullptr, michael@0: uiContext); michael@0: } michael@0: } michael@0: michael@0: SEC_PKCS7DestroyContentInfo(ci); michael@0: } michael@0: michael@0: if (srv != SECSuccess) { michael@0: aReturn.Append(internalError); michael@0: michael@0: return; michael@0: } michael@0: michael@0: SECItem binary_item; michael@0: binary_item.data = reinterpret_cast michael@0: (const_cast(p7.get())); michael@0: binary_item.len = p7.Length(); michael@0: michael@0: char *result = NSSBase64_EncodeItem(nullptr, nullptr, 0, &binary_item); michael@0: if (result) { michael@0: AppendASCIItoUTF16(result, aReturn); michael@0: } michael@0: else { michael@0: aReturn.Append(internalError); michael@0: } michael@0: michael@0: PORT_Free(result); michael@0: michael@0: return; michael@0: } michael@0: michael@0: void michael@0: nsCrypto::Logout(ErrorResult& aRv) michael@0: { michael@0: NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID); michael@0: michael@0: nsresult rv; michael@0: nsCOMPtr nssComponent(do_GetService(kNSSComponentCID, &rv)); michael@0: if (NS_FAILED(rv)) { michael@0: aRv.Throw(rv); michael@0: return; michael@0: } michael@0: michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: PK11_LogoutAll(); michael@0: SSL_ClearSessionCache(); michael@0: } michael@0: michael@0: rv = nssComponent->LogoutAuthenticatedPK11(); michael@0: if (NS_FAILED(rv)) { michael@0: aRv.Throw(rv); michael@0: } michael@0: } michael@0: michael@0: CRMFObject::CRMFObject() michael@0: { michael@0: MOZ_COUNT_CTOR(CRMFObject); michael@0: } michael@0: michael@0: CRMFObject::~CRMFObject() michael@0: { michael@0: MOZ_COUNT_DTOR(CRMFObject); michael@0: } michael@0: michael@0: JSObject* michael@0: CRMFObject::WrapObject(JSContext *aCx, bool* aTookOwnership) michael@0: { michael@0: return CRMFObjectBinding::Wrap(aCx, this, aTookOwnership); michael@0: } michael@0: michael@0: void michael@0: CRMFObject::GetRequest(nsAString& aRequest) michael@0: { michael@0: aRequest.Assign(mBase64Request); michael@0: } michael@0: michael@0: nsresult michael@0: CRMFObject::SetCRMFRequest(char *inRequest) michael@0: { michael@0: mBase64Request.AssignWithConversion(inRequest); michael@0: return NS_OK; michael@0: } michael@0: michael@0: #endif // MOZ_DISABLE_CRYPTOLEGACY michael@0: michael@0: nsPkcs11::nsPkcs11() michael@0: { michael@0: } michael@0: michael@0: nsPkcs11::~nsPkcs11() michael@0: { michael@0: } michael@0: michael@0: //Delete a PKCS11 module from the user's profile. michael@0: NS_IMETHODIMP michael@0: nsPkcs11::DeleteModule(const nsAString& aModuleName) michael@0: { michael@0: NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID); michael@0: michael@0: nsNSSShutDownPreventionLock locker; michael@0: nsresult rv; michael@0: nsString errorMessage; michael@0: michael@0: nsCOMPtr nssComponent(do_GetService(kNSSComponentCID, &rv)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: if (aModuleName.IsEmpty()) { michael@0: return NS_ERROR_ILLEGAL_VALUE; michael@0: } michael@0: michael@0: NS_ConvertUTF16toUTF8 modName(aModuleName); michael@0: int32_t modType; michael@0: SECStatus srv = SECMOD_DeleteModule(modName.get(), &modType); michael@0: if (srv == SECSuccess) { michael@0: SECMODModule *module = SECMOD_FindModule(modName.get()); michael@0: if (module) { michael@0: #ifndef MOZ_DISABLE_CRYPTOLEGACY michael@0: nssComponent->ShutdownSmartCardThread(module); michael@0: #endif michael@0: SECMOD_DestroyModule(module); michael@0: } michael@0: rv = NS_OK; michael@0: } else { michael@0: rv = NS_ERROR_FAILURE; michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: //Add a new PKCS11 module to the user's profile. michael@0: NS_IMETHODIMP michael@0: nsPkcs11::AddModule(const nsAString& aModuleName, michael@0: const nsAString& aLibraryFullPath, michael@0: int32_t aCryptoMechanismFlags, michael@0: int32_t aCipherFlags) michael@0: { michael@0: NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID); michael@0: michael@0: nsNSSShutDownPreventionLock locker; michael@0: nsresult rv; michael@0: nsCOMPtr nssComponent(do_GetService(kNSSComponentCID, &rv)); michael@0: michael@0: NS_ConvertUTF16toUTF8 moduleName(aModuleName); michael@0: nsCString fullPath; michael@0: // NSS doesn't support Unicode path. Use native charset michael@0: NS_CopyUnicodeToNative(aLibraryFullPath, fullPath); michael@0: uint32_t mechFlags = SECMOD_PubMechFlagstoInternal(aCryptoMechanismFlags); michael@0: uint32_t cipherFlags = SECMOD_PubCipherFlagstoInternal(aCipherFlags); michael@0: SECStatus srv = SECMOD_AddNewModule(moduleName.get(), fullPath.get(), michael@0: mechFlags, cipherFlags); michael@0: if (srv == SECSuccess) { michael@0: SECMODModule *module = SECMOD_FindModule(moduleName.get()); michael@0: if (module) { michael@0: #ifndef MOZ_DISABLE_CRYPTOLEGACY michael@0: nssComponent->LaunchSmartCardThread(module); michael@0: #endif michael@0: SECMOD_DestroyModule(module); michael@0: } michael@0: } michael@0: michael@0: // The error message we report to the user depends directly on michael@0: // what the return value for SEDMOD_AddNewModule is michael@0: switch (srv) { michael@0: case SECSuccess: michael@0: return NS_OK; michael@0: case SECFailure: michael@0: return NS_ERROR_FAILURE; michael@0: case -2: michael@0: return NS_ERROR_ILLEGAL_VALUE; michael@0: } michael@0: NS_ERROR("Bogus return value, this should never happen"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: