From aa3003080d72168f41c0361b3897c310a19d2765 Mon Sep 17 00:00:00 2001 From: ehsan shariati Date: Fri, 12 May 2023 01:36:21 -0400 Subject: [PATCH] Changed encryption to AES/GCM/NoPadding --- .../main/java/land/fx/fula/Cryptography.java | 37 +++++++++---- .../main/java/land/fx/fula/FulaModule.java | 55 +++++++++++++------ package.json | 2 +- 3 files changed, 65 insertions(+), 29 deletions(-) diff --git a/android/src/main/java/land/fx/fula/Cryptography.java b/android/src/main/java/land/fx/fula/Cryptography.java index a5291e0..60d9481 100644 --- a/android/src/main/java/land/fx/fula/Cryptography.java +++ b/android/src/main/java/land/fx/fula/Cryptography.java @@ -3,10 +3,13 @@ import android.util.Base64; import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.spec.InvalidKeySpecException; +import java.security.SecureRandom; +import java.nio.ByteBuffer; import java.security.spec.InvalidParameterSpecException; import javax.crypto.BadPaddingException; @@ -17,24 +20,34 @@ import javax.crypto.SecretKeyFactory; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.SecretKeySpec; +import javax.crypto.spec.GCMParameterSpec; public class Cryptography { public static String encryptMsg(String message, SecretKey secret) - throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidParameterSpecException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException { - Cipher cipher = null; - cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); - cipher.init(Cipher.ENCRYPT_MODE, secret); - byte[] cipherText = cipher.doFinal(message.getBytes("UTF-8")); - return Base64.encodeToString(cipherText, Base64.NO_WRAP); + 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); + GCMParameterSpec spec = new GCMParameterSpec(128, iv); + cipher.init(Cipher.ENCRYPT_MODE, secret, spec); + byte[] cipherText = cipher.doFinal(message.getBytes(StandardCharsets.UTF_8)); + ByteBuffer byteBuffer = ByteBuffer.allocate(iv.length + cipherText.length); + byteBuffer.put(iv); + byteBuffer.put(cipherText); + return Base64.encodeToString(byteBuffer.array(), Base64.NO_WRAP); } public static String decryptMsg(String cipherText, SecretKey secret) - throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidParameterSpecException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, UnsupportedEncodingException { - Cipher cipher = null; - cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); - cipher.init(Cipher.DECRYPT_MODE, secret); - byte[] decode = Base64.decode(cipherText, Base64.NO_WRAP); - String decryptString = new String(cipher.doFinal(decode), "UTF-8"); + throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { + ByteBuffer byteBuffer = ByteBuffer.wrap(Base64.decode(cipherText, Base64.NO_WRAP)); + byte[] iv = new byte[12]; + byteBuffer.get(iv); + byte[] cipherBytes = new byte[byteBuffer.remaining()]; + byteBuffer.get(cipherBytes); + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + GCMParameterSpec spec = new GCMParameterSpec(128, iv); + cipher.init(Cipher.DECRYPT_MODE, secret, spec); + String decryptString = new String(cipher.doFinal(cipherBytes), StandardCharsets.UTF_8); return decryptString; } diff --git a/android/src/main/java/land/fx/fula/FulaModule.java b/android/src/main/java/land/fx/fula/FulaModule.java index 5fa48e4..ef866ae 100755 --- a/android/src/main/java/land/fx/fula/FulaModule.java +++ b/android/src/main/java/land/fx/fula/FulaModule.java @@ -16,9 +16,17 @@ import org.jetbrains.annotations.Contract; import java.io.File; +import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.security.GeneralSecurityException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; import java.util.Arrays; +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import java.util.concurrent.Executors; @@ -414,24 +422,32 @@ private boolean retryFailedActionsInternal(int timeout) throws Exception { } @NonNull - private byte[] createPeerIdentity(byte[] privateKey) throws Exception { + private byte[] createPeerIdentity(byte[] privateKey) throws GeneralSecurityException, IOException { try { // 1: First: create public key from provided private key // 2: Should read the local keychain store (if it is key-value, key is public key above, // 3: if found, decrypt using the private key // 4: If not found or decryption not successful, generate an identity // 5: then encrypt and store in keychain - String encryptedKey = sharedPref.getValue(PRIVATE_KEY_STORE_ID); SecretKey secretKey = Cryptography.generateKey(privateKey); - if (encryptedKey == null) { - byte[] autoGeneratedIdentity = Fulamobile.generateEd25519Key(); - encryptedKey = Cryptography.encryptMsg(StaticHelper.bytesToBase64(autoGeneratedIdentity), secretKey); + + if (encryptedKey == null || !encryptedKey.startsWith("FULA_ENC_V2:")) { + byte[] autoGeneratedIdentity; + try { + autoGeneratedIdentity = Fulamobile.generateEd25519Key(); + } catch (Exception e) { + Log.d("ReactNative", "Failed to generate key: " + e.getMessage()); + throw new GeneralSecurityException("Failed to generate key", e); + } + encryptedKey = "FULA_ENC_V2:" + Cryptography.encryptMsg(StaticHelper.bytesToBase64(autoGeneratedIdentity), secretKey); sharedPref.add(PRIVATE_KEY_STORE_ID, encryptedKey); } - return StaticHelper.base64ToBytes(Cryptography.decryptMsg(encryptedKey, secretKey)); - } catch (Exception e) { + String decryptedKey = Cryptography.decryptMsg(encryptedKey.replace("FULA_ENC_V2:", ""), secretKey); + return StaticHelper.base64ToBytes(decryptedKey); + + } catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException | BadPaddingException | InvalidAlgorithmParameterException e) { Log.d("ReactNative", "createPeerIdentity failed with Error: " + e.getMessage()); throw (e); } @@ -512,7 +528,8 @@ public fulamobile.Client getFulaClient() { } @NonNull - private byte[] newClientInternal(byte[] identity, String storePath, String bloxAddr, String exchange, boolean autoFlush, boolean useRelay, boolean refresh) throws Exception { + private byte[] newClientInternal(byte[] identity, String storePath, String bloxAddr, String exchange, boolean autoFlush, boolean useRelay, boolean refresh) throws GeneralSecurityException, IOException { + byte[] peerIdentity = null; try { fulaConfig = new Config(); if (storePath == null || storePath.trim().isEmpty()) { @@ -522,7 +539,7 @@ private byte[] newClientInternal(byte[] identity, String storePath, String bloxA } Log.d("ReactNative", "newClientInternal storePath is set: " + fulaConfig.getStorePath()); - byte[] peerIdentity = this.createPeerIdentity(identity); + peerIdentity = this.createPeerIdentity(identity); fulaConfig.setIdentity(peerIdentity); Log.d("ReactNative", "peerIdentity is set: " + toString(fulaConfig.getIdentity())); fulaConfig.setBloxAddr(bloxAddr); @@ -535,19 +552,25 @@ private byte[] newClientInternal(byte[] identity, String storePath, String bloxA } if (this.fula == null || refresh) { Log.d("ReactNative", "Creating a new Fula instance"); - shutdownInternal(); - this.fula = Fulamobile.newClient(fulaConfig); - } - if (this.fula != null) { - this.fula.flush(); + try { + shutdownInternal(); + this.fula = Fulamobile.newClient(fulaConfig); + if (this.fula != null) { + this.fula.flush(); + } + } catch (Exception e) { + Log.d("ReactNative", "Failed to create new Fula instance: " + e.getMessage()); + throw new IOException("Failed to create new Fula instance", e); + } } - return peerIdentity; - } catch (Exception e) { + } catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException | BadPaddingException | InvalidAlgorithmParameterException e) { Log.d("ReactNative", "newclientInternal failed with Error: " + e.getMessage()); throw (e); } + return peerIdentity; } + @NonNull private String[] initInternal(byte[] identity, String storePath, String bloxAddr, String exchange, boolean autoFlush, String rootCid, boolean useRelay, boolean refresh) throws Exception { try { diff --git a/package.json b/package.json index cb27be1..aa52569 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@functionland/react-native-fula", - "version": "1.2.2", + "version": "1.2.3", "description": "This package is a bridge to use the Fula libp2p protocols in the react-native which is using wnfs", "main": "lib/commonjs/index", "module": "lib/module/index",