Skip to content

Commit

Permalink
Add possibility to set accessible attribute for iOS
Browse files Browse the repository at this point in the history
  • Loading branch information
Roman Barzyczak committed Aug 27, 2018
1 parent eac08e5 commit c6cbbbf
Show file tree
Hide file tree
Showing 8 changed files with 2,465 additions and 881 deletions.
Expand Up @@ -27,6 +27,8 @@
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;
Expand All @@ -51,7 +53,7 @@ public String getName() {
}

@ReactMethod
public void set(String alias, String input, Promise promise) {
public void set(String alias, String input, @Nullable ReadableMap options, Promise promise) {
try {
setCipherText(alias, input);
promise.resolve("stored ciphertext in app storage");
Expand Down
6 changes: 3 additions & 3 deletions example/app/app.js
Expand Up @@ -12,19 +12,19 @@ import {
View
} from 'react-native';

import RNSecureKeyStore from "react-native-secure-key-store";
import RNSecureKeyStore, {ACCESSIBLE} from "react-native-secure-key-store";

export default class example extends Component {
render() {

RNSecureKeyStore.set("key1", "value1")
RNSecureKeyStore.set("key1", "value1", {accessible: ACCESSIBLE.ALWAYS_THIS_DEVICE_ONLY})
.then((res) => {
console.log(res);
}, (err) => {
console.log(err);
});

RNSecureKeyStore.set("key2", "value2")
RNSecureKeyStore.set("key2", "value2", {accessible: ACCESSIBLE.ALWAYS_THIS_DEVICE_ONLY})
.then((res) => {
console.log(res);
}, (err) => {
Expand Down
148 changes: 92 additions & 56 deletions example/ios/example.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions example/package.json
Expand Up @@ -7,8 +7,9 @@
"test": "jest"
},
"dependencies": {
"react": "16.0.0-alpha.6",
"react-native": "0.44.0",
"react": "16.3.0-alpha.1",
"react-intl": "^2.4.0",
"react-native": "^0.55.4",
"react-native-secure-key-store": "../../react-native-secure-key-store"
},
"devDependencies": {
Expand Down
3,123 changes: 2,318 additions & 805 deletions example/yarn.lock

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions index.js
@@ -1,6 +1,17 @@

import { NativeModules } from 'react-native';

export const ACCESSIBLE = {
WHEN_UNLOCKED: 'AccessibleWhenUnlocked',
AFTER_FIRST_UNLOCK: 'AccessibleAfterFirstUnlock',
ALWAYS: 'AccessibleAlways',
WHEN_PASSCODE_SET_THIS_DEVICE_ONLY: 'AccessibleWhenPasscodeSetThisDeviceOnly',
WHEN_UNLOCKED_THIS_DEVICE_ONLY: 'AccessibleWhenUnlockedThisDeviceOnly',
AFTER_FIRST_UNLOCK_THIS_DEVICE_ONLY:
'AccessibleAfterFirstUnlockThisDeviceOnly',
ALWAYS_THIS_DEVICE_ONLY: 'AccessibleAlwaysThisDeviceOnly',
};

const { RNSecureKeyStore } = NativeModules;

export default RNSecureKeyStore;
38 changes: 31 additions & 7 deletions ios/RNSecureKeyStore.m
Expand Up @@ -52,12 +52,14 @@ - (NSString *)searchKeychainCopyMatching:(NSString *)identifier {
return value;
}

- (BOOL)createKeychainValue:(NSString *)value forIdentifier:(NSString *)identifier {
- (BOOL)createKeychainValue:(NSString *)value forIdentifier:(NSString *)identifier options: (NSDictionary * __nullable)options {
CFStringRef accessible = accessibleValue(options);
NSMutableDictionary *dictionary = [self newSearchDictionary:identifier];

NSData *valueData = [value dataUsingEncoding:NSUTF8StringEncoding];
[dictionary setObject:valueData forKey:(id)kSecValueData];

dictionary[(__bridge NSString *)kSecAttrAccessible] = (__bridge id)accessible;

OSStatus status = SecItemAdd((CFDictionaryRef)dictionary, NULL);

if (status == errSecSuccess) {
Expand All @@ -66,13 +68,14 @@ - (BOOL)createKeychainValue:(NSString *)value forIdentifier:(NSString *)identifi
return NO;
}

- (BOOL)updateKeychainValue:(NSString *)password forIdentifier:(NSString *)identifier {
- (BOOL)updateKeychainValue:(NSString *)password forIdentifier:(NSString *)identifier options:(NSDictionary * __nullable)options {

CFStringRef accessible = accessibleValue(options);
NSMutableDictionary *searchDictionary = [self newSearchDictionary:identifier];
NSMutableDictionary *updateDictionary = [[NSMutableDictionary alloc] init];
NSData *passwordData = [password dataUsingEncoding:NSUTF8StringEncoding];
[updateDictionary setObject:passwordData forKey:(id)kSecValueData];

updateDictionary[(__bridge NSString *)kSecAttrAccessible] = (__bridge id)accessible;
OSStatus status = SecItemUpdate((CFDictionaryRef)searchDictionary,
(CFDictionaryRef)updateDictionary);

Expand Down Expand Up @@ -119,17 +122,18 @@ - (void)handleAppUninstallation
return error;
}

RCT_EXPORT_METHOD(set:(NSString *)key value:(NSString *)value
RCT_EXPORT_METHOD(set: (NSString *)key value:(NSString *)value
options: (NSDictionary *)options
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
@try {
[self handleAppUninstallation];
BOOL status = [self createKeychainValue: value forIdentifier: key];
BOOL status = [self createKeychainValue: value forIdentifier: key options: options];
if (status) {
resolve(@"key stored successfully");
} else {
BOOL status = [self updateKeychainValue: value forIdentifier: key];
BOOL status = [self updateKeychainValue: value forIdentifier: key options: options];
if (status) {
resolve(@"key updated successfully");
} else {
Expand Down Expand Up @@ -183,4 +187,24 @@ - (void)handleAppUninstallation
}
}

CFStringRef accessibleValue(NSDictionary *options)
{
if (options && options[@"accessible"] != nil) {
NSDictionary *keyMap = @{
@"AccessibleWhenUnlocked": (__bridge NSString *)kSecAttrAccessibleWhenUnlocked,
@"AccessibleAfterFirstUnlock": (__bridge NSString *)kSecAttrAccessibleAfterFirstUnlock,
@"AccessibleAlways": (__bridge NSString *)kSecAttrAccessibleAlways,
@"AccessibleWhenPasscodeSetThisDeviceOnly": (__bridge NSString *)kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
@"AccessibleWhenUnlockedThisDeviceOnly": (__bridge NSString *)kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
@"AccessibleAfterFirstUnlockThisDeviceOnly": (__bridge NSString *)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly,
@"AccessibleAlwaysThisDeviceOnly": (__bridge NSString *)kSecAttrAccessibleAlwaysThisDeviceOnly
};
NSString *result = keyMap[options[@"accessible"]];
if (result) {
return (__bridge CFStringRef)result;
}
}
return kSecAttrAccessibleAfterFirstUnlock;
}

@end
11 changes: 4 additions & 7 deletions package.json
@@ -1,6 +1,6 @@
{
"name": "react-native-secure-key-store",
"version": "1.0.9",
"version": "2.0.0",
"description": "React Native Library for securely storing keys to iOS and Android devices in KeyChain and KeyStore respectively.",
"main": "index.js",
"scripts": {
Expand All @@ -21,13 +21,10 @@
},
"repository": {
"type": "git",
"url":
"git+https://github.com/pradeep1991singh/react-native-secure-key-store.git"
"url": "git+https://github.com/pradeep1991singh/react-native-secure-key-store.git"
},
"bugs": {
"url":
"https://github.com/pradeep1991singh/react-native-secure-key-store/issues"
"url": "https://github.com/pradeep1991singh/react-native-secure-key-store/issues"
},
"homepage":
"https://github.com/pradeep1991singh/react-native-secure-key-store#readme"
"homepage": "https://github.com/pradeep1991singh/react-native-secure-key-store#readme"
}

0 comments on commit c6cbbbf

Please sign in to comment.