Compare commits

..

1 Commits

Author SHA1 Message Date
pera
8906b1fc1a
Create index.d.ts 2019-08-12 13:56:34 -03:00
18 changed files with 351 additions and 330 deletions

12
.github/FUNDING.yml vendored
View File

@ -1,12 +0,0 @@
# These are supported funding model platforms
github: [pradeep1991singh]
patreon: live2code
# open_collective: # Replace with a single Open Collective username
# ko_fi: # Replace with a single Ko-fi username
# tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
# community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
# liberapay: # Replace with a single Liberapay username
# issuehunt: # Replace with a single IssueHunt username
# otechie: # Replace with a single Otechie username
# custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

View File

@ -1,72 +0,0 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '40 9 * * 5'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'java', 'javascript', 'ruby' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
steps:
- name: Checkout repository
uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
# - run: |
# echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2

View File

@ -14,6 +14,12 @@ or
$ yarn add react-native-secure-key-store
```
### Mostly automatic installation
```sh
$ react-native link react-native-secure-key-store
```
### Manual installation
@ -112,6 +118,3 @@ Permission to use, copy, modify, and/or distribute this software for any purpose
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
[![Buy Me A Coffee](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/ps91)

View File

@ -1,21 +0,0 @@
# Security Policy
## Supported Versions
Use this section to tell people about which versions of your project are
currently being supported with security updates.
| Version | Supported |
| ------- | ------------------ |
| 5.1.x | :white_check_mark: |
| 5.0.x | :x: |
| 4.0.x | :white_check_mark: |
| < 4.0 | :x: |
## Reporting a Vulnerability
Use this section to tell people how to report a vulnerability.
Tell them where to go, how often they can expect to get an update on a
reported vulnerability, what to expect if the vulnerability is accepted or
declined, etc.

View File

@ -1,37 +1,24 @@
def safeExtGet(prop, fallback) {
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
}
buildscript {
// The Android Gradle plugin is only required when opening the android folder stand-alone.
// This avoids unnecessary downloads and potential conflicts when the library is included as a
// module dependency in an application project.
if (project == rootProject) {
repositories {
google()
mavenCentral()
// JCenter is going read-only repository indefinitely
// Gradle is discouraging jcenter to avoid to avoid build issues - pipeline
// ref: https://blog.gradle.org/jcenter-shutdown
jcenter()
}
repositories {
google()
jcenter()
}
dependencies {
classpath("com.android.tools.build:gradle:3.5.2")
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.4'
}
}
apply plugin: 'com.android.library'
android {
compileSdkVersion safeExtGet('compileSdkVersion', 28)
buildToolsVersion safeExtGet('buildToolsVersion', '28.0.3')
compileSdkVersion 28
buildToolsVersion "28.0.3"
defaultConfig {
minSdkVersion safeExtGet('minSdkVersion', 23)
targetSdkVersion safeExtGet('targetSdkVersion', 28)
minSdkVersion 16
targetSdkVersion 28
versionCode 1
versionName "1.0"
}
@ -42,11 +29,10 @@ android {
repositories {
google()
mavenCentral()
jcenter()
mavenCentral()
}
dependencies {
implementation 'com.facebook.react:react-native:+'
implementation "androidx.security:security-crypto:1.0.0-rc03"
compile 'com.facebook.react:react-native:+'
}

View File

@ -6,23 +6,37 @@
package com.reactlibrary.securekeystore;
import android.content.SharedPreferences;
import android.content.Context;
import android.os.Build;
import android.security.KeyPairGeneratorSpec;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.security.crypto.EncryptedSharedPreferences;
import androidx.security.crypto.MasterKeys;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Calendar;
import android.support.annotation.Nullable;
import com.facebook.react.bridge.ReadableMap;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.security.auth.x500.X500Principal;
public class RNSecureKeyStoreModule extends ReactContextBaseJavaModule {
@ -41,7 +55,7 @@ public class RNSecureKeyStoreModule extends ReactContextBaseJavaModule {
@ReactMethod
public void set(String alias, String input, @Nullable ReadableMap options, Promise promise) {
try {
getSecureSharedPreferences().edit().putString(alias, input).commit();
setCipherText(alias, input);
promise.resolve("stored ciphertext in app storage");
} catch (Exception e) {
e.printStackTrace();
@ -50,26 +64,86 @@ public class RNSecureKeyStoreModule extends ReactContextBaseJavaModule {
}
}
private SharedPreferences getSecureSharedPreferences() throws GeneralSecurityException, IOException {
return EncryptedSharedPreferences.create(
"secret_shared_prefs",
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
reactContext,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
);
private PublicKey getOrCreatePublicKey(String alias) throws GeneralSecurityException, IOException {
KeyStore keyStore = KeyStore.getInstance(getKeyStore());
keyStore.load(null);
if (!keyStore.containsAlias(alias) || keyStore.getCertificate(alias) == null) {
Log.i(Constants.TAG, "no existing asymmetric keys for alias");
Calendar start = Calendar.getInstance();
Calendar end = Calendar.getInstance();
end.add(Calendar.YEAR, 50);
KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(getContext())
.setAlias(alias)
.setSubject(new X500Principal("CN=" + alias))
.setSerialNumber(BigInteger.ONE)
.setStartDate(start.getTime())
.setEndDate(end.getTime())
.build();
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", getKeyStore());
generator.initialize(spec);
generator.generateKeyPair();
Log.i(Constants.TAG, "created new asymmetric keys for alias");
}
return keyStore.getCertificate(alias).getPublicKey();
}
private byte[] encryptRsaPlainText(PublicKey publicKey, byte[] plainTextBytes) throws GeneralSecurityException, IOException {
Cipher cipher = Cipher.getInstance(Constants.RSA_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return encryptCipherText(cipher, plainTextBytes);
}
private byte[] encryptAesPlainText(SecretKey secretKey, String plainText) throws GeneralSecurityException, IOException {
Cipher cipher = Cipher.getInstance(Constants.AES_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
return encryptCipherText(cipher, plainText);
}
private byte[] encryptCipherText(Cipher cipher, String plainText) throws GeneralSecurityException, IOException {
return encryptCipherText(cipher, plainText.getBytes("UTF-8"));
}
private byte[] encryptCipherText(Cipher cipher, byte[] plainTextBytes) throws GeneralSecurityException, IOException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
CipherOutputStream cipherOutputStream = new CipherOutputStream(outputStream, cipher);
cipherOutputStream.write(plainTextBytes);
cipherOutputStream.close();
return outputStream.toByteArray();
}
private SecretKey getOrCreateSecretKey(String alias) throws GeneralSecurityException, IOException {
try {
return getSymmetricKey(alias);
} catch (FileNotFoundException fnfe) {
Log.i(Constants.TAG, "no existing symmetric key for alias");
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
//32bytes / 256bits AES key
keyGenerator.init(256);
SecretKey secretKey = keyGenerator.generateKey();
PublicKey publicKey = getOrCreatePublicKey(alias);
Storage.writeValues(getContext(), Constants.SKS_KEY_FILENAME + alias,
encryptRsaPlainText(publicKey, secretKey.getEncoded()));
Log.i(Constants.TAG, "created new symmetric keys for alias");
return secretKey;
}
}
private void setCipherText(String alias, String input) throws GeneralSecurityException, IOException {
Storage.writeValues(getContext(), Constants.SKS_DATA_FILENAME + alias,
encryptAesPlainText(getOrCreateSecretKey(alias), input));
}
@ReactMethod
public void get(String alias, Promise promise) {
try {
String value = getSecureSharedPreferences().getString(alias, null);
if (value == null) {
//throw FileNotFoundException to keep match old behaviour when a value is missing
throw new FileNotFoundException(alias + " has not been set");
} else {
promise.resolve(value);
}
promise.resolve(getPlainText(alias));
} catch (FileNotFoundException fnfe) {
fnfe.printStackTrace();
promise.reject("404", "{\"code\":404,\"api-level\":" + Build.VERSION.SDK_INT + ",\"message\":" + fnfe.getMessage() + "}", fnfe);
@ -80,15 +154,73 @@ public class RNSecureKeyStoreModule extends ReactContextBaseJavaModule {
}
}
private PrivateKey getPrivateKey(String alias) throws GeneralSecurityException, IOException {
KeyStore keyStore = KeyStore.getInstance(getKeyStore());
keyStore.load(null);
return (PrivateKey) keyStore.getKey(alias, null);
}
private byte[] decryptRsaCipherText(PrivateKey privateKey, byte[] cipherTextBytes) throws GeneralSecurityException, IOException {
Cipher cipher = Cipher.getInstance(Constants.RSA_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return decryptCipherText(cipher, cipherTextBytes);
}
private byte[] decryptAesCipherText(SecretKey secretKey, byte[] cipherTextBytes) throws GeneralSecurityException, IOException {
Cipher cipher = Cipher.getInstance(Constants.AES_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, secretKey);
return decryptCipherText(cipher, cipherTextBytes);
}
private byte[] decryptCipherText(Cipher cipher, byte[] cipherTextBytes) throws IOException {
ByteArrayInputStream bais = new ByteArrayInputStream(cipherTextBytes);
CipherInputStream cipherInputStream = new CipherInputStream(bais, cipher);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[256];
int bytesRead = cipherInputStream.read(buffer);
while (bytesRead != -1) {
baos.write(buffer, 0, bytesRead);
bytesRead = cipherInputStream.read(buffer);
}
return baos.toByteArray();
}
private SecretKey getSymmetricKey(String alias) throws GeneralSecurityException, IOException {
byte[] cipherTextBytes = Storage.readValues(getContext(), Constants.SKS_KEY_FILENAME + alias);
return new SecretKeySpec(decryptRsaCipherText(getPrivateKey(alias), cipherTextBytes), Constants.AES_ALGORITHM);
}
private String getPlainText(String alias) throws GeneralSecurityException, IOException {
SecretKey secretKey = getSymmetricKey(alias);
byte[] cipherTextBytes = Storage.readValues(getContext(), Constants.SKS_DATA_FILENAME + alias);
return new String(decryptAesCipherText(secretKey, cipherTextBytes), "UTF-8");
}
@ReactMethod
public void remove(String alias, Promise promise) {
Storage.resetValues(getContext(), new String[] {
Constants.SKS_DATA_FILENAME + alias,
Constants.SKS_KEY_FILENAME + alias,
});
promise.resolve("cleared alias");
}
private Context getContext() {
return getReactApplicationContext();
}
private String getKeyStore() {
try {
getSecureSharedPreferences().edit().remove(alias).commit();
promise.resolve("cleared alias");
} catch (Exception e) {
e.printStackTrace();
Log.e(Constants.TAG, "Exception: " + e.getMessage());
promise.reject("{\"code\":6,\"api-level\":" + Build.VERSION.SDK_INT + ",\"message\":" + e.getMessage() + "}");
KeyStore.getInstance(Constants.KEYSTORE_PROVIDER_1);
return Constants.KEYSTORE_PROVIDER_1;
} catch (Exception err) {
try {
KeyStore.getInstance(Constants.KEYSTORE_PROVIDER_2);
return Constants.KEYSTORE_PROVIDER_2;
} catch (Exception e) {
return Constants.KEYSTORE_PROVIDER_3;
}
}
}
}

View File

@ -0,0 +1,38 @@
package com.reactlibrary.securekeystore;
// Helper function for storing keys to internal storage.
import android.content.Context;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public final class Storage {
public static void writeValues(Context context, String filename, byte[] bytes) throws IOException {
FileOutputStream fos = context.openFileOutput(filename, Context.MODE_PRIVATE);
fos.write(bytes);
fos.close();
}
public static byte[] readValues(Context context, String filename) throws IOException {
FileInputStream fis = context.openFileInput(filename);
ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
byte[] buffer = new byte[1024];
int bytesRead = fis.read(buffer);
while(bytesRead != -1) {
baos.write(buffer, 0, bytesRead);
bytesRead = fis.read(buffer);
}
return baos.toByteArray();
}
public static void resetValues(Context context, String[] filenames) {
for(String filename : filenames) {
context.deleteFile(filename);
}
}
}

View File

@ -8,81 +8,68 @@
*/
import React, {Component} from 'react';
import {Platform, StyleSheet, Button, Text, TextInput, View} from 'react-native';
import {Platform, StyleSheet, Text, View} from 'react-native';
import RNSecureKeyStore, {ACCESSIBLE} from "react-native-secure-key-store";
const instructions = Platform.select({
ios: 'Press Cmd+R to reload,\n' + 'Cmd+D or shake for dev menu',
android:
'Double tap R on your keyboard to reload,\n' +
'Shake or press menu button for dev menu',
});
type Props = {};
export default class App extends Component<Props> {
state = {
alias: 'hello',
value: 'world'
};
getValue() {
RNSecureKeyStore.get(this.state.alias)
.then((value) => {
this.setState({
value,
});
})
.catch(console.error);
}
setValue() {
RNSecureKeyStore.set(this.state.alias, this.state.value, {})
.then(() => this.getValue())
.catch(console.error);
}
removeValue() {
RNSecureKeyStore.remove(this.state.alias)
.then(() => this.getValue())
.catch(console.error);
}
render() {
RNSecureKeyStore.set("key1", "value1", {accessible: ACCESSIBLE.ALWAYS_THIS_DEVICE_ONLY})
.then((res) => {
console.log(res);
}, (err) => {
console.log(err);
});
RNSecureKeyStore.set("key2", "value2", {accessible: ACCESSIBLE.ALWAYS_THIS_DEVICE_ONLY})
.then((res) => {
console.log(res);
}, (err) => {
console.log(err);
});
RNSecureKeyStore.get("key1")
.then((res) => {
console.log(res);
}, (err) => {
console.log(err);
});
RNSecureKeyStore.get("key2")
.then((res) => {
console.log(res);
}, (err) => {
console.log(err);
});
RNSecureKeyStore.remove("key1")
.then((res) => {
console.log(res);
}, (err) => {
console.log(err);
});
RNSecureKeyStore.remove("key2")
.then((res) => {
console.log(res);
}, (err) => {
console.log(err);
});
return (
<View style={styles.container}>
<View style={styles.row}>
<Text>Alias:</Text>
<TextInput
style={styles.textInput}
onChangeText={alias => this.setState({alias})}
value={this.state.alias}
/>
</View>
<View style={styles.row}>
<Text>Value:</Text>
<TextInput
style={styles.textInput}
onChangeText={value => this.setState({value})}
value={this.state.value}
/>
</View>
<View style={styles.row}>
<View style={styles.button}>
<Button
onPress={() => this.getValue()}
title='Get'
/>
</View>
<View style={styles.button}>
<Button
onPress={() => this.setValue()}
title='Set'
/>
</View>
<View style={styles.button}>
<Button
onPress={() => this.removeValue()}
title='Remove'
/>
</View>
</View>
<Text style={styles.welcome}>Welcome to React Native!</Text>
<Text style={styles.instructions}>To get started, edit App.js</Text>
<Text style={styles.instructions}>{instructions}</Text>
</View>
);
}
@ -95,20 +82,14 @@ const styles = StyleSheet.create({
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
row: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginTop: 20
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
textInput: {
height: 40,
width: 200,
borderColor: 'gray',
borderWidth: 1,
marginLeft: 10
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
button: {
marginLeft: 10
}
});

View File

@ -3,7 +3,7 @@
buildscript {
ext {
buildToolsVersion = "28.0.2"
minSdkVersion = 23
minSdkVersion = 16
compileSdkVersion = 28
targetSdkVersion = 27
supportLibVersion = "28.0.0"

View File

@ -16,5 +16,3 @@
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
android.useAndroidX=true
android.enableJetifier=true

View File

@ -1,12 +0,0 @@
const path = require("path");
const packagePath = path.resolve(__dirname, "../");
module.exports = {
resolver: {
extraNodeModules: {
"react-native-secure-key-store": packagePath,
},
},
watchFolders: [packagePath],
};

View File

@ -9,7 +9,7 @@
"dependencies": {
"react": "16.6.3",
"react-native": "0.58.1",
"react-native-secure-key-store": "file:.."
"react-native-secure-key-store": "../../react-native-secure-key-store"
},
"devDependencies": {
"babel-core": "^7.0.0-bridge.0",

View File

@ -684,9 +684,9 @@ acorn-walk@^6.0.1:
integrity sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw==
acorn@^5.5.3:
version "5.7.4"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.4.tgz#3e8d8a9947d0599a1796d10225d7432f4a4acf5e"
integrity sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==
version "5.7.3"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279"
integrity sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==
acorn@^6.0.1:
version "6.0.5"
@ -920,7 +920,7 @@ async-limiter@~1.0.0:
resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8"
integrity sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==
async@^2.4.0, async@^2.6.1:
async@^2.4.0, async@^2.5.0, async@^2.6.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610"
integrity sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==
@ -1341,10 +1341,10 @@ commander@~2.13.0:
resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c"
integrity sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==
commander@~2.20.3:
version "2.20.3"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
commander@~2.17.1:
version "2.17.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==
commondir@^1.0.1:
version "1.0.1"
@ -2234,11 +2234,11 @@ growly@^1.3.0:
integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=
handlebars@^4.0.11:
version "4.5.3"
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.5.3.tgz#5cf75bd8714f7605713511a56be7c349becb0482"
integrity sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA==
version "4.0.12"
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.12.tgz#2c15c8a96d46da5e266700518ba8cb8d919d5bc5"
integrity sha512-RhmTekP+FZL+XNhwS1Wf+bTTZpdLougwt5pcgA1tuz6Jcx0fpH/7z0qd71RKnZHBCxIRBHfBOnio4gViPemNzA==
dependencies:
neo-async "^2.6.0"
async "^2.5.0"
optimist "^0.6.1"
source-map "^0.6.1"
optionalDependencies:
@ -3144,9 +3144,9 @@ jest@24.0.0:
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
js-yaml@^3.12.0, js-yaml@^3.9.0:
version "3.13.1"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847"
integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==
version "3.12.1"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.1.tgz#295c8632a18a23e054cf5c9d3cecafe678167600"
integrity sha512-um46hB9wNOKlwkHgiuyEVAybXBjwFUV0Z/RaHJblRd9DXltue9FTYvzCr9ErQrK9Adz5MU4gHWVaNUfdmrC8qA==
dependencies:
argparse "^1.0.7"
esprima "^4.0.0"
@ -3389,9 +3389,9 @@ lodash.throttle@^4.1.1:
integrity sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=
lodash@^4.13.1, lodash@^4.17.10, lodash@^4.17.5, lodash@^4.3.0, lodash@^4.6.1:
version "4.17.19"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b"
integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==
version "4.17.11"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1:
version "1.4.0"
@ -3822,9 +3822,9 @@ minizlib@^1.1.1:
minipass "^2.2.1"
mixin-deep@^1.2.0:
version "1.3.2"
resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566"
integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==
version "1.3.1"
resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe"
integrity sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==
dependencies:
for-in "^1.0.2"
is-extendable "^1.0.1"
@ -3903,11 +3903,6 @@ negotiator@0.6.1:
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9"
integrity sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=
neo-async@^2.6.0:
version "2.6.1"
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c"
integrity sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==
nice-try@^1.0.4:
version "1.0.5"
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
@ -5530,11 +5525,11 @@ uglify-es@^3.1.9:
source-map "~0.6.1"
uglify-js@^3.1.4:
version "3.7.3"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.7.3.tgz#f918fce9182f466d5140f24bb0ff35c2d32dcc6a"
integrity sha512-7tINm46/3puUA4hCkKYo4Xdts+JDaVC9ZPRcG8Xw9R4nhO/gZgUM3TENq8IF4Vatk8qCig4MzP/c8G4u2BkVQg==
version "3.4.9"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.9.tgz#af02f180c1207d76432e473ed24a28f4a782bae3"
integrity sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q==
dependencies:
commander "~2.20.3"
commander "~2.17.1"
source-map "~0.6.1"
ultron@1.0.x:

18
index.d.ts vendored
View File

@ -1,19 +1,19 @@
declare module 'react-native-secure-key-store' {
export enum ACCESSIBLE {
AFTER_FIRST_UNLOCK = 'AccessibleAfterFirstUnlock',
AFTER_FIRST_UNLOCK_THIS_DEVICE_ONLY =
export const ACCESSIBLE = {
AFTER_FIRST_UNLOCK: 'AccessibleAfterFirstUnlock',
AFTER_FIRST_UNLOCK_THIS_DEVICE_ONLY:
'AccessibleAfterFirstUnlockThisDeviceOnly',
ALWAYS = 'AccessibleAlways',
ALWAYS_THIS_DEVICE_ONLY = 'AccessibleAlwaysThisDeviceOnly',
WHEN_PASSCODE_SET_THIS_DEVICE_ONLY =
ALWAYS: 'AccessibleAlways',
ALWAYS_THIS_DEVICE_ONLY: 'AccessibleAlwaysThisDeviceOnly',
WHEN_PASSCODE_SET_THIS_DEVICE_ONLY:
'AccessibleWhenPasscodeSetThisDeviceOnly',
WHEN_UNLOCKED = 'AccessibleWhenUnlocked',
WHEN_UNLOCKED_THIS_DEVICE_ONLY = 'AccessibleWhenUnlockedThisDeviceOnly',
WHEN_UNLOCKED: 'AccessibleWhenUnlocked',
WHEN_UNLOCKED_THIS_DEVICE_ONLY: 'AccessibleWhenUnlockedThisDeviceOnly',
}
interface RNSecureKeyStore {
get: (key: string) => Promise<any>
set: (key: string, value: string, options?: { accessible?: ACCESSIBLE }) => Promise<any>
set: (key: string, value: string, accessible?: ACCESSIBLE) => Promise<any>
remove: (key: string) => Promise<any>
setResetOnAppUninstallTo: (enabled: boolean) => boolean
}

View File

@ -74,7 +74,7 @@ static NSString *serviceName = @"RNSecureKeyStoreKeyChain";
}
- (BOOL)createKeychainValue:(NSString *)value forIdentifier:(NSString *)identifier options: (NSDictionary * __nullable)options {
CFStringRef accessible = accessibleVal(options);
CFStringRef accessible = accessibleValue(options);
NSMutableDictionary *dictionary = [self newSearchDictionary:identifier];
NSData *valueData = [value dataUsingEncoding:NSUTF8StringEncoding];
@ -91,7 +91,7 @@ static NSString *serviceName = @"RNSecureKeyStoreKeyChain";
- (BOOL)updateKeychainValue:(NSString *)password forIdentifier:(NSString *)identifier options:(NSDictionary * __nullable)options {
CFStringRef accessible = accessibleVal(options);
CFStringRef accessible = accessibleValue(options);
NSMutableDictionary *searchDictionary = [self newSearchDictionary:identifier];
NSMutableDictionary *updateDictionary = [[NSMutableDictionary alloc] init];
NSData *passwordData = [password dataUsingEncoding:NSUTF8StringEncoding];
@ -128,6 +128,15 @@ static NSString *serviceName = @"RNSecureKeyStoreKeyChain";
}
}
- (void)handleAppUninstallation
{
if ([self resetOnAppUninstall] && ![[NSUserDefaults standardUserDefaults] boolForKey:@"RnSksIsAppInstalled"]) {
[self clearSecureKeyStore];
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"RnSksIsAppInstalled"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
}
NSError * secureKeyStoreError(NSString *errMsg)
{
NSError *error = [NSError errorWithDomain:serviceName code:200 userInfo:@{@"reason": errMsg}];
@ -146,6 +155,7 @@ RCT_EXPORT_METHOD(set: (NSString *)key value:(NSString *)value
rejecter:(RCTPromiseRejectBlock)reject)
{
@try {
[self handleAppUninstallation];
BOOL status = [self createKeychainValue: value forIdentifier: key options: options];
if (status) {
resolve(@"key stored successfully");
@ -170,6 +180,7 @@ RCT_EXPORT_METHOD(get:(NSString *)key
rejecter:(RCTPromiseRejectBlock)reject)
{
@try {
[self handleAppUninstallation];
NSString *value = [self searchKeychainCopyMatching:key];
if (value == nil) {
NSString* errorMessage = @"{\"message\":\"key does not present\"}";
@ -203,7 +214,7 @@ RCT_EXPORT_METHOD(remove:(NSString *)key
}
}
CFStringRef accessibleVal(NSDictionary *options)
CFStringRef accessibleValue(NSDictionary *options)
{
if (options && options[@"accessible"] != nil) {
NSDictionary *keyMap = @{

View File

@ -0,0 +1,22 @@
Pod::Spec.new do |s|
s.name = "RNSecureKeyStore"
s.version = "1.0.0"
s.summary = "A package for secure storage on android and ios"
s.description = "A package for secure storage on android and ios. Stores using the keystore on Android devices, and the keychain on iOS devices."
s.homepage = "https://github.com/pradeep1991singh/react-native-secure-key-store#readme"
s.license = "MIT"
# s.license = { :type => "MIT", :file => "FILE_LICENSE" }
s.author = { "author" => "pradeep1991singh" }
s.platform = :ios, "7.0"
s.source = { :git => "https://github.com/pradeep1991singh/react-native-secure-key-store", :tag => "master" }
s.source_files = "**/*.{h,m}"
s.requires_arc = true
s.dependency "React"
#s.dependency "others"
end

View File

@ -1,15 +1,11 @@
{
"name": "react-native-secure-key-store",
"version": "2.0.10",
"version": "2.0.4",
"description": "React Native Library for securely storing keys to iOS and Android devices in KeyChain and KeyStore respectively.",
"main": "index.js",
"publishConfig": {
"registry": "https://registry.npmjs.org"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"types": "index.d.ts",
"keywords": [
"react-native",
"ios",

View File

@ -1,24 +0,0 @@
require "json"
package = JSON.parse(File.read(File.join(__dir__, "package.json")))
Pod::Spec.new do |s|
s.name = package['name']
s.version = package['version']
s.summary = package['description']
s.description = package['description']
s.homepage = package['homepage']
s.license = package['license']
s.authors = package['author']
s.platform = :ios, "7.0"
s.source = { :git => "https://github.com/pradeep1991singh/react-native-secure-key-store", :tag => "master" }
s.source_files = "ios/**/*.{h,m}"
s.requires_arc = true
s.dependency "React-Core"
#s.dependency "others"
end