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: michael@0: #include "secdert.h" michael@0: #include "nspr.h" michael@0: #include "nsNSSComponent.h" // for PIPNSS string bundle calls. michael@0: #include "keyhi.h" michael@0: #include "secder.h" michael@0: #include "cryptohi.h" michael@0: #include "base64.h" michael@0: #include "secasn1.h" michael@0: #include "pk11pqg.h" michael@0: #include "nsKeygenHandler.h" michael@0: #include "nsIServiceManager.h" michael@0: #include "nsIDOMHTMLSelectElement.h" michael@0: #include "nsIContent.h" michael@0: #include "nsKeygenThread.h" michael@0: #include "nsReadableUtils.h" michael@0: #include "nsUnicharUtils.h" michael@0: #include "nsCRT.h" michael@0: #include "nsITokenDialogs.h" michael@0: #include "nsIGenKeypairInfoDlg.h" michael@0: #include "nsNSSShutDown.h" michael@0: michael@0: //These defines are taken from the PKCS#11 spec michael@0: #define CKM_RSA_PKCS_KEY_PAIR_GEN 0x00000000 michael@0: #define CKM_DH_PKCS_KEY_PAIR_GEN 0x00000020 michael@0: #define CKM_DSA_KEY_PAIR_GEN 0x00000010 michael@0: michael@0: DERTemplate SECAlgorithmIDTemplate[] = { michael@0: { DER_SEQUENCE, michael@0: 0, nullptr, sizeof(SECAlgorithmID) }, michael@0: { DER_OBJECT_ID, michael@0: offsetof(SECAlgorithmID,algorithm), }, michael@0: { DER_OPTIONAL | DER_ANY, michael@0: offsetof(SECAlgorithmID,parameters), }, michael@0: { 0, } michael@0: }; michael@0: michael@0: DERTemplate CERTSubjectPublicKeyInfoTemplate[] = { michael@0: { DER_SEQUENCE, michael@0: 0, nullptr, sizeof(CERTSubjectPublicKeyInfo) }, michael@0: { DER_INLINE, michael@0: offsetof(CERTSubjectPublicKeyInfo,algorithm), michael@0: SECAlgorithmIDTemplate, }, michael@0: { DER_BIT_STRING, michael@0: offsetof(CERTSubjectPublicKeyInfo,subjectPublicKey), }, michael@0: { 0, } michael@0: }; michael@0: michael@0: DERTemplate CERTPublicKeyAndChallengeTemplate[] = michael@0: { michael@0: { DER_SEQUENCE, 0, nullptr, sizeof(CERTPublicKeyAndChallenge) }, michael@0: { DER_ANY, offsetof(CERTPublicKeyAndChallenge,spki), }, michael@0: { DER_IA5_STRING, offsetof(CERTPublicKeyAndChallenge,challenge), }, michael@0: { 0, } michael@0: }; michael@0: michael@0: const SEC_ASN1Template SECKEY_PQGParamsTemplate[] = { michael@0: { SEC_ASN1_SEQUENCE, 0, nullptr, sizeof(PQGParams) }, michael@0: { SEC_ASN1_INTEGER, offsetof(PQGParams,prime) }, michael@0: { SEC_ASN1_INTEGER, offsetof(PQGParams,subPrime) }, michael@0: { SEC_ASN1_INTEGER, offsetof(PQGParams,base) }, michael@0: { 0, } michael@0: }; michael@0: michael@0: michael@0: static NS_DEFINE_IID(kIDOMHTMLSelectElementIID, NS_IDOMHTMLSELECTELEMENT_IID); michael@0: michael@0: static PQGParams * michael@0: decode_pqg_params(char *aStr) michael@0: { michael@0: unsigned char *buf = nullptr; michael@0: unsigned int len; michael@0: PLArenaPool *arena = nullptr; michael@0: PQGParams *params = nullptr; michael@0: SECStatus status; michael@0: michael@0: arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); michael@0: if (!arena) michael@0: return nullptr; michael@0: michael@0: params = static_cast(PORT_ArenaZAlloc(arena, sizeof(PQGParams))); michael@0: if (!params) michael@0: goto loser; michael@0: params->arena = arena; michael@0: michael@0: buf = ATOB_AsciiToData(aStr, &len); michael@0: if ((!buf) || (len == 0)) michael@0: goto loser; michael@0: michael@0: status = SEC_ASN1Decode(arena, params, SECKEY_PQGParamsTemplate, (const char*)buf, len); michael@0: if (status != SECSuccess) michael@0: goto loser; michael@0: michael@0: return params; michael@0: michael@0: loser: michael@0: if (arena) { michael@0: PORT_FreeArena(arena, false); michael@0: } michael@0: if (buf) { michael@0: PR_Free(buf); michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: static int michael@0: pqg_prime_bits(char *str) michael@0: { michael@0: PQGParams *params = nullptr; michael@0: int primeBits = 0, i; michael@0: michael@0: params = decode_pqg_params(str); michael@0: if (!params) michael@0: goto done; /* lose */ michael@0: michael@0: for (i = 0; params->prime.data[i] == 0; i++) michael@0: /* empty */; michael@0: primeBits = (params->prime.len - i) * 8; michael@0: michael@0: done: michael@0: if (params) michael@0: PK11_PQG_DestroyParams(params); michael@0: return primeBits; michael@0: } michael@0: michael@0: typedef struct curveNameTagPairStr { michael@0: const char *curveName; michael@0: SECOidTag curveOidTag; michael@0: } CurveNameTagPair; michael@0: michael@0: static CurveNameTagPair nameTagPair[] = michael@0: { michael@0: { "prime192v1", SEC_OID_ANSIX962_EC_PRIME192V1 }, michael@0: { "prime192v2", SEC_OID_ANSIX962_EC_PRIME192V2 }, michael@0: { "prime192v3", SEC_OID_ANSIX962_EC_PRIME192V3 }, michael@0: { "prime239v1", SEC_OID_ANSIX962_EC_PRIME239V1 }, michael@0: { "prime239v2", SEC_OID_ANSIX962_EC_PRIME239V2 }, michael@0: { "prime239v3", SEC_OID_ANSIX962_EC_PRIME239V3 }, michael@0: { "prime256v1", SEC_OID_ANSIX962_EC_PRIME256V1 }, michael@0: michael@0: { "secp112r1", SEC_OID_SECG_EC_SECP112R1}, michael@0: { "secp112r2", SEC_OID_SECG_EC_SECP112R2}, michael@0: { "secp128r1", SEC_OID_SECG_EC_SECP128R1}, michael@0: { "secp128r2", SEC_OID_SECG_EC_SECP128R2}, michael@0: { "secp160k1", SEC_OID_SECG_EC_SECP160K1}, michael@0: { "secp160r1", SEC_OID_SECG_EC_SECP160R1}, michael@0: { "secp160r2", SEC_OID_SECG_EC_SECP160R2}, michael@0: { "secp192k1", SEC_OID_SECG_EC_SECP192K1}, michael@0: { "secp192r1", SEC_OID_ANSIX962_EC_PRIME192V1 }, michael@0: { "nistp192", SEC_OID_ANSIX962_EC_PRIME192V1 }, michael@0: { "secp224k1", SEC_OID_SECG_EC_SECP224K1}, michael@0: { "secp224r1", SEC_OID_SECG_EC_SECP224R1}, michael@0: { "nistp224", SEC_OID_SECG_EC_SECP224R1}, michael@0: { "secp256k1", SEC_OID_SECG_EC_SECP256K1}, michael@0: { "secp256r1", SEC_OID_ANSIX962_EC_PRIME256V1 }, michael@0: { "nistp256", SEC_OID_ANSIX962_EC_PRIME256V1 }, michael@0: { "secp384r1", SEC_OID_SECG_EC_SECP384R1}, michael@0: { "nistp384", SEC_OID_SECG_EC_SECP384R1}, michael@0: { "secp521r1", SEC_OID_SECG_EC_SECP521R1}, michael@0: { "nistp521", SEC_OID_SECG_EC_SECP521R1}, michael@0: michael@0: { "c2pnb163v1", SEC_OID_ANSIX962_EC_C2PNB163V1 }, michael@0: { "c2pnb163v2", SEC_OID_ANSIX962_EC_C2PNB163V2 }, michael@0: { "c2pnb163v3", SEC_OID_ANSIX962_EC_C2PNB163V3 }, michael@0: { "c2pnb176v1", SEC_OID_ANSIX962_EC_C2PNB176V1 }, michael@0: { "c2tnb191v1", SEC_OID_ANSIX962_EC_C2TNB191V1 }, michael@0: { "c2tnb191v2", SEC_OID_ANSIX962_EC_C2TNB191V2 }, michael@0: { "c2tnb191v3", SEC_OID_ANSIX962_EC_C2TNB191V3 }, michael@0: { "c2onb191v4", SEC_OID_ANSIX962_EC_C2ONB191V4 }, michael@0: { "c2onb191v5", SEC_OID_ANSIX962_EC_C2ONB191V5 }, michael@0: { "c2pnb208w1", SEC_OID_ANSIX962_EC_C2PNB208W1 }, michael@0: { "c2tnb239v1", SEC_OID_ANSIX962_EC_C2TNB239V1 }, michael@0: { "c2tnb239v2", SEC_OID_ANSIX962_EC_C2TNB239V2 }, michael@0: { "c2tnb239v3", SEC_OID_ANSIX962_EC_C2TNB239V3 }, michael@0: { "c2onb239v4", SEC_OID_ANSIX962_EC_C2ONB239V4 }, michael@0: { "c2onb239v5", SEC_OID_ANSIX962_EC_C2ONB239V5 }, michael@0: { "c2pnb272w1", SEC_OID_ANSIX962_EC_C2PNB272W1 }, michael@0: { "c2pnb304w1", SEC_OID_ANSIX962_EC_C2PNB304W1 }, michael@0: { "c2tnb359v1", SEC_OID_ANSIX962_EC_C2TNB359V1 }, michael@0: { "c2pnb368w1", SEC_OID_ANSIX962_EC_C2PNB368W1 }, michael@0: { "c2tnb431r1", SEC_OID_ANSIX962_EC_C2TNB431R1 }, michael@0: michael@0: { "sect113r1", SEC_OID_SECG_EC_SECT113R1}, michael@0: { "sect113r2", SEC_OID_SECG_EC_SECT113R2}, michael@0: { "sect131r1", SEC_OID_SECG_EC_SECT131R1}, michael@0: { "sect131r2", SEC_OID_SECG_EC_SECT131R2}, michael@0: { "sect163k1", SEC_OID_SECG_EC_SECT163K1}, michael@0: { "nistk163", SEC_OID_SECG_EC_SECT163K1}, michael@0: { "sect163r1", SEC_OID_SECG_EC_SECT163R1}, michael@0: { "sect163r2", SEC_OID_SECG_EC_SECT163R2}, michael@0: { "nistb163", SEC_OID_SECG_EC_SECT163R2}, michael@0: { "sect193r1", SEC_OID_SECG_EC_SECT193R1}, michael@0: { "sect193r2", SEC_OID_SECG_EC_SECT193R2}, michael@0: { "sect233k1", SEC_OID_SECG_EC_SECT233K1}, michael@0: { "nistk233", SEC_OID_SECG_EC_SECT233K1}, michael@0: { "sect233r1", SEC_OID_SECG_EC_SECT233R1}, michael@0: { "nistb233", SEC_OID_SECG_EC_SECT233R1}, michael@0: { "sect239k1", SEC_OID_SECG_EC_SECT239K1}, michael@0: { "sect283k1", SEC_OID_SECG_EC_SECT283K1}, michael@0: { "nistk283", SEC_OID_SECG_EC_SECT283K1}, michael@0: { "sect283r1", SEC_OID_SECG_EC_SECT283R1}, michael@0: { "nistb283", SEC_OID_SECG_EC_SECT283R1}, michael@0: { "sect409k1", SEC_OID_SECG_EC_SECT409K1}, michael@0: { "nistk409", SEC_OID_SECG_EC_SECT409K1}, michael@0: { "sect409r1", SEC_OID_SECG_EC_SECT409R1}, michael@0: { "nistb409", SEC_OID_SECG_EC_SECT409R1}, michael@0: { "sect571k1", SEC_OID_SECG_EC_SECT571K1}, michael@0: { "nistk571", SEC_OID_SECG_EC_SECT571K1}, michael@0: { "sect571r1", SEC_OID_SECG_EC_SECT571R1}, michael@0: { "nistb571", SEC_OID_SECG_EC_SECT571R1}, michael@0: michael@0: }; michael@0: michael@0: SECKEYECParams * michael@0: decode_ec_params(const char *curve) michael@0: { michael@0: SECKEYECParams *ecparams; michael@0: SECOidData *oidData = nullptr; michael@0: SECOidTag curveOidTag = SEC_OID_UNKNOWN; /* default */ michael@0: int i, numCurves; michael@0: michael@0: if (curve && *curve) { michael@0: numCurves = sizeof(nameTagPair)/sizeof(CurveNameTagPair); michael@0: for (i = 0; ((i < numCurves) && (curveOidTag == SEC_OID_UNKNOWN)); michael@0: i++) { michael@0: if (PL_strcmp(curve, nameTagPair[i].curveName) == 0) michael@0: curveOidTag = nameTagPair[i].curveOidTag; michael@0: } michael@0: } michael@0: michael@0: /* Return nullptr if curve name is not recognized */ michael@0: if ((curveOidTag == SEC_OID_UNKNOWN) || michael@0: (oidData = SECOID_FindOIDByTag(curveOidTag)) == nullptr) { michael@0: return nullptr; michael@0: } michael@0: michael@0: ecparams = SECITEM_AllocItem(nullptr, nullptr, (2 + oidData->oid.len)); michael@0: michael@0: if (!ecparams) michael@0: return nullptr; michael@0: michael@0: /* michael@0: * ecparams->data needs to contain the ASN encoding of an object ID (OID) michael@0: * representing the named curve. The actual OID is in michael@0: * oidData->oid.data so we simply prepend 0x06 and OID length michael@0: */ michael@0: ecparams->data[0] = SEC_ASN1_OBJECT_ID; michael@0: ecparams->data[1] = oidData->oid.len; michael@0: memcpy(ecparams->data + 2, oidData->oid.data, oidData->oid.len); michael@0: michael@0: return ecparams; michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsKeygenFormProcessor, nsIFormProcessor) michael@0: michael@0: nsKeygenFormProcessor::nsKeygenFormProcessor() michael@0: { michael@0: m_ctx = new PipUIContext(); michael@0: michael@0: } michael@0: michael@0: nsKeygenFormProcessor::~nsKeygenFormProcessor() michael@0: { michael@0: } michael@0: michael@0: nsresult michael@0: nsKeygenFormProcessor::Create(nsISupports* aOuter, const nsIID& aIID, void* *aResult) michael@0: { michael@0: nsresult rv; michael@0: NS_ENSURE_NO_AGGREGATION(aOuter); michael@0: nsKeygenFormProcessor* formProc = new nsKeygenFormProcessor(); michael@0: michael@0: nsCOMPtr stabilize = formProc; michael@0: rv = formProc->Init(); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: rv = formProc->QueryInterface(aIID, aResult); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsKeygenFormProcessor::Init() michael@0: { michael@0: static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID); michael@0: michael@0: nsresult rv; michael@0: michael@0: nsCOMPtr nssComponent; michael@0: nssComponent = do_GetService(kNSSComponentCID, &rv); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: // Init possible key size choices. michael@0: nssComponent->GetPIPNSSBundleString("HighGrade", mSECKeySizeChoiceList[0].name); michael@0: mSECKeySizeChoiceList[0].size = 2048; michael@0: michael@0: nssComponent->GetPIPNSSBundleString("MediumGrade", mSECKeySizeChoiceList[1].name); michael@0: mSECKeySizeChoiceList[1].size = 1024; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsKeygenFormProcessor::GetSlot(uint32_t aMechanism, PK11SlotInfo** aSlot) michael@0: { michael@0: return GetSlotWithMechanism(aMechanism,m_ctx,aSlot); michael@0: } michael@0: michael@0: michael@0: uint32_t MapGenMechToAlgoMech(uint32_t mechanism) michael@0: { michael@0: uint32_t searchMech; michael@0: michael@0: /* We are interested in slots based on the ability to perform michael@0: a given algorithm, not on their ability to generate keys usable michael@0: by that algorithm. Therefore, map keygen-specific mechanism tags michael@0: to tags for the corresponding crypto algorthm. */ michael@0: switch(mechanism) michael@0: { michael@0: case CKM_RSA_PKCS_KEY_PAIR_GEN: michael@0: searchMech = CKM_RSA_PKCS; michael@0: break; michael@0: case CKM_DSA_KEY_PAIR_GEN: michael@0: searchMech = CKM_DSA; michael@0: break; michael@0: case CKM_RC4_KEY_GEN: michael@0: searchMech = CKM_RC4; michael@0: break; michael@0: case CKM_DH_PKCS_KEY_PAIR_GEN: michael@0: searchMech = CKM_DH_PKCS_DERIVE; /* ### mwelch is this right? */ michael@0: break; michael@0: case CKM_DES_KEY_GEN: michael@0: /* What do we do about DES keygen? Right now, we're just using michael@0: DES_KEY_GEN to look for tokens, because otherwise we'll have michael@0: to search the token list three times. */ michael@0: case CKM_EC_KEY_PAIR_GEN: michael@0: /* The default should also work for EC key pair generation. */ michael@0: default: michael@0: searchMech = mechanism; michael@0: break; michael@0: } michael@0: return searchMech; michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: GetSlotWithMechanism(uint32_t aMechanism, michael@0: nsIInterfaceRequestor *m_ctx, michael@0: PK11SlotInfo** aSlot) michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: PK11SlotList * slotList = nullptr; michael@0: char16_t** tokenNameList = nullptr; michael@0: nsITokenDialogs * dialogs; michael@0: char16_t *unicodeTokenChosen; michael@0: PK11SlotListElement *slotElement, *tmpSlot; michael@0: uint32_t numSlots = 0, i = 0; michael@0: bool canceled; michael@0: nsresult rv = NS_OK; michael@0: michael@0: *aSlot = nullptr; michael@0: michael@0: // Get the slot michael@0: slotList = PK11_GetAllTokens(MapGenMechToAlgoMech(aMechanism), michael@0: true, true, m_ctx); michael@0: if (!slotList || !slotList->head) { michael@0: rv = NS_ERROR_FAILURE; michael@0: goto loser; michael@0: } michael@0: michael@0: if (!slotList->head->next) { michael@0: /* only one slot available, just return it */ michael@0: *aSlot = slotList->head->slot; michael@0: } else { michael@0: // Gerenate a list of slots and ask the user to choose // michael@0: tmpSlot = slotList->head; michael@0: while (tmpSlot) { michael@0: numSlots++; michael@0: tmpSlot = tmpSlot->next; michael@0: } michael@0: michael@0: // Allocate the slot name buffer // michael@0: tokenNameList = static_cast(nsMemory::Alloc(sizeof(char16_t *) * numSlots)); michael@0: if (!tokenNameList) { michael@0: rv = NS_ERROR_OUT_OF_MEMORY; michael@0: goto loser; michael@0: } michael@0: michael@0: i = 0; michael@0: slotElement = PK11_GetFirstSafe(slotList); michael@0: while (slotElement) { michael@0: tokenNameList[i] = UTF8ToNewUnicode(nsDependentCString(PK11_GetTokenName(slotElement->slot))); michael@0: slotElement = PK11_GetNextSafe(slotList, slotElement, false); michael@0: if (tokenNameList[i]) michael@0: i++; michael@0: else { michael@0: // OOM. adjust numSlots so we don't free unallocated memory. michael@0: numSlots = i; michael@0: PK11_FreeSlotListElement(slotList, slotElement); michael@0: rv = NS_ERROR_OUT_OF_MEMORY; michael@0: goto loser; michael@0: } michael@0: } michael@0: michael@0: /* Throw up the token list dialog and get back the token */ michael@0: rv = getNSSDialogs((void**)&dialogs, michael@0: NS_GET_IID(nsITokenDialogs), michael@0: NS_TOKENDIALOGS_CONTRACTID); michael@0: michael@0: if (NS_FAILED(rv)) goto loser; michael@0: michael@0: { michael@0: nsPSMUITracker tracker; michael@0: if (!tokenNameList || !*tokenNameList) { michael@0: rv = NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: else if (tracker.isUIForbidden()) { michael@0: rv = NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: else { michael@0: rv = dialogs->ChooseToken(m_ctx, (const char16_t**)tokenNameList, numSlots, &unicodeTokenChosen, &canceled); michael@0: } michael@0: } michael@0: NS_RELEASE(dialogs); michael@0: if (NS_FAILED(rv)) goto loser; michael@0: michael@0: if (canceled) { rv = NS_ERROR_NOT_AVAILABLE; goto loser; } michael@0: michael@0: // Get the slot // michael@0: slotElement = PK11_GetFirstSafe(slotList); michael@0: nsAutoString tokenStr(unicodeTokenChosen); michael@0: while (slotElement) { michael@0: if (tokenStr.Equals(NS_ConvertUTF8toUTF16(PK11_GetTokenName(slotElement->slot)))) { michael@0: *aSlot = slotElement->slot; michael@0: PK11_FreeSlotListElement(slotList, slotElement); michael@0: break; michael@0: } michael@0: slotElement = PK11_GetNextSafe(slotList, slotElement, false); michael@0: } michael@0: if(!(*aSlot)) { michael@0: rv = NS_ERROR_FAILURE; michael@0: goto loser; michael@0: } michael@0: } michael@0: michael@0: // Get a reference to the slot // michael@0: PK11_ReferenceSlot(*aSlot); michael@0: loser: michael@0: if (slotList) { michael@0: PK11_FreeSlotList(slotList); michael@0: } michael@0: if (tokenNameList) { michael@0: NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(numSlots, tokenNameList); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsKeygenFormProcessor::GetPublicKey(nsAString& aValue, nsAString& aChallenge, michael@0: nsAFlatString& aKeyType, michael@0: nsAString& aOutPublicKey, nsAString& aKeyParams) michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: nsresult rv = NS_ERROR_FAILURE; michael@0: char *keystring = nullptr; michael@0: char *keyparamsString = nullptr, *str = nullptr; michael@0: uint32_t keyGenMechanism; michael@0: int32_t primeBits; michael@0: PK11SlotInfo *slot = nullptr; michael@0: PK11RSAGenParams rsaParams; michael@0: SECOidTag algTag; michael@0: int keysize = 0; michael@0: void *params; michael@0: SECKEYPrivateKey *privateKey = nullptr; michael@0: SECKEYPublicKey *publicKey = nullptr; michael@0: CERTSubjectPublicKeyInfo *spkInfo = nullptr; michael@0: PLArenaPool *arena = nullptr; michael@0: SECStatus sec_rv = SECFailure; michael@0: SECItem spkiItem; michael@0: SECItem pkacItem; michael@0: SECItem signedItem; michael@0: CERTPublicKeyAndChallenge pkac; michael@0: pkac.challenge.data = nullptr; michael@0: nsIGeneratingKeypairInfoDialogs * dialogs; michael@0: nsKeygenThread *KeygenRunnable = 0; michael@0: nsCOMPtr runnable; michael@0: michael@0: // permanent and sensitive flags for keygen michael@0: PK11AttrFlags attrFlags = PK11_ATTR_TOKEN | PK11_ATTR_SENSITIVE | PK11_ATTR_PRIVATE; michael@0: michael@0: // Get the key size // michael@0: for (size_t i = 0; i < number_of_key_size_choices; ++i) { michael@0: if (aValue.Equals(mSECKeySizeChoiceList[i].name)) { michael@0: keysize = mSECKeySizeChoiceList[i].size; michael@0: break; michael@0: } michael@0: } michael@0: if (!keysize) { michael@0: goto loser; michael@0: } michael@0: michael@0: arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); michael@0: if (!arena) { michael@0: goto loser; michael@0: } michael@0: michael@0: // Set the keygen mechanism michael@0: if (aKeyType.IsEmpty() || aKeyType.LowerCaseEqualsLiteral("rsa")) { michael@0: keyGenMechanism = CKM_RSA_PKCS_KEY_PAIR_GEN; michael@0: } else if (aKeyType.LowerCaseEqualsLiteral("dsa")) { michael@0: char * end; michael@0: keyparamsString = ToNewCString(aKeyParams); michael@0: if (!keyparamsString) { michael@0: rv = NS_ERROR_OUT_OF_MEMORY; michael@0: goto loser; michael@0: } michael@0: michael@0: keyGenMechanism = CKM_DSA_KEY_PAIR_GEN; michael@0: if (strcmp(keyparamsString, "null") == 0) michael@0: goto loser; michael@0: str = keyparamsString; michael@0: bool found_match = false; michael@0: do { michael@0: end = strchr(str, ','); michael@0: if (end) michael@0: *end = '\0'; michael@0: primeBits = pqg_prime_bits(str); michael@0: if (keysize == primeBits) { michael@0: found_match = true; michael@0: break; michael@0: } michael@0: str = end + 1; michael@0: } while (end); michael@0: if (!found_match) { michael@0: goto loser; michael@0: } michael@0: } else if (aKeyType.LowerCaseEqualsLiteral("ec")) { michael@0: keyparamsString = ToNewCString(aKeyParams); michael@0: if (!keyparamsString) { michael@0: rv = NS_ERROR_OUT_OF_MEMORY; michael@0: goto loser; michael@0: } michael@0: michael@0: keyGenMechanism = CKM_EC_KEY_PAIR_GEN; michael@0: /* ecParams are initialized later */ michael@0: } else { michael@0: goto loser; michael@0: } michael@0: michael@0: // Get the slot michael@0: rv = GetSlot(keyGenMechanism, &slot); michael@0: if (NS_FAILED(rv)) { michael@0: goto loser; michael@0: } michael@0: switch (keyGenMechanism) { michael@0: case CKM_RSA_PKCS_KEY_PAIR_GEN: michael@0: rsaParams.keySizeInBits = keysize; michael@0: rsaParams.pe = DEFAULT_RSA_KEYGEN_PE; michael@0: algTag = DEFAULT_RSA_KEYGEN_ALG; michael@0: params = &rsaParams; michael@0: break; michael@0: case CKM_DSA_KEY_PAIR_GEN: michael@0: // XXX Fix this! XXX // michael@0: goto loser; michael@0: case CKM_EC_KEY_PAIR_GEN: michael@0: /* XXX We ought to rethink how the KEYGEN tag is michael@0: * displayed. The pulldown selections presented michael@0: * to the user must depend on the keytype. michael@0: * The displayed selection could be picked michael@0: * from the keyparams attribute (this is currently called michael@0: * the pqg attribute). michael@0: * For now, we pick ecparams from the keyparams field michael@0: * if it specifies a valid supported curve, or else michael@0: * we pick one of secp384r1, secp256r1 or secp192r1 michael@0: * respectively depending on the user's selection michael@0: * (High, Medium, Low). michael@0: * (RSA uses RSA-2048, RSA-1024 and RSA-512 for historical michael@0: * reasons, while ECC choices represent a stronger mapping) michael@0: * NOTE: The user's selection michael@0: * is silently ignored when a valid curve is presented michael@0: * in keyparams. michael@0: */ michael@0: if ((params = decode_ec_params(keyparamsString)) == nullptr) { michael@0: /* The keyparams attribute did not specify a valid michael@0: * curve name so use a curve based on the keysize. 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: switch (keysize) { michael@0: case 2048: michael@0: params = decode_ec_params("secp384r1"); michael@0: break; michael@0: case 1024: michael@0: case 512: michael@0: params = decode_ec_params("secp256r1"); michael@0: break; michael@0: } michael@0: } michael@0: /* XXX The signature algorithm ought to choose the hashing michael@0: * algorithm based on key size once ECDSA variations based michael@0: * on SHA256 SHA384 and SHA512 are standardized. michael@0: */ michael@0: algTag = SEC_OID_ANSIX962_ECDSA_SIGNATURE_WITH_SHA1_DIGEST; michael@0: break; michael@0: default: michael@0: goto loser; michael@0: } michael@0: michael@0: /* Make sure token is initialized. */ michael@0: rv = setPassword(slot, m_ctx); michael@0: if (NS_FAILED(rv)) michael@0: goto loser; michael@0: michael@0: sec_rv = PK11_Authenticate(slot, true, m_ctx); michael@0: if (sec_rv != SECSuccess) { michael@0: goto loser; 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: NS_IF_ADDREF(KeygenRunnable); michael@0: } michael@0: michael@0: if (NS_FAILED(rv) || !KeygenRunnable) { michael@0: rv = NS_OK; michael@0: privateKey = PK11_GenerateKeyPairWithFlags(slot, keyGenMechanism, params, michael@0: &publicKey, attrFlags, m_ctx); michael@0: } else { michael@0: KeygenRunnable->SetParams( slot, attrFlags, nullptr, 0, michael@0: keyGenMechanism, params, m_ctx ); 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(m_ctx, 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, &privateKey, &publicKey); michael@0: if (NS_SUCCEEDED(rv) && used_slot) { michael@0: PK11_FreeSlot(used_slot); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (NS_FAILED(rv) || !privateKey) { michael@0: goto loser; michael@0: } michael@0: // just in case we'll need to authenticate to the db -jp // michael@0: privateKey->wincx = m_ctx; michael@0: michael@0: /* michael@0: * Create a subject public key info from the public key. michael@0: */ michael@0: spkInfo = SECKEY_CreateSubjectPublicKeyInfo(publicKey); michael@0: if ( !spkInfo ) { michael@0: goto loser; michael@0: } michael@0: michael@0: /* michael@0: * Now DER encode the whole subjectPublicKeyInfo. michael@0: */ michael@0: sec_rv=DER_Encode(arena, &spkiItem, CERTSubjectPublicKeyInfoTemplate, spkInfo); michael@0: if (sec_rv != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: michael@0: /* michael@0: * set up the PublicKeyAndChallenge data structure, then DER encode it michael@0: */ michael@0: pkac.spki = spkiItem; michael@0: pkac.challenge.len = aChallenge.Length(); michael@0: pkac.challenge.data = (unsigned char *)ToNewCString(aChallenge); michael@0: if (!pkac.challenge.data) { michael@0: rv = NS_ERROR_OUT_OF_MEMORY; michael@0: goto loser; michael@0: } michael@0: michael@0: sec_rv = DER_Encode(arena, &pkacItem, CERTPublicKeyAndChallengeTemplate, &pkac); michael@0: if ( sec_rv != SECSuccess ) { michael@0: goto loser; michael@0: } michael@0: michael@0: /* michael@0: * now sign the DER encoded PublicKeyAndChallenge michael@0: */ michael@0: sec_rv = SEC_DerSignData(arena, &signedItem, pkacItem.data, pkacItem.len, michael@0: privateKey, algTag); michael@0: if ( sec_rv != SECSuccess ) { michael@0: goto loser; michael@0: } michael@0: michael@0: /* michael@0: * Convert the signed public key and challenge into base64/ascii. michael@0: */ michael@0: keystring = BTOA_DataToAscii(signedItem.data, signedItem.len); michael@0: if (!keystring) { michael@0: rv = NS_ERROR_OUT_OF_MEMORY; michael@0: goto loser; michael@0: } michael@0: michael@0: CopyASCIItoUTF16(keystring, aOutPublicKey); michael@0: free(keystring); michael@0: michael@0: rv = NS_OK; michael@0: loser: michael@0: if ( sec_rv != SECSuccess ) { michael@0: if ( privateKey ) { michael@0: PK11_DestroyTokenObject(privateKey->pkcs11Slot,privateKey->pkcs11ID); michael@0: } michael@0: if ( publicKey ) { michael@0: PK11_DestroyTokenObject(publicKey->pkcs11Slot,publicKey->pkcs11ID); michael@0: } michael@0: } michael@0: if ( spkInfo ) { michael@0: SECKEY_DestroySubjectPublicKeyInfo(spkInfo); michael@0: } michael@0: if ( publicKey ) { michael@0: SECKEY_DestroyPublicKey(publicKey); michael@0: } michael@0: if ( privateKey ) { michael@0: SECKEY_DestroyPrivateKey(privateKey); michael@0: } michael@0: if ( arena ) { michael@0: PORT_FreeArena(arena, true); michael@0: } michael@0: if (slot) { michael@0: PK11_FreeSlot(slot); michael@0: } michael@0: if (KeygenRunnable) { michael@0: NS_RELEASE(KeygenRunnable); michael@0: } michael@0: if (keyparamsString) { michael@0: nsMemory::Free(keyparamsString); michael@0: } michael@0: if (pkac.challenge.data) { michael@0: nsMemory::Free(pkac.challenge.data); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: NS_METHOD michael@0: nsKeygenFormProcessor::ProcessValue(nsIDOMHTMLElement *aElement, michael@0: const nsAString& aName, michael@0: nsAString& aValue) michael@0: { michael@0: nsAutoString challengeValue; michael@0: nsAutoString keyTypeValue; michael@0: nsAutoString keyParamsValue; michael@0: michael@0: aElement->GetAttribute(NS_LITERAL_STRING("keytype"), keyTypeValue); michael@0: if (keyTypeValue.IsEmpty()) { michael@0: // If this field is not present, we default to rsa. michael@0: keyTypeValue.AssignLiteral("rsa"); michael@0: } michael@0: michael@0: aElement->GetAttribute(NS_LITERAL_STRING("pqg"), michael@0: keyParamsValue); michael@0: /* XXX We can still support the pqg attribute in the keygen michael@0: * tag for backward compatibility while introducing a more michael@0: * general attribute named keyparams. michael@0: */ michael@0: if (keyParamsValue.IsEmpty()) { michael@0: aElement->GetAttribute(NS_LITERAL_STRING("keyparams"), michael@0: keyParamsValue); michael@0: } michael@0: michael@0: aElement->GetAttribute(NS_LITERAL_STRING("challenge"), challengeValue); michael@0: michael@0: return GetPublicKey(aValue, challengeValue, keyTypeValue, michael@0: aValue, keyParamsValue); michael@0: } michael@0: michael@0: NS_METHOD nsKeygenFormProcessor::ProvideContent(const nsAString& aFormType, michael@0: nsTArray& aContent, michael@0: nsAString& aAttribute) michael@0: { michael@0: if (Compare(aFormType, NS_LITERAL_STRING("SELECT"), michael@0: nsCaseInsensitiveStringComparator()) == 0) { michael@0: michael@0: for (size_t i = 0; i < number_of_key_size_choices; ++i) { michael@0: aContent.AppendElement(mSECKeySizeChoiceList[i].name); michael@0: } michael@0: aAttribute.AssignLiteral("-mozilla-keygen"); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: