android/src/main/java/dev/mcodex/RNSensitiveInfo/RNSensitiveInfoModule.java
/**
     *  NOTE: In order to support our use-case with the current IAM module we make a
     *  trick where we store 2 keys one of which is used just for detection of changes in biometric data
     */
In order to support our use-case with the current IAM module we make a
trick where we store 2 keys one of which is used just for detection of changes in biometric data
In order to support our use - case with the current IAM module we make a trick where we store 2 keys one of which is used just for detection of changes in biometric data
private void decryptWithAes(final String encrypted, final boolean showModal, final HashMap strings, final Promise pm, final boolean authenticated) {
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M
                && hasSetupBiometricCredential()) {
            String[] inputs = encrypted.split(DELIMITER);
            if (inputs.length < 2) {
                pm.reject(AppConstants.KM_UNABLE_TO_DECRYPT_KEY, "DecryptionFailed");
            }
            try {
                class DecryptWithAesCallback extends BiometricPrompt.AuthenticationCallback {
                    @Override
                    public void onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationResult result) {
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                            decryptWithAes(encrypted, true, strings, pm, true);
                        }
                    }
                    @Override
                    public void onAuthenticationError(int errorCode, @NonNull CharSequence errString) {
                        pm.reject(String.valueOf(errorCode), errString.toString());
                    }
                    @Override
                    public void onAuthenticationFailed() {
                        getReactApplicationContext().getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
                                .emit(AppConstants.E_AUTHENTICATION_NOT_RECOGNIZED, "Authentication not recognized.");
                    }
                }
                if (!authenticated) {
                    byte[] iv = Base64.decode(inputs[0], Base64.DEFAULT);
                    Cipher cipher = Cipher.getInstance(AES_DEFAULT_TRANSFORMATION);
                    SecretKey secretKeyBiometric = (SecretKey) mKeyStore.getKey(KEY_AES_BIOMETRIC, null);
                    cipher.init(Cipher.DECRYPT_MODE, secretKeyBiometric, new GCMParameterSpec(GCM_TAG_LENGTH, iv));
                    showDialog(strings, new DecryptWithAesCallback(), new BiometricPrompt.CryptoObject(cipher));
                } else {
                    byte[] iv = Base64.decode(inputs[0], Base64.DEFAULT);
                    byte[] cipherBytes = Base64.decode(inputs[1], Base64.DEFAULT);
                    SecretKey secretKey = (SecretKey) mKeyStore.getKey(KEY_ALIAS_AES, null);
                    Cipher cipher = Cipher.getInstance(AES_DEFAULT_TRANSFORMATION);
                    cipher.init(Cipher.DECRYPT_MODE, secretKey, new GCMParameterSpec(GCM_TAG_LENGTH, iv));
                    SecretKeyFactory factory = SecretKeyFactory.getInstance(secretKey.getAlgorithm(), ANDROID_KEYSTORE_PROVIDER);
                    KeyInfo info = (KeyInfo) factory.getKeySpec(secretKey, KeyInfo.class);
                    byte[] decryptedBytes = cipher.doFinal(cipherBytes);
                    pm.resolve(new String(decryptedBytes));
                }
            } catch (InvalidKeyException | UnrecoverableKeyException e) {
                try {
                    mKeyStore.deleteEntry(KEY_ALIAS_AES);
                    prepareKey();
                } catch (Exception keyResetError) {
                    pm.reject(AppConstants.KM_UNABLE_TO_DECRYPT_KEY, keyResetError);
                }
                pm.reject(AppConstants.E_BIOMETRICS_INVALIDATED, e);
            } catch (IllegalBlockSizeException e){
                if(e.getCause() != null && e.getCause().getMessage().contains("Key user not authenticated")) {
                    try {
                        mKeyStore.deleteEntry(KEY_ALIAS_AES);
                        prepareKey();
                        pm.reject(AppConstants.KM_ERROR_KEY_USER_NOT_AUTHENTICATED, e.getCause().getMessage());
                    } catch (Exception keyResetError) {
                        pm.reject(AppConstants.KM_UNABLE_TO_DECRYPT_KEY, keyResetError);
                    }
                } else {
                    pm.reject(AppConstants.E_BIOMETRICS_INVALIDATED, e);
                }
            } catch (BadPaddingException e){
                Log.d("RNSensitiveInfo", "Biometric key invalid");
                pm.reject(AppConstants.E_BIOMETRICS_INVALIDATED, e.getCause().getMessage());
            } catch (SecurityException e) {
                pm.reject(AppConstants.E_SECURITY_VIOLATION, e);
            } catch (Exception e) {
                pm.reject(AppConstants.E_INIT_FAILURE, e);
            }
        } else {
            pm.reject(AppConstants.E_BIOMETRIC_NOT_SUPPORTED, "Biometrics not supported");
        }
    }
private void decryptWithAes ( final String encrypted , final boolean showModal , final HashMap strings , final Promise pm , final boolean authenticated ) { if ( android . os . Build . VERSION . SDK_INT >= android . os . Build . VERSION_CODES . M && hasSetupBiometricCredential ( ) ) { String [ ] inputs = encrypted . split ( DELIMITER ) ; if ( inputs . length < 2 ) { pm . reject ( AppConstants . KM_UNABLE_TO_DECRYPT_KEY , "DecryptionFailed" ) ; } try { class DecryptWithAesCallback extends BiometricPrompt . AuthenticationCallback { @ Override public void onAuthenticationSucceeded ( @ NonNull BiometricPrompt . AuthenticationResult result ) { if ( Build . VERSION . SDK_INT >= Build . VERSION_CODES . M ) { decryptWithAes ( encrypted , true , strings , pm , true ) ; } } @ Override public void onAuthenticationError ( int errorCode , @ NonNull CharSequence errString ) { pm . reject ( String . valueOf ( errorCode ) , errString . toString ( ) ) ; } @ Override public void onAuthenticationFailed ( ) { getReactApplicationContext ( ) . getJSModule ( DeviceEventManagerModule . RCTDeviceEventEmitter . class ) . emit ( AppConstants . E_AUTHENTICATION_NOT_RECOGNIZED , "Authentication not recognized." ) ; } } if ( ! authenticated ) { byte [ ] iv = Base64 . decode ( inputs [ 0 ] , Base64 . DEFAULT ) ; Cipher cipher = Cipher . getInstance ( AES_DEFAULT_TRANSFORMATION ) ; SecretKey secretKeyBiometric = ( SecretKey ) mKeyStore . getKey ( KEY_AES_BIOMETRIC , null ) ; cipher . init ( Cipher . DECRYPT_MODE , secretKeyBiometric , new GCMParameterSpec ( GCM_TAG_LENGTH , iv ) ) ; showDialog ( strings , new DecryptWithAesCallback ( ) , new BiometricPrompt . CryptoObject ( cipher ) ) ; } else { byte [ ] iv = Base64 . decode ( inputs [ 0 ] , Base64 . DEFAULT ) ; byte [ ] cipherBytes = Base64 . decode ( inputs [ 1 ] , Base64 . DEFAULT ) ; SecretKey secretKey = ( SecretKey ) mKeyStore . getKey ( KEY_ALIAS_AES , null ) ; Cipher cipher = Cipher . getInstance ( AES_DEFAULT_TRANSFORMATION ) ; cipher . init ( Cipher . DECRYPT_MODE , secretKey , new GCMParameterSpec ( GCM_TAG_LENGTH , iv ) ) ; SecretKeyFactory factory = SecretKeyFactory . getInstance ( secretKey . getAlgorithm ( ) , ANDROID_KEYSTORE_PROVIDER ) ; KeyInfo info = ( KeyInfo ) factory . getKeySpec ( secretKey , KeyInfo . class ) ; byte [ ] decryptedBytes = cipher . doFinal ( cipherBytes ) ; pm . resolve ( new String ( decryptedBytes ) ) ; } } catch ( InvalidKeyException | UnrecoverableKeyException e ) { try { mKeyStore . deleteEntry ( KEY_ALIAS_AES ) ; prepareKey ( ) ; } catch ( Exception keyResetError ) { pm . reject ( AppConstants . KM_UNABLE_TO_DECRYPT_KEY , keyResetError ) ; } pm . reject ( AppConstants . E_BIOMETRICS_INVALIDATED , e ) ; } catch ( IllegalBlockSizeException e ) { if ( e . getCause ( ) != null && e . getCause ( ) . getMessage ( ) . contains ( "Key user not authenticated" ) ) { try { mKeyStore . deleteEntry ( KEY_ALIAS_AES ) ; prepareKey ( ) ; pm . reject ( AppConstants . KM_ERROR_KEY_USER_NOT_AUTHENTICATED , e . getCause ( ) . getMessage ( ) ) ; } catch ( Exception keyResetError ) { pm . reject ( AppConstants . KM_UNABLE_TO_DECRYPT_KEY , keyResetError ) ; } } else { pm . reject ( AppConstants . E_BIOMETRICS_INVALIDATED , e ) ; } } catch ( BadPaddingException e ) { Log . d ( "RNSensitiveInfo" , "Biometric key invalid" ) ; pm . reject ( AppConstants . E_BIOMETRICS_INVALIDATED , e . getCause ( ) . getMessage ( ) ) ; } catch ( SecurityException e ) { pm . reject ( AppConstants . E_SECURITY_VIOLATION , e ) ; } catch ( Exception e ) { pm . reject ( AppConstants . E_INIT_FAILURE , e ) ; } } else { pm . reject ( AppConstants . E_BIOMETRIC_NOT_SUPPORTED , "Biometrics not supported" ) ; } }
NOTE: In order to support our use-case with the current IAM module we make a
  trick where we store 2 keys one of which is used just for detection of changes in biometric data
NOTE : In order to support our use - case with the current IAM module we make a trick where we store 2 keys one of which is used just for detection of changes in biometric data