diff --git a/android/build.gradle b/android/build.gradle index a32f571..eee09c7 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -63,7 +63,7 @@ dependencies { //noinspection GradleDynamicVersion implementation "com.facebook.react:react-native:+" // From node_modules implementation 'com.github.functionland:fula-build-aar:1.13.0' // From jitpack.io - implementation 'com.github.functionland:wnfs-build-aar:v1.4.1' // From jitpack.io + implementation 'com.github.functionland:wnfs-android:v1.7.3' // From jitpack.io implementation 'commons-io:commons-io:20030203.000550' implementation 'commons-codec:commons-codec:1.15' // implementation files('mobile.aar') diff --git a/android/src/main/java/land/fx/fula/Cryptography.java b/android/src/main/java/land/fx/fula/Cryptography.java index 60d9481..022bfb7 100644 --- a/android/src/main/java/land/fx/fula/Cryptography.java +++ b/android/src/main/java/land/fx/fula/Cryptography.java @@ -23,11 +23,13 @@ import javax.crypto.spec.GCMParameterSpec; public class Cryptography { - public static String encryptMsg(String message, SecretKey secret) + public static String encryptMsg(String message, SecretKey secret, byte[] iv) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException { Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); - byte[] iv = new byte[12]; // Ensure this is randomly generated for each encryption. - new SecureRandom().nextBytes(iv); + if (iv == null || iv.length == 0) { + iv = new byte[12]; // Ensure this is randomly generated for each encryption. + new SecureRandom().nextBytes(iv); + } GCMParameterSpec spec = new GCMParameterSpec(128, iv); cipher.init(Cipher.ENCRYPT_MODE, secret, spec); byte[] cipherText = cipher.doFinal(message.getBytes(StandardCharsets.UTF_8)); diff --git a/android/src/main/java/land/fx/fula/FulaModule.java b/android/src/main/java/land/fx/fula/FulaModule.java index 6722f39..c0a0da6 100755 --- a/android/src/main/java/land/fx/fula/FulaModule.java +++ b/android/src/main/java/land/fx/fula/FulaModule.java @@ -64,7 +64,6 @@ public void initialize() { Config fulaConfig; String appDir; String fulaStorePath; - String privateForest; land.fx.wnfslib.Config rootConfig; SharedPreferenceHelper sharedPref; SecretKey secretKeyGlobal; @@ -94,10 +93,12 @@ public byte[] get(@NonNull byte[] cid) { @NonNull @Override - public byte[] put(@NonNull byte[] data, long codec) { + public byte[] put(@NonNull byte[] cid, byte[] data) { try { + long codec = (long)cid[1] & 0xFF; + byte[] put_cid = this.internalClient.put(data, codec); //Log.d("ReactNative", "data="+ Arrays.toString(data) +" ;codec="+codec); - return this.internalClient.put(data, codec); + return put_cid; } catch (Exception e) { Log.d("ReactNative", "put Error="+e.getMessage()); e.printStackTrace(); @@ -254,10 +255,9 @@ public void init(String identityString, String storePath, String bloxAddr, Strin byte[] identity = toByte(identityString); Log.d("ReactNative", "init identity= " + identityString); String[] obj = this.initInternal(identity, storePath, bloxAddr, exchange, autoFlush, rootConfig, useRelay, refresh); - Log.d("ReactNative", "init object created: [ " + obj[0] + ", " + obj[1] + ", " + obj[2] + " ]"); + Log.d("ReactNative", "init object created: [ " + obj[0] + ", " + obj[1] + " ]"); resultData.putString("peerId", obj[0]); resultData.putString("rootCid", obj[1]); - resultData.putString("private_ref", obj[2]); promise.resolve(resultData); } catch (Exception e) { Log.d("ReactNative", "init failed with Error: " + e.getMessage()); @@ -512,7 +512,7 @@ private byte[] createPeerIdentity(byte[] identity) throws GeneralSecurityExcepti Log.d("ReactNative", "Failed to generate libp2pId: " + e.getMessage()); throw new GeneralSecurityException("Failed to generate libp2pId", e); } - encryptedLibp2pId = "FULA_ENC_V3:" + Cryptography.encryptMsg(StaticHelper.bytesToBase64(libp2pId), encryptionSecretKey); + encryptedLibp2pId = "FULA_ENC_V3:" + Cryptography.encryptMsg(StaticHelper.bytesToBase64(libp2pId), encryptionSecretKey, null); sharedPref.add(PRIVATE_KEY_STORE_PEERID, encryptedLibp2pId); } else { Log.d("ReactNative", "encryptedLibp2pId is correct. decrypting " + encryptedLibp2pId); @@ -534,34 +534,31 @@ private byte[] createPeerIdentity(byte[] identity) throws GeneralSecurityExcepti } private void createNewRootConfig(FulaModule.Client iClient, byte[] identity) throws Exception { - this.privateForest = Fs.createPrivateForest(iClient); - Log.d("ReactNative", "privateForest is created: " + this.privateForest); - this.rootConfig = Fs.createRootDir(iClient, this.privateForest, identity); + this.rootConfig = Fs.init(iClient, identity); + Log.d("ReactNative", "rootConfig is created " + this.rootConfig.getCid()); if (this.fula != null) { this.fula.flush(); } - Log.d("ReactNative", "new rootConfig is created: cid=" + this.rootConfig.getCid()+" & private_ref="+this.rootConfig.getPrivate_ref()); - this.encrypt_and_store_config(); } - private String getPrivateRef(FulaModule.Client iClient, byte[] wnfsKey, String rootCid) throws Exception { - Log.d("ReactNative", "getPrivateRef called: rootCid=" + rootCid); - String privateRef = Fs.getPrivateRef(iClient, wnfsKey, rootCid); - Log.d("ReactNative", "getPrivateRef completed: privateRef=" + privateRef); - return privateRef; + private void reloadFS(FulaModule.Client iClient, byte[] wnfsKey, String rootCid) throws Exception { + Log.d("ReactNative", "reloadFS called: rootCid=" + rootCid); + Fs.loadWithWNFSKey(iClient, wnfsKey, rootCid); + Log.d("ReactNative", "reloadFS completed"); } private boolean encrypt_and_store_config() throws Exception { try { if(this.identityEncryptedGlobal != null && !this.identityEncryptedGlobal.isEmpty()) { - String cid_encrypted = Cryptography.encryptMsg(this.rootConfig.getCid(), this.secretKeyGlobal); - String private_ref_encrypted = Cryptography.encryptMsg(this.rootConfig.getPrivate_ref(), this.secretKeyGlobal); + Log.d("ReactNative", "encrypt_and_store_config started"); + + String cid_encrypted = Cryptography.encryptMsg(this.rootConfig.getCid(), this.secretKeyGlobal, null); sharedPref.add("FULA_ENC_V3:cid_encrypted_" + this.identityEncryptedGlobal, cid_encrypted); - sharedPref.add("FULA_ENC_V3:private_ref_encrypted_" + this.identityEncryptedGlobal, private_ref_encrypted); return true; } else { + Log.d("ReactNative", "encrypt_and_store_config failed because identityEncryptedGlobal is empty"); return false; } } catch (Exception e) { @@ -576,15 +573,13 @@ private boolean logoutInternal(byte[] identity, String storePath) throws Excepti this.fula.flush(); } SecretKey secretKey = Cryptography.generateKey(identity); - - String identity_encrypted = Cryptography.encryptMsg(Arrays.toString(identity), secretKey); + byte[] iv = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B }; + String identity_encrypted = Cryptography.encryptMsg(Arrays.toString(identity), secretKey, iv); sharedPref.remove("FULA_ENC_V3:cid_encrypted_"+ identity_encrypted); - sharedPref.remove("FULA_ENC_V3:private_ref_encrypted_"+identity_encrypted); //TODO: Should also remove peerid @Mahdi sharedPref.remove("FULA_ENC_V3:cid_encrypted_"+ identity_encrypted); - sharedPref.remove("FULA_ENC_V3:private_ref_encrypted_"+ identity_encrypted); this.rootConfig = null; this.secretKeyGlobal = null; @@ -664,87 +659,54 @@ private String[] initInternal(byte[] identity, String storePath, String bloxAddr } SecretKey secretKey = Cryptography.generateKey(identity); - String identity_encrypted =Cryptography.encryptMsg(Arrays.toString(identity), secretKey); + Log.d("ReactNative", "secretKey generated: " + secretKey.toString()); + byte[] iv = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B }; + String identity_encrypted =Cryptography.encryptMsg(Arrays.toString(identity), secretKey, iv); + Log.d("ReactNative", "identity_encrypted generated: " + identity_encrypted + " for identity: " + Arrays.toString(identity)); this.identityEncryptedGlobal = identity_encrypted; this.secretKeyGlobal = secretKey; - if (this.rootConfig == null || this.rootConfig.getCid().isEmpty() || this.rootConfig.getPrivate_ref().isEmpty()) { + if ( this.rootConfig == null || this.rootConfig.getCid().isEmpty() ) { Log.d("ReactNative", "this.rootCid is empty."); //Load from keystore String cid_encrypted_fetched = sharedPref.getValue("FULA_ENC_V3:cid_encrypted_"+ identity_encrypted); - String private_ref_encrypted_fetched = sharedPref.getValue("FULA_ENC_V3:private_ref_encrypted_"+identity_encrypted); Log.d("ReactNative", "Here1"); String cid = ""; - String private_ref = ""; if(cid_encrypted_fetched != null && !cid_encrypted_fetched.isEmpty()) { Log.d("ReactNative", "decrypting cid="+cid_encrypted_fetched+" with secret="+secretKey.toString()); cid = Cryptography.decryptMsg(cid_encrypted_fetched, secretKey); } - if(private_ref_encrypted_fetched != null && !private_ref_encrypted_fetched.isEmpty()) { - Log.d("ReactNative", "decrypting private_ref="+private_ref_encrypted_fetched+" with secret="+secretKey.toString()); - private_ref = Cryptography.decryptMsg(private_ref_encrypted_fetched, secretKey); - } + Log.d("ReactNative", "Here2"); - //Log.d("ReactNative", "Attempted to fetch cid from keystore; cid="+cid+" & private_ref="+private_ref); - if((cid == null || cid.isEmpty()) || (private_ref == null || private_ref.isEmpty()) ){ - Log.d("ReactNative", "cid or PrivateRef was not found"); + //Log.d("ReactNative", "Attempted to fetch cid from keystore; cid="+cid); + if(cid == null || cid.isEmpty()) { + Log.d("ReactNative", "cid was not found"); if(rootCid != null && !rootCid.isEmpty()){ Log.d("ReactNative", "Re-setting cid from input: "+rootCid); cid = rootCid; } - if((private_ref == null || private_ref.isEmpty()) && (cid != null && !cid.isEmpty())){ - Log.d("ReactNative", "Re-fetching privateRef from wnfs: cid="+cid); - private_ref = this.getPrivateRef(this.client, identity, cid); - Log.d("ReactNative", "Re-fetching privateRef from wnfs: "+private_ref); - } - if(cid == null || cid.isEmpty() || private_ref == null || private_ref.isEmpty()) { - Log.d("ReactNative", "Tried to recover cid and privateRef but was not successful. Creating new ones"); + if(cid == null || cid.isEmpty()) { + Log.d("ReactNative", "Tried to recover cid but was not successful. Creating new ones"); this.createNewRootConfig(this.client, identity); - } else { - Log.d("ReactNative", "Tried to recover cid and privateRef and was successful. cid:"+cid+" & private_ref="+private_ref); - this.rootConfig = new land.fx.wnfslib.Config(cid, private_ref); - this.encrypt_and_store_config(); } - } else if(cid != null && !cid.isEmpty() && private_ref != null && !private_ref.isEmpty()) { - Log.d("ReactNative", "Found cid and private ref in keychain store"); - if(cid != null && !cid.isEmpty() && private_ref != null && !private_ref.isEmpty()) { - Log.d("ReactNative", "Recovered cid and private ref from keychain store. cid="+cid+" & private_ref="+private_ref); - this.rootConfig = new land.fx.wnfslib.Config(cid, private_ref); - } else{ - Log.d("ReactNative", "Found but Could not recover cid and private_ref from keychain store"); - this.createNewRootConfig(this.client, identity); - } - } else{ - Log.d("ReactNative", "This cid and private_ref generation should never happen!!!"); - //Create new root and store cid and private_ref - this.createNewRootConfig(this.client, identity); + } else { + Log.d("ReactNative", "Recovered cid and private ref from keychain store. cid="+cid); + this.rootConfig = new land.fx.wnfslib.Config(cid); + this.reloadFS(this.client, identity, cid); + this.encrypt_and_store_config(); } - Log.d("ReactNative", "creating rootConfig completed"); - /* - byte[] testbyte = convertStringToByte("-104,40,24,-93,24,100,24,114,24,111,24,111,24,116,24,-126,24,-126,0,0,24,-128,24,103,24,118,24,101,24,114,24,115,24,105,24,111,24,110,24,101,24,48,24,46,24,49,24,46,24,48,24,105,24,115,24,116,24,114,24,117,24,99,24,116,24,117,24,114,24,101,24,100,24,104,24,97,24,109,24,116"); - long testcodec = 85; - byte[] testputcid = this.client.put(testbyte, testcodec); - Log.d("ReactNative", "client.put test done"+ Arrays.toString(testputcid)); - byte[] testfetchedcid = convertStringToByte("1,113,18,32,-6,-63,-128,79,-102,-89,57,77,-8,67,-98,8,-81,40,-87,123,122,29,-52,-124,-60,-53,100,105,125,123,-5,-99,41,106,-124,-64"); - byte[] testfetchedbytes = this.client.get(testfetchedcid); - Log.d("ReactNative", "client.get test done"+ Arrays.toString(testfetchedbytes)); - */ - - - Log.d("ReactNative", "rootConfig is created: cid=" + this.rootConfig.getCid()+ "& private_ref="+this - .rootConfig.getPrivate_ref()); + Log.d("ReactNative", "rootConfig is created: cid=" + this.rootConfig.getCid()); } else { - Log.d("ReactNative", "rootConfig existed: cid=" + this.rootConfig.getCid()+ " & private_ref="+this.rootConfig.getPrivate_ref()); + Log.d("ReactNative", "rootConfig existed: cid=" + this.rootConfig.getCid()); } String peerId = this.fula.id(); - String[] obj = new String[3]; + String[] obj = new String[2]; obj[0] = peerId; obj[1] = this.rootConfig.getCid(); - obj[2] = this.rootConfig.getPrivate_ref(); Log.d("ReactNative", "initInternal is completed successfully"); if (this.fula != null) { this.fula.flush(); @@ -761,7 +723,7 @@ public void mkdir(String path, Promise promise) { ThreadUtils.runOnExecutor(() -> { Log.d("ReactNative", "mkdir: path = " + path); try { - land.fx.wnfslib.Config config = Fs.mkdir(this.client, this.rootConfig.getCid(), this.rootConfig.getPrivate_ref(), path); + land.fx.wnfslib.Config config = Fs.mkdir(this.client, this.rootConfig.getCid(), path); if(config != null) { this.rootConfig = config; this.encrypt_and_store_config(); @@ -794,7 +756,7 @@ public void writeFile(String fulaTargetFilename, String localFilename, Promise p try { if (this.client != null) { Log.d("ReactNative", "writeFileFromPath started: this.rootConfig.getCid=" + this.rootConfig.getCid()+ ", fulaTargetFilename="+fulaTargetFilename + ", localFilename="+localFilename); - land.fx.wnfslib.Config config = Fs.writeFileFromPath(this.client, this.rootConfig.getCid(), this.rootConfig.getPrivate_ref(), fulaTargetFilename, localFilename); + land.fx.wnfslib.Config config = Fs.writeFileStreamFromPath(this.client, this.rootConfig.getCid(), fulaTargetFilename, localFilename); if(config != null) { this.rootConfig = config; this.encrypt_and_store_config(); @@ -827,7 +789,7 @@ public void writeFileContent(String path, String contentString, Promise promise) Log.d("ReactNative", "writeFile: path = " + path); try { byte[] content = this.convertStringToByte(contentString); - land.fx.wnfslib.Config config = Fs.writeFile(this.client, this.rootConfig.getCid(), this.rootConfig.getPrivate_ref(), path, content); + land.fx.wnfslib.Config config = Fs.writeFile(this.client, this.rootConfig.getCid(), path, content); this.rootConfig = config; this.encrypt_and_store_config(); if (this.fula != null) { @@ -846,9 +808,8 @@ public void ls(String path, Promise promise) { ThreadUtils.runOnExecutor(() -> { Log.d("ReactNative", "ls: path = " + path); try { - byte[] res = Fs.ls(this.client, this.rootConfig.getCid(), this.rootConfig.getPrivate_ref(), path); + byte[] res = Fs.ls(this.client, this.rootConfig.getCid(), path); - //JSONArray jsonArray = new JSONArray(res); String s = new String(res, StandardCharsets.UTF_8); Log.d("ReactNative", "ls: res = " + s); promise.resolve(s); @@ -864,7 +825,7 @@ public void rm(String path, Promise promise) { ThreadUtils.runOnExecutor(() -> { Log.d("ReactNative", "rm: path = " + path); try { - land.fx.wnfslib.Config config = Fs.rm(this.client, this.rootConfig.getCid(), this.rootConfig.getPrivate_ref(), path); + land.fx.wnfslib.Config config = Fs.rm(this.client, this.rootConfig.getCid(), path); if(config != null) { this.rootConfig = config; this.encrypt_and_store_config(); @@ -888,7 +849,7 @@ public void cp(String sourcePath, String targetPath, Promise promise) { ThreadUtils.runOnExecutor(() -> { Log.d("ReactNative", "rm: sourcePath = " + sourcePath); try { - land.fx.wnfslib.Config config = Fs.cp(this.client, this.rootConfig.getCid(), this.rootConfig.getPrivate_ref(), sourcePath, targetPath); + land.fx.wnfslib.Config config = Fs.cp(this.client, this.rootConfig.getCid(), sourcePath, targetPath); if(config != null) { this.rootConfig = config; this.encrypt_and_store_config(); @@ -912,7 +873,7 @@ public void mv(String sourcePath, String targetPath, Promise promise) { ThreadUtils.runOnExecutor(() -> { Log.d("ReactNative", "rm: sourcePath = " + sourcePath); try { - land.fx.wnfslib.Config config = Fs.mv(this.client, this.rootConfig.getCid(), this.rootConfig.getPrivate_ref(), sourcePath, targetPath); + land.fx.wnfslib.Config config = Fs.mv(this.client, this.rootConfig.getCid(), sourcePath, targetPath); if(config != null) { this.rootConfig = config; this.encrypt_and_store_config(); @@ -943,7 +904,7 @@ public void readFile(String fulaTargetFilename, String localFilename, Promise pr ThreadUtils.runOnExecutor(() -> { Log.d("ReactNative", "readFile: fulaTargetFilename = " + fulaTargetFilename); try { - String path = Fs.readFilestreamToPath(this.client, this.rootConfig.getCid(), this.rootConfig.getPrivate_ref(), fulaTargetFilename, localFilename); + String path = Fs.readFilestreamToPath(this.client, this.rootConfig.getCid(), fulaTargetFilename, localFilename); promise.resolve(path); } catch (Exception e) { Log.d("get", e.getMessage()); @@ -957,7 +918,7 @@ public void readFileContent(String path, Promise promise) { ThreadUtils.runOnExecutor(() -> { Log.d("ReactNative", "readFileContent: path = " + path); try { - byte[] res = Fs.readFile(this.client, this.rootConfig.getCid(), this.rootConfig.getPrivate_ref(), path); + byte[] res = Fs.readFile(this.client, this.rootConfig.getCid(), path); String resString = toString(res); promise.resolve(resString); } catch (Exception e) { diff --git a/example/src/App.tsx b/example/src/App.tsx index 7437772..c55bdfd 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -1,8 +1,12 @@ import React from 'react'; -import { StyleSheet, Text, View, Button, TextInput } from 'react-native'; +import { StyleSheet, Text, View, Button } from 'react-native'; -import { fula, blockchain, chainApi, fxblox } from '@functionland/react-native-fula'; -import { listFailedActions } from '../../.history/src/protocols/fula_20230710112519'; +import { + fula, + blockchain, + chainApi, + fxblox, +} from '@functionland/react-native-fula'; const App = () => { const [key, setKey] = React.useState(''); @@ -17,20 +21,20 @@ const App = () => { var RNFS = require('react-native-fs'); const readFile = () => { RNFS.readDir(RNFS.DocumentDirectoryPath) - .then((result) => { + .then((result: { path: any; }[]) => { console.log('GOT RESULT', result); return Promise.all([RNFS.stat(result[0].path), result[0].path]); }) - .then((statResult) => { + .then((statResult: any[]) => { if (statResult[0].isFile()) { return RNFS.readFile(statResult[1], 'utf8'); } return 'no file'; }) - .then((contents) => { + .then((contents: any) => { console.log(contents); }) - .catch((err) => { + .catch((err: { message: any; code: any; }) => { console.log(err.message, err.code); }); }; @@ -41,29 +45,13 @@ const App = () => { 94, 225, 7, 153, 168, 239, 94, 7, 187, 123, 158, 149, 149, 227, 170, 32, 54, 203, 243, 211, 78, 120, 114, 199, 1, 197, 134, 6, 91, 87, 152, ]; - const privateKeyString = "\\test"; - const bloxAddr = '/dns/relay.dev.fx.land/tcp/4001/p2p/12D3KooWDRrBaAfPwsGJivBoUw5fE7ZpDiyfUjqgiURq2DEcL835/p2p-circuit/p2p/12D3KooWLGatFxDzMrKd4S6UC4GAtuM4zcFJW8RPuMR9SH7j46A8'; - const newClient = async () => { - try { - return fula.newClient( - privateKey.toString(), - '', - bloxAddr, - '' - ); - } catch (e) { - console.log(e); - return Promise.reject(e); - } - }; + + const bloxAddr = + '/dns/relay.dev.fx.land/tcp/4001/p2p/12D3KooWDRrBaAfPwsGJivBoUw5fE7ZpDiyfUjqgiURq2DEcL835/p2p-circuit/p2p/12D3KooWLGatFxDzMrKd4S6UC4GAtuM4zcFJW8RPuMR9SH7j46A8'; + const initFula = async () => { try { - return fula.init( - privateKey.toString(), - '', - bloxAddr, - '' - ); + return fula.init(privateKey.toString(), '', bloxAddr, ''); } catch (e) { console.log(e); return Promise.reject(e); @@ -137,57 +125,62 @@ const App = () => { title={inprogress ? 'Putting & Getting...' : 'Test'} onPress={async () => { try { - fula - .testData(privateKey.toString(), bloxAddr) - .then((res) => { - console.log('tested'); - console.log(res); - }) - .catch((e) => { - console.log('test failed'); - console.log(e); - }); - + fula + .testData(privateKey.toString(), bloxAddr) + .then((res: any) => { + console.log('tested'); + console.log(res); + }) + .catch((e: any) => { + console.log('test failed'); + console.log(e); + }); } catch (e) {} }} color={inprogress ? 'green' : 'blue'} />