├── .gitattributes ├── .gitmodules ├── android ├── src │ └── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── com │ │ └── reactlibrary │ │ ├── RNSecp256k1Package.java │ │ ├── RNSecp256k1Ext.java │ │ └── RNSecp256k1Module.java ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── CMakeLists.txt ├── build.gradle ├── gradlew.bat └── gradlew ├── ios ├── RNSecp256k1.h ├── RNSecp256k1.xcworkspace │ └── contents.xcworkspacedata ├── RNSecp256k1Ext.h ├── RNSecp256k1.podspec ├── base64.h ├── RNSecp256k1Ext.m ├── base64.m ├── RNSecp256k1.m └── RNSecp256k1.xcodeproj │ └── project.pbxproj ├── package.json ├── .gitignore ├── COPYING ├── README.md └── index.js /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "external/secp256k1"] 2 | path = external/secp256k1 3 | url = https://github.com/fingera/secp256k1.git 4 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /ios/RNSecp256k1.h: -------------------------------------------------------------------------------- 1 | 2 | #if __has_include("RCTBridgeModule.h") 3 | #import "RCTBridgeModule.h" 4 | #else 5 | #import 6 | #endif 7 | 8 | @interface RNSecp256k1 : NSObject 9 | 10 | @end 11 | -------------------------------------------------------------------------------- /ios/RNSecp256k1.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | 3 | 5 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /ios/RNSecp256k1Ext.h: -------------------------------------------------------------------------------- 1 | // 2 | // RNSecp256k1Ext.h 3 | // RNSecp256k1 4 | // 5 | // Created by 刘宇钧 on 2018/12/27. 6 | // Copyright © 2018 liuyujun. All rights reserved. 7 | // 8 | 9 | 10 | #if __has_include("RCTBridgeModule.h") 11 | #import "RCTBridgeModule.h" 12 | #else 13 | #import 14 | #endif 15 | 16 | @interface RNSecp256k1Ext : NSObject 17 | 18 | @end 19 | 20 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "name": "react-native-secp256k1", 4 | "version": "1.0.5", 5 | "description": "bitcoin-core/secp256k1 working with react-native", 6 | "main": "index.js", 7 | "repository": { 8 | "type": "git", 9 | "url": "git@github.com:fingera/react-native-secp256k1.git" 10 | }, 11 | "scripts": { 12 | "test": "echo \"Error: no test specified\" && exit 1" 13 | }, 14 | "keywords": [ 15 | "react-native", 16 | "secp256k1", 17 | "bitcoin" 18 | ], 19 | "author": "liuyujun", 20 | "license": "MIT", 21 | "peerDependencies": { 22 | "react-native": "^0.41.2" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # OSX 3 | # 4 | .DS_Store 5 | 6 | # node.js 7 | # 8 | node_modules/ 9 | npm-debug.log 10 | yarn-error.log 11 | 12 | 13 | # Xcode 14 | # 15 | build/ 16 | *.pbxuser 17 | !default.pbxuser 18 | *.mode1v3 19 | !default.mode1v3 20 | *.mode2v3 21 | !default.mode2v3 22 | *.perspectivev3 23 | !default.perspectivev3 24 | xcuserdata 25 | *.xccheckout 26 | *.moved-aside 27 | DerivedData 28 | *.hmap 29 | *.ipa 30 | *.xcuserstate 31 | project.xcworkspace 32 | .externalNativeBuild 33 | 34 | 35 | # Android/IntelliJ 36 | # 37 | build/ 38 | .idea 39 | .gradle 40 | local.properties 41 | *.iml 42 | gradle-wrapper.jar 43 | 44 | # BUCK 45 | buck-out/ 46 | \.buckd/ 47 | *.keystore 48 | -------------------------------------------------------------------------------- /ios/RNSecp256k1.podspec: -------------------------------------------------------------------------------- 1 | 2 | Pod::Spec.new do |s| 3 | s.homepage = "https://github.com/fingera/react-native-secp256k1" 4 | s.name = "RNSecp256k1" 5 | s.version = "1.0.0" 6 | s.summary = "Secp256k1 implementation for React Native" 7 | s.description = "Secp256k1 uses CommonCrypto and custom base64 for data encoding." 8 | s.license = "MIT" 9 | s.author = { "author" => "Yujun 'fingera' Liu" } 10 | s.platform = :ios, "7.0" 11 | s.source = { :git => "https://github.com/author/RNSecp256k1.git", :tag => "v1.0.0" } 12 | s.source_files = "RNSecp256k1/**/*.{h,m}" 13 | s.requires_arc = true 14 | 15 | s.dependency "React" 16 | 17 | end 18 | -------------------------------------------------------------------------------- /android/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.4) 2 | 3 | set(CMAKE_C_STANDARD 90) 4 | set(CMAKE_C_STANDARD_REQUIRED ON) 5 | 6 | add_definitions(-DUSE_NUM_NONE=1) 7 | add_definitions(-DUSE_FIELD_INV_BUILTIN=1) 8 | add_definitions(-DUSE_SCALAR_INV_BUILTIN=1) 9 | add_definitions(-DUSE_FIELD_10X26=1) 10 | add_definitions(-DUSE_SCALAR_8X32=1) 11 | add_definitions(-DENABLE_MODULE_ECDH=1) 12 | add_definitions(-DENABLE_MODULE_RECOVERY=1) 13 | 14 | include_directories(../external/secp256k1) 15 | include_directories(../external/secp256k1/src) 16 | 17 | add_library( secp256k1 18 | SHARED 19 | ../external/secp256k1/src/java/org_bitcoin_NativeSecp256k1.c 20 | ../external/secp256k1/src/java/org_bitcoin_Secp256k1Context.c 21 | ../external/secp256k1/src/secp256k1.c ) 22 | 23 | target_link_libraries( secp256k1 ) 24 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactlibrary/RNSecp256k1Package.java: -------------------------------------------------------------------------------- 1 | 2 | package com.reactlibrary; 3 | 4 | import java.util.Arrays; 5 | import java.util.Collections; 6 | import java.util.List; 7 | 8 | import com.facebook.react.ReactPackage; 9 | import com.facebook.react.bridge.NativeModule; 10 | import com.facebook.react.bridge.ReactApplicationContext; 11 | import com.facebook.react.uimanager.ViewManager; 12 | import com.facebook.react.bridge.JavaScriptModule; 13 | public class RNSecp256k1Package implements ReactPackage { 14 | @Override 15 | public List createNativeModules(ReactApplicationContext reactContext) { 16 | return Arrays.asList(new RNSecp256k1Module(reactContext), new RNSecp256k1Ext(reactContext)); 17 | } 18 | 19 | // Deprecated from RN 0.47 20 | public List> createJSModules() { 21 | return Collections.emptyList(); 22 | } 23 | 24 | @Override 25 | public List createViewManagers(ReactApplicationContext reactContext) { 26 | return Collections.emptyList(); 27 | } 28 | } -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 liuyujun 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | 2 | buildscript { 3 | repositories { 4 | jcenter() 5 | } 6 | 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:2.3.3' 9 | } 10 | } 11 | 12 | apply plugin: 'com.android.library' 13 | 14 | android { 15 | compileSdkVersion 26 16 | buildToolsVersion '27.0.3' 17 | 18 | defaultConfig { 19 | minSdkVersion 16 20 | targetSdkVersion 26 21 | versionCode 1 22 | versionName "1.0" 23 | } 24 | lintOptions { 25 | abortOnError false 26 | } 27 | sourceSets { 28 | main { 29 | java { 30 | srcDir "../external/secp256k1/src/java" 31 | exclude "**/NativeSecp256k1Test.java" 32 | } 33 | } 34 | } 35 | 36 | externalNativeBuild { 37 | cmake { 38 | path "CMakeLists.txt" 39 | } 40 | } 41 | } 42 | 43 | repositories { 44 | mavenCentral() 45 | } 46 | 47 | dependencies { 48 | implementation 'com.facebook.react:react-native:+' 49 | implementation 'com.google.guava:guava:27.0.1-android' 50 | } 51 | -------------------------------------------------------------------------------- /ios/base64.h: -------------------------------------------------------------------------------- 1 | // 2 | // base64.h 3 | // RNSecp256k1 4 | // 5 | // Created by 刘宇钧 on 2018/12/27. 6 | // Copyright © 2018 liuyujun. All rights reserved. 7 | // 8 | 9 | #ifndef base64_h 10 | #define base64_h 11 | #if __has_include("RCTBridgeModule.h") 12 | #import "RCTBridgeModule.h" 13 | #else 14 | #import 15 | #endif 16 | 17 | #include 18 | #include 19 | #include "secp256k1.h" 20 | #include "secp256k1_ecdh.h" 21 | 22 | 23 | 24 | extern secp256k1_context *kSecp256k1Context; 25 | 26 | size_t from_base64_max_len(size_t str_len); 27 | size_t from_base64(const char *str, size_t str_len, void *buf); 28 | 29 | size_t to_base64_len(size_t buf_size); 30 | void to_base64(const void *buf, size_t buf_size, char *out); 31 | 32 | 33 | static const size_t kMaxBufferLength = 4096; 34 | 35 | static inline size_t decode_base64(NSString *str, void *buf) { 36 | const char *c_str = [str UTF8String]; 37 | size_t c_str_len = strlen(c_str); 38 | if (from_base64_max_len(c_str_len) > kMaxBufferLength) { 39 | return 0; 40 | } 41 | 42 | return from_base64(c_str, c_str_len, buf); 43 | } 44 | 45 | static inline NSString *generateECDH(NSString *pub, NSString *priv, void *ecdh) { 46 | unsigned char rawPub[kMaxBufferLength]; 47 | unsigned char rawPriv[kMaxBufferLength]; 48 | size_t rawPubLen = decode_base64(pub, rawPub); 49 | size_t rawPrivLen = decode_base64(priv, rawPriv); 50 | if (rawPubLen == 0 || rawPrivLen != 32) { 51 | return @"bad encode"; 52 | } 53 | secp256k1_pubkey pubkey; 54 | if (!secp256k1_ec_pubkey_parse(kSecp256k1Context, &pubkey, rawPub, rawPubLen)) { 55 | return @"pubkey bad"; 56 | } 57 | if (!secp256k1_ecdh(kSecp256k1Context, ecdh, &pubkey, rawPriv, NULL, NULL)) { 58 | return @"genetate fail"; 59 | } 60 | return nil; 61 | } 62 | 63 | static inline void resolveBase64(RCTPromiseResolveBlock resolve, void *buffer, size_t size) { 64 | unsigned char base[to_base64_len(size) + 1]; 65 | to_base64(buffer, size, base); 66 | base[to_base64_len(size)] = 0; 67 | resolve([NSString stringWithUTF8String:base]); 68 | } 69 | 70 | #endif /* base64_h */ 71 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # react-native-secp256k1 3 | 4 | ## Getting started 5 | 6 | `$ npm install react-native-secp256k1 --save` 7 | 8 | ### Mostly automatic installation 9 | 10 | `$ react-native link react-native-secp256k1` 11 | 12 | ### Manual installation 13 | 14 | 15 | #### iOS 16 | 17 | 1. In XCode, in the project navigator, right click `Libraries` ➜ `Add Files to [your project's name]` 18 | 2. Go to `node_modules` ➜ `react-native-secp256k1` and add `RNSecp256k1.xcodeproj` 19 | 3. In XCode, in the project navigator, select your project. Add `libRNSecp256k1.a` to your project's `Build Phases` ➜ `Link Binary With Libraries` 20 | 4. Run your project (`Cmd+R`)< 21 | 22 | #### Android 23 | 24 | 1. Open up `android/app/src/main/java/[...]/MainActivity.java` 25 | - Add `import com.reactlibrary.RNSecp256k1Package;` to the imports at the top of the file 26 | - Add `new RNSecp256k1Package()` to the list returned by the `getPackages()` method 27 | 2. Append the following lines to `android/settings.gradle`: 28 | ``` 29 | include ':react-native-secp256k1' 30 | project(':react-native-secp256k1').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-secp256k1/android') 31 | ``` 32 | 3. Insert the following lines inside the dependencies block in `android/app/build.gradle`: 33 | ``` 34 | compile project(':react-native-secp256k1') 35 | ``` 36 | 37 | 38 | ## Usage 39 | ```javascript 40 | import secp256k1 from 'react-native-secp256k1'; 41 | 42 | async function main() { 43 | const privA = await secp256k1.ext.generateKey(); 44 | const privB = await secp256k1.ext.generateKey(); 45 | 46 | const pubA = await secp256k1.computePubkey(privA, true); 47 | const pubB = await secp256k1.computePubkey(privB, true); 48 | 49 | // sign verify 50 | const data = "1H1SJuGwoSFTqNI8wvVWEdGRpBvTnzLckoZ1QTF7gI0"; 51 | const sigA = await secp256k1.sign(data, privA); 52 | console.log("verify: ", await secp256k1.verify(data, sigA, pubA)); 53 | 54 | // ecdh && aes256 55 | const encryped1 = await secp256k1.ext.encryptECDH(privA, pubB, "Hello World"); 56 | const decryped1 = await secp256k1.ext.decryptECDH(privB, pubA, encryped1); 57 | console.log(decryped1); 58 | 59 | // all: https://github.com/fingera/react-native-secp256k1-demo 60 | } 61 | 62 | main().then(() => { 63 | console.log("Done"); 64 | }).catch((err) => { 65 | console.error(err); 66 | }); 67 | 68 | ``` 69 | -------------------------------------------------------------------------------- /android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /ios/RNSecp256k1Ext.m: -------------------------------------------------------------------------------- 1 | // 2 | // RNSecp256k1Ext.c 3 | // RNSecp256k1 4 | // 5 | // Created by 刘宇钧 on 2018/12/27. 6 | // Copyright © 2018 liuyujun. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #include "RNSecp256k1Ext.h" 12 | 13 | #include "base64.h" 14 | 15 | @implementation RNSecp256k1Ext 16 | 17 | 18 | - (dispatch_queue_t)methodQueue 19 | { 20 | return dispatch_get_main_queue(); 21 | } 22 | + (BOOL)requiresMainQueueSetup 23 | { 24 | return YES; 25 | } 26 | RCT_EXPORT_MODULE() 27 | 28 | 29 | RCT_EXPORT_METHOD(generateKey:(RCTPromiseResolveBlock)resolve 30 | rejecter:(RCTPromiseRejectBlock)reject) { 31 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 32 | unsigned char rawPriv[32]; 33 | do { 34 | int result = SecRandomCopyBytes(kSecRandomDefault, sizeof(rawPriv), rawPriv); 35 | if(result != 0) { 36 | NSLog(@"SecRandomCopyBytes failed for some reason"); 37 | for (int i = 0; i < sizeof(rawPriv); i++) { 38 | rawPriv[i] = (uint8_t)rand(); 39 | } 40 | } 41 | } while (!secp256k1_ec_seckey_verify(kSecp256k1Context, rawPriv)); 42 | 43 | resolveBase64(resolve, rawPriv, sizeof(rawPriv)); 44 | }); 45 | } 46 | 47 | void ccc(bool encrypt, NSString *priv, NSString *pub, NSString *data, RCTPromiseResolveBlock resolve, RCTPromiseRejectBlock reject) { 48 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 49 | unsigned char ecdh[32]; 50 | NSString *err = generateECDH(pub, priv, ecdh); 51 | if (err != nil) { 52 | reject(@"Error", err, nil); 53 | return; 54 | } 55 | NSData *utf8 = [data dataUsingEncoding:NSUTF8StringEncoding]; 56 | void *raw; 57 | size_t rawLen; 58 | if (encrypt) { 59 | size_t paddingLen = 16 - ([utf8 length] % 16); 60 | if (paddingLen < 2) paddingLen += 16; 61 | paddingLen--; 62 | rawLen = paddingLen + [utf8 length] + 1; 63 | raw = malloc(rawLen); 64 | *(uint8_t *)raw = (uint8_t)paddingLen; 65 | int r = SecRandomCopyBytes(kSecRandomDefault, paddingLen, (uint8_t *)raw + 1); (void)r; 66 | memcpy((uint8_t *)raw + paddingLen + 1, [utf8 bytes], [utf8 length]); 67 | } else { 68 | raw = malloc(from_base64_max_len([utf8 length])); 69 | rawLen = from_base64([utf8 bytes], [utf8 length], raw); 70 | } 71 | 72 | CCCryptorRef crypto = nil; 73 | CCCryptorStatus status = CCCryptorCreate(encrypt ? kCCEncrypt : kCCDecrypt, kCCAlgorithmAES, 0, 74 | ecdh, sizeof(ecdh), NULL, &crypto); 75 | if (status != kCCSuccess) { 76 | free(raw); 77 | if (crypto != nil) CCCryptorRelease(crypto); 78 | reject(@"Error", [NSString stringWithFormat:@"cryptor create error: %d", (int)status], nil); 79 | return; 80 | } 81 | size_t outLen = CCCryptorGetOutputLength(crypto, rawLen, true); 82 | size_t updateLen = 0; 83 | size_t finalLen = 0; 84 | char *out = malloc(outLen + 1); 85 | status = CCCryptorUpdate(crypto, raw, rawLen, out, outLen, &updateLen); 86 | if (status != kCCSuccess) { 87 | free(raw); 88 | CCCryptorRelease(crypto); 89 | free(out); 90 | reject(@"Error", [NSString stringWithFormat:@"cryptor update error: %d", (int)status], nil); 91 | return; 92 | } 93 | status = CCCryptorFinal(crypto, out + updateLen, outLen - updateLen, &finalLen); 94 | if (status != kCCSuccess) { 95 | free(raw); 96 | CCCryptorRelease(crypto); 97 | free(out); 98 | reject(@"Error", [NSString stringWithFormat:@"cryptor final error: %d", (int)status], nil); 99 | return; 100 | } 101 | if (encrypt) { 102 | resolveBase64(resolve, out, updateLen + finalLen); 103 | } else { 104 | size_t dataStart = (size_t)out[0] + 1; 105 | size_t realLen = updateLen + finalLen; 106 | if (realLen <= dataStart) { 107 | reject(@"Error", @"Bad Data", nil); 108 | } else { 109 | out[updateLen + finalLen] = 0; 110 | resolve([NSString stringWithUTF8String:out + dataStart]); 111 | } 112 | } 113 | free(raw); 114 | CCCryptorRelease(crypto); 115 | free(out); 116 | }); 117 | } 118 | 119 | RCT_EXPORT_METHOD(encryptECDH:(NSString *)priv pub:(NSString *)pub data:(NSString *)data resolve:(RCTPromiseResolveBlock)resolve 120 | rejecter:(RCTPromiseRejectBlock)reject) { 121 | ccc(true, priv, pub, data, resolve, reject); 122 | } 123 | 124 | 125 | RCT_EXPORT_METHOD(decryptECDH:(NSString *)priv pub:(NSString *)pub data:(NSString *)data resolve:(RCTPromiseResolveBlock)resolve 126 | rejecter:(RCTPromiseRejectBlock)reject) { 127 | ccc(false, priv, pub, data, resolve, reject); 128 | } 129 | @end 130 | 131 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactlibrary/RNSecp256k1Ext.java: -------------------------------------------------------------------------------- 1 | package com.reactlibrary; 2 | 3 | import android.os.AsyncTask; 4 | import android.util.Base64; 5 | 6 | import com.facebook.react.bridge.Promise; 7 | import com.facebook.react.bridge.ReactApplicationContext; 8 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 9 | import com.facebook.react.bridge.ReactMethod; 10 | 11 | import org.bitcoin.NativeSecp256k1; 12 | 13 | import java.io.UnsupportedEncodingException; 14 | import java.security.SecureRandom; 15 | 16 | import javax.crypto.Cipher; 17 | import javax.crypto.spec.IvParameterSpec; 18 | import javax.crypto.spec.SecretKeySpec; 19 | 20 | public class RNSecp256k1Ext extends ReactContextBaseJavaModule { 21 | 22 | SecureRandom GRandom = new SecureRandom(); 23 | 24 | private static byte[] Ase(byte[] byteData, byte[] byteKey, int opmode, IvParameterSpec iv) throws Exception { 25 | Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); 26 | SecretKeySpec skeySpec = new SecretKeySpec(byteKey, "AES"); 27 | cipher.init(opmode, skeySpec, iv); 28 | byte[] decrypted = cipher.doFinal(byteData); 29 | return decrypted; 30 | } 31 | 32 | private void AesECDH(final String priv, final String pub, final byte[] data, final int mode, final Promise promise) { 33 | AsyncTask.execute(new Runnable() { 34 | @Override 35 | public void run() { 36 | try { 37 | byte[] privraw = Base64.decode(priv, Base64.NO_PADDING); 38 | byte[] pubraw = Base64.decode(pub, Base64.NO_PADDING); 39 | byte[] sec = NativeSecp256k1.createECDHSecret(privraw, pubraw); 40 | byte[] eData = data; 41 | byte[] iv = new byte[]{ 42 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43 | }; 44 | IvParameterSpec ivSpec = new IvParameterSpec(iv); 45 | if (mode == Cipher.ENCRYPT_MODE) { 46 | int paddingLen = 16 - (data.length % 16); 47 | if (paddingLen < 2) { 48 | paddingLen += 16; 49 | } 50 | paddingLen--; 51 | byte[] random = new byte[paddingLen]; 52 | GRandom.nextBytes(random); 53 | byte[] newData = new byte[random.length + data.length + 1]; 54 | newData[0] = (byte)paddingLen; 55 | System.arraycopy(random, 0, newData, 1, random.length); 56 | System.arraycopy(data, 0, newData, 1 + random.length, data.length); 57 | eData = newData; 58 | } 59 | 60 | byte[] encryped = Ase(eData, sec, mode, ivSpec); 61 | 62 | if (mode == Cipher.ENCRYPT_MODE) { 63 | promise.resolve(Base64.encodeToString(encryped, Base64.NO_PADDING | Base64.NO_WRAP)); 64 | } else { 65 | // DoUnpadding 66 | int dataStart = (int)encryped[0] + 1; 67 | int realLen = encryped.length - dataStart; 68 | byte[] realData = new byte[realLen]; 69 | System.arraycopy(encryped, dataStart, realData, 0, realLen); 70 | promise.resolve(new String(realData, "UTF-8")); 71 | } 72 | } catch (Exception ex) { 73 | ex.printStackTrace(); 74 | promise.reject("Error", ex.toString()); 75 | } 76 | } 77 | }); 78 | } 79 | 80 | 81 | public RNSecp256k1Ext(ReactApplicationContext reactContext) { 82 | super(reactContext); 83 | } 84 | @Override 85 | public String getName() { 86 | return "RNSecp256k1Ext"; 87 | } 88 | 89 | @ReactMethod 90 | public void generateKey(final Promise promise) { 91 | AsyncTask.execute(new Runnable() { 92 | @Override 93 | public void run() { 94 | try { 95 | byte[] privraw = new byte[32]; 96 | do { 97 | GRandom.nextBytes(privraw); 98 | } while (!NativeSecp256k1.secKeyVerify(privraw)); 99 | promise.resolve(Base64.encodeToString(privraw, Base64.NO_PADDING | Base64.NO_WRAP)); 100 | } catch (Exception ex) { 101 | ex.printStackTrace(); 102 | promise.reject("Error", ex.toString()); 103 | } 104 | } 105 | }); 106 | } 107 | 108 | @ReactMethod 109 | public void encryptECDH(final String priv, final String pub, final String data, final Promise promise) { 110 | try { 111 | AesECDH(priv, pub, data.getBytes("UTF-8"), Cipher.ENCRYPT_MODE, promise); 112 | } catch (UnsupportedEncodingException e) { 113 | e.printStackTrace(); 114 | promise.reject("Error", e.toString()); 115 | } 116 | } 117 | 118 | @ReactMethod 119 | public void decryptECDH(final String priv, final String pub, final String data, final Promise promise) { 120 | AesECDH(priv, pub, Base64.decode(data, Base64.NO_PADDING), Cipher.DECRYPT_MODE, promise); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /ios/base64.m: -------------------------------------------------------------------------------- 1 | // 2 | // base64.c 3 | // RNSecp256k1 4 | // 5 | // Created by 刘宇钧 on 2018/12/27. 6 | // Copyright © 2018 liuyujun. All rights reserved. 7 | // 8 | 9 | #include "base64.h" 10 | 11 | 12 | static const char BASE64_STANDARD_ENCODE[65] = 13 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 14 | static const unsigned char BASE64_STANDARD_DECODE[256] = { 15 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 16 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 17 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 18 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3E, 19 | 0xFF, 0xFF, 0xFF, 0x3F, // +/ 20 | 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0xFF, 21 | 0xFF, 0xFF, 0x00, 0xFF, 0xFF, // 0-9 = 22 | 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 23 | 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, // A-O 24 | 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 25 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // P-Z 26 | 0xFF, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 27 | 0x24, 0x25, 0x26, 0x27, 0x28, // a-o 28 | 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 29 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // p-z 30 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 31 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 32 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 33 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 34 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 35 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 36 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 37 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 38 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 39 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 40 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 41 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 42 | }; 43 | 44 | size_t to_base64_len(size_t buf_size) { 45 | size_t r = (buf_size / 3) * 4; 46 | switch (buf_size % 3) { 47 | case 1: 48 | r += 2; 49 | break; 50 | case 2: 51 | r += 3; 52 | break; 53 | default: 54 | break; 55 | } 56 | return r; 57 | } 58 | void to_base64(const void *buf, size_t buf_size, char *out) { 59 | size_t tail_len = buf_size % 3; 60 | size_t loop_size = buf_size - tail_len; 61 | const uint8_t *input = (const uint8_t *)buf; 62 | const char *encode_str = BASE64_STANDARD_ENCODE; 63 | 64 | for (size_t i = 0; i < loop_size; i += 3) { 65 | // 11111111 11111111 11111111 66 | // 11111122 22223333 33444444 67 | uint8_t byte1 = input[0] >> 2; 68 | uint8_t byte2 = ((input[0] & 0x3) << 4) | (input[1] >> 4); 69 | uint8_t byte3 = ((input[1] & 0xF) << 2) | (input[2] >> 6); 70 | uint8_t byte4 = input[2] & 0x3F; 71 | out[0] = encode_str[byte1]; 72 | out[1] = encode_str[byte2]; 73 | out[2] = encode_str[byte3]; 74 | out[3] = encode_str[byte4]; 75 | out += 4; 76 | input += 3; 77 | } 78 | 79 | if (tail_len) { 80 | uint8_t byte1 = input[0] >> 2; 81 | uint8_t byte2 = ((input[0] & 0x3) << 4); 82 | out[0] = encode_str[byte1]; 83 | if (tail_len == 2) { 84 | byte2 |= (input[1] >> 4); 85 | uint8_t byte3 = (input[1] & 0xF) << 2; 86 | out[1] = encode_str[byte2]; 87 | out[2] = encode_str[byte3]; 88 | } else { 89 | out[1] = encode_str[byte2]; 90 | } 91 | } 92 | } 93 | size_t from_base64_max_len(size_t str_len) { 94 | return ((str_len + 3) / 4) * 3; 95 | } 96 | size_t from_base64(const char *str, size_t str_len, void *buf) { 97 | uint8_t byte1, byte2, byte3, byte4; 98 | size_t tail_len = str_len % 4; 99 | size_t loop_size = str_len - tail_len; 100 | const uint8_t *input = (const uint8_t *)str; 101 | uint8_t *output = (uint8_t *)buf; 102 | const unsigned char *decode_str = BASE64_STANDARD_DECODE; 103 | 104 | for (size_t i = 0; i < loop_size; i += 4) { 105 | // 11111111 11111111 11111111 106 | // 11111122 22223333 33444444 107 | byte1 = decode_str[input[0]]; 108 | byte2 = decode_str[input[1]]; 109 | byte3 = decode_str[input[2]]; 110 | byte4 = decode_str[input[3]]; 111 | 112 | if (byte1 == 0xFF || byte2 == 0xFF || byte3 == 0xFF || byte4 == 0xFF) 113 | return output - (uint8_t *)buf; 114 | 115 | output[0] = (byte1 << 2) | (byte2 >> 4); 116 | output[1] = (byte2 << 4) | (byte3 >> 2); 117 | output[2] = (byte3 << 6) | byte4; 118 | 119 | output += 3; 120 | input += 4; 121 | } 122 | 123 | // 剩余1个字节是不合法的,编码的时候最后一个字节会编成2个字节 124 | switch (tail_len) { 125 | case 2: 126 | byte1 = decode_str[input[0]]; 127 | byte2 = decode_str[input[1]]; 128 | if (byte1 == 0xFF || byte2 == 0xFF) break; 129 | *output++ = (byte1 << 2) | ((byte2 >> 4) & 0x3); 130 | break; 131 | case 3: 132 | byte1 = decode_str[input[0]]; 133 | byte2 = decode_str[input[1]]; 134 | byte3 = decode_str[input[2]]; 135 | if (byte1 == 0xFF || byte2 == 0xFF || byte3 == 0xFF) break; 136 | output[0] = (byte1 << 2) | ((byte2 >> 4) & 0x3); 137 | output[1] = (byte2 << 4) | ((byte3 >> 2) & 0xF); 138 | output += 2; 139 | break; 140 | default: 141 | break; 142 | } 143 | 144 | return output - (uint8_t *)buf; 145 | } 146 | -------------------------------------------------------------------------------- /android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 2 | import { NativeModules } from 'react-native'; 3 | 4 | const { RNSecp256k1, RNSecp256k1Ext } = NativeModules; 5 | 6 | 7 | ////////////////////////////////////// hex ///////////////////////////////////// 8 | function to_hex_value(c) { 9 | if (c >= 0x30 && c <= 0x39) return c - 0x30; 10 | if (c >= 0x41 && c <= 0x5A) return (c - 0x41) + 10; 11 | if (c >= 0x61 && c <= 0x7A) return (c - 0x61) + 10; 12 | return 0; 13 | } 14 | RNSecp256k1.hex_decode = function (str) { 15 | const bytes = []; 16 | let len = str.length; 17 | if (len % 2 === 1) len--; // ingore single char 18 | for (let i = 0; i < len; i += 2) { 19 | const c1 = to_hex_value(str.charCodeAt(i)); 20 | const c2 = to_hex_value(str.charCodeAt(i + 1)); 21 | bytes.push(c2 | (c1 << 4)); 22 | } 23 | return bytes; 24 | } 25 | const hex_str = "0123456789ABCDEF"; 26 | 27 | RNSecp256k1.hex_encode = function (bytes) { 28 | let str = ""; 29 | for (let i = 0; i < bytes.length; i++) { 30 | const b = bytes[i]; 31 | str += hex_str[b >> 4]; 32 | str += hex_str[b & 0xF]; 33 | } 34 | return str; 35 | } 36 | 37 | //////////////////////////////////// base64 //////////////////////////////////// 38 | const to_char = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 39 | const from_char = []; 40 | for (let i = 0; i < to_char.length; i++) { 41 | from_char[to_char.charCodeAt(i)] = i; 42 | } 43 | 44 | RNSecp256k1.base64_encode = function (byteArray) { 45 | let result = ""; 46 | const tail_len = byteArray.length % 3; 47 | const chunk_len = byteArray.length - tail_len; 48 | let i = 0; 49 | while (i < chunk_len) { 50 | result += to_char[byteArray[i] >> 2]; 51 | result += to_char[((byteArray[i] & 3) << 4) | (byteArray[i + 1] >> 4)]; 52 | result += to_char[((byteArray[i + 1] & 15) << 2) | (byteArray[i + 2] >> 6)]; 53 | result += to_char[byteArray[i + 2] & 63]; 54 | i += 3; 55 | } 56 | if (tail_len) { 57 | result += to_char[byteArray[i] >> 2]; 58 | if (tail_len === 1) { 59 | result += to_char[((byteArray[i] & 3) << 4)]; 60 | } else { 61 | result += to_char[((byteArray[i] & 3) << 4) | (byteArray[i + 1] >> 4)]; 62 | result += to_char[((byteArray[i + 1] & 15) << 2)]; 63 | } 64 | } 65 | return result; 66 | } 67 | 68 | RNSecp256k1.base64_decode = function (str) { 69 | const bytes = []; 70 | let byte1, byte2, byte3, byte4; 71 | str = str.replace(/[\r\n\t ]/g, ""); 72 | const tail_len = str.length % 4; 73 | const chunk_len = str.length - tail_len; 74 | if (tail_len === 1) { 75 | throw new Error(`bad char ${str} len ${str.length}`); 76 | } 77 | let i = 0; 78 | while (i < chunk_len) { 79 | byte1 = from_char[str.charCodeAt(i++)]; 80 | byte2 = from_char[str.charCodeAt(i++)]; 81 | byte3 = from_char[str.charCodeAt(i++)]; 82 | byte4 = from_char[str.charCodeAt(i++)]; 83 | if (byte1 === undefined || byte2 === undefined 84 | || byte3 === undefined || byte4 === undefined) { 85 | throw new Error(`bad char ${str} ${i}`); 86 | } 87 | bytes.push(((byte1 << 2) & 0xFF) | (byte2 >> 4)); 88 | bytes.push(((byte2 << 4) & 0xFF) | (byte3 >> 2)); 89 | bytes.push(((byte3 << 6) & 0xFF) | byte4); 90 | } 91 | if (tail_len) { 92 | byte1 = from_char[str.charCodeAt(i++)]; 93 | byte2 = from_char[str.charCodeAt(i++)]; 94 | if (byte1 === undefined || byte2 === undefined) { 95 | throw new Error(`bad char ${str} ${i}`); 96 | } 97 | bytes.push(((byte1 << 2) & 0xFF) | (byte2 >> 4)); 98 | if (tail_len === 3) { 99 | byte3 = from_char[str.charCodeAt(i++)]; 100 | if (byte3 === undefined) throw new Error(`bad char ${str} ${i}`); 101 | bytes.push(((byte2 << 4) & 0xFF) | (byte3 >> 2)); 102 | } 103 | } 104 | return bytes; 105 | } 106 | 107 | //////////////////////////////// raw interface //////////////////////////////// 108 | RNSecp256k1.raw_verify = async function (data, signature, pub) { 109 | const bData = base64_encode(data); 110 | const bSig = base64_encode(signature); 111 | const bPub = base64_encode(pub); 112 | return await RNSecp256k1.verify(bData, bSig, bPub); 113 | }; 114 | RNSecp256k1.raw_sign = async function (data, priv) { 115 | const bData = base64_encode(data); 116 | const bPriv = base64_encode(priv); 117 | const bSignature = await RNSecp256k1.sign(bData, bPriv); 118 | return base64_decode(bSignature); 119 | }; 120 | RNSecp256k1.raw_secKeyVerify = async function (priv) { 121 | const bPriv = base64_encode(priv); 122 | return await RNSecp256k1.secKeyVerify(bPriv); 123 | }; 124 | RNSecp256k1.raw_computePubkey = async function (priv, compressed) { 125 | const bPriv = base64_encode(priv); 126 | const bPub = await RNSecp256k1.computePubkey(bPriv, compressed ? true : false); 127 | return base64_decode(bPub); 128 | }; 129 | RNSecp256k1.raw_createECDHSecret = async function (priv, pub) { 130 | const bPriv = base64_encode(priv); 131 | const bPub = base64_encode(pub); 132 | const bSecret = await RNSecp256k1.createECDHSecret(bPriv, bPub); 133 | return base64_decode(bSecret); 134 | }; 135 | /* 136 | RNSecp256k1.raw_randomize = async function (random) { 137 | const bRandom = base64_encode(random); 138 | return await RNSecp256k1.randomize(bRandom); 139 | }; 140 | */ 141 | RNSecp256k1.raw_privKeyTweakMul = async function (priv, tweak) { 142 | const bPriv = base64_encode(priv); 143 | const bTweak = base64_encode(tweak); 144 | const bResult = await RNSecp256k1.privKeyTweakMul(bPriv, bTweak); 145 | return base64_decode(bResult); 146 | }; 147 | RNSecp256k1.raw_privKeyTweakAdd = async function (priv, tweak) { 148 | const bPriv = base64_encode(priv); 149 | const bTweak = base64_encode(tweak); 150 | const bResult = await RNSecp256k1.privKeyTweakAdd(bPriv, bTweak); 151 | return base64_decode(bResult); 152 | }; 153 | RNSecp256k1.raw_pubKeyTweakMul = async function (pub, tweak) { 154 | const bPub = base64_encode(pub); 155 | const bTweak = base64_encode(tweak); 156 | const bResult = await RNSecp256k1.pubKeyTweakMul(bPub, bTweak); 157 | return base64_decode(bResult); 158 | }; 159 | RNSecp256k1.raw_pubKeyTweakAdd = async function (pub, tweak) { 160 | const bPub = base64_encode(pub); 161 | const bTweak = base64_encode(tweak); 162 | const bResult = await RNSecp256k1.pubKeyTweakAdd(bPub, bTweak); 163 | return base64_decode(bResult); 164 | }; 165 | 166 | RNSecp256k1.ext = RNSecp256k1Ext; 167 | 168 | export default RNSecp256k1; 169 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactlibrary/RNSecp256k1Module.java: -------------------------------------------------------------------------------- 1 | 2 | package com.reactlibrary; 3 | 4 | import android.os.AsyncTask; 5 | import android.util.Base64; 6 | import android.util.Log; 7 | 8 | import com.facebook.react.bridge.Promise; 9 | import com.facebook.react.bridge.ReactApplicationContext; 10 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 11 | import com.facebook.react.bridge.ReactMethod; 12 | import com.facebook.react.bridge.Callback; 13 | 14 | import org.bitcoin.NativeSecp256k1; 15 | import org.bitcoin.NativeSecp256k1Util; 16 | import org.bitcoin.Secp256k1Context; 17 | 18 | import java.security.SecureRandom; 19 | import java.util.HashMap; 20 | import java.util.Map; 21 | 22 | public class RNSecp256k1Module extends ReactContextBaseJavaModule { 23 | 24 | private final ReactApplicationContext reactContext; 25 | 26 | public RNSecp256k1Module(ReactApplicationContext reactContext) { 27 | super(reactContext); 28 | this.reactContext = reactContext; 29 | SecureRandom random = new SecureRandom(); 30 | try { 31 | NativeSecp256k1.randomize(random.generateSeed(32)); 32 | } catch (NativeSecp256k1Util.AssertFailException e) { 33 | e.printStackTrace(); 34 | } 35 | } 36 | 37 | @Override 38 | public Map getConstants() { 39 | final Map constants = new HashMap<>(); 40 | constants.put("isEnabled", Secp256k1Context.isEnabled()); 41 | return constants; 42 | } 43 | 44 | @Override 45 | public String getName() { 46 | return "RNSecp256k1"; 47 | } 48 | 49 | @ReactMethod 50 | public void verify(final String data, final String signature, final String pub, final Promise promise) { 51 | AsyncTask.execute(new Runnable() { 52 | @Override 53 | public void run() { 54 | try { 55 | byte[] dataraw = Base64.decode(data, Base64.NO_PADDING); 56 | byte[] signatureraw = Base64.decode(signature, Base64.NO_PADDING); 57 | byte[] pubraw = Base64.decode(pub, Base64.NO_PADDING); 58 | 59 | promise.resolve(NativeSecp256k1.verify(dataraw, signatureraw, pubraw)); 60 | } catch (Exception ex) { 61 | ex.printStackTrace(); 62 | promise.reject("Error", ex.toString()); 63 | } 64 | 65 | } 66 | }); 67 | } 68 | 69 | @ReactMethod 70 | public void sign(final String data, final String priv, final Promise promise) { 71 | AsyncTask.execute(new Runnable() { 72 | @Override 73 | public void run() { 74 | try { 75 | byte[] dataraw = Base64.decode(data, Base64.NO_PADDING); 76 | byte[] privraw = Base64.decode(priv, Base64.NO_PADDING); 77 | byte[] signatureraw = NativeSecp256k1.sign(dataraw, privraw); 78 | 79 | promise.resolve(Base64.encodeToString(signatureraw, Base64.NO_PADDING | Base64.NO_WRAP)); 80 | } catch (Exception ex) { 81 | ex.printStackTrace(); 82 | promise.reject("Error", ex.toString()); 83 | } 84 | } 85 | }); 86 | } 87 | 88 | @ReactMethod 89 | public void secKeyVerify(final String priv, final Promise promise) { 90 | AsyncTask.execute(new Runnable() { 91 | @Override 92 | public void run() { 93 | try { 94 | byte[] privraw = Base64.decode(priv, Base64.NO_PADDING); 95 | promise.resolve(NativeSecp256k1.secKeyVerify(privraw)); 96 | } catch (Exception ex) { 97 | ex.printStackTrace(); 98 | promise.reject("Error", ex.toString()); 99 | } 100 | } 101 | }); 102 | } 103 | 104 | @ReactMethod 105 | public void computePubkey(final String priv, final boolean compressed, final Promise promise) { 106 | AsyncTask.execute(new Runnable() { 107 | @Override 108 | public void run() { 109 | try { 110 | byte[] privraw = Base64.decode(priv, Base64.NO_PADDING); 111 | byte[] pubraw = NativeSecp256k1.computePubkey(privraw, compressed); 112 | promise.resolve(Base64.encodeToString(pubraw, Base64.NO_PADDING | Base64.NO_WRAP)); 113 | } catch (Exception ex) { 114 | ex.printStackTrace(); 115 | promise.reject("Error", ex.toString()); 116 | } 117 | } 118 | }); 119 | } 120 | 121 | @ReactMethod 122 | public void createECDHSecret(final String priv, final String pub, final Promise promise) { 123 | AsyncTask.execute(new Runnable() { 124 | @Override 125 | public void run() { 126 | try { 127 | byte[] privraw = Base64.decode(priv, Base64.NO_PADDING); 128 | byte[] pubraw = Base64.decode(pub, Base64.NO_PADDING); 129 | byte[] secretraw = NativeSecp256k1.createECDHSecret(privraw, pubraw); 130 | promise.resolve(Base64.encodeToString(secretraw, Base64.NO_PADDING | Base64.NO_WRAP)); 131 | } catch (Exception ex) { 132 | ex.printStackTrace(); 133 | promise.reject("Error", ex.toString()); 134 | } 135 | } 136 | }); 137 | } 138 | /* 139 | @ReactMethod 140 | public void randomize(final String random, final Promise promise) { 141 | AsyncTask.execute(new Runnable() { 142 | @Override 143 | public void run() { 144 | try { 145 | byte[] randomraw = Base64.decode(random, Base64.NO_PADDING); 146 | promise.resolve(NativeSecp256k1.randomize(randomraw)); 147 | } catch (Exception ex) { 148 | ex.printStackTrace(); 149 | promise.reject("Error", ex.toString()); 150 | } 151 | } 152 | }); 153 | } 154 | */ 155 | @ReactMethod 156 | public void privKeyTweakMul(final String priv, final String tweak, final Promise promise) { 157 | AsyncTask.execute(new Runnable() { 158 | @Override 159 | public void run() { 160 | try { 161 | byte[] privraw = Base64.decode(priv, Base64.NO_PADDING); 162 | byte[] tweakraw = Base64.decode(tweak, Base64.NO_PADDING); 163 | byte[] result = NativeSecp256k1.privKeyTweakMul(privraw, tweakraw); 164 | promise.resolve(Base64.encodeToString(result, Base64.NO_PADDING | Base64.NO_WRAP)); 165 | } catch (Exception ex) { 166 | ex.printStackTrace(); 167 | promise.reject("Error", ex.toString()); 168 | } 169 | } 170 | }); 171 | } 172 | 173 | @ReactMethod 174 | public void privKeyTweakAdd(final String priv, final String tweak, final Promise promise) { 175 | AsyncTask.execute(new Runnable() { 176 | @Override 177 | public void run() { 178 | try { 179 | byte[] privraw = Base64.decode(priv, Base64.NO_PADDING); 180 | byte[] tweakraw = Base64.decode(tweak, Base64.NO_PADDING); 181 | byte[] result = NativeSecp256k1.privKeyTweakAdd(privraw, tweakraw); 182 | promise.resolve(Base64.encodeToString(result, Base64.NO_PADDING | Base64.NO_WRAP)); 183 | } catch (Exception ex) { 184 | ex.printStackTrace(); 185 | promise.reject("Error", ex.toString()); 186 | } 187 | } 188 | }); 189 | } 190 | 191 | @ReactMethod 192 | public void pubKeyTweakMul(final String pub, final String tweak, final Promise promise) { 193 | AsyncTask.execute(new Runnable() { 194 | @Override 195 | public void run() { 196 | try { 197 | byte[] pubraw = Base64.decode(pub, Base64.NO_PADDING); 198 | byte[] tweakraw = Base64.decode(tweak, Base64.NO_PADDING); 199 | byte[] result = NativeSecp256k1.pubKeyTweakMul(pubraw, tweakraw); 200 | promise.resolve(Base64.encodeToString(result, Base64.NO_PADDING | Base64.NO_WRAP)); 201 | } catch (Exception ex) { 202 | ex.printStackTrace(); 203 | promise.reject("Error", ex.toString()); 204 | } 205 | } 206 | }); 207 | } 208 | 209 | @ReactMethod 210 | public void pubKeyTweakAdd(final String pub, final String tweak, final Promise promise) { 211 | AsyncTask.execute(new Runnable() { 212 | @Override 213 | public void run() { 214 | try { 215 | byte[] pubraw = Base64.decode(pub, Base64.NO_PADDING); 216 | byte[] tweakraw = Base64.decode(tweak, Base64.NO_PADDING); 217 | byte[] result = NativeSecp256k1.pubKeyTweakAdd(pubraw, tweakraw); 218 | promise.resolve(Base64.encodeToString(result, Base64.NO_PADDING | Base64.NO_WRAP)); 219 | } catch (Exception ex) { 220 | ex.printStackTrace(); 221 | promise.reject("Error", ex.toString()); 222 | } 223 | } 224 | }); 225 | } 226 | } -------------------------------------------------------------------------------- /ios/RNSecp256k1.m: -------------------------------------------------------------------------------- 1 | 2 | #import "RNSecp256k1.h" 3 | 4 | #include "base64.h" 5 | 6 | @implementation RNSecp256k1 7 | 8 | 9 | secp256k1_context *kSecp256k1Context = nil; 10 | 11 | - (dispatch_queue_t)methodQueue 12 | { 13 | uint8_t seed[32]; 14 | int result = SecRandomCopyBytes(kSecRandomDefault, sizeof(seed), seed); 15 | if(result != 0) { 16 | NSLog(@"SecRandomCopyBytes failed for some reason"); 17 | for (int i = 0; i < sizeof(seed); i++) { 18 | seed[i] = (uint8_t)rand(); 19 | } 20 | } 21 | kSecp256k1Context = secp256k1_context_create(SECP256K1_FLAGS_BIT_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); 22 | int r = secp256k1_context_randomize(kSecp256k1Context, seed); (void)r; 23 | return dispatch_get_main_queue(); 24 | } 25 | - (NSDictionary *)constantsToExport 26 | { 27 | return @{ @"isEnabled": @TRUE }; 28 | } 29 | + (BOOL)requiresMainQueueSetup 30 | { 31 | return YES; 32 | } 33 | RCT_EXPORT_MODULE() 34 | 35 | 36 | RCT_EXPORT_METHOD(verify:(NSString *)data sig:(NSString *)sig pub:(NSString *)pub resolve:(RCTPromiseResolveBlock)resolve 37 | rejecter:(RCTPromiseRejectBlock)reject) { 38 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 39 | unsigned char rawData[kMaxBufferLength]; 40 | unsigned char rawSig[kMaxBufferLength]; 41 | unsigned char rawPub[kMaxBufferLength]; 42 | size_t rawDataLen = decode_base64(data, rawData); 43 | size_t rawSigLen = decode_base64(sig, rawSig); 44 | size_t rawPubLen = decode_base64(pub, rawPub); 45 | if (rawDataLen != 32 || rawSigLen == 0 || rawPubLen == 0) { 46 | reject(@"Error", @"Data or Sig or Pubkey invalid", nil); 47 | return; 48 | } 49 | 50 | secp256k1_ecdsa_signature sig; 51 | secp256k1_pubkey pubkey; 52 | if (!secp256k1_ecdsa_signature_parse_der(kSecp256k1Context, &sig, rawSig, rawSigLen)) { 53 | reject(@"Error", @"signature invalid", nil); 54 | return; 55 | } 56 | if (!secp256k1_ec_pubkey_parse(kSecp256k1Context, &pubkey, rawPub, rawPubLen)) { 57 | reject(@"Error", @"pubkey invalid", nil); 58 | return; 59 | } 60 | int r = secp256k1_ecdsa_verify(kSecp256k1Context, &sig, rawData, &pubkey); 61 | resolve([NSNumber numberWithInt:r]); 62 | }); 63 | } 64 | 65 | RCT_EXPORT_METHOD(sign:(NSString *)data priv:(NSString *)priv resolve:(RCTPromiseResolveBlock)resolve 66 | rejecter:(RCTPromiseRejectBlock)reject) { 67 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 68 | unsigned char rawData[kMaxBufferLength]; 69 | unsigned char rawPriv[kMaxBufferLength]; 70 | size_t rawDataLen = decode_base64(data, rawData); 71 | size_t rawPrivLen = decode_base64(priv, rawPriv); 72 | if (rawDataLen != 32 || rawPrivLen != 32) { 73 | reject(@"Error", @"Data or Key invalid", nil); 74 | return; 75 | } 76 | secp256k1_ecdsa_signature sig; 77 | if (!secp256k1_ecdsa_sign(kSecp256k1Context, &sig, rawData, rawPriv, NULL, NULL)) { 78 | resolve(@""); 79 | return; 80 | } 81 | unsigned char rawSig[72]; 82 | size_t rawSigLen = 72; 83 | secp256k1_ecdsa_signature_serialize_der(kSecp256k1Context, rawSig, &rawSigLen, &sig ); 84 | 85 | resolveBase64(resolve, rawSig, rawSigLen); 86 | }); 87 | } 88 | 89 | RCT_EXPORT_METHOD(secKeyVerify:(NSString *)priv resolve:(RCTPromiseResolveBlock)resolve 90 | rejecter:(RCTPromiseRejectBlock)reject) { 91 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 92 | unsigned char rawPriv[kMaxBufferLength]; 93 | size_t rawPrivLen = decode_base64(priv, rawPriv); 94 | if (rawPrivLen != 32) { 95 | reject(@"Error", @"Key invalid", nil); 96 | return; 97 | } 98 | int r = secp256k1_ec_seckey_verify(kSecp256k1Context, rawPriv); 99 | resolve([NSNumber numberWithInt:r]); 100 | }); 101 | } 102 | 103 | 104 | RCT_EXPORT_METHOD(computePubkey:(NSString *)priv compress:(BOOL)compress resolve:(RCTPromiseResolveBlock)resolve 105 | rejecter:(RCTPromiseRejectBlock)reject) { 106 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 107 | unsigned char rawPriv[kMaxBufferLength]; 108 | size_t rawPrivLen = decode_base64(priv, rawPriv); 109 | if (rawPrivLen != 32) { 110 | reject(@"Error", @"Key invalid", nil); 111 | return; 112 | } 113 | secp256k1_pubkey pubkey; 114 | if (!secp256k1_ec_pubkey_create(kSecp256k1Context, &pubkey, rawPriv)) { 115 | resolve(@""); 116 | return; 117 | } 118 | unsigned char rawPub[65]; 119 | size_t rawPubLen = 65; 120 | unsigned int flags = compress ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED; 121 | secp256k1_ec_pubkey_serialize(kSecp256k1Context, rawPub, &rawPubLen, &pubkey, flags); 122 | 123 | resolveBase64(resolve, rawPub, rawPubLen); 124 | }); 125 | } 126 | 127 | RCT_EXPORT_METHOD(privKeyTweakAdd:(NSString *)priv data:(NSString *)data resolve:(RCTPromiseResolveBlock)resolve 128 | rejecter:(RCTPromiseRejectBlock)reject) { 129 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 130 | unsigned char rawData[kMaxBufferLength]; 131 | unsigned char rawPriv[kMaxBufferLength]; 132 | size_t rawDataLen = decode_base64(data, rawData); 133 | size_t rawPrivLen = decode_base64(priv, rawPriv); 134 | if (rawDataLen != 32 || rawPrivLen != 32) { 135 | reject(@"Error", @"Priv or Data invalid", nil); 136 | return; 137 | } 138 | int r = secp256k1_ec_privkey_tweak_add(kSecp256k1Context, rawPriv, rawData); 139 | (void)r; 140 | 141 | resolveBase64(resolve, rawPriv, rawPrivLen); 142 | }); 143 | } 144 | 145 | RCT_EXPORT_METHOD(privKeyTweakMul:(NSString *)priv data:(NSString *)data resolve:(RCTPromiseResolveBlock)resolve 146 | rejecter:(RCTPromiseRejectBlock)reject) { 147 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 148 | unsigned char rawData[kMaxBufferLength]; 149 | unsigned char rawPriv[kMaxBufferLength]; 150 | size_t rawDataLen = decode_base64(data, rawData); 151 | size_t rawPrivLen = decode_base64(priv, rawPriv); 152 | if (rawDataLen != 32 || rawPrivLen != 32) { 153 | reject(@"Error", @"Priv or Data invalid", nil); 154 | return; 155 | } 156 | int r = secp256k1_ec_privkey_tweak_mul(kSecp256k1Context, rawPriv, rawData); 157 | (void)r; 158 | 159 | resolveBase64(resolve, rawPriv, rawPrivLen); 160 | }); 161 | } 162 | 163 | RCT_EXPORT_METHOD(pubKeyTweakMul:(NSString *)pub data:(NSString *)data resolve:(RCTPromiseResolveBlock)resolve 164 | rejecter:(RCTPromiseRejectBlock)reject) { 165 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 166 | unsigned char rawData[kMaxBufferLength]; 167 | unsigned char rawPub[kMaxBufferLength]; 168 | size_t rawDataLen = decode_base64(data, rawData); 169 | size_t rawPubLen = decode_base64(pub, rawPub); 170 | if (rawDataLen != 32 || rawPubLen == 0) { 171 | reject(@"Error", @"Priv or Data invalid", nil); 172 | return; 173 | } 174 | secp256k1_pubkey pubkey; 175 | if (!secp256k1_ec_pubkey_parse(kSecp256k1Context, &pubkey, rawPub, rawPubLen)) { 176 | reject(@"Error", @"pubkey invalid", nil); 177 | return; 178 | } 179 | int r = secp256k1_ec_pubkey_tweak_mul(kSecp256k1Context, &pubkey, rawData); 180 | (void)r; 181 | 182 | unsigned int flags = rawPubLen == 33 ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED; 183 | secp256k1_ec_pubkey_serialize(kSecp256k1Context, rawPub, &rawPubLen, &pubkey, flags); 184 | 185 | resolveBase64(resolve, rawPub, rawPubLen); 186 | }); 187 | } 188 | 189 | RCT_EXPORT_METHOD(pubKeyTweakAdd:(NSString *)pub data:(NSString *)data resolve:(RCTPromiseResolveBlock)resolve 190 | rejecter:(RCTPromiseRejectBlock)reject) { 191 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 192 | unsigned char rawData[kMaxBufferLength]; 193 | unsigned char rawPub[kMaxBufferLength]; 194 | size_t rawDataLen = decode_base64(data, rawData); 195 | size_t rawPubLen = decode_base64(pub, rawPub); 196 | if (rawDataLen != 32 || rawPubLen == 0) { 197 | reject(@"Error", @"Priv or Data invalid", nil); 198 | return; 199 | } 200 | secp256k1_pubkey pubkey; 201 | if (!secp256k1_ec_pubkey_parse(kSecp256k1Context, &pubkey, rawPub, rawPubLen)) { 202 | reject(@"Error", @"pubkey invalid", nil); 203 | return; 204 | } 205 | int r = secp256k1_ec_pubkey_tweak_add(kSecp256k1Context, &pubkey, rawData); 206 | (void)r; 207 | 208 | unsigned int flags = rawPubLen == 33 ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED; 209 | secp256k1_ec_pubkey_serialize(kSecp256k1Context, rawPub, &rawPubLen, &pubkey, flags); 210 | 211 | resolveBase64(resolve, rawPub, rawPubLen); 212 | }); 213 | } 214 | 215 | RCT_EXPORT_METHOD(createECDHSecret:(NSString *)priv priv:(NSString *)pub resolve:(RCTPromiseResolveBlock)resolve 216 | rejecter:(RCTPromiseRejectBlock)reject) { 217 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 218 | unsigned char ecdh[32]; 219 | NSString *err = generateECDH(pub, priv, ecdh); 220 | if (err != nil) { 221 | reject(@"Error", err, nil); 222 | } 223 | resolveBase64(resolve, ecdh, sizeof(ecdh)); 224 | }); 225 | } 226 | 227 | @end 228 | 229 | -------------------------------------------------------------------------------- /ios/RNSecp256k1.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 687D336021D35CF1000D02F6 /* secp256k1.c in Sources */ = {isa = PBXBuildFile; fileRef = 687D335F21D35CF1000D02F6 /* secp256k1.c */; }; 11 | 687D336621D4AB1F000D02F6 /* base64.m in Sources */ = {isa = PBXBuildFile; fileRef = 687D336521D4AB1F000D02F6 /* base64.m */; }; 12 | 687D336921D4AE5A000D02F6 /* RNSecp256k1Ext.m in Sources */ = {isa = PBXBuildFile; fileRef = 687D336821D4AE5A000D02F6 /* RNSecp256k1Ext.m */; }; 13 | B3E7B58A1CC2AC0600A0062D /* RNSecp256k1.m in Sources */ = {isa = PBXBuildFile; fileRef = B3E7B5891CC2AC0600A0062D /* RNSecp256k1.m */; }; 14 | /* End PBXBuildFile section */ 15 | 16 | /* Begin PBXCopyFilesBuildPhase section */ 17 | 58B511D91A9E6C8500147676 /* CopyFiles */ = { 18 | isa = PBXCopyFilesBuildPhase; 19 | buildActionMask = 2147483647; 20 | dstPath = "include/$(PRODUCT_NAME)"; 21 | dstSubfolderSpec = 16; 22 | files = ( 23 | ); 24 | runOnlyForDeploymentPostprocessing = 0; 25 | }; 26 | /* End PBXCopyFilesBuildPhase section */ 27 | 28 | /* Begin PBXFileReference section */ 29 | 134814201AA4EA6300B7C361 /* libRNSecp256k1.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNSecp256k1.a; sourceTree = BUILT_PRODUCTS_DIR; }; 30 | 687D335F21D35CF1000D02F6 /* secp256k1.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = secp256k1.c; path = ../external/secp256k1/src/secp256k1.c; sourceTree = ""; }; 31 | 687D336421D4AB1F000D02F6 /* base64.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = base64.h; sourceTree = ""; }; 32 | 687D336521D4AB1F000D02F6 /* base64.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = base64.m; sourceTree = ""; }; 33 | 687D336721D4AE5A000D02F6 /* RNSecp256k1Ext.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNSecp256k1Ext.h; sourceTree = ""; }; 34 | 687D336821D4AE5A000D02F6 /* RNSecp256k1Ext.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNSecp256k1Ext.m; sourceTree = ""; }; 35 | B3E7B5881CC2AC0600A0062D /* RNSecp256k1.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSecp256k1.h; sourceTree = ""; }; 36 | B3E7B5891CC2AC0600A0062D /* RNSecp256k1.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSecp256k1.m; sourceTree = ""; }; 37 | /* End PBXFileReference section */ 38 | 39 | /* Begin PBXFrameworksBuildPhase section */ 40 | 58B511D81A9E6C8500147676 /* Frameworks */ = { 41 | isa = PBXFrameworksBuildPhase; 42 | buildActionMask = 2147483647; 43 | files = ( 44 | ); 45 | runOnlyForDeploymentPostprocessing = 0; 46 | }; 47 | /* End PBXFrameworksBuildPhase section */ 48 | 49 | /* Begin PBXGroup section */ 50 | 134814211AA4EA7D00B7C361 /* Products */ = { 51 | isa = PBXGroup; 52 | children = ( 53 | 134814201AA4EA6300B7C361 /* libRNSecp256k1.a */, 54 | ); 55 | name = Products; 56 | sourceTree = ""; 57 | }; 58 | 58B511D21A9E6C8500147676 = { 59 | isa = PBXGroup; 60 | children = ( 61 | 687D336721D4AE5A000D02F6 /* RNSecp256k1Ext.h */, 62 | 687D336821D4AE5A000D02F6 /* RNSecp256k1Ext.m */, 63 | 687D336421D4AB1F000D02F6 /* base64.h */, 64 | 687D336521D4AB1F000D02F6 /* base64.m */, 65 | 687D335F21D35CF1000D02F6 /* secp256k1.c */, 66 | B3E7B5881CC2AC0600A0062D /* RNSecp256k1.h */, 67 | B3E7B5891CC2AC0600A0062D /* RNSecp256k1.m */, 68 | 134814211AA4EA7D00B7C361 /* Products */, 69 | ); 70 | sourceTree = ""; 71 | }; 72 | /* End PBXGroup section */ 73 | 74 | /* Begin PBXNativeTarget section */ 75 | 58B511DA1A9E6C8500147676 /* RNSecp256k1 */ = { 76 | isa = PBXNativeTarget; 77 | buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNSecp256k1" */; 78 | buildPhases = ( 79 | 58B511D71A9E6C8500147676 /* Sources */, 80 | 58B511D81A9E6C8500147676 /* Frameworks */, 81 | 58B511D91A9E6C8500147676 /* CopyFiles */, 82 | ); 83 | buildRules = ( 84 | ); 85 | dependencies = ( 86 | ); 87 | name = RNSecp256k1; 88 | productName = RCTDataManager; 89 | productReference = 134814201AA4EA6300B7C361 /* libRNSecp256k1.a */; 90 | productType = "com.apple.product-type.library.static"; 91 | }; 92 | /* End PBXNativeTarget section */ 93 | 94 | /* Begin PBXProject section */ 95 | 58B511D31A9E6C8500147676 /* Project object */ = { 96 | isa = PBXProject; 97 | attributes = { 98 | LastUpgradeCheck = 0830; 99 | ORGANIZATIONNAME = Facebook; 100 | TargetAttributes = { 101 | 58B511DA1A9E6C8500147676 = { 102 | CreatedOnToolsVersion = 6.1.1; 103 | }; 104 | }; 105 | }; 106 | buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNSecp256k1" */; 107 | compatibilityVersion = "Xcode 3.2"; 108 | developmentRegion = English; 109 | hasScannedForEncodings = 0; 110 | knownRegions = ( 111 | en, 112 | ); 113 | mainGroup = 58B511D21A9E6C8500147676; 114 | productRefGroup = 58B511D21A9E6C8500147676; 115 | projectDirPath = ""; 116 | projectRoot = ""; 117 | targets = ( 118 | 58B511DA1A9E6C8500147676 /* RNSecp256k1 */, 119 | ); 120 | }; 121 | /* End PBXProject section */ 122 | 123 | /* Begin PBXSourcesBuildPhase section */ 124 | 58B511D71A9E6C8500147676 /* Sources */ = { 125 | isa = PBXSourcesBuildPhase; 126 | buildActionMask = 2147483647; 127 | files = ( 128 | 687D336021D35CF1000D02F6 /* secp256k1.c in Sources */, 129 | 687D336621D4AB1F000D02F6 /* base64.m in Sources */, 130 | 687D336921D4AE5A000D02F6 /* RNSecp256k1Ext.m in Sources */, 131 | B3E7B58A1CC2AC0600A0062D /* RNSecp256k1.m in Sources */, 132 | ); 133 | runOnlyForDeploymentPostprocessing = 0; 134 | }; 135 | /* End PBXSourcesBuildPhase section */ 136 | 137 | /* Begin XCBuildConfiguration section */ 138 | 58B511ED1A9E6C8500147676 /* Debug */ = { 139 | isa = XCBuildConfiguration; 140 | buildSettings = { 141 | ALWAYS_SEARCH_USER_PATHS = NO; 142 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 143 | CLANG_CXX_LIBRARY = "libc++"; 144 | CLANG_ENABLE_MODULES = YES; 145 | CLANG_ENABLE_OBJC_ARC = YES; 146 | CLANG_WARN_BOOL_CONVERSION = YES; 147 | CLANG_WARN_CONSTANT_CONVERSION = YES; 148 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 149 | CLANG_WARN_EMPTY_BODY = YES; 150 | CLANG_WARN_ENUM_CONVERSION = YES; 151 | CLANG_WARN_INFINITE_RECURSION = YES; 152 | CLANG_WARN_INT_CONVERSION = YES; 153 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 154 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 155 | CLANG_WARN_UNREACHABLE_CODE = YES; 156 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 157 | COPY_PHASE_STRIP = NO; 158 | ENABLE_STRICT_OBJC_MSGSEND = YES; 159 | ENABLE_TESTABILITY = YES; 160 | GCC_C_LANGUAGE_STANDARD = gnu99; 161 | GCC_DYNAMIC_NO_PIC = NO; 162 | GCC_NO_COMMON_BLOCKS = YES; 163 | GCC_OPTIMIZATION_LEVEL = 0; 164 | GCC_PREPROCESSOR_DEFINITIONS = ( 165 | "DEBUG=1", 166 | "$(inherited)", 167 | ); 168 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 169 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 170 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 171 | GCC_WARN_UNDECLARED_SELECTOR = YES; 172 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 173 | GCC_WARN_UNUSED_FUNCTION = YES; 174 | GCC_WARN_UNUSED_VARIABLE = YES; 175 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 176 | MTL_ENABLE_DEBUG_INFO = YES; 177 | ONLY_ACTIVE_ARCH = YES; 178 | SDKROOT = iphoneos; 179 | }; 180 | name = Debug; 181 | }; 182 | 58B511EE1A9E6C8500147676 /* Release */ = { 183 | isa = XCBuildConfiguration; 184 | buildSettings = { 185 | ALWAYS_SEARCH_USER_PATHS = NO; 186 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 187 | CLANG_CXX_LIBRARY = "libc++"; 188 | CLANG_ENABLE_MODULES = YES; 189 | CLANG_ENABLE_OBJC_ARC = YES; 190 | CLANG_WARN_BOOL_CONVERSION = YES; 191 | CLANG_WARN_CONSTANT_CONVERSION = YES; 192 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 193 | CLANG_WARN_EMPTY_BODY = YES; 194 | CLANG_WARN_ENUM_CONVERSION = YES; 195 | CLANG_WARN_INFINITE_RECURSION = YES; 196 | CLANG_WARN_INT_CONVERSION = YES; 197 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 198 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 199 | CLANG_WARN_UNREACHABLE_CODE = YES; 200 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 201 | COPY_PHASE_STRIP = YES; 202 | ENABLE_NS_ASSERTIONS = NO; 203 | ENABLE_STRICT_OBJC_MSGSEND = YES; 204 | GCC_C_LANGUAGE_STANDARD = gnu99; 205 | GCC_NO_COMMON_BLOCKS = YES; 206 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 207 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 208 | GCC_WARN_UNDECLARED_SELECTOR = YES; 209 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 210 | GCC_WARN_UNUSED_FUNCTION = YES; 211 | GCC_WARN_UNUSED_VARIABLE = YES; 212 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 213 | MTL_ENABLE_DEBUG_INFO = NO; 214 | SDKROOT = iphoneos; 215 | VALIDATE_PRODUCT = YES; 216 | }; 217 | name = Release; 218 | }; 219 | 58B511F01A9E6C8500147676 /* Debug */ = { 220 | isa = XCBuildConfiguration; 221 | buildSettings = { 222 | HEADER_SEARCH_PATHS = ( 223 | "$(inherited)", 224 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 225 | "$(SRCROOT)/../../../React/**", 226 | "$(SRCROOT)/../../react-native/React/**", 227 | "$(SRCROOT)/../external/secp256k1/**", 228 | "$(SRCROOT)/../external/secp256k1/src/**", 229 | ); 230 | LIBRARY_SEARCH_PATHS = "$(inherited)"; 231 | OTHER_CFLAGS = ( 232 | "-DUSE_NUM_NONE=1\n\n-DUSE_FIELD_INV_BUILTIN=1\n\n-DUSE_SCALAR_INV_BUILTIN=1\n\n-DUSE_FIELD_10X26=1\n\n-DUSE_SCALAR_8X32=1\n\n-DENABLE_MODULE_ECDH=1\n\n-DENABLE_MODULE_RECOVERY=1", 233 | "-DUSE_FIELD_INV_BUILTIN=1\n\n-DUSE_SCALAR_INV_BUILTIN=1\n\n-DUSE_FIELD_10X26=1\n\n-DUSE_SCALAR_8X32=1\n\n-DENABLE_MODULE_ECDH=1\n\n-DENABLE_MODULE_RECOVERY=1\n\n-DUSE_FIELD_INV_BUILTIN=1\n\n-DUSE_SCALAR_INV_BUILTIN=1\n\n-DUSE_FIELD_10X26=1\n\n-DUSE_SCALAR_8X32=1\n\n-DENABLE_MODULE_ECDH=1\n\n-DENABLE_MODULE_RECOVERY=1", 234 | "-DUSE_SCALAR_INV_BUILTIN=1", 235 | "-DUSE_FIELD_10X26=1", 236 | "-DUSE_SCALAR_8X32=1", 237 | "-DENABLE_MODULE_ECDH=1", 238 | "-DENABLE_MODULE_RECOVERY=1", 239 | ); 240 | OTHER_LDFLAGS = "-ObjC"; 241 | PRODUCT_NAME = RNSecp256k1; 242 | SKIP_INSTALL = YES; 243 | }; 244 | name = Debug; 245 | }; 246 | 58B511F11A9E6C8500147676 /* Release */ = { 247 | isa = XCBuildConfiguration; 248 | buildSettings = { 249 | HEADER_SEARCH_PATHS = ( 250 | "$(inherited)", 251 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 252 | "$(SRCROOT)/../../../React/**", 253 | "$(SRCROOT)/../../react-native/React/**", 254 | "$(SRCROOT)/../external/secp256k1/**", 255 | "$(SRCROOT)/../external/secp256k1/src/**", 256 | ); 257 | LIBRARY_SEARCH_PATHS = "$(inherited)"; 258 | OTHER_CFLAGS = ( 259 | "-DUSE_NUM_NONE=1\n\n-DUSE_FIELD_INV_BUILTIN=1\n\n-DUSE_SCALAR_INV_BUILTIN=1\n\n-DUSE_FIELD_10X26=1\n\n-DUSE_SCALAR_8X32=1\n\n-DENABLE_MODULE_ECDH=1\n\n-DENABLE_MODULE_RECOVERY=1", 260 | "-DUSE_FIELD_INV_BUILTIN=1\n\n-DUSE_SCALAR_INV_BUILTIN=1\n\n-DUSE_FIELD_10X26=1\n\n-DUSE_SCALAR_8X32=1\n\n-DENABLE_MODULE_ECDH=1\n\n-DENABLE_MODULE_RECOVERY=1\n\n-DUSE_FIELD_INV_BUILTIN=1\n\n-DUSE_SCALAR_INV_BUILTIN=1\n\n-DUSE_FIELD_10X26=1\n\n-DUSE_SCALAR_8X32=1\n\n-DENABLE_MODULE_ECDH=1\n\n-DENABLE_MODULE_RECOVERY=1", 261 | "-DUSE_SCALAR_INV_BUILTIN=1", 262 | "-DUSE_FIELD_10X26=1", 263 | "-DUSE_SCALAR_8X32=1", 264 | "-DENABLE_MODULE_ECDH=1", 265 | "-DENABLE_MODULE_RECOVERY=1", 266 | ); 267 | OTHER_LDFLAGS = "-ObjC"; 268 | PRODUCT_NAME = RNSecp256k1; 269 | SKIP_INSTALL = YES; 270 | }; 271 | name = Release; 272 | }; 273 | /* End XCBuildConfiguration section */ 274 | 275 | /* Begin XCConfigurationList section */ 276 | 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNSecp256k1" */ = { 277 | isa = XCConfigurationList; 278 | buildConfigurations = ( 279 | 58B511ED1A9E6C8500147676 /* Debug */, 280 | 58B511EE1A9E6C8500147676 /* Release */, 281 | ); 282 | defaultConfigurationIsVisible = 0; 283 | defaultConfigurationName = Release; 284 | }; 285 | 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNSecp256k1" */ = { 286 | isa = XCConfigurationList; 287 | buildConfigurations = ( 288 | 58B511F01A9E6C8500147676 /* Debug */, 289 | 58B511F11A9E6C8500147676 /* Release */, 290 | ); 291 | defaultConfigurationIsVisible = 0; 292 | defaultConfigurationName = Release; 293 | }; 294 | /* End XCConfigurationList section */ 295 | }; 296 | rootObject = 58B511D31A9E6C8500147676 /* Project object */; 297 | } 298 | --------------------------------------------------------------------------------