diff --git a/README.md b/README.md index c3be50b7..3d0747e7 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![Travis](https://img.shields.io/travis/oblador/react-native-keychain.svg)](https://travis-ci.org/oblador/react-native-keychain) [![npm](https://img.shields.io/npm/v/react-native-keychain.svg)](https://npmjs.com/package/react-native-keychain) [![npm](https://img.shields.io/npm/dm/react-native-keychain.svg)](https://npmjs.com/package/react-native-keychain) -Keychain/Keystore Access for React Native. +Keychain/Keystore Access for React Native. ## Installation @@ -91,9 +91,9 @@ Inquire if the type of local authentication policy is supported on this device w Get what type of hardware biometry support the device has. Resolves to a `Keychain.BIOMETRY_TYPE` value when supported, otherwise `null`. > This method returns `null`, if the device haven't enrolled into fingerprint/FaceId. Even though it has hardware for it. -### `getSecurityLevel()` (Android only) +### `getSecurityLevel({ accessControl })` (Android only) -Get security level that is supported on the current device with the current OS. +Get security level that is supported on the current device with the current OS for the `accessControl` option. ### Security Levels (Android only) @@ -107,11 +107,11 @@ If set, `securityLevel` parameter specifies minimum security level that the encr | Key | Platform | Description | Default | |---|---|---|---| -|**`accessControl`**|iOS only|This dictates how a keychain item may be used, see possible values in `Keychain.ACCESS_CONTROL`. |*None*| +|**`accessControl`**|All|This dictates how a keychain item may be used, see possible values in `Keychain.ACCESS_CONTROL`. |*None*| |**`accessible`**|iOS only|This dictates when a keychain item is accessible, see possible values in `Keychain.ACCESSIBLE`. |*`Keychain.ACCESSIBLE.WHEN_UNLOCKED`*| |**`accessGroup`**|iOS only|In which App Group to share the keychain. Requires additional setup with entitlements. |*None*| |**`authenticationPrompt`**|iOS only|What to prompt the user when unlocking the keychain with biometry or device password. |`Authenticate to retrieve secret`| -|**`authenticationType`**|iOS only|Policies specifying which forms of authentication are acceptable. |`Keychain.AUTHENTICATION_TYPE.DEVICE_PASSCODE_OR_BIOMETRICS`| +|**`authenticationType`**|All|Policies specifying which forms of authentication are acceptable. |`Keychain.AUTHENTICATION_TYPE.DEVICE_PASSCODE_OR_BIOMETRICS`| |**`service`**|All|Reverse domain name qualifier for the service associated with password. |*App bundle ID*| #### `Keychain.ACCESS_CONTROL` enum @@ -200,7 +200,7 @@ include ':app' + project(':react-native-keychain').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-keychain/android') ``` -* Edit `android/app/build.gradle` (note: **app** folder) to look like this: +* Edit `android/app/build.gradle` (note: **app** folder) to look like this: ```diff apply plugin: 'com.android.application' @@ -238,7 +238,7 @@ public class MainActivity extends extends ReactActivity { ... } ``` - + #### Proguard Rules On Android builds that use proguard (like release), you may see the following error: @@ -298,7 +298,7 @@ Now your tests should run successfully, though note that writing and reading to ## Notes -### Android +### Android The module will automatically use the appropriate CipherStorage implementation based on API level: diff --git a/RNKeychainManager/RNKeychainManager.m b/RNKeychainManager/RNKeychainManager.m index c4ad7e59..da6d8b04 100644 --- a/RNKeychainManager/RNKeychainManager.m +++ b/RNKeychainManager/RNKeychainManager.m @@ -285,7 +285,7 @@ - (OSStatus)deleteCredentialsForServer:(NSString *)server } #endif -RCT_EXPORT_METHOD(setGenericPasswordForOptions:(NSDictionary *)options withUsername:(NSString *)username withPassword:(NSString *)password withSecurityLevel:(__unused NSString *)level resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) +RCT_EXPORT_METHOD(setGenericPasswordForOptions:(NSDictionary *)options withUsername:(NSString *)username withPassword:(NSString *)password withSecurityLevel:(__unused NSString *)level withAccessControl:(__unused NSString *)accessControl resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { NSString *service = serviceValue(options); NSDictionary *attributes = attributes = @{ @@ -359,7 +359,7 @@ - (OSStatus)deleteCredentialsForServer:(NSString *)server return resolve(@(YES)); } -RCT_EXPORT_METHOD(setInternetCredentialsForServer:(NSString *)server withUsername:(NSString*)username withPassword:(NSString*)password withSecurityLevel:(__unused NSString *)level withOptions:(NSDictionary *)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) +RCT_EXPORT_METHOD(setInternetCredentialsForServer:(NSString *)server withUsername:(NSString*)username withPassword:(NSString*)password withSecurityLevel:(__unused NSString *)level withAccessControl:(__unused NSString *)accessControl withOptions:(NSDictionary *)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { [self deleteCredentialsForServer:server]; diff --git a/android/build.gradle b/android/build.gradle index bd2fe043..2cb95242 100755 --- a/android/build.gradle +++ b/android/build.gradle @@ -52,4 +52,5 @@ dependencies { implementation 'com.facebook.react:react-native:+' // From node_modules implementation 'androidx.annotation:annotation:1.1.0' implementation 'com.facebook.conceal:conceal:1.1.3@aar' + implementation "androidx.biometric:biometric:1.0.0-rc01" } diff --git a/android/gradle/wrapper/gradle-wrapper.jar b/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..f6b961fd Binary files /dev/null and b/android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..2d5100df --- /dev/null +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Sat Oct 19 13:24:56 BST 2019 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip diff --git a/android/gradlew b/android/gradlew new file mode 100644 index 00000000..cccdd3d5 --- /dev/null +++ b/android/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/android/gradlew.bat b/android/gradlew.bat new file mode 100644 index 00000000..e95643d6 --- /dev/null +++ b/android/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index ddbe5066..dbefd667 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -1,4 +1,6 @@ - + + + diff --git a/android/src/main/java/com/oblador/keychain/DeviceAvailability.java b/android/src/main/java/com/oblador/keychain/DeviceAvailability.java index 090d47bb..e42dc100 100644 --- a/android/src/main/java/com/oblador/keychain/DeviceAvailability.java +++ b/android/src/main/java/com/oblador/keychain/DeviceAvailability.java @@ -1,5 +1,7 @@ package com.oblador.keychain; +import android.Manifest; +import android.content.pm.PackageManager; import android.os.Build; import android.content.Context; import android.app.KeyguardManager; @@ -10,7 +12,9 @@ public static boolean isFingerprintAuthAvailable(Context context) { if (android.os.Build.VERSION.SDK_INT >= 23) { FingerprintManager fingerprintManager = (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE); - return fingerprintManager != null && fingerprintManager.isHardwareDetected() && + return fingerprintManager != null && + context.checkSelfPermission(Manifest.permission.USE_FINGERPRINT) == PackageManager.PERMISSION_GRANTED && + fingerprintManager.isHardwareDetected() && fingerprintManager.hasEnrolledFingerprints(); } return false; diff --git a/android/src/main/java/com/oblador/keychain/KeychainModule.java b/android/src/main/java/com/oblador/keychain/KeychainModule.java index 61d061e8..39044022 100644 --- a/android/src/main/java/com/oblador/keychain/KeychainModule.java +++ b/android/src/main/java/com/oblador/keychain/KeychainModule.java @@ -11,23 +11,28 @@ import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.WritableMap; + import com.oblador.keychain.PrefsStorage.ResultSet; import com.oblador.keychain.cipherStorage.CipherStorage; import com.oblador.keychain.cipherStorage.CipherStorage.DecryptionResult; import com.oblador.keychain.cipherStorage.CipherStorage.EncryptionResult; +import com.oblador.keychain.cipherStorage.CipherStorage.DecryptionResultHandler; import com.oblador.keychain.cipherStorage.CipherStorageFacebookConceal; import com.oblador.keychain.cipherStorage.CipherStorageKeystoreAESCBC; +import com.oblador.keychain.cipherStorage.CipherStorageKeystoreRSAECB; import com.oblador.keychain.exceptions.CryptoFailedException; import com.oblador.keychain.exceptions.EmptyParameterException; import com.oblador.keychain.exceptions.KeyStoreAccessException; +import java.security.InvalidKeyException; import java.util.HashMap; import java.util.Map; import javax.annotation.Nullable; -public class KeychainModule extends ReactContextBaseJavaModule { +import androidx.fragment.app.FragmentActivity; +public class KeychainModule extends ReactContextBaseJavaModule { public static final String E_EMPTY_PARAMETERS = "E_EMPTY_PARAMETERS"; public static final String E_CRYPTO_FAILED = "E_CRYPTO_FAILED"; public static final String E_KEYSTORE_ACCESS_ERROR = "E_KEYSTORE_ACCESS_ERROR"; @@ -36,6 +41,14 @@ public class KeychainModule extends ReactContextBaseJavaModule { public static final String FINGERPRINT_SUPPORTED_NAME = "Fingerprint"; public static final String EMPTY_STRING = ""; + + public static final String AUTHENTICATION_TYPE_KEY = "authenticationType"; + public static final String AUTHENTICATION_TYPE_DEVICE_PASSCODE_OR_BIOMETRICS = "AuthenticationWithBiometricsDevicePasscode"; + public static final String AUTHENTICATION_TYPE_BIOMETRICS = "AuthenticationWithBiometrics"; + + public static final String ACCESS_CONTROL_BIOMETRY_ANY = "BiometryAny"; + public static final String ACCESS_CONTROL_BIOMETRY_CURRENT_SET = "BiometryCurrentSet"; + private final Map cipherStorageMap = new HashMap<>(); private final PrefsStorage prefsStorage; @@ -50,6 +63,9 @@ public KeychainModule(ReactApplicationContext reactContext) { addCipherStorageToMap(new CipherStorageFacebookConceal(reactContext)); addCipherStorageToMap(new CipherStorageKeystoreAESCBC()); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + addCipherStorageToMap(new CipherStorageKeystoreRSAECB()); + } } private void addCipherStorageToMap(CipherStorage cipherStorage) { @@ -67,20 +83,22 @@ public Map getConstants() { } @ReactMethod - public void getSecurityLevel(Promise promise) { - promise.resolve(getSecurityLevel().name()); - } + public void getSecurityLevel(String accessControl, Promise promise) { + boolean useBiometry = getUseBiometry(accessControl); + promise.resolve(getSecurityLevel(useBiometry).name()); + } @ReactMethod - public void setGenericPasswordForOptions(String service, String username, String password, String minimumSecurityLevel, Promise promise) { + public void setGenericPasswordForOptions(String service, String username, String password, String minimumSecurityLevel, String accessControl, Promise promise) { try { SecurityLevel level = SecurityLevel.valueOf(minimumSecurityLevel); if (username == null || username.isEmpty() || password == null || password.isEmpty()) { throw new EmptyParameterException("you passed empty or null username/password"); } + service = getDefaultServiceIfNull(service); - CipherStorage currentCipherStorage = getCipherStorageForCurrentAPILevel(); + CipherStorage currentCipherStorage = getCipherStorageForCurrentAPILevel(getUseBiometry(accessControl)); validateCipherStorageSecurityLevel(currentCipherStorage, level); EncryptionResult result = currentCipherStorage.encrypt(service, username, password, level); @@ -97,59 +115,96 @@ public void setGenericPasswordForOptions(String service, String username, String } @ReactMethod - public void getGenericPasswordForOptions(String service, Promise promise) { + public void getGenericPasswordForOptions(String service, final Promise promise) { + final String serviceOrDefault = getDefaultServiceIfNull(service); + CipherStorage cipherStorage = null; try { - service = getDefaultServiceIfNull(service); - - CipherStorage currentCipherStorage = getCipherStorageForCurrentAPILevel(); - - ResultSet resultSet = prefsStorage.getEncryptedEntry(service); + ResultSet resultSet = prefsStorage.getEncryptedEntry(serviceOrDefault); if (resultSet == null) { - Log.e(KEYCHAIN_MODULE, "No entry found for service: " + service); + Log.e(KEYCHAIN_MODULE, "No entry found for service: " + serviceOrDefault); promise.resolve(false); return; } - final DecryptionResult decryptionResult = decryptCredentials(service, currentCipherStorage, resultSet); - - WritableMap credentials = Arguments.createMap(); - - credentials.putString("service", service); - credentials.putString("username", decryptionResult.username); - credentials.putString("password", decryptionResult.password); + // Android < M will throw an exception as biometry is not supported. + CipherStorage biometryCipherStorage = null; + try { + biometryCipherStorage = getCipherStorageForCurrentAPILevel(true); + } catch(Exception e) { } + final CipherStorage nonBiometryCipherStorage = getCipherStorageForCurrentAPILevel(false); + if (biometryCipherStorage != null && resultSet.cipherStorageName.equals(biometryCipherStorage.getCipherStorageName())) { + cipherStorage = biometryCipherStorage; + } else if (nonBiometryCipherStorage != null && resultSet.cipherStorageName.equals(nonBiometryCipherStorage.getCipherStorageName())) { + cipherStorage = nonBiometryCipherStorage; + } - promise.resolve(credentials); - } catch (KeyStoreAccessException e) { - Log.e(KEYCHAIN_MODULE, e.getMessage()); - promise.reject(E_KEYSTORE_ACCESS_ERROR, e); + final CipherStorage currentCipherStorage = cipherStorage; + if (currentCipherStorage != null) { + DecryptionResultHandler decryptionHandler = new DecryptionResultHandler() { + @Override + public void onDecrypt(DecryptionResult decryptionResult, String error) { + if (decryptionResult != null) { + WritableMap credentials = Arguments.createMap(); + + credentials.putString("service", serviceOrDefault); + credentials.putString("username", decryptionResult.username); + credentials.putString("password", decryptionResult.password); + + promise.resolve(credentials); + } else { + promise.reject(E_CRYPTO_FAILED, error); + } + } + }; + // The encrypted data is encrypted using the current CipherStorage, so we just decrypt and return + currentCipherStorage.decrypt(decryptionHandler, serviceOrDefault, resultSet.usernameBytes, resultSet.passwordBytes, (FragmentActivity) this.getCurrentActivity()); + } + else { + // The encrypted data is encrypted using an older CipherStorage, so we need to decrypt the data first, then encrypt it using the current CipherStorage, then store it again and return + final CipherStorage oldCipherStorage = getCipherStorageByName(resultSet.cipherStorageName); + + DecryptionResultHandler decryptionHandler = new DecryptionResultHandler() { + @Override + public void onDecrypt(DecryptionResult decryptionResult, String error) { + if (decryptionResult != null) { + WritableMap credentials = Arguments.createMap(); + + credentials.putString("service", serviceOrDefault); + credentials.putString("username", decryptionResult.username); + credentials.putString("password", decryptionResult.password); + + try { + migrateCipherStorage(serviceOrDefault, nonBiometryCipherStorage, oldCipherStorage, decryptionResult); + } catch (CryptoFailedException e) { + Log.e(KEYCHAIN_MODULE, "Migrating to a less safe storage is not allowed. Keeping the old one"); + } catch (KeyStoreAccessException e) { + Log.e(KEYCHAIN_MODULE, e.getMessage()); + promise.reject(E_KEYSTORE_ACCESS_ERROR, e); + } + + promise.resolve(credentials); + } else { + promise.reject(E_CRYPTO_FAILED, error); + } + } + }; + // decrypt using the older cipher storage + oldCipherStorage.decrypt(decryptionHandler, serviceOrDefault, resultSet.usernameBytes, resultSet.passwordBytes, (FragmentActivity) this.getCurrentActivity()); + } + } catch (InvalidKeyException e) { + Log.e(KEYCHAIN_MODULE, String.format("Key for service %s permanently invalidated", serviceOrDefault)); + try { + cipherStorage.removeKey(serviceOrDefault); + } catch (Exception error) { + Log.e(KEYCHAIN_MODULE, "Failed removing invalidated key: " + error.getMessage()); + } + promise.resolve(false); } catch (CryptoFailedException e) { Log.e(KEYCHAIN_MODULE, e.getMessage()); promise.reject(E_CRYPTO_FAILED, e); } } - private DecryptionResult decryptCredentials(String service, CipherStorage currentCipherStorage, ResultSet resultSet) throws CryptoFailedException, KeyStoreAccessException { - if (resultSet.cipherStorageName.equals(currentCipherStorage.getCipherStorageName())) { - // The encrypted data is encrypted using the current CipherStorage, so we just decrypt and return - return currentCipherStorage.decrypt(service, resultSet.usernameBytes, resultSet.passwordBytes); - } - - // The encrypted data is encrypted using an older CipherStorage, so we need to decrypt the data first, then encrypt it using the current CipherStorage, then store it again and return - CipherStorage oldCipherStorage = getCipherStorageByName(resultSet.cipherStorageName); - // decrypt using the older cipher storage - - DecryptionResult decryptionResult = oldCipherStorage.decrypt(service, resultSet.usernameBytes, resultSet.passwordBytes); - // encrypt using the current cipher storage - - try { - migrateCipherStorage(service, currentCipherStorage, oldCipherStorage, decryptionResult); - } catch (CryptoFailedException e) { - Log.e(KEYCHAIN_MODULE, "Migrating to a less safe storage is not allowed. Keeping the old one"); - } - - return decryptionResult; - } - private void migrateCipherStorage(String service, CipherStorage newCipherStorage, CipherStorage oldCipherStorage, DecryptionResult decryptionResult) throws KeyStoreAccessException, CryptoFailedException { // don't allow to degrade security level when transferring, the new storage should be as safe as the old one. EncryptionResult encryptionResult = newCipherStorage.encrypt(service, decryptionResult.username, decryptionResult.password, decryptionResult.getSecurityLevel()); @@ -197,8 +252,8 @@ public void hasInternetCredentialsForServer(@NonNull String server, Promise prom } @ReactMethod - public void setInternetCredentialsForServer(@NonNull String server, String username, String password, String minimumSecurityLevel, ReadableMap unusedOptions, Promise promise) { - setGenericPasswordForOptions(server, username, password, minimumSecurityLevel, promise); + public void setInternetCredentialsForServer(@NonNull String server, String username, String password, String minimumSecurityLevel, String accessControl, ReadableMap unusedOptions, Promise promise) { + setGenericPasswordForOptions(server, username, password, minimumSecurityLevel, accessControl, promise); } @ReactMethod @@ -211,6 +266,28 @@ public void resetInternetCredentialsForServer(@NonNull String server, ReadableMa resetGenericPasswordForOptions(server, promise); } + @ReactMethod + public void canCheckAuthentication(ReadableMap options, Promise promise) { + String authenticationType = null; + if (options != null && options.hasKey(AUTHENTICATION_TYPE_KEY)) { + authenticationType = options.getString(AUTHENTICATION_TYPE_KEY); + } + + if (authenticationType == null + || (!authenticationType.equals(AUTHENTICATION_TYPE_DEVICE_PASSCODE_OR_BIOMETRICS) + && !authenticationType.equals(AUTHENTICATION_TYPE_BIOMETRICS))) { + promise.resolve(false); + return; + } + + try { + boolean fingerprintAuthAvailable = isFingerprintAuthAvailable(); + promise.resolve(fingerprintAuthAvailable); + } catch (Exception e) { + promise.resolve(false); + } + } + @ReactMethod public void getSupportedBiometryType(Promise promise) { try { @@ -226,14 +303,22 @@ public void getSupportedBiometryType(Promise promise) { } } + private boolean getUseBiometry(String accessControl) { + return accessControl != null + && (accessControl.equals(ACCESS_CONTROL_BIOMETRY_ANY) + || accessControl.equals(ACCESS_CONTROL_BIOMETRY_CURRENT_SET)); + } + // The "Current" CipherStorage is the cipherStorage with the highest API level that is lower than or equal to the current API level - private CipherStorage getCipherStorageForCurrentAPILevel() throws CryptoFailedException { + private CipherStorage getCipherStorageForCurrentAPILevel(boolean useBiometry) throws CryptoFailedException { int currentAPILevel = Build.VERSION.SDK_INT; CipherStorage currentCipherStorage = null; for (CipherStorage cipherStorage : cipherStorageMap.values()) { int cipherStorageAPILevel = cipherStorage.getMinSupportedApiLevel(); + boolean biometrySupported = cipherStorage.getCipherBiometrySupported(); // Is the cipherStorage supported on the current API level? - boolean isSupported = (cipherStorageAPILevel <= currentAPILevel); + boolean isSupported = (cipherStorageAPILevel <= currentAPILevel) + && (biometrySupported == useBiometry); if (!isSupported) { continue; } @@ -245,6 +330,7 @@ private CipherStorage getCipherStorageForCurrentAPILevel() throws CryptoFailedEx if (currentCipherStorage == null) { throw new CryptoFailedException("Unsupported Android SDK " + Build.VERSION.SDK_INT); } + return currentCipherStorage; } @@ -262,29 +348,31 @@ private void validateCipherStorageSecurityLevel(CipherStorage cipherStorage, Sec private CipherStorage getCipherStorageByName(String cipherStorageName) { - return cipherStorageMap.get(cipherStorageName); + CipherStorage storage = cipherStorageMap.get(cipherStorageName); + + return storage; } private boolean isFingerprintAuthAvailable() { return DeviceAvailability.isFingerprintAuthAvailable(getReactApplicationContext()); } - private boolean isSecureHardwareAvailable() { + private boolean isSecureHardwareAvailable(boolean useBiometry) { try { - return getCipherStorageForCurrentAPILevel().supportsSecureHardware(); + return getCipherStorageForCurrentAPILevel(useBiometry).supportsSecureHardware(); } catch (CryptoFailedException e) { return false; } } - private SecurityLevel getSecurityLevel() { + private SecurityLevel getSecurityLevel(boolean useBiometry) { try { - CipherStorage storage = getCipherStorageForCurrentAPILevel(); + CipherStorage storage = getCipherStorageForCurrentAPILevel(useBiometry); if (!storage.securityLevel().satisfiesSafetyThreshold(SecurityLevel.SECURE_SOFTWARE)) { return SecurityLevel.ANY; } - if (isSecureHardwareAvailable()) { + if (isSecureHardwareAvailable(useBiometry)) { return SecurityLevel.SECURE_HARDWARE; } else { return SecurityLevel.SECURE_SOFTWARE; diff --git a/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorage.java b/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorage.java index 7557ce16..2e26d5a8 100644 --- a/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorage.java +++ b/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorage.java @@ -1,11 +1,15 @@ package com.oblador.keychain.cipherStorage; -import androidx.annotation.NonNull; +import android.security.keystore.KeyPermanentlyInvalidatedException; import com.oblador.keychain.SecurityLevel; import com.oblador.keychain.exceptions.CryptoFailedException; import com.oblador.keychain.exceptions.KeyStoreAccessException; +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentActivity; + public interface CipherStorage { abstract class CipherResult { public final T username; @@ -39,14 +43,20 @@ public SecurityLevel getSecurityLevel() { } } + interface DecryptionResultHandler { + void onDecrypt(DecryptionResult decryptionResult, String error); + } + EncryptionResult encrypt(@NonNull String service, @NonNull String username, @NonNull String password, SecurityLevel level) throws CryptoFailedException; - DecryptionResult decrypt(@NonNull String service, @NonNull byte[] username, @NonNull byte[] password) throws CryptoFailedException; + void decrypt(@NonNull DecryptionResultHandler decryptionResultHandler, @NonNull String service, @NonNull byte[] username, @NonNull byte[] password, FragmentActivity activity) throws CryptoFailedException, KeyPermanentlyInvalidatedException; void removeKey(@NonNull String service) throws KeyStoreAccessException; String getCipherStorageName(); + boolean getCipherBiometrySupported(); + int getMinSupportedApiLevel(); SecurityLevel securityLevel(); diff --git a/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorageFacebookConceal.java b/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorageFacebookConceal.java index 3162f994..587ae325 100644 --- a/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorageFacebookConceal.java +++ b/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorageFacebookConceal.java @@ -1,7 +1,7 @@ package com.oblador.keychain.cipherStorage; import android.os.Build; -import androidx.annotation.NonNull; +import android.security.keystore.KeyPermanentlyInvalidatedException; import com.facebook.android.crypto.keychain.AndroidConceal; import com.facebook.android.crypto.keychain.SharedPrefsBackedKeyChain; @@ -15,6 +15,9 @@ import java.nio.charset.Charset; +import androidx.annotation.NonNull; +import androidx.fragment.app.FragmentActivity; + public class CipherStorageFacebookConceal implements CipherStorage { public static final String CIPHER_STORAGE_NAME = "FacebookConceal"; public static final String KEYCHAIN_DATA = "RN_KEYCHAIN"; @@ -30,11 +33,17 @@ public String getCipherStorageName() { return CIPHER_STORAGE_NAME; } + @Override + public boolean getCipherBiometrySupported() { + return false; + } + @Override public int getMinSupportedApiLevel() { return Build.VERSION_CODES.JELLY_BEAN; } + @Override public SecurityLevel securityLevel() { return SecurityLevel.ANY; @@ -69,7 +78,7 @@ public EncryptionResult encrypt(@NonNull String service, @NonNull String usernam } @Override - public DecryptionResult decrypt(@NonNull String service, @NonNull byte[] username, @NonNull byte[] password) throws CryptoFailedException { + public void decrypt(@NonNull DecryptionResultHandler decryptionResultHandler, @NonNull String service, @NonNull byte[] username, @NonNull byte[] password, FragmentActivity activity) throws CryptoFailedException, KeyPermanentlyInvalidatedException { if (!crypto.isAvailable()) { throw new CryptoFailedException("Crypto is missing"); } @@ -80,10 +89,9 @@ public DecryptionResult decrypt(@NonNull String service, @NonNull byte[] usernam byte[] decryptedUsername = crypto.decrypt(username, usernameEntity); byte[] decryptedPassword = crypto.decrypt(password, passwordEntity); - return new DecryptionResult( + decryptionResultHandler.onDecrypt(new DecryptionResult( new String(decryptedUsername, Charset.forName("UTF-8")), - new String(decryptedPassword, Charset.forName("UTF-8")), - SecurityLevel.ANY); + new String(decryptedPassword, Charset.forName("UTF-8")), SecurityLevel.ANY), null); } catch (Exception e) { throw new CryptoFailedException("Decryption failed for service " + service, e); } diff --git a/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorageKeystoreAESCBC.java b/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorageKeystoreAESCBC.java index d7c9ab5a..4ae160b6 100644 --- a/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorageKeystoreAESCBC.java +++ b/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorageKeystoreAESCBC.java @@ -4,8 +4,11 @@ import android.os.Build; import android.security.keystore.KeyGenParameterSpec; import android.security.keystore.KeyInfo; +import android.security.keystore.KeyPermanentlyInvalidatedException; import android.security.keystore.KeyProperties; import androidx.annotation.NonNull; +import androidx.fragment.app.FragmentActivity; + import android.util.Log; import com.oblador.keychain.SecurityLevel; @@ -55,6 +58,11 @@ public String getCipherStorageName() { return CIPHER_STORAGE_NAME; } + @Override + public boolean getCipherBiometrySupported() { + return false; + } + @Override public int getMinSupportedApiLevel() { return Build.VERSION_CODES.M; @@ -165,7 +173,7 @@ private void generateKeyAndStoreUnderAlias(@NonNull String service, SecurityLeve } @Override - public DecryptionResult decrypt(@NonNull String service, @NonNull byte[] username, @NonNull byte[] password) throws CryptoFailedException { + public void decrypt(@NonNull DecryptionResultHandler decryptionResultHandler, @NonNull String service, @NonNull byte[] username, @NonNull byte[] password, FragmentActivity activity) throws CryptoFailedException, KeyPermanentlyInvalidatedException { service = getDefaultServiceIfEmpty(service); try { @@ -179,7 +187,9 @@ public DecryptionResult decrypt(@NonNull String service, @NonNull byte[] usernam String decryptedUsername = decryptBytes(key, username); String decryptedPassword = decryptBytes(key, password); - return new DecryptionResult(decryptedUsername, decryptedPassword, getSecurityLevel((SecretKey) key)); + decryptionResultHandler.onDecrypt(new DecryptionResult( + decryptedUsername, + decryptedPassword, SecurityLevel.ANY), null); } catch (KeyStoreException | UnrecoverableKeyException | NoSuchAlgorithmException e) { throw new CryptoFailedException("Could not get key from Keystore", e); } catch (KeyStoreAccessException e) { diff --git a/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorageKeystoreMarshmallowBase.java b/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorageKeystoreMarshmallowBase.java new file mode 100644 index 00000000..52402124 --- /dev/null +++ b/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorageKeystoreMarshmallowBase.java @@ -0,0 +1,158 @@ +package com.oblador.keychain.cipherStorage; + +import android.annotation.TargetApi; +import android.os.Build; +import android.security.keystore.KeyGenParameterSpec; +import android.security.keystore.KeyInfo; +import android.security.keystore.StrongBoxUnavailableException; +import androidx.annotation.NonNull; +import android.util.Log; + +import com.oblador.keychain.SecurityLevel; +import com.oblador.keychain.exceptions.CryptoFailedException; +import com.oblador.keychain.exceptions.KeyStoreAccessException; + +import java.io.IOException; +import java.security.InvalidAlgorithmParameterException; +import java.security.Key; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.cert.CertificateException; +import java.security.spec.InvalidKeySpecException; + +import androidx.biometric.BiometricPrompt; + +@TargetApi(Build.VERSION_CODES.M) +public abstract class CipherStorageKeystoreMarshmallowBase implements CipherStorage { + public static final String TAG = "Keystore"; + public static final String DEFAULT_SERVICE = "RN_KEYCHAIN_DEFAULT_ALIAS"; + public static final String KEYSTORE_TYPE = "AndroidKeyStore"; + + @Override + public int getMinSupportedApiLevel() { + return Build.VERSION_CODES.M; + } + + @Override + public SecurityLevel securityLevel() { + // it can guarantee security levels up to SECURE_HARDWARE/SE/StrongBox + return SecurityLevel.SECURE_HARDWARE; + } + + @Override + public boolean supportsSecureHardware() { + final String testKeyAlias = "AndroidKeyStore#supportsSecureHardware"; + + try { + Key key = tryGenerateRegularSecurityKey(testKeyAlias); + return validateKeySecurityLevel(SecurityLevel.SECURE_HARDWARE, key); + } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException | NoSuchProviderException e) { + return false; + } finally { + try { + removeKey(testKeyAlias); + } catch (KeyStoreAccessException e) { + Log.e(TAG, "Unable to remove temp key from keychain", e); + } + } + } + + @TargetApi(Build.VERSION_CODES.M) + protected boolean validateKeySecurityLevel(SecurityLevel level, Key generatedKey) { + return getSecurityLevel(generatedKey).satisfiesSafetyThreshold(level); + } + + @TargetApi(Build.VERSION_CODES.M) + protected SecurityLevel getSecurityLevel(Key key) { + try { + KeyInfo keyInfo = getKeyInfo(key); + return keyInfo.isInsideSecureHardware() ? SecurityLevel.SECURE_HARDWARE : SecurityLevel.SECURE_SOFTWARE; + } catch (NoSuchAlgorithmException | NoSuchProviderException | InvalidKeySpecException e) { + return SecurityLevel.ANY; + } + } + + protected abstract KeyInfo getKeyInfo(Key key) throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException; + + @Override + public void removeKey(@NonNull String service) throws KeyStoreAccessException { + service = getDefaultServiceIfEmpty(service); + + try { + KeyStore keyStore = getKeyStoreAndLoad(); + + if (keyStore.containsAlias(service)) { + keyStore.deleteEntry(service); + } + } catch (KeyStoreException e) { + throw new KeyStoreAccessException("Failed to access Keystore", e); + } catch (Exception e) { + throw new KeyStoreAccessException("Unknown error " + e.getMessage(), e); + } + } + + protected KeyStore getKeyStoreAndLoad() throws KeyStoreException, KeyStoreAccessException { + try { + KeyStore keyStore = KeyStore.getInstance(KEYSTORE_TYPE); + keyStore.load(null); + return keyStore; + } catch (NoSuchAlgorithmException | CertificateException | IOException e) { + throw new KeyStoreAccessException("Could not access Keystore", e); + } + } + + @NonNull + protected String getDefaultServiceIfEmpty(@NonNull String service) { + return service.isEmpty() ? DEFAULT_SERVICE : service; + } + + protected void generateKeyAndStoreUnderAlias(@NonNull String service, SecurityLevel requiredLevel) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException, CryptoFailedException { + // Firstly, try to generate the key as safe as possible (strongbox). + // see https://developer.android.com/training/articles/keystore#HardwareSecurityModule + Key key = tryGenerateStrongBoxSecurityKey(service); + if (key == null) { + // If that is not possible, we generate the key in a regular way + // (it still might be generated in hardware, but not in StrongBox) + key = tryGenerateRegularSecurityKey(service); + } + + if(!validateKeySecurityLevel(requiredLevel, key)) { + throw new CryptoFailedException("Cannot generate keys with required security guarantees"); + } + } + + @TargetApi(Build.VERSION_CODES.P) + protected Key tryGenerateStrongBoxSecurityKey(String service) throws NoSuchAlgorithmException, + InvalidAlgorithmParameterException, NoSuchProviderException { + // StrongBox is only supported on Android P and higher + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { + return null; + } + try { + return generateKey(getKeyGenSpecBuilder(service).setIsStrongBoxBacked(true).build()); + } catch (Exception e) { + if (e instanceof StrongBoxUnavailableException) { + Log.i(TAG, "StrongBox is unavailable on this device"); + } else { + Log.e(TAG, "An error occurred when trying to generate a StrongBoxSecurityKey: " + e.getMessage()); + } + return null; + } + } + + @TargetApi(Build.VERSION_CODES.M) + protected Key tryGenerateRegularSecurityKey(String service) throws NoSuchAlgorithmException, + InvalidAlgorithmParameterException, NoSuchProviderException { + return generateKey(getKeyGenSpecBuilder(service).build()); + } + + // returns true if the key was generated successfully + @TargetApi(Build.VERSION_CODES.M) + protected abstract Key generateKey(KeyGenParameterSpec spec) throws NoSuchProviderException, + NoSuchAlgorithmException, InvalidAlgorithmParameterException; + + @TargetApi(Build.VERSION_CODES.M) + protected abstract KeyGenParameterSpec.Builder getKeyGenSpecBuilder(String service); +} diff --git a/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorageKeystoreRSAECB.java b/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorageKeystoreRSAECB.java new file mode 100644 index 00000000..a501adc4 --- /dev/null +++ b/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorageKeystoreRSAECB.java @@ -0,0 +1,270 @@ +package com.oblador.keychain.cipherStorage; + +import android.Manifest; +import android.annotation.TargetApi; +import android.app.KeyguardManager; +import android.content.Context; +import android.content.pm.PackageManager; +import android.os.Build; +import android.os.CancellationSignal; +import android.security.keystore.KeyGenParameterSpec; +import android.security.keystore.KeyInfo; +import android.security.keystore.KeyPermanentlyInvalidatedException; +import android.security.keystore.KeyProperties; +import android.security.keystore.UserNotAuthenticatedException; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; + +import com.facebook.react.bridge.ReactApplicationContext; + +import com.facebook.react.bridge.ReactContext; +import com.oblador.keychain.SecurityLevel; +import com.oblador.keychain.components.BiometricPromptHelper; +import com.oblador.keychain.exceptions.CryptoFailedException; +import com.oblador.keychain.exceptions.KeyStoreAccessException; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.Charset; +import java.security.InvalidAlgorithmParameterException; +import java.security.Key; +import java.security.KeyFactory; +import java.security.KeyPairGenerator; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PublicKey; +import java.security.UnrecoverableKeyException; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.concurrent.Executors; + +import javax.crypto.Cipher; +import javax.crypto.CipherInputStream; +import javax.crypto.CipherOutputStream; + +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentActivity; + +@RequiresApi(Build.VERSION_CODES.M) +public class CipherStorageKeystoreRSAECB extends CipherStorageKeystoreMarshmallowBase implements BiometricPromptHelper.BiometricAuthenticationResult { + private static final String CIPHER_STORAGE_NAME = "KeystoreRSAECB"; + private static final String KEYSTORE_TYPE = "AndroidKeyStore"; + private static final String ENCRYPTION_ALGORITHM = KeyProperties.KEY_ALGORITHM_RSA; + private static final String ENCRYPTION_BLOCK_MODE = KeyProperties.BLOCK_MODE_ECB; + private static final String ENCRYPTION_PADDING = KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1; + private static final String ENCRYPTION_TRANSFORMATION = + ENCRYPTION_ALGORITHM + "/" + + ENCRYPTION_BLOCK_MODE + "/" + + ENCRYPTION_PADDING; + private static final int ENCRYPTION_KEY_SIZE = 3072; + + private class CipherDecryptionParams { + private final DecryptionResultHandler resultHandler; + private final Key key; + private final byte[] username; + private final byte[] password; + + private CipherDecryptionParams(DecryptionResultHandler handler, Key key, byte[] username, byte[] password) { + this.resultHandler = handler; + this.key = key; + this.username = username; + this.password = password; + } + } + + private CipherDecryptionParams mDecryptParams; + + @Override + public void onError(int errorCode, @Nullable CharSequence errString) { + if (mDecryptParams != null && mDecryptParams.resultHandler != null) { + mDecryptParams.resultHandler.onDecrypt(null, errString != null ? errString.toString() : "Impossible to authenticate"); + mDecryptParams = null; + } + } + + @Override + public void onSuccess() { + if (mDecryptParams != null && mDecryptParams.resultHandler != null) { + try { + String decryptedUsername = decryptBytes(mDecryptParams.key, mDecryptParams.username); + String decryptedPassword = decryptBytes(mDecryptParams.key, mDecryptParams.password); + mDecryptParams.resultHandler.onDecrypt(new DecryptionResult(decryptedUsername, decryptedPassword, SecurityLevel.ANY), null); + } catch (Exception e) { + mDecryptParams.resultHandler.onDecrypt(null, e.getMessage()); + } + mDecryptParams = null; + } + } + + // returns true if the key was generated successfully + @TargetApi(Build.VERSION_CODES.M) + protected Key generateKey(KeyGenParameterSpec spec) throws NoSuchProviderException, + NoSuchAlgorithmException, InvalidAlgorithmParameterException { + KeyPairGenerator generator = KeyPairGenerator.getInstance(ENCRYPTION_ALGORITHM, KEYSTORE_TYPE); + generator.initialize(spec); + return generator.generateKeyPair().getPrivate(); + } + + protected KeyInfo getKeyInfo(Key key) throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException { + KeyFactory factory = KeyFactory.getInstance(key.getAlgorithm(), KEYSTORE_TYPE); + KeyInfo keyInfo = factory.getKeySpec(key, KeyInfo.class); + return keyInfo; + } + + @Override + public String getCipherStorageName() { + return CIPHER_STORAGE_NAME; + } + + @Override + public boolean getCipherBiometrySupported() { + return true; + } + + @Override + public int getMinSupportedApiLevel() { + return Build.VERSION_CODES.M; + } + + @Override + public EncryptionResult encrypt(@NonNull String service, @NonNull String username, @NonNull String password, SecurityLevel level) throws CryptoFailedException { + service = getDefaultServiceIfEmpty(service); + + try { + KeyStore keyStore = getKeyStoreAndLoad(); + + if (!keyStore.containsAlias(service)) { + generateKeyAndStoreUnderAlias(service, level); + } + + KeyFactory keyFactory = KeyFactory.getInstance(ENCRYPTION_ALGORITHM); + PublicKey publicKey = keyStore.getCertificate(service).getPublicKey(); + KeySpec spec = new X509EncodedKeySpec(publicKey.getEncoded()); + Key key = keyFactory.generatePublic(spec); + + byte[] encryptedUsername = encryptString(key, service, username); + byte[] encryptedPassword = encryptString(key, service, password); + + return new EncryptionResult(encryptedUsername, encryptedPassword, this); + } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException | NoSuchProviderException e) { + throw new CryptoFailedException("Could not encrypt data for service " + service, e); + } catch (KeyStoreException | KeyStoreAccessException e) { + throw new CryptoFailedException("Could not access Keystore for service " + service, e); + } catch (Exception e) { + throw new CryptoFailedException("Unknown error: " + e.getMessage(), e); + } + } + + @TargetApi(Build.VERSION_CODES.M) + protected KeyGenParameterSpec.Builder getKeyGenSpecBuilder(String service) { + return new KeyGenParameterSpec.Builder( + service, + KeyProperties.PURPOSE_DECRYPT | KeyProperties.PURPOSE_ENCRYPT) + .setBlockModes(ENCRYPTION_BLOCK_MODE) + .setEncryptionPaddings(ENCRYPTION_PADDING) + .setRandomizedEncryptionRequired(true) + .setUserAuthenticationRequired(true) + .setUserAuthenticationValidityDurationSeconds(1) + .setKeySize(ENCRYPTION_KEY_SIZE); + } + + @Override + public void decrypt(@NonNull DecryptionResultHandler decryptionResultHandler, @NonNull String service, @NonNull byte[] username, @NonNull byte[] password, FragmentActivity activity) throws CryptoFailedException, KeyPermanentlyInvalidatedException { + service = getDefaultServiceIfEmpty(service); + + BiometricPromptHelper mBiometricHelper = new BiometricPromptHelper(activity); + KeyStore keyStore; + Key key; + + try { + keyStore = getKeyStoreAndLoad(); + key = keyStore.getKey(service, null); + } catch (KeyStoreException | UnrecoverableKeyException | NoSuchAlgorithmException e) { + throw new CryptoFailedException("Could not get key from Keystore", e); + } catch (KeyStoreAccessException e) { + throw new CryptoFailedException("Could not access Keystore", e); + } catch (Exception e) { + throw new CryptoFailedException("Unknown error: " + e.getMessage(), e); + } + + String decryptedUsername; + String decryptedPassword; + try { + // try to get a Cipher, if exception is thrown, authentication is needed + decryptedUsername = decryptBytes(key, username); + decryptedPassword = decryptBytes(key, password); + } catch (UserNotAuthenticatedException e) { + mDecryptParams = new CipherDecryptionParams(decryptionResultHandler, key, username, password); + if (!mBiometricHelper.canStartFingerprintAuthentication()) { + throw new CryptoFailedException("Could not start fingerprint Authentication"); + } + try { + // The Cipher is locked, we will decrypt once fingerprint is recognised. + mBiometricHelper.startFingerprintAuthentication(this); + } catch (Exception e1) { + e1.printStackTrace(); + throw new CryptoFailedException("Could not start fingerprint Authentication", e1); + } + return; + } + + // The Cipher is unlocked, we can decrypt straight away. + decryptionResultHandler.onDecrypt(new DecryptionResult(decryptedUsername, decryptedPassword, SecurityLevel.ANY), null); + } + + private byte[] encryptString(Key key, String service, String value) throws CryptoFailedException { + try { + Cipher cipher = Cipher.getInstance(ENCRYPTION_TRANSFORMATION); + cipher.init(Cipher.ENCRYPT_MODE, key); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + // encrypt the value using a CipherOutputStream + CipherOutputStream cipherOutputStream = new CipherOutputStream(outputStream, cipher); + cipherOutputStream.write(value.getBytes("UTF-8")); + cipherOutputStream.close(); + return outputStream.toByteArray(); + } catch (Exception e) { + throw new CryptoFailedException("Could not encrypt value for service " + service, e); + } + } + + private Cipher getDecryptionCipher(Key key) throws CryptoFailedException, UserNotAuthenticatedException, KeyPermanentlyInvalidatedException { + try { + Cipher cipher = Cipher.getInstance(ENCRYPTION_TRANSFORMATION); + // read the initialization vector from the beginning of the stream + cipher.init(Cipher.DECRYPT_MODE, key); + + return cipher; + } catch (UserNotAuthenticatedException | KeyPermanentlyInvalidatedException e) { + throw e; + } catch (Exception e) { + throw new CryptoFailedException("Could not generate cipher", e); + } + } + + private String decryptBytes(Key key, byte[] bytes) throws CryptoFailedException, UserNotAuthenticatedException, KeyPermanentlyInvalidatedException { + try { + Cipher cipher = getDecryptionCipher(key); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + // decrypt the bytes using a CipherInputStream + CipherInputStream cipherInputStream = new CipherInputStream( + inputStream, cipher); + ByteArrayOutputStream output = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + while (true) { + int n = cipherInputStream.read(buffer, 0, buffer.length); + if (n <= 0) { + break; + } + output.write(buffer, 0, n); + } + return new String(output.toByteArray(), Charset.forName("UTF-8")); + } catch (IOException e) { + throw new CryptoFailedException("Could not decrypt bytes", e); + } + } +} diff --git a/android/src/main/java/com/oblador/keychain/components/BiometricPromptHelper.java b/android/src/main/java/com/oblador/keychain/components/BiometricPromptHelper.java new file mode 100644 index 00000000..f4c39285 --- /dev/null +++ b/android/src/main/java/com/oblador/keychain/components/BiometricPromptHelper.java @@ -0,0 +1,83 @@ +package com.oblador.keychain.components; + +import android.Manifest; +import android.annotation.TargetApi; +import android.app.KeyguardManager; +import android.content.Context; +import android.content.pm.PackageManager; +import android.os.Build; +import android.os.CancellationSignal; + +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReactContext; +import com.oblador.keychain.SecurityLevel; + +import java.security.Key; +import java.util.concurrent.Executors; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.biometric.BiometricManager; +import androidx.biometric.BiometricPrompt; +import androidx.fragment.app.FragmentActivity; + +@TargetApi(Build.VERSION_CODES.M) +public class BiometricPromptHelper extends BiometricPrompt.AuthenticationCallback { + private CancellationSignal mBiometricPromptCancellationSignal; + private BiometricPrompt mBiometricPrompt; + private FragmentActivity mActivity; + private BiometricAuthenticationResult mBiometricAuthenticationResult; + + public interface BiometricAuthenticationResult { + void onError(int errorCode, @Nullable CharSequence errString); + void onSuccess (); + } + + public BiometricPromptHelper(FragmentActivity activity) { + mActivity = activity; + } + + // We don't really want to do anything here + // the error message is handled by the info view. + // And we don't want to throw an error, as the user can still retry. + @Override + public void onAuthenticationFailed() {} + + @Override + public void onAuthenticationError(int errorCode, @Nullable CharSequence errString) { + mBiometricPromptCancellationSignal.cancel(); + mBiometricAuthenticationResult.onError(errorCode, errString); + } + + @Override + public void onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationResult result) { + mBiometricAuthenticationResult.onSuccess(); + } + + public boolean canStartFingerprintAuthentication() { + return BiometricManager.from(mActivity).canAuthenticate() == BiometricManager.BIOMETRIC_SUCCESS; + } + + public void startFingerprintAuthentication(BiometricAuthenticationResult biometricAuthenticationResult) throws Exception { + mBiometricAuthenticationResult = biometricAuthenticationResult; + // If we have a previous cancellationSignal, cancel it. + if (mBiometricPromptCancellationSignal != null) { + mBiometricPromptCancellationSignal.cancel(); + } + + mBiometricPrompt = new BiometricPrompt(mActivity, Executors.newSingleThreadExecutor(), this); + mBiometricPromptCancellationSignal = new CancellationSignal(); + + final BiometricPrompt.PromptInfo promptInfo = new BiometricPrompt.PromptInfo.Builder() + .setTitle("Authentication required") + .setNegativeButtonText("Cancel") + .setSubtitle("Please use biometric authentication to unlock the app") + .build(); + + mActivity.runOnUiThread(new Runnable() { + public void run() { + mBiometricPrompt.authenticate(promptInfo); + } + }); + } +} diff --git a/android/src/main/res/color-v26/biometric_error_color.xml b/android/src/main/res/color-v26/biometric_error_color.xml new file mode 100644 index 00000000..443e7f5a --- /dev/null +++ b/android/src/main/res/color-v26/biometric_error_color.xml @@ -0,0 +1,23 @@ + + + + + + + + \ No newline at end of file diff --git a/android/src/main/res/drawable-v23/fingerprint_dialog_error_to_fp.xml b/android/src/main/res/drawable-v23/fingerprint_dialog_error_to_fp.xml new file mode 100644 index 00000000..ef2bfaa3 --- /dev/null +++ b/android/src/main/res/drawable-v23/fingerprint_dialog_error_to_fp.xml @@ -0,0 +1,354 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/src/main/res/drawable-v23/fingerprint_dialog_fp_to_error.xml b/android/src/main/res/drawable-v23/fingerprint_dialog_fp_to_error.xml new file mode 100644 index 00000000..1b788582 --- /dev/null +++ b/android/src/main/res/drawable-v23/fingerprint_dialog_fp_to_error.xml @@ -0,0 +1,589 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/src/main/res/layout/fingerprint_dialog_layout.xml b/android/src/main/res/layout/fingerprint_dialog_layout.xml new file mode 100644 index 00000000..a29c1a9d --- /dev/null +++ b/android/src/main/res/layout/fingerprint_dialog_layout.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + diff --git a/android/src/main/res/values-af/strings.xml b/android/src/main/res/values-af/strings.xml new file mode 100644 index 00000000..f82e5feb --- /dev/null +++ b/android/src/main/res/values-af/strings.xml @@ -0,0 +1,29 @@ + + + + + "Raak die vingerafdruksensor" + "Nie herken nie" + "Hulpboodskapgebied" + "Vingerafdrukhardeware is nie beskikbaar nie." + "Geen vingerafdrukke is geregistreer nie." + "Hierdie toetstel het nie \'n vingerafdruksensor nie" + "Vingerafdrukhandeling is deur gebruiker gekanselleer." + "Te veel pogings. Probeer later weer." + "Onbekende fout" + diff --git a/android/src/main/res/values-am/strings.xml b/android/src/main/res/values-am/strings.xml new file mode 100644 index 00000000..378326da --- /dev/null +++ b/android/src/main/res/values-am/strings.xml @@ -0,0 +1,29 @@ + + + + + "የጣት አሻራ ዳሳሹን ይንኩ" + "አልታወቀም" + "የእገዛ መልዕክት አካባቢ" + "የጣት አሻራ ሃርድዌር የለም።" + "ምንም የጣት አሻራዎች አልተመዘገቡም።" + "ይህ መሣሪያ የጣት አሻራ ዳሳሽ የለውም" + "የጣት አሻራ ክወና በተጠቃሚ ተሰርዟል።" + "በጣም ብዙ ሙከራዎች። እባክዎ ቆይተው እንደገና ይሞክሩ።" + "ያልታወቀ ስህተት" + diff --git a/android/src/main/res/values-ar/strings.xml b/android/src/main/res/values-ar/strings.xml new file mode 100644 index 00000000..902ba58d --- /dev/null +++ b/android/src/main/res/values-ar/strings.xml @@ -0,0 +1,29 @@ + + + + + "المس زر استشعار بصمات الإصبع" + "لم يتم التعرف عليها." + "منطقة رسالة المساعدة" + "جهاز بصمة الإصبع غير متاح." + "ليست هناك بصمات إصبع مسجَّلة." + "لا يحتوي هذا الجهاز على جهاز استشعار بصمات الأصابع." + "تم إلغاء تشغيل بصمة الإصبع بواسطة المستخدم." + "تم إجراء محاولات كثيرة جدًا. يُرجى المحاولة مرة أخرى لاحقًا." + "خطأ غير معروف" + diff --git a/android/src/main/res/values-as/strings.xml b/android/src/main/res/values-as/strings.xml new file mode 100644 index 00000000..2ab09ac3 --- /dev/null +++ b/android/src/main/res/values-as/strings.xml @@ -0,0 +1,29 @@ + + + + + "ফিংগাৰপ্ৰিণ্ট ছেন্সৰটো স্পৰ্শ কৰক" + "চিনাক্ত কৰিব পৰা নাই" + "সহায় বাৰ্তাৰ ক্ষেত্ৰ" + "ফিংগাৰপ্ৰিণ্ট হাৰ্ডৱেৰ নাই।" + "কোনো ফিংগাৰপ্ৰিণ্ট যোগ কৰা নহ\'ল।" + "এই ডিভাইচটোত ফিংগাৰপ্ৰিণ্ট ছেন্সৰ নাই" + "ব্যৱহাৰকাৰীয়ে ফিংগাৰপ্ৰিণ্টৰ দ্বাৰা বিশ্বাসযোগ্যতা প্ৰমাণীকৰণ কাৰ্য বাতিল কৰিছে।" + "অতি বেছি চেষ্টা অনুগ্ৰহ কৰি পিছত আকৌ চেষ্টা কৰক।" + "অজ্ঞাত আসোঁৱাহ" + diff --git a/android/src/main/res/values-az/strings.xml b/android/src/main/res/values-az/strings.xml new file mode 100644 index 00000000..1d430a70 --- /dev/null +++ b/android/src/main/res/values-az/strings.xml @@ -0,0 +1,29 @@ + + + + + "Barmaq izi sensoruna klikləyin" + "Tanınmır" + "Yardım mesajı bölməsi" + "Barmaq izi avadanlığı əlçatan deyil." + "Barmaq izi qeydə alınmayıb." + "Bu cihazda barmaq izi sensoru yoxdur" + "Barmaq izi əməliyyatı istifadəçi tərəfindən ləğv edildi." + "Həddən çox cəhd oldu. Sonra sınayın." + "Naməlum xəta" + diff --git a/android/src/main/res/values-b+sr+Latn/strings.xml b/android/src/main/res/values-b+sr+Latn/strings.xml new file mode 100644 index 00000000..1b92557e --- /dev/null +++ b/android/src/main/res/values-b+sr+Latn/strings.xml @@ -0,0 +1,29 @@ + + + + + "Dodirn. senzor za otisak prsta" + "Nije prepoznat" + "Oblast poruke za pomoć" + "Hardver za otiske prstiju nije dostupan." + "Nije registrovan nijedan otisak prsta." + "Ovaj uređaj nema senzor za otisak prsta" + "Korisnik je otkazao radnju sa otiskom prsta." + "Previše pokušaja. Probajte ponovo kasnije." + "Nepoznata greška" + diff --git a/android/src/main/res/values-be/strings.xml b/android/src/main/res/values-be/strings.xml new file mode 100644 index 00000000..077483c4 --- /dev/null +++ b/android/src/main/res/values-be/strings.xml @@ -0,0 +1,29 @@ + + + + + "Дакраніцеся да сканера адбіткаў пальцаў" + "Не распазнана" + "Поле даведачнага паведамлення" + "Апаратныя сродкі для зняцця адбіткаў пальцаў недаступныя." + "Адбіткі пальцаў не зарэгістраваны." + "На гэтай прыладзе няма сканера адбіткаў пальцаў" + "Аўтэнтыфікацыя па адбітках пальцаў скасавана карыстальнікам." + "Занадта шмат спроб. Паўтарыце спробу пазней." + "Невядомая памылка" + diff --git a/android/src/main/res/values-bg/strings.xml b/android/src/main/res/values-bg/strings.xml new file mode 100644 index 00000000..1c7e60b7 --- /dev/null +++ b/android/src/main/res/values-bg/strings.xml @@ -0,0 +1,29 @@ + + + + + "Докоснете сензора за отпечатъци" + "Не е разпознато" + "Област за помощно съобщение" + "Хардуерът за отпечатъци не е налице." + "Няма регистрирани отпечатъци." + "Това устройство няма сензор за отпечатъци" + "Операцията за удостоверяване чрез отпечатък бе анулирана от потребителя." + "Твърде много опити. Моля, опитайте отново по-късно." + "Неизвестна грешка" + diff --git a/android/src/main/res/values-bn/strings.xml b/android/src/main/res/values-bn/strings.xml new file mode 100644 index 00000000..f336e0de --- /dev/null +++ b/android/src/main/res/values-bn/strings.xml @@ -0,0 +1,29 @@ + + + + + "আঙ্গুলের ছাপের সেন্সর টাচ করুন" + "শনাক্ত করা যায়নি" + "সহায়তার মেসেজ দেখানোর জায়গা" + "আঙ্গুলের ছাপ নেওয়ার হার্ডওয়্যার উপলভ্য নয়।" + "কোনও আঙ্গুলের ছাপ নথিভুক্ত নেই।" + "এই ডিভাইসে আঙ্গুলের ছাপ নেওয়ার সেন্সর নেই" + "ব্যবহারকারী আঙ্গুলের ছাপ নেওয়ার অপারেশনটি বাতিল করেছেন।" + "অনেকবার চেষ্টা করেছেন। পরে আবার চেষ্টা করুন।" + "অজানা সমস্যা" + diff --git a/android/src/main/res/values-bs/strings.xml b/android/src/main/res/values-bs/strings.xml new file mode 100644 index 00000000..d5243421 --- /dev/null +++ b/android/src/main/res/values-bs/strings.xml @@ -0,0 +1,29 @@ + + + + + "Dodirnite senzor za otisak prsta" + "Nije prepoznato" + "Prostor za poruku za pomoć" + "Hardver za otisak prsta nije dostupan." + "Nije prijavljen nijedan otisak prsta." + "Ovaj uređaj nema senzor za otisak prsta" + "Korisnik je otkazao radnju s otiskom prsta." + "Previše pokušaja. Pokušajte ponovo kasnije." + "Nepoznata greška" + diff --git a/android/src/main/res/values-ca/strings.xml b/android/src/main/res/values-ca/strings.xml new file mode 100644 index 00000000..b682b3d0 --- /dev/null +++ b/android/src/main/res/values-ca/strings.xml @@ -0,0 +1,29 @@ + + + + + "Toca sensor d\'empremtes digitals" + "No s\'ha reconegut" + "Àrea de missatge d\'ajuda" + "El maquinari per a empremtes digitals no està disponible." + "No s\'ha registrat cap empremta digital." + "Aquest dispositiu no té sensor d\'empremtes digitals" + "L\'usuari ha cancel·lat l\'operació d\'empremta digital." + "Massa intents. Torna-ho a provar més tard." + "Error desconegut" + diff --git a/android/src/main/res/values-cs/strings.xml b/android/src/main/res/values-cs/strings.xml new file mode 100644 index 00000000..ec8335dd --- /dev/null +++ b/android/src/main/res/values-cs/strings.xml @@ -0,0 +1,29 @@ + + + + + "Dotkněte se snímače otisků prstů" + "Nerozpoznáno" + "Oblast pro zprávu nápovědy" + "Není k dispozici hardware ke snímání otisků prstů." + "Nejsou zaregistrovány žádné otisky prstů." + "Toto zařízení nemá snímač otisků prstů" + "Uživatel operaci s otiskem prstu zrušil." + "Příliš mnoho pokusů. Zkuste to později." + "Neznámá chyba" + diff --git a/android/src/main/res/values-da/strings.xml b/android/src/main/res/values-da/strings.xml new file mode 100644 index 00000000..276bcf30 --- /dev/null +++ b/android/src/main/res/values-da/strings.xml @@ -0,0 +1,29 @@ + + + + + "Sæt finger på fingeraftrykslæser" + "Ikke genkendt" + "Område med hjælpemeddelelse" + "Hardwaren til fingeraftryk er ikke tilgængelig." + "Der er ikke registreret nogen fingeraftryk." + "Denne enhed har ingen fingeraftrykslæser" + "Fingeraftrykshandlingen blev annulleret af brugeren." + "Der var for mange forsøg Prøv igen senere." + "Ukendt fejl" + diff --git a/android/src/main/res/values-de/strings.xml b/android/src/main/res/values-de/strings.xml new file mode 100644 index 00000000..7a957d72 --- /dev/null +++ b/android/src/main/res/values-de/strings.xml @@ -0,0 +1,29 @@ + + + + + "Fingerabdrucksensor berühren" + "Nicht erkannt" + "Bereich für die Hilfemeldung" + "Fingerabdruckhardware nicht verfügbar." + "Keine Fingerabdrücke erfasst." + "Dieses Gerät hat keinen Fingerabdrucksensor" + "Vorgang der Fingerabdruckauthentifizierung vom Nutzer abgebrochen." + "Zu viele Versuche. Versuche es bitte später noch einmal." + "Unbekannter Fehler" + diff --git a/android/src/main/res/values-el/strings.xml b/android/src/main/res/values-el/strings.xml new file mode 100644 index 00000000..ce9ff06f --- /dev/null +++ b/android/src/main/res/values-el/strings.xml @@ -0,0 +1,29 @@ + + + + + "Αγγίξτε τον αισθ. δακτ. αποτ." + "Δεν αναγνωρίστηκε" + "Περιοχή μηνυμάτων βοήθειας" + "Ο εξοπλισμός δακτυλικού αποτυπώματος δεν είναι διαθέσιμος." + "Δεν έχουν καταχωριστεί δακτυλικά αποτυπώματα." + "Αυτή η συσκευή δεν διαθέτει αισθητήρα δακτυλικών αποτυπωμάτων" + "Η λειτουργία δακτυλικού αποτυπώματος ακυρώθηκε από τον χρήστη." + "Υπερβολικά πολλές προσπάθειες. Δοκιμάστε ξανά αργότερα." + "Άγνωστο σφάλμα" + diff --git a/android/src/main/res/values-en-rAU/strings.xml b/android/src/main/res/values-en-rAU/strings.xml new file mode 100644 index 00000000..6aca8292 --- /dev/null +++ b/android/src/main/res/values-en-rAU/strings.xml @@ -0,0 +1,29 @@ + + + + + "Touch the fingerprint sensor" + "Not recognised" + "Help message area" + "Fingerprint hardware not available." + "No fingerprints enrolled." + "This device does not have a fingerprint sensor" + "Fingerprint operation cancelled by user." + "Too many attempts. Please try again later." + "Unknown error" + diff --git a/android/src/main/res/values-en-rGB/strings.xml b/android/src/main/res/values-en-rGB/strings.xml new file mode 100644 index 00000000..6aca8292 --- /dev/null +++ b/android/src/main/res/values-en-rGB/strings.xml @@ -0,0 +1,29 @@ + + + + + "Touch the fingerprint sensor" + "Not recognised" + "Help message area" + "Fingerprint hardware not available." + "No fingerprints enrolled." + "This device does not have a fingerprint sensor" + "Fingerprint operation cancelled by user." + "Too many attempts. Please try again later." + "Unknown error" + diff --git a/android/src/main/res/values-en-rIN/strings.xml b/android/src/main/res/values-en-rIN/strings.xml new file mode 100644 index 00000000..6aca8292 --- /dev/null +++ b/android/src/main/res/values-en-rIN/strings.xml @@ -0,0 +1,29 @@ + + + + + "Touch the fingerprint sensor" + "Not recognised" + "Help message area" + "Fingerprint hardware not available." + "No fingerprints enrolled." + "This device does not have a fingerprint sensor" + "Fingerprint operation cancelled by user." + "Too many attempts. Please try again later." + "Unknown error" + diff --git a/android/src/main/res/values-es-rUS/strings.xml b/android/src/main/res/values-es-rUS/strings.xml new file mode 100644 index 00000000..c11d24ca --- /dev/null +++ b/android/src/main/res/values-es-rUS/strings.xml @@ -0,0 +1,29 @@ + + + + + "Toca el sensor de huellas dig." + "No se reconoció" + "Área de mensajes de ayuda" + "El hardware para detectar huellas digitales no está disponible." + "No se registraron huellas digitales." + "Este dispositivo no tiene sensor de huellas digitales" + "El usuario canceló la operación de huella digital." + "Demasiados intentos. Vuelve a intentarlo más tarde." + "Error desconocido" + diff --git a/android/src/main/res/values-es/strings.xml b/android/src/main/res/values-es/strings.xml new file mode 100644 index 00000000..cac5baf2 --- /dev/null +++ b/android/src/main/res/values-es/strings.xml @@ -0,0 +1,29 @@ + + + + + "Toca sensor huellas digitales" + "No se reconoce" + "Área de mensaje de ayuda" + "El hardware de huella digital no está disponible." + "No se ha registrado ninguna huella digital." + "El dispositivo no tiene ningún sensor de huellas digitales" + "El usuario ha cancelado la operación de huella digital." + "Has realizado demasiados intentos. Vuelve a probar más tarde." + "Error desconocido" + diff --git a/android/src/main/res/values-et/strings.xml b/android/src/main/res/values-et/strings.xml new file mode 100644 index 00000000..a6715fec --- /dev/null +++ b/android/src/main/res/values-et/strings.xml @@ -0,0 +1,29 @@ + + + + + "Puudutage sõrmejäljeandurit" + "Ei tuvastatud" + "Abisõnumi ala" + "Sõrmejälje riistvara pole saadaval." + "Ühtegi sõrmejälge pole registreeritud." + "Selles seadmes pole sõrmejäljeandurit" + "Kasutaja tühistas sõrmejälje kasutamise." + "Liiga palju katseid. Proovige hiljem uuesti." + "Tundmatu viga" + diff --git a/android/src/main/res/values-eu/strings.xml b/android/src/main/res/values-eu/strings.xml new file mode 100644 index 00000000..28a28dc0 --- /dev/null +++ b/android/src/main/res/values-eu/strings.xml @@ -0,0 +1,29 @@ + + + + + "Sakatu hatz-marken sentsorea" + "Ez da ezagutu" + "Laguntza-mezuaren eremua" + "Hatz-marken hardwarea ez dago erabilgarri." + "Ez da erregistratu hatz-markarik." + "Gailu honek ez du hatz-marken sentsorerik" + "Erabiltzaileak bertan behera utzi du hatz-marka bidezko eragiketa." + "Saiakera gehiegi egin dira. Saiatu berriro geroago." + "Errore ezezaguna" + diff --git a/android/src/main/res/values-fa/strings.xml b/android/src/main/res/values-fa/strings.xml new file mode 100644 index 00000000..182ba44f --- /dev/null +++ b/android/src/main/res/values-fa/strings.xml @@ -0,0 +1,29 @@ + + + + + "حسگر اثر انگشت را لمس کنید" + "شناسایی نشد" + "بخش پیام راهنما" + "سخت‌افزار اثرانگشت در دسترس نیست." + "اثر انگشتی ثبت نشده است." + "این دستگاه حسگر اثر انگشت ندارد" + "کاربر عملیات اثر انگشت را لغو کرد" + "تعداد تلاش‌ها بیش از حد مجاز است. لطفاً بعداً دوباره امتحان کنید." + "خطای ناشناس" + diff --git a/android/src/main/res/values-fi/strings.xml b/android/src/main/res/values-fi/strings.xml new file mode 100644 index 00000000..e90defad --- /dev/null +++ b/android/src/main/res/values-fi/strings.xml @@ -0,0 +1,29 @@ + + + + + "Kosketa sormenjälkitunnistinta" + "Ei tunnistettu" + "Ohjeviestialue" + "Sormenjälkilaitteisto ei ole käytettävissä." + "Sormenjälkiä ei ole lisätty." + "Laitteessa ei ole sormenjälkitunnistinta." + "Käyttäjä peruutti sormenjälkitoiminnon." + "Liian monta epäonnistunutta yritystä. Yritä myöhemmin uudelleen." + "Tuntematon virhe" + diff --git a/android/src/main/res/values-fr-rCA/strings.xml b/android/src/main/res/values-fr-rCA/strings.xml new file mode 100644 index 00000000..9cb2e9a0 --- /dev/null +++ b/android/src/main/res/values-fr-rCA/strings.xml @@ -0,0 +1,29 @@ + + + + + "Touch. capteur empr. digitales" + "Doigt non reconnu" + "Zone de message d\'aide" + "Le matériel de lecture d\'empreintes digitales n\'est pas accessible." + "Aucune empreinte digitale enregistrée." + "Cet appareil ne possède pas de capteur d\'empreintes digitales" + "L\'opération d\'authentification par empreinte digitale a été annulée par l\'utilisateur." + "Trop de tentatives. Veuillez réessayer plus tard." + "Erreur inconnue" + diff --git a/android/src/main/res/values-fr/strings.xml b/android/src/main/res/values-fr/strings.xml new file mode 100644 index 00000000..4127fe30 --- /dev/null +++ b/android/src/main/res/values-fr/strings.xml @@ -0,0 +1,29 @@ + + + + + "Appuyez sur lecteur d\'empreinte" + "Non reconnue" + "Zone de message d\'aide" + "Matériel de lecture d\'empreinte digitale indisponible." + "Aucune empreinte digitale enregistrée." + "Aucun lecteur d\'empreinte digitale n\'est installé sur cet appareil" + "Opération d\'authentification par empreinte digitale annulée par l\'utilisateur." + "Tentatives trop nombreuses. Veuillez réessayer plus tard." + "Erreur inconnue" + diff --git a/android/src/main/res/values-gl/strings.xml b/android/src/main/res/values-gl/strings.xml new file mode 100644 index 00000000..ed87183b --- /dev/null +++ b/android/src/main/res/values-gl/strings.xml @@ -0,0 +1,29 @@ + + + + + "Toca o sensor de impresión dixital" + "Non se recoñeceu" + "Área de mensaxes de axuda" + "O hardware de impresión dixital non está dispoñible." + "Non se rexistraron impresións dixitais." + "Este dispositivo non ten sensor de impresión dixital" + "O usuario cancelou a operación da impresión dixital." + "Tentáchelo demasiadas veces. Proba de novo máis tarde." + "Produciuse un erro descoñecido" + diff --git a/android/src/main/res/values-gu/strings.xml b/android/src/main/res/values-gu/strings.xml new file mode 100644 index 00000000..9cbceb6c --- /dev/null +++ b/android/src/main/res/values-gu/strings.xml @@ -0,0 +1,29 @@ + + + + + "ફિંગરપ્રિન્ટના સેન્સરને સ્પર્શ કરો" + "ઓળખાયેલ નથી" + "સહાય સંદેશનું ક્ષેત્ર" + "ફિંગરપ્રિન્ટ હાર્ડવેર ઉપલબ્ધ નથી." + "કોઈ ફિંગરપ્રિન્ટની નોંધણી કરવામાં આવી નથી." + "આ ડિવાઇસમાં કોઈ ફિંગરપ્રિન્ટ સેન્સર નથી" + "ફિંગરપ્રિન્ટ ચકાસવાની પ્રક્રિયા વપરાશકર્તાએ રદ કરી." + "ખૂબ વધારે પ્રયત્નો કર્યા. કૃપા કરીને પછીથી ફરી પ્રયાસ કરો." + "અજાણી ભૂલ" + diff --git a/android/src/main/res/values-hi/strings.xml b/android/src/main/res/values-hi/strings.xml new file mode 100644 index 00000000..ee692ba6 --- /dev/null +++ b/android/src/main/res/values-hi/strings.xml @@ -0,0 +1,29 @@ + + + + + "फ़िंगरप्रिंट सेंसर को छुएं" + "पहचान नहीं हो पाई" + "सहायता का मैसेज दिखाने की जगह" + "फ़िंगरप्रिंट हार्डवेयर मौजूद नहीं है." + "कोई फ़िंगरप्रिंट रजिस्टर नहीं किया गया है." + "इस डिवाइस में फ़िंगरप्रिंट सेंसर नहीं है" + "उपयोगकर्ता ने फिंगरप्रिंट की पुष्टि की कार्रवाई रद्द कर दी है." + "कई बार कोशिश की जा चुकी है. कृपया बाद में फिर से कोशिश करें." + "अनजान गड़बड़ी" + diff --git a/android/src/main/res/values-hr/strings.xml b/android/src/main/res/values-hr/strings.xml new file mode 100644 index 00000000..953fc5dd --- /dev/null +++ b/android/src/main/res/values-hr/strings.xml @@ -0,0 +1,29 @@ + + + + + "Dodirnite senzor otiska prsta" + "Nije prepoznat" + "Područje poruke za pomoć" + "Hardver za otisak prsta nije dostupan." + "Nije registriran nijedan otisak prsta." + "Ovaj uređaj nema senzor otiska prsta" + "Radnju s otiskom prsta otkazao je korisnik." + "Previše pokušaja. Pokušajte ponovno kasnije." + "Nepoznata pogreška" + diff --git a/android/src/main/res/values-hu/strings.xml b/android/src/main/res/values-hu/strings.xml new file mode 100644 index 00000000..326f7d98 --- /dev/null +++ b/android/src/main/res/values-hu/strings.xml @@ -0,0 +1,29 @@ + + + + + "Érintse meg az ujjlenyomat-érzékelőt" + "Nem ismerhető fel" + "Súgószöveg területe" + "Az ujjlenyomathoz szükséges hardverhez nem lehet hozzáférni." + "Nincsenek regisztrált ujjlenyomatok." + "Ez az eszköz nem rendelkezik ujjlenyomat-érzékelővel" + "Az ujjlenyomattal kapcsolatos műveletet a felhasználó megszakította." + "Túl sok próbálkozás. Próbálja újra később." + "Ismeretlen hiba" + diff --git a/android/src/main/res/values-hy/strings.xml b/android/src/main/res/values-hy/strings.xml new file mode 100644 index 00000000..b10d90d9 --- /dev/null +++ b/android/src/main/res/values-hy/strings.xml @@ -0,0 +1,29 @@ + + + + + "Հպեք մատնահետքերի սկաներին" + "Չհաջողվեց ճանաչել" + "Օգնության հաղորդագրության դաշտ" + "Մատնահետքերի սարքն անհասանելի է:" + "Գրանցված մատնահետքեր չկան:" + "Սարքը չունի մատնահետքերի սկաներ" + "Մատնահետքով նույնականացման գործողությունը չեղարկվել է օգտատիրոջ կողմից:" + "Չափից շատ փորձեր եք կատարել: Փորձեք ավելի ուշ:" + "Անհայտ սխալ" + diff --git a/android/src/main/res/values-in/strings.xml b/android/src/main/res/values-in/strings.xml new file mode 100644 index 00000000..4096d25b --- /dev/null +++ b/android/src/main/res/values-in/strings.xml @@ -0,0 +1,29 @@ + + + + + "Sentuh sensor sidik jari" + "Tidak dikenali" + "Area pesan bantuan" + "Hardware sidik jari tidak tersedia." + "Tidak ada sidik jari yang terdaftar." + "Perangkat ini tidak memiliki sensor sidik jari" + "Operasi sidik jari dibatalkan oleh pengguna." + "Terlalu banyak upaya yang gagal. Coba lagi nanti." + "Error tidak diketahui" + diff --git a/android/src/main/res/values-is/strings.xml b/android/src/main/res/values-is/strings.xml new file mode 100644 index 00000000..0326ad40 --- /dev/null +++ b/android/src/main/res/values-is/strings.xml @@ -0,0 +1,29 @@ + + + + + "Snertu fingrafaralesarann" + "Þekktist ekki" + "Svæði hjálparskilaboða" + "Fingrafarsvélbúnaður ekki til staðar." + "Engin fingraför hafa verið skráð." + "Þetta tæki er ekki með fingrafaralesara" + "Notandi hætti við að nota fingrafar." + "Of margar tilraunir. Reyndu aftur síðar." + "Óþekkt villa" + diff --git a/android/src/main/res/values-it/strings.xml b/android/src/main/res/values-it/strings.xml new file mode 100644 index 00000000..52f31b9a --- /dev/null +++ b/android/src/main/res/values-it/strings.xml @@ -0,0 +1,29 @@ + + + + + "Tocca sensore impronte digitali" + "Non riconosciuta" + "Area dei messaggi di assistenza" + "Hardware per l\'impronta digitale non disponibile." + "Nessuna impronta digitale registrata." + "Questo dispositivo non è dotato di sensore di impronte digitali" + "Operazione di autenticazione dell\'impronta digitale annullata dall\'utente." + "È stato effettuato un numero eccessivo di tentativi. Riprova più tardi." + "Errore sconosciuto" + diff --git a/android/src/main/res/values-iw/strings.xml b/android/src/main/res/values-iw/strings.xml new file mode 100644 index 00000000..8b7e3c31 --- /dev/null +++ b/android/src/main/res/values-iw/strings.xml @@ -0,0 +1,29 @@ + + + + + "יש לגעת בחיישן טביעות האצבע" + "לא זוהתה" + "אזור הודעת עזרה" + "החומרה בשביל טביעת אצבע אינה זמינה." + "לא נרשמו טביעות אצבע." + "במכשיר זה אין חיישן טביעות אצבע" + "פעולת טביעת האצבע בוטלה בידי המשתמש." + "ניסית יותר מדי פעמים. יש לנסות שוב מאוחר יותר." + "שגיאה לא ידועה" + diff --git a/android/src/main/res/values-ja/strings.xml b/android/src/main/res/values-ja/strings.xml new file mode 100644 index 00000000..ba741b9c --- /dev/null +++ b/android/src/main/res/values-ja/strings.xml @@ -0,0 +1,29 @@ + + + + + "指紋認証センサーをタップ" + "認識されませんでした" + "ヘルプ メッセージ領域" + "指紋認証ハードウェアは使用できません。" + "指紋が登録されていません。" + "このデバイスには指紋認証センサーがありません" + "指紋認証操作がユーザーによりキャンセルされました。" + "入力回数が上限を超えました。しばらくしてからもう一度お試しください。" + "不明なエラーです" + diff --git a/android/src/main/res/values-ka/strings.xml b/android/src/main/res/values-ka/strings.xml new file mode 100644 index 00000000..9780ba88 --- /dev/null +++ b/android/src/main/res/values-ka/strings.xml @@ -0,0 +1,29 @@ + + + + + "შეეხეთ თითის ანაბეჭდის სენსორს" + "არ არის ამოცნობილი" + "დამხმარე შეტყობინების არე" + "თითის ანაბეჭდის აპარატურა მიუწვდომელია." + "თითის ანაბეჭდები არ არის რეგისტრირებული." + "ამ მოწყობილობას არ აქვს თითის ანაბეჭდის სენსორი" + "თითის ანაბეჭდის ოპერაცია გააუქმა მომხმარებელმა." + "მეტისმეტად ბევრი მცდელობა იყო. გთხოვთ, ცადოთ მოგვიანებით." + "უცნობი შეცდომა" + diff --git a/android/src/main/res/values-kk/strings.xml b/android/src/main/res/values-kk/strings.xml new file mode 100644 index 00000000..5ba17648 --- /dev/null +++ b/android/src/main/res/values-kk/strings.xml @@ -0,0 +1,29 @@ + + + + + "Саусақ ізін оқу сканерін түртіңіз" + "Танылмады" + "Анықтама хабары аумағы" + "Саусақ ізі жабдығы қолжетімді емес." + "Саусақ іздері тіркелмеген." + "Бұл құрылғыда саусақ ізін оқу сканері жоқ" + "Пайдаланушы саусақ ізі операциясынан бас тартты." + "Тым көп әрекет жасалды. Кейінірек қайталап көріңіз." + "Белгісіз қате" + diff --git a/android/src/main/res/values-km/strings.xml b/android/src/main/res/values-km/strings.xml new file mode 100644 index 00000000..a7f641e6 --- /dev/null +++ b/android/src/main/res/values-km/strings.xml @@ -0,0 +1,29 @@ + + + + + "ប៉ះ​ឧបករណ៍​ចាប់ស្នាម​ម្រាមដៃ" + "មិនអាចសម្គាល់បានទេ" + "តំបន់សារ​ជំនួយ" + "មិន​អាច​ប្រើហាតវែរ​ស្នាមម្រាមដៃបាន​ទេ។" + "មិន​មាន​ការ​ថតបញ្ចូល​ស្នាម​ម្រាមដៃទេ។" + "ឧបករណ៍នេះ​មិនមាន​ឧបករណ៍ចាប់​ស្នាមម្រាមដៃទេ" + "ប្រតិបត្តិការ​ស្នាម​ម្រាម​ដៃ​ត្រូវ​បាន​បោះ​បង់​ដោយ​អ្នក​ប្រើប្រាស់។" + "ផ្ទៀងផ្ទាត់​មិនត្រឹមត្រូវ​ច្រើនដងពេក​។ សូម​ព្យាយាម​ម្ដង​ទៀត​នៅ​ពេល​ក្រោយ​។" + "មាន​បញ្ហា​ដែល​មិន​ស្គាល់" + diff --git a/android/src/main/res/values-kn/strings.xml b/android/src/main/res/values-kn/strings.xml new file mode 100644 index 00000000..ac2a497d --- /dev/null +++ b/android/src/main/res/values-kn/strings.xml @@ -0,0 +1,29 @@ + + + + + "ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಸೆನ್ಸರ್‌‌ ಅನ್ನು ಸ್ಪರ್ಶಿಸಿ" + "ಗುರುತಿಸಲಾಗಿಲ್ಲ" + "ಸಹಾಯ ಸಂದೇಶ ಪ್ರದೇಶ" + "ಫಿಂಗರ್‌ ಫ್ರಿಂಟ್‌ ಹಾರ್ಡ್‌ವೇರ್‌ ಲಭ್ಯವಿಲ್ಲ." + "ಯಾವುದೇ ಫಿಂಗರ್‌ಪ್ರಿಂಟ್‌ ಅನ್ನು ನೋಂದಣಿ ಮಾಡಿಲ್ಲ." + "ಈ ಸಾಧನವು ಫಿಂಗರ್‌ಪ್ರಿಂಟ್‌ ಸೆನ್ಸಾರ್ ಅನ್ನು ಹೊಂದಿಲ್ಲ" + "ಬಳಕೆದಾರರಿಂದ ಫಿಂಗರ್‌ ಫ್ರಿಂಟ್‌ ಕಾರ್ಯಾಚರಣೆಯನ್ನು ರದ್ದುಪಡಿಸಲಾಗಿದೆ." + "ಹಲವು ಬಾರಿ ಪ್ರಯತ್ನಿಸಿರುವಿರಿ ನಂತರ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ." + "ಅಪರಿಚಿತ ದೋಷ" + diff --git a/android/src/main/res/values-ko/strings.xml b/android/src/main/res/values-ko/strings.xml new file mode 100644 index 00000000..fe338bd8 --- /dev/null +++ b/android/src/main/res/values-ko/strings.xml @@ -0,0 +1,29 @@ + + + + + "지문 센서를 터치하세요." + "인식할 수 없음" + "도움말 메시지 영역" + "지문 인식 하드웨어를 사용할 수 없습니다." + "등록된 지문이 없습니다." + "기기에 지문 센서가 없습니다." + "사용자가 지문 인식 작업을 취소했습니다." + "시도 횟수가 너무 많습니다. 나중에 다시 시도해 주세요." + "알 수 없는 오류" + diff --git a/android/src/main/res/values-ky/strings.xml b/android/src/main/res/values-ky/strings.xml new file mode 100644 index 00000000..faecae05 --- /dev/null +++ b/android/src/main/res/values-ky/strings.xml @@ -0,0 +1,29 @@ + + + + + "Манжа изинин сенсорун басыңыз" + "Таанылган жок" + "Жардам билдирүүсү" + "Манжа изинин аппараттык камсыздоосу жеткиликтүү эмес." + "Бир да манжа изи катталган эмес." + "Бул түзмөктө манжа изинин сенсору жок" + "Манжа изи менен аныктыгын текшерүүнү колдонуучу жокко чыгарды." + "Өтө көп жолу аракет кылдыңыз. Кийинчерээк кайра кайталап көрүңүз." + "Белгисиз ката" + diff --git a/android/src/main/res/values-lo/strings.xml b/android/src/main/res/values-lo/strings.xml new file mode 100644 index 00000000..d9d50b7c --- /dev/null +++ b/android/src/main/res/values-lo/strings.xml @@ -0,0 +1,29 @@ + + + + + "ແຕະເຊັນເຊີລາຍນິ້ວມື" + "ບໍ່ຮັບຮູ້" + "ພື້ນທີ່ຂໍ້ຄວາມຊ່ວຍເຫຼືອ" + "ບໍ່​ມີ​ຮາດ​ແວລາຍ​ນີ້ວ​ມື​ທີ່ສາມາດໃຊ້ໄດ້." + "ບໍ່ມີການລົງທະບຽນລາຍນິ້ວມື." + "ອຸປະກອນນີ້ບໍ່ມີເຊັນເຊີລາຍນິ້ວມື" + "ຜູ້ໃຊ້ໄດ້ຍົກເລີກຄຳສັ່ງລາຍນິ້ວມືແລ້ວ." + "ມີຄວາມພະຍາຍາມຫຼາຍເທື່ອເກີນໄປ. ກະລຸນາລອງໃໝ່ໃນພາຍຫຼັງ." + "ຄວາມຜິດພາດທີ່ບໍ່ຮູ້ຈັກ" + diff --git a/android/src/main/res/values-lt/strings.xml b/android/src/main/res/values-lt/strings.xml new file mode 100644 index 00000000..4bda21ed --- /dev/null +++ b/android/src/main/res/values-lt/strings.xml @@ -0,0 +1,29 @@ + + + + + "Palieskite piršto antspaudo jutiklį" + "Neatpažinta" + "Pagalbos pranešimo sritis" + "Piršto antspaudo aparatinė įranga nepasiekiama." + "Neužregistruota jokių kontrolinių kodų." + "Šiame įrenginyje nėra piršto antspaudo jutiklio" + "Piršto antspaudo operaciją atšaukė naudotojas." + "Per daug bandymų. Vėliau bandykite dar kartą." + "Nežinoma klaida" + diff --git a/android/src/main/res/values-lv/strings.xml b/android/src/main/res/values-lv/strings.xml new file mode 100644 index 00000000..4bcda7fd --- /dev/null +++ b/android/src/main/res/values-lv/strings.xml @@ -0,0 +1,29 @@ + + + + + "Pieskarieties pirksta nospieduma sensoram" + "Nav atpazīts" + "Palīdzības ziņojuma apgabals" + "Pirksta nospieduma aparatūra nav pieejama." + "Nav reģistrēts neviens pirksta nospiedums." + "Šajā ierīcē nav pirksta nospieduma sensora" + "Lietotājs atcēla pirksta nospieduma darbību." + "Pārāk daudz mēģinājumu. Lūdzu, vēlāk mēģiniet vēlreiz." + "Nezināma kļūda" + diff --git a/android/src/main/res/values-mk/strings.xml b/android/src/main/res/values-mk/strings.xml new file mode 100644 index 00000000..d3c3f98a --- /dev/null +++ b/android/src/main/res/values-mk/strings.xml @@ -0,0 +1,29 @@ + + + + + "Допрете го сенз. за отпечатоци" + "Непознат" + "Поле за пораки за помош" + "Нема достапен хардвер за отпечатоци." + "Нема запишани отпечатоци." + "Уредов нема сензор за отпечатоци" + "Корисникот ја откажа потврдата со отпечаток." + "Премногу обиди. Обидете се повторно подоцна." + "Непозната грешка" + diff --git a/android/src/main/res/values-ml/strings.xml b/android/src/main/res/values-ml/strings.xml new file mode 100644 index 00000000..d244e8ce --- /dev/null +++ b/android/src/main/res/values-ml/strings.xml @@ -0,0 +1,29 @@ + + + + + "വിരലടയാള സെൻസർ സ്‌പർശിക്കുക" + "തിരിച്ചറിഞ്ഞില്ല" + "സഹായ സന്ദേശ ഏരിയ" + "ഫിംഗർപ്രിന്റ് ഹാർഡ്‌വെയർ ലഭ്യമല്ല." + "ഫിംഗർപ്രിന്റുകളൊന്നും എൻറോൾ ചെയ്‌തിട്ടില്ല." + "ഈ ഉപകരണത്തിൽ വിരലടയാള സെൻസർ ഇല്ല" + "ഫിംഗർപ്രിന്റിന്റെ പ്രവർത്തനം ഉപയോക്താവ് റദ്ദാക്കി." + "നിരവധി ശ്രമങ്ങൾ. പിന്നീട് വീണ്ടും ശ്രമിക്കുക." + "അജ്ഞാത പിശക്" + diff --git a/android/src/main/res/values-mn/strings.xml b/android/src/main/res/values-mn/strings.xml new file mode 100644 index 00000000..da900393 --- /dev/null +++ b/android/src/main/res/values-mn/strings.xml @@ -0,0 +1,29 @@ + + + + + "Хурууны хээ мэдрэгчид хүрнэ үү" + "Таниагүй" + "Туслах мессежний хэсэг" + "Хурууны хээний техник хангамж боломжгүй байна." + "Бүртгүүлсэн хурууны хээ алга байна." + "Энэ төхөөрөмжид хурууны хээ мэдрэгч алга байна" + "Хэрэглэгч хурууны хээний баталгаажуулалтыг болиулсан байна." + "Хэт олон удаа оролдлоо. Та дараа дахин оролдоно уу." + "Тодорхойгүй алдаа гарлаа" + diff --git a/android/src/main/res/values-mr/strings.xml b/android/src/main/res/values-mr/strings.xml new file mode 100644 index 00000000..58c9c048 --- /dev/null +++ b/android/src/main/res/values-mr/strings.xml @@ -0,0 +1,29 @@ + + + + + "फिंगरप्रिंट सेन्सरला स्पर्श करा" + "ओळखले नाही" + "मदत मेसेज परिसर" + "फिंगरप्रिंट हार्डवेअर उपलब्‍ध नाही." + "कोणत्याही फिंगरप्रिंटची नोंद झाली नाही." + "या डिव्हाइसवर फिंगरप्रिंट सेन्सर नाही" + "वापरकर्त्याने फिंगरप्रिंट ऑपरेशन रद्द केले." + "खूप जास्त प्रयत्न. कृपया नंतर पुन्हा प्रयत्न करा." + "अज्ञात एरर" + diff --git a/android/src/main/res/values-ms/strings.xml b/android/src/main/res/values-ms/strings.xml new file mode 100644 index 00000000..472319fb --- /dev/null +++ b/android/src/main/res/values-ms/strings.xml @@ -0,0 +1,29 @@ + + + + + "Sentuh penderia cap jari" + "Tidak dikenali" + "Bahagian mesej bantuan" + "Perkakasan cap jari tidak tersedia." + "Tiada cap jari didaftarkan." + "Peranti ini tiada penderia cap jari" + "Pengendalian cap jari dibatalkan oleh pengguna." + "Terlalu banyak percubaan. Sila cuba sebentar lagi." + "Ralat tidak diketahui" + diff --git a/android/src/main/res/values-my/strings.xml b/android/src/main/res/values-my/strings.xml new file mode 100644 index 00000000..ac23e9d1 --- /dev/null +++ b/android/src/main/res/values-my/strings.xml @@ -0,0 +1,29 @@ + + + + + "လက်ဗွေအာရုံခံကိရိယာကို တို့ပါ" + "မသိပါ" + "အကူအညီမက်ဆေ့ဂျ် နေရာ" + "လက်ဗွေစက်ပစ္စည်း မရနိုင်ပါ။" + "မည်သည့် လက်ဗွေကိုမျှ ထည့်သွင်းမထားပါ။" + "ဤစက်ပစ္စည်းတွင် လက်ဗွေအာရုံခံကိရိယာ မရှိပါ" + "လက်ဗွေဖြင့် အထောက်အထားစိစစ်ခြင်းကို အသုံးပြုသူက ပယ်ဖျက်ထားသည်။" + "အကြိမ်များစွာ စမ်းပြီးပါပြီ။ နောက်မှ ထပ်စမ်းကြည့်ပါ။" + "အမျိုးအမည်မသိ အမှား" + diff --git a/android/src/main/res/values-nb/strings.xml b/android/src/main/res/values-nb/strings.xml new file mode 100644 index 00000000..d7740688 --- /dev/null +++ b/android/src/main/res/values-nb/strings.xml @@ -0,0 +1,29 @@ + + + + + "Trykk på fingeravtrykkssensoren" + "Ikke gjenkjent" + "Område for hjelpemelding" + "Maskinvare for fingeravtrykk er ikke tilgjengelig." + "Ingen fingeravtrykk er registrert." + "Denne enheten har ikke fingeravtrykkssensor" + "Fingeravtrykk-operasjonen ble avbrutt av brukeren." + "Du har gjort for mange forsøk. Prøv på nytt senere." + "Ukjent feil" + diff --git a/android/src/main/res/values-ne/strings.xml b/android/src/main/res/values-ne/strings.xml new file mode 100644 index 00000000..5b6715f1 --- /dev/null +++ b/android/src/main/res/values-ne/strings.xml @@ -0,0 +1,29 @@ + + + + + "फिंगरप्रिन्ट सेन्सरमा छुनुहोस्‌" + "पहिचान भएन" + "मद्दतसम्बन्धी सन्देशको क्षेत्र" + "फिंगरप्रिन्ट हार्डवेयर उपलब्ध छैन।" + "कुनै पनि फिंगरप्रिन्ट दर्ता गरिएको छैन।" + "यो यन्त्रमा कुनै फिंगरप्रिन्ट सेन्सर छैन" + "प्रयोगकर्ताले फिंगरप्रिन्टसम्बन्धी कारबाही रद्द गर्नुभयो।" + "अत्यधिक पटक प्रयासहरू गरिए। कृपया पछि फेरि प्रयास गर्नुहोस्।" + "अज्ञात त्रुटि" + diff --git a/android/src/main/res/values-nl/strings.xml b/android/src/main/res/values-nl/strings.xml new file mode 100644 index 00000000..dd02f57a --- /dev/null +++ b/android/src/main/res/values-nl/strings.xml @@ -0,0 +1,29 @@ + + + + + "Raak de vingerafdruksensor aan" + "Niet herkend" + "Gebied voor Help-berichten" + "Hardware voor vingerafdruk niet beschikbaar." + "Geen vingerafdrukken geregistreerd." + "Dit apparaat heeft geen vingerafdruksensor" + "Vingerafdrukverificatie geannuleerd door gebruiker." + "Te veel pogingen. Probeer het later opnieuw." + "Onbekende fout" + diff --git a/android/src/main/res/values-or/strings.xml b/android/src/main/res/values-or/strings.xml new file mode 100644 index 00000000..7b3eac07 --- /dev/null +++ b/android/src/main/res/values-or/strings.xml @@ -0,0 +1,29 @@ + + + + + "ଟିପଚିହ୍ନ ସେନସର୍‌କୁ ଛୁଅଁନ୍ତୁ" + "ଚିହ୍ନଟ ହେଲାନାହିଁ" + "ସହାୟତା ମେସେଜ୍ କ୍ଷେତ୍ର" + "ଆଙ୍ଗୁଠି ଚିହ୍ନ ହାର୍ଡୱେର୍‍ ଉପଲବ୍ଧ ନାହିଁ।" + "କୌଣସି ଆଙ୍ଗୁଠି ଚିହ୍ନ ପଞ୍ଜୀକୃତ ହୋଇନାହିଁ।" + "ଏହି ଡିଭାଇସ୍‌ରେ ଆଙ୍ଗୁଠି ଚିହ୍ନ ସେନସର୍‌ ନାହିଁ" + "ୟୁଜର୍‌ଙ୍କ ଦ୍ଵାରା ଆଙ୍ଗୁଠି ଚିହ୍ନ ନେବା କାମକୁ କ୍ୟାନ୍ସଲ୍ କରିଦିଆଯାଇଛି।" + "ବହୁତ ଅଧିକ ପ୍ରଚେଷ୍ଟା। ଦୟାକରି ପରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।" + "ଅଜଣା ତ୍ରୁଟି" + diff --git a/android/src/main/res/values-pa/strings.xml b/android/src/main/res/values-pa/strings.xml new file mode 100644 index 00000000..dbc68341 --- /dev/null +++ b/android/src/main/res/values-pa/strings.xml @@ -0,0 +1,29 @@ + + + + + "ਫਿੰਗਰਪ੍ਰਿੰਟ ਸੈਂਸਰ ਨੂੰ ਸਪਰਸ਼ ਕਰੋ" + "ਪਛਾਣ ਨਹੀਂ ਹੋਈ" + "ਮਦਦ ਸੁਨੇਹਾ ਖੇਤਰ" + "ਫਿੰਗਰਪ੍ਰਿੰਟ ਹਾਰਡਵੇਅਰ ਉਪਲਬਧ ਨਹੀਂ।" + "ਕੋਈ ਫਿੰਗਰਪ੍ਰਿੰਟ ਦਰਜ ਨਹੀਂ ਕੀਤਾ ਗਿਆ।" + "ਇਸ ਡੀਵਾਈਸ ਵਿੱਚ ਫਿੰਗਰਪ੍ਰਿੰਟ ਸੈਂਸਰ ਨਹੀਂ ਹੈ" + "ਫਿੰਗਰਪ੍ਰਿੰਟ ਦੇ ਪੁਸ਼ਟੀਕਰਨ ਦੀ ਕਾਰਵਾਈ ਵਰਤੋਂਕਾਰ ਵੱਲੋਂ ਰੱਦ ਕੀਤੀ ਗਈ।" + "ਬਹੁਤ ਜ਼ਿਆਦਾ ਕੋਸ਼ਿਸ਼ਾਂ। ਕਿਰਪਾ ਕਰਕੇ ਬਾਅਦ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।" + "ਅਗਿਆਤ ਗੜਬੜ" + diff --git a/android/src/main/res/values-pl/strings.xml b/android/src/main/res/values-pl/strings.xml new file mode 100644 index 00000000..959fe51e --- /dev/null +++ b/android/src/main/res/values-pl/strings.xml @@ -0,0 +1,29 @@ + + + + + "Dotknij czytnika linii papilarnych" + "Nie rozpoznano" + "Obszar komunikatu pomocy" + "Czytnik linii papilarnych nie jest dostępny." + "Nie zarejestrowano odcisków palców." + "To urządzenie nie jest wyposażone w czytnik linii papilarnych" + "Odczyt odcisku palca został anulowany przez użytkownika." + "Zbyt wiele prób. Spróbuj ponownie później." + "Nieznany błąd" + diff --git a/android/src/main/res/values-pt-rBR/strings.xml b/android/src/main/res/values-pt-rBR/strings.xml new file mode 100644 index 00000000..21ab05cf --- /dev/null +++ b/android/src/main/res/values-pt-rBR/strings.xml @@ -0,0 +1,29 @@ + + + + + "Toque no sensor de digital" + "Não reconhecido" + "Área da mensagem de ajuda" + "Hardware de impressão digital não disponível." + "Nenhuma impressão digital registrada." + "Este dispositivo não tem um sensor de impressão digital" + "Operação de impressão digital cancelada pelo usuário." + "Muitas tentativas. Tente novamente mais tarde." + "Erro desconhecido" + diff --git a/android/src/main/res/values-pt-rPT/strings.xml b/android/src/main/res/values-pt-rPT/strings.xml new file mode 100644 index 00000000..751843a4 --- /dev/null +++ b/android/src/main/res/values-pt-rPT/strings.xml @@ -0,0 +1,29 @@ + + + + + "Toque no sensor de impressões digitais" + "Não reconhecida." + "Área da mensagem de ajuda" + "Hardware de impressão digital não disponível." + "Nenhuma impressão digital registada." + "Este dispositivo não tem sensor de impressões digitais." + "Operação de impressão digital cancelada pelo utilizador." + "Demasiadas tentativas. Tente novamente mais tarde." + "Erro desconhecido." + diff --git a/android/src/main/res/values-pt/strings.xml b/android/src/main/res/values-pt/strings.xml new file mode 100644 index 00000000..21ab05cf --- /dev/null +++ b/android/src/main/res/values-pt/strings.xml @@ -0,0 +1,29 @@ + + + + + "Toque no sensor de digital" + "Não reconhecido" + "Área da mensagem de ajuda" + "Hardware de impressão digital não disponível." + "Nenhuma impressão digital registrada." + "Este dispositivo não tem um sensor de impressão digital" + "Operação de impressão digital cancelada pelo usuário." + "Muitas tentativas. Tente novamente mais tarde." + "Erro desconhecido" + diff --git a/android/src/main/res/values-ro/strings.xml b/android/src/main/res/values-ro/strings.xml new file mode 100644 index 00000000..ce2ecbf6 --- /dev/null +++ b/android/src/main/res/values-ro/strings.xml @@ -0,0 +1,29 @@ + + + + + "Atingeți senzorul de amprentă" + "Nu este recunoscut" + "Zona mesajelor de ajutor" + "Hardware-ul pentru amprenta digitală nu este disponibil." + "Nu au fost înregistrate amprente digitale." + "Acest dispozitiv nu are senzor de amprentă" + "Operațiunea privind amprenta digitală a fost anulată de utilizator." + "Prea multe încercări. Încercați din nou mai târziu." + "Eroare necunoscută" + diff --git a/android/src/main/res/values-ru/strings.xml b/android/src/main/res/values-ru/strings.xml new file mode 100644 index 00000000..18702035 --- /dev/null +++ b/android/src/main/res/values-ru/strings.xml @@ -0,0 +1,29 @@ + + + + + "Коснитесь сканера отпечатков." + "Не распознано" + "Справочное сообщение" + "Сканер отпечатков пальцев недоступен." + "Нет отсканированных отпечатков пальцев." + "На этом устройстве нет сканера отпечатков пальцев." + "Операция с отпечатком пальца отменена пользователем." + "Слишком много попыток входа. Попробуйте ещё раз позже." + "Неизвестная ошибка" + diff --git a/android/src/main/res/values-si/strings.xml b/android/src/main/res/values-si/strings.xml new file mode 100644 index 00000000..9710cb70 --- /dev/null +++ b/android/src/main/res/values-si/strings.xml @@ -0,0 +1,29 @@ + + + + + "ඇඟිලි සලකුණු සංවේදකය ස්පර්ශ කරන්න" + "හඳුනා නොගන්නා ලදී" + "උදවු පණිවිඩ ප්‍රදේශය" + "ඇඟිලි සලකුණු දෘඪාංගය ලද නොහැකිය." + "ඇඟිලි සලකුණු ඇතුළත් කර නොමැත." + "මෙම උපාංගයේ ඇඟිලි සලකුණු සංවේදකයක් නොමැත" + "පරිශීලක විසින් ඇඟිලි සලකුණු මෙහෙයුම අවසන් කරන ලදී." + "උත්සාහ ඉතා වැඩියි. පසුව නැවත උත්සාහ කරන්න." + "නොදන්නා දෝෂයකි" + diff --git a/android/src/main/res/values-sk/strings.xml b/android/src/main/res/values-sk/strings.xml new file mode 100644 index 00000000..926deae6 --- /dev/null +++ b/android/src/main/res/values-sk/strings.xml @@ -0,0 +1,29 @@ + + + + + "Dotknite sa senzora odtlačkov prstov" + "Nerozpoznané" + "Oblasť správy pomocníka" + "Hardvér na snímanie odtlačku prsta nie je k dispozícii." + "Neregistrovali ste žiadne odtlačky prstov." + "Toto zariadenie nemá senzor odtlačkov prstov" + "Overenie odtlačku prsta zrušil používateľ." + "Príliš veľa pokusov. Skúste to znova neskôr." + "Neznáma chyba" + diff --git a/android/src/main/res/values-sl/strings.xml b/android/src/main/res/values-sl/strings.xml new file mode 100644 index 00000000..ec8aab3c --- /dev/null +++ b/android/src/main/res/values-sl/strings.xml @@ -0,0 +1,29 @@ + + + + + "Dotaknite se tipala prst. odt." + "Ni prepoznano" + "Območje sporočila pomoči" + "Strojna oprema za prstne odtise ni na voljo." + "Ni prijavljenih prstnih odtisov." + "Ta naprava nima tipala prstnih odtisov" + "Dejanje s prstnim odtisom je preklical uporabnik." + "Preveč poskusov. Poskusite znova pozneje." + "Neznana napaka" + diff --git a/android/src/main/res/values-sq/strings.xml b/android/src/main/res/values-sq/strings.xml new file mode 100644 index 00000000..66332f55 --- /dev/null +++ b/android/src/main/res/values-sq/strings.xml @@ -0,0 +1,29 @@ + + + + + "Prek sensorin e gjurmës së gishtit" + "Nuk njihet" + "Zona e mesazhit të ndihmës" + "Hardueri i gjurmës së gishtit nuk mundësohet." + "Nuk ka asnjë gjurmë gishti të regjistruar." + "Kjo pajisje nuk ka një sensor të gjurmës së gishtit" + "Veprimi i gjurmës së gishtit u anulua nga përdoruesi." + "Janë bërë shumë përpjekje. Provo përsëri më vonë." + "Gabim i panjohur" + diff --git a/android/src/main/res/values-sr/strings.xml b/android/src/main/res/values-sr/strings.xml new file mode 100644 index 00000000..2e85509e --- /dev/null +++ b/android/src/main/res/values-sr/strings.xml @@ -0,0 +1,29 @@ + + + + + "Додирн. сензор за отисак прста" + "Није препознат" + "Област поруке за помоћ" + "Хардвер за отиске прстију није доступан." + "Није регистрован ниједан отисак прста." + "Овај уређај нема сензор за отисак прста" + "Корисник је отказао радњу са отиском прста." + "Превише покушаја. Пробајте поново касније." + "Непозната грешка" + diff --git a/android/src/main/res/values-sv/strings.xml b/android/src/main/res/values-sv/strings.xml new file mode 100644 index 00000000..26a88f83 --- /dev/null +++ b/android/src/main/res/values-sv/strings.xml @@ -0,0 +1,29 @@ + + + + + "Tryck på fingeravtryckssensorn" + "Identifierades inte" + "Område för hjälpmeddelande" + "Det finns ingen maskinvara för fingeravtryck." + "Inga fingeravtryck har registrerats." + "Enheten har ingen fingeravtryckssensor" + "Fingeravtrycksåtgärden avbröts av användaren." + "För många försök. Försök igen senare." + "Okänt fel" + diff --git a/android/src/main/res/values-sw/strings.xml b/android/src/main/res/values-sw/strings.xml new file mode 100644 index 00000000..e8c0bd97 --- /dev/null +++ b/android/src/main/res/values-sw/strings.xml @@ -0,0 +1,29 @@ + + + + + "Gusa kitambua alama ya kidole" + "Haitambuliwi" + "Sehemu ya ujumbe wa usaidizi" + "Maunzi ya kitambulisho hayapatikani." + "Hakuna alama za vidole zilizojumuishwa." + "Kifaa hiki hakina kitambua alama ya kidole" + "Mtumiaji ameghairi uthibitishaji wa alama ya kidole." + "Umejaribu mara nyingi mno. Tafadhali jaribu tena baadaye." + "Hitilafu isiyojulikana" + diff --git a/android/src/main/res/values-ta/strings.xml b/android/src/main/res/values-ta/strings.xml new file mode 100644 index 00000000..db04a0d6 --- /dev/null +++ b/android/src/main/res/values-ta/strings.xml @@ -0,0 +1,29 @@ + + + + + "கைரேகை சென்சாரைத் தொடுக" + "பொருந்தவில்லை" + "உதவிச் செய்திக்கான பகுதி" + "கைரேகை வன்பொருள் இல்லை." + "கைரேகைப் பதிவுகள் எதுவுமில்லை." + "இந்தச் சாதனத்தில் கைரேகை சென்சார் இல்லை" + "கைரேகைச் சரிபார்ப்பு பயனரால் ரத்துசெய்யப்பட்டது." + "பலமுறை முயன்றுவிட்டீர்கள். பிறகு முயலவும்." + "அறியப்படாத பிழை" + diff --git a/android/src/main/res/values-te/strings.xml b/android/src/main/res/values-te/strings.xml new file mode 100644 index 00000000..e9627ccb --- /dev/null +++ b/android/src/main/res/values-te/strings.xml @@ -0,0 +1,29 @@ + + + + + "వేలిముద్ర సెన్సార్‌ను తాకండి" + "గుర్తించబడలేదు" + "సహాయ సందేశ ప్రాంతం" + "వేలిముద్ర హార్డ్‌వేర్ అందుబాటులో లేదు." + "వేలిముద్రలు నమోదు చేయబడలేదు." + "ఈ పరికరంలో వేలిముద్ర సెన్సార్ లేదు" + "వేలిముద్ర చర్యని వినియోగదారు రద్దు చేసారు." + "చాలా ఎక్కువ ప్రయత్నాలు చేసారు. దయచేసి తర్వాత మళ్లీ ప్రయత్నించండి." + "తెలియని ఎర్రర్" + diff --git a/android/src/main/res/values-th/strings.xml b/android/src/main/res/values-th/strings.xml new file mode 100644 index 00000000..50e92476 --- /dev/null +++ b/android/src/main/res/values-th/strings.xml @@ -0,0 +1,29 @@ + + + + + "แตะเซ็นเซอร์ลายนิ้วมือ" + "ไม่รู้จัก" + "พื้นที่ข้อความช่วยเหลือ" + "ฮาร์ดแวร์ลายนิ้วมือไม่พร้อมใช้งาน" + "ไม่มีลายนิ้วมือที่ลงทะเบียน" + "อุปกรณ์นี้ไม่มีเซ็นเซอร์ลายนิ้วมือ" + "ผู้ใช้ยกเลิกการทำงานของลายนิ้วมือ" + "ลองหลายครั้งเกินไป โปรดลองอีกครั้งภายหลัง" + "ข้อผิดพลาดที่ไม่รู้จัก" + diff --git a/android/src/main/res/values-tl/strings.xml b/android/src/main/res/values-tl/strings.xml new file mode 100644 index 00000000..3efe18fa --- /dev/null +++ b/android/src/main/res/values-tl/strings.xml @@ -0,0 +1,29 @@ + + + + + "Pindutin ang fingerprint sensor" + "Hindi nakilala" + "Lugar ng mensahe ng tulong" + "Hindi available ang hardware na ginagamitan ng fingerprint." + "Walang naka-enroll na fingerprint." + "Walang sensor para sa fingerprint ang device na ito" + "Kinansela ng user ang operasyon sa fingerprint." + "Masyadong maraming pagsubok. Pakisubukang muli sa ibang pagkakataon." + "Hindi alam na error" + diff --git a/android/src/main/res/values-tr/strings.xml b/android/src/main/res/values-tr/strings.xml new file mode 100644 index 00000000..9d26498b --- /dev/null +++ b/android/src/main/res/values-tr/strings.xml @@ -0,0 +1,29 @@ + + + + + "Parmak izi sensörüne dokunun" + "Tanınmadı" + "Yardım mesajı alanı" + "Parmak izi donanımı kullanılamıyor." + "Parmak izi kaydedilmedi." + "Bu cihazda parmak izi sensörü yok" + "Parmak izi işlemi kullanıcı tarafından iptal edildi." + "Çok fazla deneme yapıldı. Lütfen daha sonra tekrar deneyin." + "Bilinmeyen hata" + diff --git a/android/src/main/res/values-uk/strings.xml b/android/src/main/res/values-uk/strings.xml new file mode 100644 index 00000000..98c3093b --- /dev/null +++ b/android/src/main/res/values-uk/strings.xml @@ -0,0 +1,29 @@ + + + + + "Торкніться сканера відбитків пальців" + "Не розпізнано" + "Область довідкового повідомлення" + "Апаратне забезпечення для сканування відбитка недоступне." + "Відбитки пальців не зареєстровано." + "На цьому пристрої немає сканера відбитків пальців" + "Користувач скасував дію з відбитком пальця." + "Забагато спроб. Зачекайте." + "Невідома помилка" + diff --git a/android/src/main/res/values-ur/strings.xml b/android/src/main/res/values-ur/strings.xml new file mode 100644 index 00000000..1900936b --- /dev/null +++ b/android/src/main/res/values-ur/strings.xml @@ -0,0 +1,29 @@ + + + + + "فنگر پرنٹ سینسر کو ٹچ کریں" + "شناخت نہیں ہو سکی" + "مدد کے پیغام کا علاقہ" + "فنگر پرنٹ ہارڈ ویئر دستیاب نہیں ہے۔" + "کوئی فنگر پرنٹ مندرج نہیں ہے۔" + "اس آلہ میں فنگر پرنٹ سینسر نہیں ہے" + "صارف نے فنگر پرنٹ کی کارروائی منسوخ کر دی۔" + "کافی زیادہ کوششیں۔ براہ کرم بعد میں دوبارہ کوشش کریں۔" + "نامعلوم خرابی" + diff --git a/android/src/main/res/values-uz/strings.xml b/android/src/main/res/values-uz/strings.xml new file mode 100644 index 00000000..f7ac87b0 --- /dev/null +++ b/android/src/main/res/values-uz/strings.xml @@ -0,0 +1,29 @@ + + + + + "Barmoq izi skaneriga tegining" + "Aniqlanmadi" + "Yordam xabari" + "Barmoq izi skaneri ish holatida emas." + "Hech qanday barmoq izi qayd qilinmagan." + "Bu qurilmada barmoq izi skaneri yo‘q" + "Barmoq izi amali foydalanuvchi tomonidan bekor qilindi" + "Juda koʻp urinish amalga oshirildi. Keyinroq qaytadan urining." + "Notanish xato" + diff --git a/android/src/main/res/values-vi/strings.xml b/android/src/main/res/values-vi/strings.xml new file mode 100644 index 00000000..89bee557 --- /dev/null +++ b/android/src/main/res/values-vi/strings.xml @@ -0,0 +1,29 @@ + + + + + "Chạm vào cảm biến vân tay" + "Không nhận dạng được" + "Vùng thông báo trợ giúp" + "Không dùng được phần cứng vân tay." + "Chưa đăng ký vân tay." + "Thiết bị này không có cảm biến vân tay" + "Người dùng đã hủy thao tác dùng dấu vân tay." + "Bạn đã thử quá nhiều lần. Vui lòng thử lại sau." + "Lỗi không xác định" + diff --git a/android/src/main/res/values-zh-rCN/strings.xml b/android/src/main/res/values-zh-rCN/strings.xml new file mode 100644 index 00000000..5b493801 --- /dev/null +++ b/android/src/main/res/values-zh-rCN/strings.xml @@ -0,0 +1,29 @@ + + + + + "请轻触指纹传感器" + "无法识别" + "帮助消息区域" + "指纹硬件无法使用。" + "未注册任何指纹。" + "此设备没有指纹传感器" + "用户取消了指纹操作。" + "尝试次数过多,请稍后重试。" + "未知错误" + diff --git a/android/src/main/res/values-zh-rHK/strings.xml b/android/src/main/res/values-zh-rHK/strings.xml new file mode 100644 index 00000000..602cf505 --- /dev/null +++ b/android/src/main/res/values-zh-rHK/strings.xml @@ -0,0 +1,29 @@ + + + + + "請輕觸指紋感應器" + "未能識別" + "說明訊息區域" + "無法使用指紋硬件。" + "尚未註冊任何指紋。" + "此裝置沒有指紋感應器" + "使用者已取消指紋操作。" + "嘗試次數過多。請稍後再試。" + "不明錯誤" + diff --git a/android/src/main/res/values-zh-rTW/strings.xml b/android/src/main/res/values-zh-rTW/strings.xml new file mode 100644 index 00000000..43d6fdc2 --- /dev/null +++ b/android/src/main/res/values-zh-rTW/strings.xml @@ -0,0 +1,29 @@ + + + + + "請輕觸指紋感應器" + "無法辨識" + "說明訊息區域" + "指紋硬體無法使用。" + "未登錄任何指紋。" + "這個裝置沒有指紋感應器" + "使用者已取消指紋驗證作業。" + "嘗試次數過多,請稍後再試。" + "不明的錯誤" + diff --git a/android/src/main/res/values-zu/strings.xml b/android/src/main/res/values-zu/strings.xml new file mode 100644 index 00000000..ce71b984 --- /dev/null +++ b/android/src/main/res/values-zu/strings.xml @@ -0,0 +1,29 @@ + + + + + "Thinta inzwa yesigxivizo somunwe" + "Akwaziwa" + "Indawo yosizo lomlayezo" + "Izingxenyekazi zekhompuyutha zezingxivizo zeminwe azitholakali." + "Azikho izigxivizo zeminwe ezibhalisiwe." + "Le divayisi ayinayo inzwa yezigxivizo zeminwe" + "Umsebenzi wesigxivizo somunwe sikhanselwe umsebenzisi." + "Imizamo eminingi kakhulu. Sicela uzame futhi ngokuhamba kwesikhathi." + "Iphutha elingaziwe" + diff --git a/android/src/main/res/values/colors.xml b/android/src/main/res/values/colors.xml new file mode 100644 index 00000000..931d36b5 --- /dev/null +++ b/android/src/main/res/values/colors.xml @@ -0,0 +1,20 @@ + + + + + #ff5722 + diff --git a/android/src/main/res/values/dimens.xml b/android/src/main/res/values/dimens.xml new file mode 100644 index 00000000..17cee36d --- /dev/null +++ b/android/src/main/res/values/dimens.xml @@ -0,0 +1,21 @@ + + + + + + 64dp + diff --git a/android/src/main/res/values/strings.xml b/android/src/main/res/values/strings.xml new file mode 100644 index 00000000..1ee15347 --- /dev/null +++ b/android/src/main/res/values/strings.xml @@ -0,0 +1,39 @@ + + + + + + Touch the fingerprint sensor + + Not recognized + + Help message area + + Fingerprint hardware not available. + + No fingerprints enrolled. + + This device does not have a fingerprint sensor + + Fingerprint operation canceled by user. + + Too many attempts. Please try again later. + + Unknown error + diff --git a/index.js b/index.js index af6c61e3..d131a279 100644 --- a/index.js +++ b/index.js @@ -63,17 +63,17 @@ export type Options = { * on the current device. * @return {Promise} Resolves to `SECURITY_LEVEL` when supported, otherwise `null`. */ -export function getSecurityLevel(): Promise)> { +export function getSecurityLevel(options?: Options): Promise)> { if (!RNKeychainManager.getSecurityLevel){ return Promise.resolve(null); } - return RNKeychainManager.getSecurityLevel(); + return RNKeychainManager.getSecurityLevel(getAccessControl(options)); } /** * Inquire if the type of local authentication policy (LAPolicy) is supported * on this device with the device settings the user chose. - * @param {object} options LAPolicy option, iOS only + * @param {object} options LAPolicy option on iOS, authenticationType on Android * @return {Promise} Resolves to `true` when supported, otherwise `false` */ export function canImplyAuthentication(options?: Options): Promise { @@ -99,7 +99,6 @@ export function getSupportedBiometryType(): Promise { return RNKeychainManager.getGenericPasswordForOptions( - getOptionsArgument(serviceOrOptions) + getOptionsArgument(serviceOrOptions), ); } diff --git a/react-native-keychain.iml b/react-native-keychain.iml new file mode 100644 index 00000000..56db60ed --- /dev/null +++ b/react-native-keychain.iml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file