CVE-2024-39846 (Java, published 2024-06-29T21:15Z)
Description: value: NewPass before 1.2.0 stores passwords (rather than password hashes) directly, which makes it easier to obtain unauthorized access to sensitive information. NOTE: in each case, data at rest is encrypted, but is decrypted within process memory during use.
Severity: nan (CVSS v3 base score: )
Weakness: NVD-CWE-noinfo \u2014 Insufficient Information
There is insufficient information about the issue to classify it; details are unkown or unspecified.
Repository: https://github.com/6eero/NewPass
Files changed:
app/src/main/java/com/gero/newpass/encryption/PasswordUtils.java
app/src/main/java/com/gero/newpass/view/activities/LoginActivity.java
app/src/main/java/com/gero/newpass/viewmodel/LoginViewModel.java
Commit message: Feature: Store password hash in encrypted shared preferences

Previously, the database encryption password (used for login as well and created during the first launch) was directly stored encrypted in EncryptedSharedPreferences. With this commit, when a user inputs the password to create the user during the first launch, the password is hashed using PBKDF2WithHmacSHA256, and the resulting hash is stored in encrypted shared preferences.

Then, every time the user inputs the known password to login, the password is hashed using the same algorithm, the same number of iterations, and is confronted with the (hashed) password retrieved from the EncryptedSharedPreferences.

This improves security by ensuring that the actual password is not stored directly, but rather its hash, making it more difficult for unauthorized access to obtain sensitive information.


Vulnerable code:
            String savedPasswordSharedPreferences = encryptedSharedPreferences.getString("password", "");
                StringHelper.setSharedString(savedPasswordSharedPreferences);
        String password = encryptedSharedPreferences.getString("password", "");
        Boolean isPasswordEmpty = password.isEmpty();
            loginViewModel.createUser(passwordInput, encryptedSharedPreferences);
                    loginViewModel.loginUserWithPassword(passwordInput, encryptedSharedPreferences);
    public void loginUserWithPassword(String password, EncryptedSharedPreferences sharedPreferences) {
        String savedPasswordSharedPreferences = sharedPreferences.getString("password", "");
        if (savedPasswordSharedPreferences.equals(password)) {
    public void createUser(String password, EncryptedSharedPreferences sharedPreferences) {
            editor.putString("password", password);

Fixed code:
package com.gero.newpass.encryption;

import android.util.Base64;
import android.util.Log;

import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;

import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;

public class PasswordUtils {

    private static final int SALT_LENGTH = 16;   // in bytes (at least 16 bytes)
    private static final int HASH_LENGTH = 256; // in bits
    private static final int ITERATIONS = 10000;

    public static String hashPassword(String password) throws NoSuchAlgorithmException, InvalidKeySpecException {

        SecureRandom random = new SecureRandom();
        byte[] salt = new byte[SALT_LENGTH];
        random.nextBytes(salt);


        PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, ITERATIONS, HASH_LENGTH);
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
        byte[] hash = keyFactory.generateSecret(spec).getEncoded();

        String saltBase64 = Base64.encodeToString(salt, Base64.NO_WRAP);
        String hashBase64 = Base64.encodeToString(hash, Base64.NO_WRAP);

        return saltBase64 + ":" + hashBase64;
    }

    public static boolean verifyPassword(String password, String storedPassword) throws NoSuchAlgorithmException, InvalidKeySpecException {

        String[] parts = storedPassword.split(":");

        if (parts.length != 2) {
            throw new IllegalArgumentException("Stored password must have the format 'salt:hash'");
        }

        String saltBase64 = parts[0];
        String hashBase64 = parts[1];

        byte[] salt = Base64.decode(saltBase64, Base64.NO_WRAP);
        byte[] hash = Base64.decode(hashBase64, Base64.NO_WRAP);

        /*
        The password provided by the user during the login attempt is hashed using the same algorithm,
        the same number of iterations and the same salt retrieved from the database.
         */
        PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, ITERATIONS, hash.length * 8);
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
        byte[] testHash = keyFactory.generateSecret(spec).getEncoded();

        return java.util.Arrays.equals(hash, testHash);
    }
}
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
            String hashedPassword = encryptedSharedPreferences.getString("password", "");
                StringHelper.setSharedString(hashedPassword);
        String hashedPassword = encryptedSharedPreferences.getString("password", "");
        Boolean isPasswordEmpty = hashedPassword.isEmpty();
            try {
                loginViewModel.createUser(passwordInput, encryptedSharedPreferences);
            } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
                throw new RuntimeException(e);
            }
                    try {
                        loginViewModel.loginUserWithPassword(passwordInput, encryptedSharedPreferences);
                    } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
                        throw new RuntimeException(e);
                    }
import com.gero.newpass.encryption.PasswordUtils;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
    public void loginUserWithPassword(String password, EncryptedSharedPreferences sharedPreferences) throws NoSuchAlgorithmException, InvalidKeySpecException {
        String hashedPassword = sharedPreferences.getString("password", "");
        if (PasswordUtils.verifyPassword(password, hashedPassword)) {
    public void createUser(String password, EncryptedSharedPreferences sharedPreferences) throws NoSuchAlgorithmException, InvalidKeySpecException {

            String hashedPassword = PasswordUtils.hashPassword(password);
            editor.putString("password", hashedPassword);