├── .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 |
--------------------------------------------------------------------------------