Example #1
0
void
GatherKeygenTelemetry(uint32_t keyGenMechanism, int keysize, char* curve)
{
  if (keyGenMechanism == CKM_RSA_PKCS_KEY_PAIR_GEN) {
    if (keysize > 8196 || keysize < 0) {
      return;
    }

    nsCString telemetryValue("rsa");
    telemetryValue.AppendPrintf("%d", keysize);
    mozilla::Telemetry::Accumulate(
        mozilla::Telemetry::KEYGEN_GENERATED_KEY_TYPE, telemetryValue);
  } else if (keyGenMechanism == CKM_EC_KEY_PAIR_GEN) {
    nsCString secp384r1 = NS_LITERAL_CSTRING("secp384r1");
    nsCString secp256r1 = NS_LITERAL_CSTRING("secp256r1");

    SECKEYECParams* decoded = decode_ec_params(curve);
    if (!decoded) {
      switch (keysize) {
        case 2048:
          mozilla::Telemetry::Accumulate(
              mozilla::Telemetry::KEYGEN_GENERATED_KEY_TYPE, secp384r1);
          break;
        case 1024:
        case 512:
          mozilla::Telemetry::Accumulate(
              mozilla::Telemetry::KEYGEN_GENERATED_KEY_TYPE, secp256r1);
          break;
      }
    } else {
      SECITEM_FreeItem(decoded, true);
      if (secp384r1.EqualsIgnoreCase(curve, secp384r1.Length())) {
          mozilla::Telemetry::Accumulate(
              mozilla::Telemetry::KEYGEN_GENERATED_KEY_TYPE, secp384r1);
      } else if (secp256r1.EqualsIgnoreCase(curve, secp256r1.Length())) {
          mozilla::Telemetry::Accumulate(
              mozilla::Telemetry::KEYGEN_GENERATED_KEY_TYPE, secp256r1);
      } else {
        mozilla::Telemetry::Accumulate(
            mozilla::Telemetry::KEYGEN_GENERATED_KEY_TYPE, NS_LITERAL_CSTRING("other_ec"));
      }
    }
  } else {
    MOZ_CRASH("Unknown keygen algorithm");
    return;
  }
}
Example #2
0
nsresult
nsKeygenFormProcessor::GetPublicKey(const nsAString& aValue,
                                    const nsAString& aChallenge,
                                    const nsAFlatString& aKeyType,
                                    nsAString& aOutPublicKey,
                                    const nsAString& aKeyParams)
{
    nsNSSShutDownPreventionLock locker;
    if (isAlreadyShutDown()) {
      return NS_ERROR_NOT_AVAILABLE;
    }

    nsresult rv = NS_ERROR_FAILURE;
    char *keystring = nullptr;
    char *keyparamsString = nullptr;
    uint32_t keyGenMechanism;
    PK11SlotInfo *slot = nullptr;
    PK11RSAGenParams rsaParams;
    SECOidTag algTag;
    int keysize = 0;
    void *params = nullptr;
    SECKEYPrivateKey *privateKey = nullptr;
    SECKEYPublicKey *publicKey = nullptr;
    CERTSubjectPublicKeyInfo *spkInfo = nullptr;
    SECStatus srv = SECFailure;
    SECItem spkiItem;
    SECItem pkacItem;
    SECItem signedItem;
    CERTPublicKeyAndChallenge pkac;
    pkac.challenge.data = nullptr;
    nsIGeneratingKeypairInfoDialogs * dialogs;
    nsKeygenThread *KeygenRunnable = 0;
    nsCOMPtr<nsIKeygenThread> runnable;

    // permanent and sensitive flags for keygen
    PK11AttrFlags attrFlags = PK11_ATTR_TOKEN | PK11_ATTR_SENSITIVE | PK11_ATTR_PRIVATE;

    UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
    if (!arena) {
        goto loser;
    }

    // Get the key size //
    for (size_t i = 0; i < number_of_key_size_choices; ++i) {
        if (aValue.Equals(mSECKeySizeChoiceList[i].name)) {
            keysize = mSECKeySizeChoiceList[i].size;
            break;
        }
    }
    if (!keysize) {
        goto loser;
    }

    // Set the keygen mechanism
    if (aKeyType.IsEmpty() || aKeyType.LowerCaseEqualsLiteral("rsa")) {
        keyGenMechanism = CKM_RSA_PKCS_KEY_PAIR_GEN;
    } else if (aKeyType.LowerCaseEqualsLiteral("ec")) {
        keyparamsString = ToNewCString(aKeyParams);
        if (!keyparamsString) {
            rv = NS_ERROR_OUT_OF_MEMORY;
            goto loser;
        }

        keyGenMechanism = CKM_EC_KEY_PAIR_GEN;
        /* ecParams are initialized later */
    } else {
        goto loser;
    }

    // Get the slot
    rv = GetSlot(keyGenMechanism, &slot);
    if (NS_FAILED(rv)) {
        goto loser;
    }
    switch (keyGenMechanism) {
        case CKM_RSA_PKCS_KEY_PAIR_GEN:
            rsaParams.keySizeInBits = keysize;
            rsaParams.pe = DEFAULT_RSA_KEYGEN_PE;
            algTag = DEFAULT_RSA_KEYGEN_ALG;
            params = &rsaParams;
            break;
        case CKM_EC_KEY_PAIR_GEN:
            /* XXX We ought to rethink how the KEYGEN tag is 
             * displayed. The pulldown selections presented
             * to the user must depend on the keytype.
             * The displayed selection could be picked
             * from the keyparams attribute (this is currently called
             * the pqg attribute).
             * For now, we pick ecparams from the keyparams field
             * if it specifies a valid supported curve, or else 
             * we pick one of secp384r1, secp256r1 or secp192r1
             * respectively depending on the user's selection
             * (High, Medium, Low). 
             * (RSA uses RSA-2048, RSA-1024 and RSA-512 for historical
             * reasons, while ECC choices represent a stronger mapping)
             * NOTE: The user's selection
             * is silently ignored when a valid curve is presented
             * in keyparams.
             */
            if ((params = decode_ec_params(keyparamsString)) == nullptr) {
                /* The keyparams attribute did not specify a valid
                 * curve name so use a curve based on the keysize.
                 * NOTE: Here keysize is used only as an indication of
                 * High/Medium/Low strength; elliptic curve
                 * cryptography uses smaller keys than RSA to provide
                 * equivalent security.
                 */
                switch (keysize) {
                case 2048:
                    params = decode_ec_params("secp384r1");
                    break;
                case 1024:
                case 512:
                    params = decode_ec_params("secp256r1");
                    break;
                } 
            }
            /* XXX The signature algorithm ought to choose the hashing
             * algorithm based on key size once ECDSA variations based
             * on SHA256 SHA384 and SHA512 are standardized.
             */
            algTag = SEC_OID_ANSIX962_ECDSA_SIGNATURE_WITH_SHA1_DIGEST;
            break;
      default:
          goto loser;
      }

    /* Make sure token is initialized. */
    rv = setPassword(slot, m_ctx, locker);
    if (NS_FAILED(rv))
        goto loser;

    srv = PK11_Authenticate(slot, true, m_ctx);
    if (srv != SECSuccess) {
        goto loser;
    }

    rv = getNSSDialogs((void**)&dialogs,
                       NS_GET_IID(nsIGeneratingKeypairInfoDialogs),
                       NS_GENERATINGKEYPAIRINFODIALOGS_CONTRACTID);

    if (NS_SUCCEEDED(rv)) {
        KeygenRunnable = new nsKeygenThread();
        NS_IF_ADDREF(KeygenRunnable);
    }

    if (NS_FAILED(rv) || !KeygenRunnable) {
        rv = NS_OK;
        privateKey = PK11_GenerateKeyPairWithFlags(slot, keyGenMechanism, params,
                                                   &publicKey, attrFlags, m_ctx);
    } else {
        KeygenRunnable->SetParams( slot, attrFlags, nullptr, 0,
                                   keyGenMechanism, params, m_ctx );

        runnable = do_QueryInterface(KeygenRunnable);
        
        if (runnable) {
            rv = dialogs->DisplayGeneratingKeypairInfo(m_ctx, runnable);
            // We call join on the thread so we can be sure that no
            // simultaneous access to the passed parameters will happen.
            KeygenRunnable->Join();

            NS_RELEASE(dialogs);
            if (NS_SUCCEEDED(rv)) {
                PK11SlotInfo *used_slot = nullptr;
                rv = KeygenRunnable->ConsumeResult(&used_slot, &privateKey, &publicKey);
                if (NS_SUCCEEDED(rv) && used_slot) {
                  PK11_FreeSlot(used_slot);
                }
            }
        }
    }
    
    if (NS_FAILED(rv) || !privateKey) {
        goto loser;
    }
    // just in case we'll need to authenticate to the db -jp //
    privateKey->wincx = m_ctx;

    /*
     * Create a subject public key info from the public key.
     */
    spkInfo = SECKEY_CreateSubjectPublicKeyInfo(publicKey);
    if ( !spkInfo ) {
        goto loser;
    }

    /*
     * Now DER encode the whole subjectPublicKeyInfo.
     */
    srv = DER_Encode(arena.get(), &spkiItem, CERTSubjectPublicKeyInfoTemplate,
                     spkInfo);
    if (srv != SECSuccess) {
        goto loser;
    }

    /*
     * set up the PublicKeyAndChallenge data structure, then DER encode it
     */
    pkac.spki = spkiItem;
    pkac.challenge.len = aChallenge.Length();
    pkac.challenge.data = (unsigned char *)ToNewCString(aChallenge);
    if (!pkac.challenge.data) {
        rv = NS_ERROR_OUT_OF_MEMORY;
        goto loser;
    }

    srv = DER_Encode(arena.get(), &pkacItem, CERTPublicKeyAndChallengeTemplate,
                     &pkac);
    if (srv != SECSuccess) {
        goto loser;
    }

    /*
     * now sign the DER encoded PublicKeyAndChallenge
     */
    srv = SEC_DerSignData(arena.get(), &signedItem, pkacItem.data, pkacItem.len,
                          privateKey, algTag);
    if (srv != SECSuccess) {
        goto loser;
    }

    /*
     * Convert the signed public key and challenge into base64/ascii.
     */
    keystring = BTOA_DataToAscii(signedItem.data, signedItem.len);
    if (!keystring) {
        rv = NS_ERROR_OUT_OF_MEMORY;
        goto loser;
    }

    CopyASCIItoUTF16(keystring, aOutPublicKey);
    free(keystring);

    rv = NS_OK;

    GatherKeygenTelemetry(keyGenMechanism, keysize, keyparamsString);
loser:
    if (srv != SECSuccess) {
        if ( privateKey ) {
            PK11_DestroyTokenObject(privateKey->pkcs11Slot,privateKey->pkcs11ID);
        }
        if ( publicKey ) {
            PK11_DestroyTokenObject(publicKey->pkcs11Slot,publicKey->pkcs11ID);
        }
    }
    if ( spkInfo ) {
        SECKEY_DestroySubjectPublicKeyInfo(spkInfo);
    }
    if ( publicKey ) {
        SECKEY_DestroyPublicKey(publicKey);
    }
    if ( privateKey ) {
        SECKEY_DestroyPrivateKey(privateKey);
    }
    if (slot) {
        PK11_FreeSlot(slot);
    }
    if (KeygenRunnable) {
        NS_RELEASE(KeygenRunnable);
    }
    if (keyparamsString) {
        free(keyparamsString);
    }
    if (pkac.challenge.data) {
        free(pkac.challenge.data);
    }
    // If params is non-null and doesn't point to rsaParams, it was allocated
    // in decode_ec_params. We have to free this memory.
    if (params && params != &rsaParams) {
        SECITEM_FreeItem(static_cast<SECItem*>(params), true);
        params = nullptr;
    }
    return rv;
}
Example #3
0
nsresult
nsKeygenFormProcessor::GetPublicKey(nsAString& aValue, nsAString& aChallenge,
                                    nsAFlatString& aKeyType,
                                    nsAString& aOutPublicKey, nsAString& aKeyParams)
{
    nsNSSShutDownPreventionLock locker;
    nsresult rv = NS_ERROR_FAILURE;
    char *keystring = nsnull;
    char *keyparamsString = nsnull, *str = nsnull;
    KeyType type;
    PRUint32 keyGenMechanism;
    PRInt32 primeBits;
    PK11SlotInfo *slot = nsnull;
    PK11RSAGenParams rsaParams;
    SECOidTag algTag;
    int keysize = 0;
    void *params;
    SECKEYPrivateKey *privateKey = nsnull;
    SECKEYPublicKey *publicKey = nsnull;
    CERTSubjectPublicKeyInfo *spkInfo = nsnull;
    PRArenaPool *arena = nsnull;
    SECStatus sec_rv = SECFailure;
    SECItem spkiItem;
    SECItem pkacItem;
    SECItem signedItem;
    CERTPublicKeyAndChallenge pkac;
    pkac.challenge.data = nsnull;
    nsIGeneratingKeypairInfoDialogs * dialogs;
    nsKeygenThread *KeygenRunnable = 0;
    nsCOMPtr<nsIKeygenThread> runnable;

    // Get the key size //
    for (size_t i = 0; i < number_of_key_size_choices; ++i) {
        if (aValue.Equals(mSECKeySizeChoiceList[i].name)) {
            keysize = mSECKeySizeChoiceList[i].size;
            break;
        }
    }
    if (!keysize) {
        goto loser;
    }

    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    if (!arena) {
        goto loser;
    }

    // Set the keygen mechanism
    if (aKeyType.IsEmpty() || aKeyType.LowerCaseEqualsLiteral("rsa")) {
        type = rsaKey;
        keyGenMechanism = CKM_RSA_PKCS_KEY_PAIR_GEN;
    } else if (aKeyType.LowerCaseEqualsLiteral("dsa")) {
        char * end;
        keyparamsString = ToNewCString(aKeyParams);
        if (!keyparamsString) {
            rv = NS_ERROR_OUT_OF_MEMORY;
            goto loser;
        }

        type = dsaKey;
        keyGenMechanism = CKM_DSA_KEY_PAIR_GEN;
        if (strcmp(keyparamsString, "null") == 0)
            goto loser;
        str = keyparamsString;
        PRBool found_match = PR_FALSE;
        do {
            end = strchr(str, ',');
            if (end != nsnull)
                *end = '\0';
            primeBits = pqg_prime_bits(str);
            if (keysize == primeBits) {
                found_match = PR_TRUE;
                break;
            }
            str = end + 1;
        } while (end != nsnull);
        if (!found_match) {
            goto loser;
        }
    } else if (aKeyType.LowerCaseEqualsLiteral("ec")) {
        keyparamsString = ToNewCString(aKeyParams);
        if (!keyparamsString) {
            rv = NS_ERROR_OUT_OF_MEMORY;
            goto loser;
        }

        type = ecKey;
        keyGenMechanism = CKM_EC_KEY_PAIR_GEN;
        /* ecParams are initialized later */
    } else {
        goto loser;
    }

    // Get the slot
    rv = GetSlot(keyGenMechanism, &slot);
    if (NS_FAILED(rv)) {
        goto loser;
    }
    switch (keyGenMechanism) {
    case CKM_RSA_PKCS_KEY_PAIR_GEN:
        rsaParams.keySizeInBits = keysize;
        rsaParams.pe = DEFAULT_RSA_KEYGEN_PE;
        algTag = DEFAULT_RSA_KEYGEN_ALG;
        params = &rsaParams;
        break;
    case CKM_DSA_KEY_PAIR_GEN:
        // XXX Fix this! XXX //
        goto loser;
    case CKM_EC_KEY_PAIR_GEN:
        /* XXX We ought to rethink how the KEYGEN tag is
         * displayed. The pulldown selections presented
         * to the user must depend on the keytype.
         * The displayed selection could be picked
         * from the keyparams attribute (this is currently called
         * the pqg attribute).
         * For now, we pick ecparams from the keyparams field
         * if it specifies a valid supported curve, or else
         * we pick one of secp384r1, secp256r1 or secp192r1
         * respectively depending on the user's selection
         * (High, Medium, Low).
         * (RSA uses RSA-2048, RSA-1024 and RSA-512 for historical
         * reasons, while ECC choices represent a stronger mapping)
         * NOTE: The user's selection
         * is silently ignored when a valid curve is presented
         * in keyparams.
         */
        if ((params = decode_ec_params(keyparamsString)) == nsnull) {
            /* The keyparams attribute did not specify a valid
             * curve name so use a curve based on the keysize.
             * NOTE: Here keysize is used only as an indication of
             * High/Medium/Low strength; elliptic curve
             * cryptography uses smaller keys than RSA to provide
             * equivalent security.
             */
            switch (keysize) {
            case 2048:
                params = decode_ec_params("secp384r1");
                break;
            case 1024:
            case 512:
                params = decode_ec_params("secp256r1");
                break;
            }
        }
        /* XXX The signature algorithm ought to choose the hashing
         * algorithm based on key size once ECDSA variations based
         * on SHA256 SHA384 and SHA512 are standardized.
         */
        algTag = SEC_OID_ANSIX962_ECDSA_SIGNATURE_WITH_SHA1_DIGEST;
        break;
    default:
        goto loser;
    }

    /* Make sure token is initialized. */
    rv = setPassword(slot, m_ctx);
    if (NS_FAILED(rv))
        goto loser;

    sec_rv = PK11_Authenticate(slot, PR_TRUE, m_ctx);
    if (sec_rv != SECSuccess) {
        goto loser;
    }

    rv = getNSSDialogs((void**)&dialogs,
                       NS_GET_IID(nsIGeneratingKeypairInfoDialogs),
                       NS_GENERATINGKEYPAIRINFODIALOGS_CONTRACTID);

    if (NS_SUCCEEDED(rv)) {
        KeygenRunnable = new nsKeygenThread();
        NS_IF_ADDREF(KeygenRunnable);
    }

    if (NS_FAILED(rv) || !KeygenRunnable) {
        rv = NS_OK;
        privateKey = PK11_GenerateKeyPair(slot, keyGenMechanism, params,
                                          &publicKey, PR_TRUE, PR_TRUE, m_ctx);
    } else {
        KeygenRunnable->SetParams( slot, keyGenMechanism, params, PR_TRUE, PR_TRUE, m_ctx );

        runnable = do_QueryInterface(KeygenRunnable);

        if (runnable) {
            {
                nsPSMUITracker tracker;
                if (tracker.isUIForbidden()) {
                    rv = NS_ERROR_NOT_AVAILABLE;
                }
                else {
                    rv = dialogs->DisplayGeneratingKeypairInfo(m_ctx, runnable);
                    // We call join on the thread,
                    // so we can be sure that no simultaneous access to the passed parameters will happen.
                    KeygenRunnable->Join();
                }
            }

            NS_RELEASE(dialogs);
            if (NS_SUCCEEDED(rv)) {
                rv = KeygenRunnable->GetParams(&privateKey, &publicKey);
            }
        }
    }

    if (NS_FAILED(rv) || !privateKey) {
        goto loser;
    }
    // just in case we'll need to authenticate to the db -jp //
    privateKey->wincx = m_ctx;

    /*
     * Create a subject public key info from the public key.
     */
    spkInfo = SECKEY_CreateSubjectPublicKeyInfo(publicKey);
    if ( !spkInfo ) {
        goto loser;
    }

    /*
     * Now DER encode the whole subjectPublicKeyInfo.
     */
    sec_rv=DER_Encode(arena, &spkiItem, CERTSubjectPublicKeyInfoTemplate, spkInfo);
    if (sec_rv != SECSuccess) {
        goto loser;
    }

    /*
     * set up the PublicKeyAndChallenge data structure, then DER encode it
     */
    pkac.spki = spkiItem;
    pkac.challenge.len = aChallenge.Length();
    pkac.challenge.data = (unsigned char *)ToNewCString(aChallenge);
    if (!pkac.challenge.data) {
        rv = NS_ERROR_OUT_OF_MEMORY;
        goto loser;
    }

    sec_rv = DER_Encode(arena, &pkacItem, CERTPublicKeyAndChallengeTemplate, &pkac);
    if ( sec_rv != SECSuccess ) {
        goto loser;
    }

    /*
     * now sign the DER encoded PublicKeyAndChallenge
     */
    sec_rv = SEC_DerSignData(arena, &signedItem, pkacItem.data, pkacItem.len,
                             privateKey, algTag);
    if ( sec_rv != SECSuccess ) {
        goto loser;
    }

    /*
     * Convert the signed public key and challenge into base64/ascii.
     */
    keystring = BTOA_DataToAscii(signedItem.data, signedItem.len);
    if (!keystring) {
        rv = NS_ERROR_OUT_OF_MEMORY;
        goto loser;
    }

    CopyASCIItoUTF16(keystring, aOutPublicKey);
    nsCRT::free(keystring);

    rv = NS_OK;
loser:
    if ( sec_rv != SECSuccess ) {
        if ( privateKey ) {
            PK11_DestroyTokenObject(privateKey->pkcs11Slot,privateKey->pkcs11ID);
        }
        if ( publicKey ) {
            PK11_DestroyTokenObject(publicKey->pkcs11Slot,publicKey->pkcs11ID);
        }
    }
    if ( spkInfo ) {
        SECKEY_DestroySubjectPublicKeyInfo(spkInfo);
    }
    if ( publicKey ) {
        SECKEY_DestroyPublicKey(publicKey);
    }
    if ( privateKey ) {
        SECKEY_DestroyPrivateKey(privateKey);
    }
    if ( arena ) {
        PORT_FreeArena(arena, PR_TRUE);
    }
    if (slot != nsnull) {
        PK11_FreeSlot(slot);
    }
    if (KeygenRunnable) {
        NS_RELEASE(KeygenRunnable);
    }
    if (keyparamsString) {
        nsMemory::Free(keyparamsString);
    }
    if (pkac.challenge.data) {
        nsMemory::Free(pkac.challenge.data);
    }
    return rv;
}