Compare commits

...
Author SHA1 Message Date
Roman Volosovskyi f3bbccdaee _ 2020-04-21 18:11:03 +03:00
Roman Volosovskyi 455886d770 _ 2020-04-21 17:38:47 +03:00
Roman Volosovskyi ae19379479 init 2020-04-21 15:41:58 +03:00
Bitgamma 900de62a4c Merge pull request #23 from bitgamma/master
avoid NVM writes (to improve login speed with Keycard)
2020-04-16 11:20:47 +03:00
Michele Balistreri 267bf1184f restore compatibility with current status-react 2020-04-16 10:12:34 +03:00
Roman Volosovskyi 38ca8597e2 Add nfc state events 2020-03-05 14:54:40 +02:00
Michele Balistreri cf7e3138c9 remove redundant derivation 2020-01-09 15:48:52 +03:00
Vitaliy Vlasov 8fb12850d7 Update status-keycard-java to 3.0.1 2019-12-27 18:31:15 +02:00
Michele Balistreri 905bcb6f0d avoid NVM writes 2019-12-23 17:03:11 +01:00
10 changed files with 399 additions and 31 deletions
+6 -1
View File
@@ -23,6 +23,11 @@ android {
versionCode 1
versionName "1.0"
}
lintOptions {
abortOnError false
}
}
repositories {
@@ -41,5 +46,5 @@ dependencies {
implementation 'com.facebook.react:react-native:+'
implementation 'org.bouncycastle:bcprov-jdk15on:1.60'
implementation 'org.apache.commons:commons-lang3:3.9'
implementation 'com.github.status-im.status-keycard-java:android:2.2.1'
implementation 'com.github.status-im.status-keycard-java:android:3.0.1'
}
@@ -247,6 +247,20 @@ public class RNStatusKeycardModule extends ReactContextBaseJavaModule implements
}).start();
}
@ReactMethod
public void signWithPath(final String pairing, final String pin, final String path, final String hash, final Promise promise) {
new Thread(new Runnable() {
public void run() {
try {
promise.resolve(smartCard.signWithPath(pairing, pin, path, hash));
} catch (IOException | APDUException e) {
Log.d(TAG, e.getMessage());
promise.reject(e);
}
}
}).start();
}
@ReactMethod
public void signPinless(final String hash, final Promise promise) {
new Thread(new Runnable() {
@@ -110,9 +110,13 @@ public class SmartCard extends BroadcastReceiver implements CardListener {
boolean on = false;
switch (state) {
case NfcAdapter.STATE_ON:
eventEmitter.emit("keyCardOnNFCEnabled", null);
log("NFC ON");
break;
case NfcAdapter.STATE_OFF:
eventEmitter.emit("keyCardOnNFCDisabled", null);
log("NFC OFF");
break;
default:
log("other");
}
@@ -277,8 +281,10 @@ public class SmartCard extends BroadcastReceiver implements CardListener {
KeyPath currentPath = new KeyPath(cmdSet.getStatus(KeycardCommandSet.GET_STATUS_P1_KEY_PATH).checkOK().getData());
Log.i(TAG, "Current key path: " + currentPath);
cmdSet.deriveKey(path).checkOK();
Log.i(TAG, "Derived " + path);
if (!currentPath.toString().equals(path)) {
cmdSet.deriveKey(path).checkOK();
Log.i(TAG, "Derived " + path);
}
}
public String exportKey(final String pairingBase64, final String pin) throws IOException, APDUException {
@@ -330,20 +336,20 @@ public class SmartCard extends BroadcastReceiver implements CardListener {
cmdSet.verifyPIN(pin).checkOK();
Log.i(TAG, "pin verified");
byte[] tlvMaster = cmdSet.exportKey(MASTER_PATH, true, true).checkOK().getData();
byte[] tlvEncryption = cmdSet.exportKey(ENCRYPTION_PATH, false, false).checkOK().getData();
BIP32KeyPair encryptionKeyPair = BIP32KeyPair.fromTLV(tlvEncryption);
byte[] tlvMaster = cmdSet.exportKey(MASTER_PATH, false, true).checkOK().getData();
BIP32KeyPair masterPair = BIP32KeyPair.fromTLV(tlvMaster);
byte[] tlvRoot = cmdSet.exportKey(ROOT_PATH, true, true).checkOK().getData();
byte[] tlvRoot = cmdSet.exportKey(ROOT_PATH, false, true).checkOK().getData();
BIP32KeyPair keyPair = BIP32KeyPair.fromTLV(tlvRoot);
byte[] tlv = cmdSet.exportKey(WALLET_PATH, true, true).checkOK().getData();
BIP32KeyPair walletKeyPair = BIP32KeyPair.fromTLV(tlv);
byte[] tlvWhisper = cmdSet.exportKey(WHISPER_PATH, false, false).checkOK().getData();
BIP32KeyPair whisperKeyPair = BIP32KeyPair.fromTLV(tlvWhisper);
byte[] tlv2 = cmdSet.exportKey(WHISPER_PATH, false, false).checkOK().getData();
BIP32KeyPair whisperKeyPair = BIP32KeyPair.fromTLV(tlv2);
byte[] tlv3 = cmdSet.exportKey(ENCRYPTION_PATH, false, false).checkOK().getData();
BIP32KeyPair encryptionKeyPair = BIP32KeyPair.fromTLV(tlv3);
byte[] tlvWallet = cmdSet.exportKey(WALLET_PATH, true, true).checkOK().getData();
BIP32KeyPair walletKeyPair = BIP32KeyPair.fromTLV(tlvWallet);
ApplicationInfo info = new ApplicationInfo(cmdSet.select().checkOK().getData());
@@ -380,32 +386,24 @@ public class SmartCard extends BroadcastReceiver implements CardListener {
byte[] seed = Mnemonic.toBinarySeed(mnemonic, "");
BIP32KeyPair keyPair = BIP32KeyPair.fromBinarySeed(seed);
cmdSet.loadKey(keyPair);
cmdSet.loadKey(keyPair).checkOK();
log("keypair loaded to card");
cmdSet.deriveKey(ROOT_PATH).checkOK();
byte[] tlvRoot = cmdSet.exportKey(ROOT_PATH, false, true).checkOK().getData();
Log.i(TAG, "Derived " + ROOT_PATH);
byte[] tlvRoot = cmdSet.exportCurrentKey(true).checkOK().getData();
BIP32KeyPair rootKeyPair = BIP32KeyPair.fromTLV(tlvRoot);
cmdSet.deriveKey(WALLET_PATH).checkOK();
Log.i(TAG, "Derived " + WALLET_PATH);
byte[] tlv = cmdSet.exportCurrentKey(true).checkOK().getData();
BIP32KeyPair walletKeyPair = BIP32KeyPair.fromTLV(tlv);
cmdSet.deriveKey(WHISPER_PATH).checkOK();
byte[] tlvWhisper = cmdSet.exportKey(WHISPER_PATH, false, false).checkOK().getData();
Log.i(TAG, "Derived " + WHISPER_PATH);
BIP32KeyPair whisperKeyPair = BIP32KeyPair.fromTLV(tlvWhisper);
byte[] tlv2 = cmdSet.exportCurrentKey(false).checkOK().getData();
BIP32KeyPair whisperKeyPair = BIP32KeyPair.fromTLV(tlv2);
cmdSet.deriveKey(ENCRYPTION_PATH).checkOK();
byte[] tlvEncryption = cmdSet.exportKey(ENCRYPTION_PATH, false, false).checkOK().getData();
Log.i(TAG, "Derived " + ENCRYPTION_PATH);
BIP32KeyPair encryptionKeyPair = BIP32KeyPair.fromTLV(tlvEncryption);
byte[] tlv3 = cmdSet.exportCurrentKey(false).checkOK().getData();
BIP32KeyPair encryptionKeyPair = BIP32KeyPair.fromTLV(tlv3);
byte[] tlvWallet = cmdSet.exportKey(WALLET_PATH, true, true).checkOK().getData();
Log.i(TAG, "Derived " + WALLET_PATH);
BIP32KeyPair walletKeyPair = BIP32KeyPair.fromTLV(tlvWallet);
ApplicationInfo info = new ApplicationInfo(cmdSet.select().checkOK().getData());
@@ -575,6 +573,39 @@ public class SmartCard extends BroadcastReceiver implements CardListener {
return sig;
}
public String signWithPath(final String pairingBase64, final String pin, final String path, final String message) throws IOException, APDUException {
KeycardCommandSet cmdSet = new KeycardCommandSet(this.cardChannel);
cmdSet.select().checkOK();
Pairing pairing = new Pairing(pairingBase64);
cmdSet.setPairing(pairing);
cmdSet.autoOpenSecureChannel();
Log.i(TAG, "secure channel opened");
cmdSet.verifyPIN(pin).checkOK();
Log.i(TAG, "pin verified");
byte[] hash = Hex.decode(message);
RecoverableSignature signature = new RecoverableSignature(hash, cmdSet.signWithPath(hash, path, false).checkOK().getData());
Log.i(TAG, "Signed hash: " + Hex.toHexString(hash));
Log.i(TAG, "Recovery ID: " + signature.getRecId());
Log.i(TAG, "R: " + Hex.toHexString(signature.getR()));
Log.i(TAG, "S: " + Hex.toHexString(signature.getS()));
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(signature.getR());
out.write(signature.getS());
out.write(signature.getRecId());
String sig = Hex.toHexString(out.toByteArray());
Log.i(TAG, "Signature: " + sig);
return sig;
}
public String signPinless(final String message) throws IOException, APDUException {
CashCommandSet cmdSet = new CashCommandSet(this.cardChannel);
cmdSet.select().checkOK();
+3 -1
View File
@@ -11,10 +11,12 @@ import Keycard from "react-native-status-keycard";
```javascript
import { DeviceEventEmitter } from 'react-native';
// Listen to connect/disconnect events
// Listen to connect/disconnect and nfc events
componentDidMount () {
DeviceEventEmitter.addListener("keyCardOnConnected", () => console.log("keycard connected"));
DeviceEventEmitter.addListener("keyCardOnDisconnected", () => console.log("keycard disconnected"));
DeviceEventEmitter.addListener("keyCardOnNFCEnabled", () => console.log("nfc enabled"));
DeviceEventEmitter.addListener("keyCardOnNFCDisabled", () => console.log("nfc disabled"));
}
```
+5
View File
@@ -0,0 +1,5 @@
#import <React/RCTBridgeModule.h>
@interface StatusKeycard : NSObject <RCTBridgeModule>
@end
+14
View File
@@ -0,0 +1,14 @@
#import "StatusKeycard.h"
@implementation StatusKeycard
RCT_EXPORT_MODULE()
RCT_EXPORT_METHOD(sampleMethod:(NSString *)stringArgument numberParameter:(nonnull NSNumber *)numberArgument callback:(RCTResponseSenderBlock)callback)
{
// TODO: Implement some actually useful functionality
callback(@[[NSString stringWithFormat: @"numberArgument: %@ stringArgument: %@", numberArgument, stringArgument]]);
}
@end
+271
View File
@@ -0,0 +1,271 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
B3E7B58A1CC2AC0600A0062D /* StatusKeycard.m in Sources */ = {isa = PBXBuildFile; fileRef = B3E7B5891CC2AC0600A0062D /* StatusKeycard.m */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
58B511D91A9E6C8500147676 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "include/$(PRODUCT_NAME)";
dstSubfolderSpec = 16;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
134814201AA4EA6300B7C361 /* libStatusKeycard.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libStatusKeycard.a; sourceTree = BUILT_PRODUCTS_DIR; };
B3E7B5881CC2AC0600A0062D /* StatusKeycard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StatusKeycard.h; sourceTree = "<group>"; };
B3E7B5891CC2AC0600A0062D /* StatusKeycard.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StatusKeycard.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
58B511D81A9E6C8500147676 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
134814211AA4EA7D00B7C361 /* Products */ = {
isa = PBXGroup;
children = (
134814201AA4EA6300B7C361 /* libStatusKeycard.a */,
);
name = Products;
sourceTree = "<group>";
};
58B511D21A9E6C8500147676 = {
isa = PBXGroup;
children = (
B3E7B5881CC2AC0600A0062D /* StatusKeycard.h */,
B3E7B5891CC2AC0600A0062D /* StatusKeycard.m */,
134814211AA4EA7D00B7C361 /* Products */,
);
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
58B511DA1A9E6C8500147676 /* StatusKeycard */ = {
isa = PBXNativeTarget;
buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "StatusKeycard" */;
buildPhases = (
58B511D71A9E6C8500147676 /* Sources */,
58B511D81A9E6C8500147676 /* Frameworks */,
58B511D91A9E6C8500147676 /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = StatusKeycard;
productName = RCTDataManager;
productReference = 134814201AA4EA6300B7C361 /* libStatusKeycard.a */;
productType = "com.apple.product-type.library.static";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
58B511D31A9E6C8500147676 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0920;
ORGANIZATIONNAME = Facebook;
TargetAttributes = {
58B511DA1A9E6C8500147676 = {
CreatedOnToolsVersion = 6.1.1;
};
};
};
buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "StatusKeycard" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = 58B511D21A9E6C8500147676;
productRefGroup = 58B511D21A9E6C8500147676;
projectDirPath = "";
projectRoot = "";
targets = (
58B511DA1A9E6C8500147676 /* StatusKeycard */,
);
};
/* End PBXProject section */
/* Begin PBXSourcesBuildPhase section */
58B511D71A9E6C8500147676 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
B3E7B58A1CC2AC0600A0062D /* StatusKeycard.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
58B511ED1A9E6C8500147676 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
};
name = Debug;
};
58B511EE1A9E6C8500147676 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
58B511F01A9E6C8500147676 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../../../React/**",
"$(SRCROOT)/../../react-native/React/**",
);
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = StatusKeycard;
SKIP_INSTALL = YES;
};
name = Debug;
};
58B511F11A9E6C8500147676 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../../../React/**",
"$(SRCROOT)/../../react-native/React/**",
);
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = StatusKeycard;
SKIP_INSTALL = YES;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "StatusKeycard" */ = {
isa = XCConfigurationList;
buildConfigurations = (
58B511ED1A9E6C8500147676 /* Debug */,
58B511EE1A9E6C8500147676 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "StatusKeycard" */ = {
isa = XCConfigurationList;
buildConfigurations = (
58B511F01A9E6C8500147676 /* Debug */,
58B511F11A9E6C8500147676 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 58B511D31A9E6C8500147676 /* Project object */;
}
+7
View File
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:StatusKeycard.xcodeproj">
</FileRef>
</Workspace>
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "react-native-status-keycard",
"homepage": "https://keycard.status.im/",
"version": "2.5.16",
"version": "2.5.17",
"description": "React Native library to interact with Status Keycard using NFC connection (Android only)",
"main": "index.js",
"scripts": {
+19
View File
@@ -0,0 +1,19 @@
require "json"
pkg = JSON.parse(File.read("package.json"))
Pod::Spec.new do |s|
s.name = pkg["name"]
s.name = "react-native-status-keycard"
s.version = pkg["version"]
s.summary = pkg["description"]
s.requires_arc = true
s.license = pkg["license"]
s.homepage = pkg["homepage"]
s.author = pkg["author"]
s.source = { :git => pkg["repository"]["url"],
:tag => "v" + pkg["version"] }
s.source_files = 'ios/**/*.{h,m}'
s.platform = :ios, "8.0"
s.dependency 'React/Core'
end