@functionland/react-native-fula 1.2.1 → 1.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/.gradle/7.5.1/checksums/checksums.lock +0 -0
- package/android/.gradle/7.5.1/fileHashes/fileHashes.bin +0 -0
- package/android/.gradle/7.5.1/fileHashes/fileHashes.lock +0 -0
- package/android/.gradle/7.5.1/fileHashes/resourceHashesCache.bin +0 -0
- package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
- package/android/.gradle/buildOutputCleanup/outputFiles.bin +0 -0
- package/android/src/main/java/land/fx/fula/Cryptography.java +60 -47
- package/android/src/main/java/land/fx/fula/FulaModule.java +78 -45
- package/package.json +2 -2
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,47 +1,60 @@
|
|
|
1
|
-
package land.fx.fula;
|
|
2
|
-
|
|
3
|
-
import android.util.Base64;
|
|
4
|
-
|
|
5
|
-
import java.io.UnsupportedEncodingException;
|
|
6
|
-
import java.
|
|
7
|
-
import java.security.
|
|
8
|
-
import java.security.
|
|
9
|
-
import java.security.
|
|
10
|
-
import java.security.spec.
|
|
11
|
-
|
|
12
|
-
import
|
|
13
|
-
import
|
|
14
|
-
|
|
15
|
-
import javax.crypto.
|
|
16
|
-
import javax.crypto.
|
|
17
|
-
import javax.crypto.
|
|
18
|
-
import javax.crypto.
|
|
19
|
-
import javax.crypto.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
1
|
+
package land.fx.fula;
|
|
2
|
+
|
|
3
|
+
import android.util.Base64;
|
|
4
|
+
|
|
5
|
+
import java.io.UnsupportedEncodingException;
|
|
6
|
+
import java.nio.charset.StandardCharsets;
|
|
7
|
+
import java.security.InvalidAlgorithmParameterException;
|
|
8
|
+
import java.security.InvalidKeyException;
|
|
9
|
+
import java.security.NoSuchAlgorithmException;
|
|
10
|
+
import java.security.spec.InvalidKeySpecException;
|
|
11
|
+
import java.security.SecureRandom;
|
|
12
|
+
import java.nio.ByteBuffer;
|
|
13
|
+
import java.security.spec.InvalidParameterSpecException;
|
|
14
|
+
|
|
15
|
+
import javax.crypto.BadPaddingException;
|
|
16
|
+
import javax.crypto.Cipher;
|
|
17
|
+
import javax.crypto.IllegalBlockSizeException;
|
|
18
|
+
import javax.crypto.NoSuchPaddingException;
|
|
19
|
+
import javax.crypto.SecretKey;
|
|
20
|
+
import javax.crypto.SecretKeyFactory;
|
|
21
|
+
import javax.crypto.spec.PBEKeySpec;
|
|
22
|
+
import javax.crypto.spec.SecretKeySpec;
|
|
23
|
+
import javax.crypto.spec.GCMParameterSpec;
|
|
24
|
+
|
|
25
|
+
public class Cryptography {
|
|
26
|
+
public static String encryptMsg(String message, SecretKey secret)
|
|
27
|
+
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException {
|
|
28
|
+
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
|
|
29
|
+
byte[] iv = new byte[12]; // Ensure this is randomly generated for each encryption.
|
|
30
|
+
new SecureRandom().nextBytes(iv);
|
|
31
|
+
GCMParameterSpec spec = new GCMParameterSpec(128, iv);
|
|
32
|
+
cipher.init(Cipher.ENCRYPT_MODE, secret, spec);
|
|
33
|
+
byte[] cipherText = cipher.doFinal(message.getBytes(StandardCharsets.UTF_8));
|
|
34
|
+
ByteBuffer byteBuffer = ByteBuffer.allocate(iv.length + cipherText.length);
|
|
35
|
+
byteBuffer.put(iv);
|
|
36
|
+
byteBuffer.put(cipherText);
|
|
37
|
+
return Base64.encodeToString(byteBuffer.array(), Base64.NO_WRAP);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
public static String decryptMsg(String cipherText, SecretKey secret)
|
|
41
|
+
throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
|
|
42
|
+
ByteBuffer byteBuffer = ByteBuffer.wrap(Base64.decode(cipherText, Base64.NO_WRAP));
|
|
43
|
+
byte[] iv = new byte[12];
|
|
44
|
+
byteBuffer.get(iv);
|
|
45
|
+
byte[] cipherBytes = new byte[byteBuffer.remaining()];
|
|
46
|
+
byteBuffer.get(cipherBytes);
|
|
47
|
+
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
|
|
48
|
+
GCMParameterSpec spec = new GCMParameterSpec(128, iv);
|
|
49
|
+
cipher.init(Cipher.DECRYPT_MODE, secret, spec);
|
|
50
|
+
String decryptString = new String(cipher.doFinal(cipherBytes), StandardCharsets.UTF_8);
|
|
51
|
+
return decryptString;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
public static SecretKey generateKey(byte[] key)
|
|
55
|
+
throws NoSuchAlgorithmException, InvalidKeySpecException {
|
|
56
|
+
PBEKeySpec pbeKeySpec = new PBEKeySpec(StaticHelper.bytesToBase64(key).toCharArray(), key, 1000, 128);
|
|
57
|
+
SecretKey pbeKey = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256").generateSecret(pbeKeySpec);
|
|
58
|
+
return new SecretKeySpec(pbeKey.getEncoded(), "AES");
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -16,15 +16,25 @@ import org.apache.commons.io.FileUtils;
|
|
|
16
16
|
import org.jetbrains.annotations.Contract;
|
|
17
17
|
|
|
18
18
|
import java.io.File;
|
|
19
|
+
import java.io.IOException;
|
|
19
20
|
import java.nio.charset.StandardCharsets;
|
|
21
|
+
import java.security.GeneralSecurityException;
|
|
22
|
+
import java.security.InvalidAlgorithmParameterException;
|
|
23
|
+
import java.security.InvalidKeyException;
|
|
24
|
+
import java.security.NoSuchAlgorithmException;
|
|
20
25
|
import java.util.Arrays;
|
|
21
26
|
|
|
27
|
+
import javax.crypto.BadPaddingException;
|
|
28
|
+
import javax.crypto.IllegalBlockSizeException;
|
|
29
|
+
import javax.crypto.NoSuchPaddingException;
|
|
22
30
|
import javax.crypto.SecretKey;
|
|
23
31
|
|
|
24
32
|
import java.util.concurrent.Executors;
|
|
25
33
|
import java.util.concurrent.ScheduledExecutorService;
|
|
26
34
|
import java.util.concurrent.TimeUnit;
|
|
27
35
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
36
|
+
import java.util.concurrent.Future;
|
|
37
|
+
import java.util.concurrent.TimeoutException;
|
|
28
38
|
|
|
29
39
|
import org.json.JSONObject;
|
|
30
40
|
import org.json.JSONArray;
|
|
@@ -263,42 +273,50 @@ public class FulaModule extends ReactContextBaseJavaModule {
|
|
|
263
273
|
|
|
264
274
|
private boolean checkConnectionInternal(int timeout) throws Exception {
|
|
265
275
|
try {
|
|
266
|
-
|
|
267
|
-
|
|
276
|
+
Log.d("ReactNative", "checkConnectionInternal started");
|
|
277
|
+
if (this.fula != null) {
|
|
278
|
+
try {
|
|
279
|
+
Log.d("ReactNative", "connectToBlox started");
|
|
280
|
+
|
|
281
|
+
AtomicBoolean connectionStatus = new AtomicBoolean(false);
|
|
282
|
+
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
|
|
283
|
+
Future<?> future = executor.submit(() -> {
|
|
268
284
|
try {
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
|
|
273
|
-
executor.schedule(() -> {
|
|
274
|
-
try {
|
|
275
|
-
this.fula.connectToBlox();
|
|
276
|
-
connectionStatus.set(true);
|
|
277
|
-
Log.d("ReactNative", "checkConnectionInternal succeeded ");
|
|
278
|
-
} catch (Exception e) {
|
|
279
|
-
Log.d("ReactNative", "checkConnectionInternal failed with Error: " + e.getMessage());
|
|
280
|
-
}
|
|
281
|
-
}, 0, TimeUnit.SECONDS);
|
|
282
|
-
|
|
283
|
-
executor.schedule(() -> {
|
|
284
|
-
executor.shutdown();
|
|
285
|
-
}, timeout, TimeUnit.SECONDS);
|
|
286
|
-
|
|
287
|
-
executor.awaitTermination(timeout, TimeUnit.SECONDS);
|
|
288
|
-
return connectionStatus.get();
|
|
285
|
+
this.fula.connectToBlox();
|
|
286
|
+
connectionStatus.set(true);
|
|
287
|
+
Log.d("ReactNative", "checkConnectionInternal succeeded ");
|
|
289
288
|
} catch (Exception e) {
|
|
290
|
-
|
|
291
|
-
return false;
|
|
289
|
+
Log.d("ReactNative", "checkConnectionInternal failed with Error: " + e.getMessage());
|
|
292
290
|
}
|
|
293
|
-
|
|
294
|
-
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
try {
|
|
294
|
+
future.get(timeout, TimeUnit.SECONDS);
|
|
295
|
+
} catch (TimeoutException te) {
|
|
296
|
+
// If the timeout occurs, shut down the executor and return false
|
|
297
|
+
executor.shutdownNow();
|
|
295
298
|
return false;
|
|
299
|
+
} finally {
|
|
300
|
+
// If the future task is done, we can shut down the executor
|
|
301
|
+
if (future.isDone()) {
|
|
302
|
+
executor.shutdown();
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
return connectionStatus.get();
|
|
307
|
+
} catch (Exception e) {
|
|
308
|
+
Log.d("ReactNative", "checkConnectionInternal failed with Error: " + e.getMessage());
|
|
309
|
+
return false;
|
|
296
310
|
}
|
|
311
|
+
} else {
|
|
312
|
+
Log.d("ReactNative", "checkConnectionInternal failed because fula is not initialized ");
|
|
313
|
+
return false;
|
|
314
|
+
}
|
|
297
315
|
} catch (Exception e) {
|
|
298
|
-
|
|
299
|
-
|
|
316
|
+
Log.d("ReactNative", "checkConnectionInternal failed with Error: " + e.getMessage());
|
|
317
|
+
throw (e);
|
|
300
318
|
}
|
|
301
|
-
}
|
|
319
|
+
}
|
|
302
320
|
|
|
303
321
|
@ReactMethod
|
|
304
322
|
private void checkFailedActions(boolean retry, int timeout, Promise promise) throws Exception {
|
|
@@ -404,24 +422,32 @@ public class FulaModule extends ReactContextBaseJavaModule {
|
|
|
404
422
|
}
|
|
405
423
|
|
|
406
424
|
@NonNull
|
|
407
|
-
private byte[] createPeerIdentity(byte[] privateKey) throws
|
|
425
|
+
private byte[] createPeerIdentity(byte[] privateKey) throws GeneralSecurityException, IOException {
|
|
408
426
|
try {
|
|
409
427
|
// 1: First: create public key from provided private key
|
|
410
428
|
// 2: Should read the local keychain store (if it is key-value, key is public key above,
|
|
411
429
|
// 3: if found, decrypt using the private key
|
|
412
430
|
// 4: If not found or decryption not successful, generate an identity
|
|
413
431
|
// 5: then encrypt and store in keychain
|
|
414
|
-
|
|
415
432
|
String encryptedKey = sharedPref.getValue(PRIVATE_KEY_STORE_ID);
|
|
416
433
|
SecretKey secretKey = Cryptography.generateKey(privateKey);
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
434
|
+
|
|
435
|
+
if (encryptedKey == null || !encryptedKey.startsWith("FULA_ENC_V2:")) {
|
|
436
|
+
byte[] autoGeneratedIdentity;
|
|
437
|
+
try {
|
|
438
|
+
autoGeneratedIdentity = Fulamobile.generateEd25519Key();
|
|
439
|
+
} catch (Exception e) {
|
|
440
|
+
Log.d("ReactNative", "Failed to generate key: " + e.getMessage());
|
|
441
|
+
throw new GeneralSecurityException("Failed to generate key", e);
|
|
442
|
+
}
|
|
443
|
+
encryptedKey = "FULA_ENC_V2:" + Cryptography.encryptMsg(StaticHelper.bytesToBase64(autoGeneratedIdentity), secretKey);
|
|
420
444
|
sharedPref.add(PRIVATE_KEY_STORE_ID, encryptedKey);
|
|
421
445
|
}
|
|
422
|
-
return StaticHelper.base64ToBytes(Cryptography.decryptMsg(encryptedKey, secretKey));
|
|
423
446
|
|
|
424
|
-
|
|
447
|
+
String decryptedKey = Cryptography.decryptMsg(encryptedKey.replace("FULA_ENC_V2:", ""), secretKey);
|
|
448
|
+
return StaticHelper.base64ToBytes(decryptedKey);
|
|
449
|
+
|
|
450
|
+
} catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException | BadPaddingException | InvalidAlgorithmParameterException e) {
|
|
425
451
|
Log.d("ReactNative", "createPeerIdentity failed with Error: " + e.getMessage());
|
|
426
452
|
throw (e);
|
|
427
453
|
}
|
|
@@ -502,7 +528,8 @@ public class FulaModule extends ReactContextBaseJavaModule {
|
|
|
502
528
|
}
|
|
503
529
|
|
|
504
530
|
@NonNull
|
|
505
|
-
private byte[] newClientInternal(byte[] identity, String storePath, String bloxAddr, String exchange, boolean autoFlush, boolean useRelay, boolean refresh) throws
|
|
531
|
+
private byte[] newClientInternal(byte[] identity, String storePath, String bloxAddr, String exchange, boolean autoFlush, boolean useRelay, boolean refresh) throws GeneralSecurityException, IOException {
|
|
532
|
+
byte[] peerIdentity = null;
|
|
506
533
|
try {
|
|
507
534
|
fulaConfig = new Config();
|
|
508
535
|
if (storePath == null || storePath.trim().isEmpty()) {
|
|
@@ -512,7 +539,7 @@ public class FulaModule extends ReactContextBaseJavaModule {
|
|
|
512
539
|
}
|
|
513
540
|
Log.d("ReactNative", "newClientInternal storePath is set: " + fulaConfig.getStorePath());
|
|
514
541
|
|
|
515
|
-
|
|
542
|
+
peerIdentity = this.createPeerIdentity(identity);
|
|
516
543
|
fulaConfig.setIdentity(peerIdentity);
|
|
517
544
|
Log.d("ReactNative", "peerIdentity is set: " + toString(fulaConfig.getIdentity()));
|
|
518
545
|
fulaConfig.setBloxAddr(bloxAddr);
|
|
@@ -525,19 +552,25 @@ public class FulaModule extends ReactContextBaseJavaModule {
|
|
|
525
552
|
}
|
|
526
553
|
if (this.fula == null || refresh) {
|
|
527
554
|
Log.d("ReactNative", "Creating a new Fula instance");
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
555
|
+
try {
|
|
556
|
+
shutdownInternal();
|
|
557
|
+
this.fula = Fulamobile.newClient(fulaConfig);
|
|
558
|
+
if (this.fula != null) {
|
|
559
|
+
this.fula.flush();
|
|
560
|
+
}
|
|
561
|
+
} catch (Exception e) {
|
|
562
|
+
Log.d("ReactNative", "Failed to create new Fula instance: " + e.getMessage());
|
|
563
|
+
throw new IOException("Failed to create new Fula instance", e);
|
|
564
|
+
}
|
|
533
565
|
}
|
|
534
|
-
|
|
535
|
-
} catch (Exception e) {
|
|
566
|
+
} catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException | BadPaddingException | InvalidAlgorithmParameterException e) {
|
|
536
567
|
Log.d("ReactNative", "newclientInternal failed with Error: " + e.getMessage());
|
|
537
568
|
throw (e);
|
|
538
569
|
}
|
|
570
|
+
return peerIdentity;
|
|
539
571
|
}
|
|
540
572
|
|
|
573
|
+
|
|
541
574
|
@NonNull
|
|
542
575
|
private String[] initInternal(byte[] identity, String storePath, String bloxAddr, String exchange, boolean autoFlush, String rootCid, boolean useRelay, boolean refresh) throws Exception {
|
|
543
576
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@functionland/react-native-fula",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.3",
|
|
4
4
|
"description": "This package is a bridge to use the Fula libp2p protocols in the react-native which is using wnfs",
|
|
5
5
|
"main": "lib/commonjs/index",
|
|
6
6
|
"module": "lib/module/index",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"lint": "eslint \"**/*.{js,ts,tsx}\"",
|
|
29
29
|
"prepare": "bob build",
|
|
30
30
|
"release": "release-it",
|
|
31
|
-
"example": "set NODE_OPTIONS
|
|
31
|
+
"example": "set NODE_OPTIONS='' && yarn --cwd example",
|
|
32
32
|
"pods": "cd example && pod-install --quiet",
|
|
33
33
|
"bootstrap": "yarn example && yarn && yarn pods"
|
|
34
34
|
},
|