diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8d40ef8..9369329 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -15,6 +15,7 @@ android:supportsRtl="true" android:theme="@style/AppTheme" tools:ignore="GoogleAppIndexingWarning"> + @@ -22,6 +23,7 @@ + @@ -30,9 +32,7 @@ android:label="@string/login" /> - diff --git a/app/src/main/java/lu/circl/mispbump/AESActivity.java b/app/src/main/java/lu/circl/mispbump/AESActivity.java deleted file mode 100644 index c4836a0..0000000 --- a/app/src/main/java/lu/circl/mispbump/AESActivity.java +++ /dev/null @@ -1,65 +0,0 @@ -package lu.circl.mispbump; - -import android.support.v7.app.ActionBar; -import android.support.v7.app.AppCompatActivity; -import android.os.Bundle; -import android.support.v7.widget.Toolbar; -import android.view.View; -import android.widget.Button; -import android.widget.TextView; - -import lu.circl.mispbump.security.AESSecurity; - -public class AESActivity extends AppCompatActivity { - - private TextView info; - private Button button; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_aes); - - // populate Toolbar (Actionbar) - Toolbar myToolbar = findViewById(R.id.toolbar); - setSupportActionBar(myToolbar); - - ActionBar ab = getSupportActionBar(); - if (ab != null) { - ab.setDisplayHomeAsUpEnabled(true); - } - - info = findViewById(R.id.aes_info); - button = findViewById(R.id.aes_button); - - button.setOnClickListener(onButtonClick); - } - - private View.OnClickListener onButtonClick = new View.OnClickListener() { - @Override - public void onClick(View v) { - AESSecurity sec1 = new AESSecurity(); - AESSecurity sec2 = new AESSecurity(); - - String message = "Geheimer Text von A als auch von B ..."; - - String pub1 = sec1.getPublicKey().toString(); - info.setText("A PK: " + pub1 + "\n"); - - String pub2 = sec2.getPublicKey().toString(); - info.append("B PK: " + pub2 + "\n"); - - sec1.setForeignPublicKey(sec2.getPublicKey()); - sec2.setForeignPublicKey(sec1.getPublicKey()); - info.append("\n-- public keys wurden ausgetauscht --\n\n"); - - String enc1 = sec1.encrypt(message); - info.append("A encrypted: " + enc1 + "\n"); - String enc2 = sec2.encrypt(message); - info.append("B encrypted: " + enc2 + "\n\n"); - - info.append("A entschlüsselt B's Nachricht: " + sec1.decrypt(enc2) + "\n"); - info.append("B entschlüsselt A's Nachricht: " + sec2.decrypt(enc1) + "\n"); - } - }; -} diff --git a/app/src/main/java/lu/circl/mispbump/HomeActivity.java b/app/src/main/java/lu/circl/mispbump/HomeActivity.java index 1f5666e..f406916 100644 --- a/app/src/main/java/lu/circl/mispbump/HomeActivity.java +++ b/app/src/main/java/lu/circl/mispbump/HomeActivity.java @@ -57,12 +57,6 @@ public class HomeActivity extends AppCompatActivity { @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { - - case R.id.aes: - Intent i = new Intent(getApplicationContext(), AESActivity.class); - startActivity(i); - return true; - case R.id.main_menu_clear_and_logout: clearDeviceAndLogOut(); return true; diff --git a/app/src/main/java/lu/circl/mispbump/SyncActivity.java b/app/src/main/java/lu/circl/mispbump/SyncActivity.java index 8469adc..e91a192 100644 --- a/app/src/main/java/lu/circl/mispbump/SyncActivity.java +++ b/app/src/main/java/lu/circl/mispbump/SyncActivity.java @@ -1,62 +1,150 @@ package lu.circl.mispbump; -import android.app.Activity; import android.graphics.Bitmap; -import android.graphics.Color; -import android.graphics.Point; +import android.os.Bundle; +import android.support.design.widget.FloatingActionButton; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; +import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; -import android.os.Bundle; +import android.support.v7.widget.Toolbar; +import android.util.Log; import android.view.View; import android.widget.ImageView; -import android.widget.TextView; import android.widget.Toast; -import com.google.zxing.BarcodeFormat; -import com.google.zxing.EncodeHintType; -import com.google.zxing.MultiFormatWriter; -import com.google.zxing.WriterException; -import com.google.zxing.common.BitMatrix; -import com.google.zxing.qrcode.QRCodeWriter; -import com.journeyapps.barcodescanner.BarcodeEncoder; +import com.google.gson.Gson; -import java.nio.IntBuffer; -import java.nio.ShortBuffer; -import java.util.HashMap; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; import java.util.List; -import java.util.Map; +import lu.circl.mispbump.auxiliary.PreferenceManager; +import lu.circl.mispbump.auxiliary.QrCodeGenerator; +import lu.circl.mispbump.auxiliary.RandomString; import lu.circl.mispbump.cam.CameraFragment; import lu.circl.mispbump.restful_client.MispRestClient; import lu.circl.mispbump.restful_client.MispServer; +import lu.circl.mispbump.restful_client.MispUser; import lu.circl.mispbump.restful_client.Organisation; import lu.circl.mispbump.restful_client.Server; import lu.circl.mispbump.restful_client.User; import lu.circl.mispbump.security.AESSecurity; +/** + * Step 1: Add partner org as local org (uuid must be the same) + * Step 2: Add SyncUser to local partner org + * Step 3: Add SyncServer with SyncUser's authkey + *

+ * What do we need to transmit? + * 1. Own organisation details + * 2. Authkey of SyncUser + * 3. Server url + */ public class SyncActivity extends AppCompatActivity { private static final String TAG = "SyncActivity"; - private CameraFragment cameraFragment; - - private MispRestClient restClient; - private ImageView qrCodeView; private AESSecurity aesSecurity; + private MispRestClient restClient; + private CameraFragment cameraFragment; + private ImageView qrCodeView; + private FloatingActionButton continueButton; + + private SyncState currentSyncState = SyncState.publicKeyExchange; + + private enum SyncState { + publicKeyExchange, + dataExchange + } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_sync); - qrCodeView = findViewById(R.id.qrcode); - aesSecurity = AESSecurity.getInstance(); + Toolbar myToolbar = findViewById(R.id.toolbar); + setSupportActionBar(myToolbar); - showPublicKeyQr(); - enableCameraFragment(); + ActionBar ab = getSupportActionBar(); + if (ab != null) { + ab.setDisplayHomeAsUpEnabled(true); + } + + qrCodeView = findViewById(R.id.qrcode); + continueButton = findViewById(R.id.continue_fab); + continueButton.setOnClickListener(onContinueClicked); + continueButton.hide(); + + aesSecurity = AESSecurity.getInstance(); + restClient = new MispRestClient(this); + + enableSyncOptionsFragment(); } + private View.OnClickListener onContinueClicked = new View.OnClickListener() { + @Override + public void onClick(View v) { + switch (currentSyncState) { + case publicKeyExchange: + currentSyncState = SyncState.dataExchange; + showInformationQr(); + continueButton.hide(); + cameraFragment.setReadQrEnabled(true); + break; + + case dataExchange: + // TODO upload + break; + } + } + }; + + /** + * Callback for the camera fragment. + * Delivers the content of a scanned QR code. + */ + private CameraFragment.QrScanCallback onQrCodeScanned = new CameraFragment.QrScanCallback() { + @Override + public void qrScanResult(String qrData) { + switch (currentSyncState) { + case publicKeyExchange: + try { + aesSecurity.setForeignPublicKey(AESSecurity.publicKeyFromString(qrData)); + cameraFragment.setReadQrEnabled(false); + + runOnUiThread(new Runnable() { + @Override + public void run() { + continueButton.show(); + } + }); + } catch (NoSuchAlgorithmException e) { + MakeToast("Something gone wrong while parsing the key (no such algorithm)"); + } catch (InvalidKeySpecException e) { + MakeToast("Something gone wrong while parsing the key (invalid key spec)"); + } + + break; + + case dataExchange: + // disable qr read + cameraFragment.setReadQrEnabled(false); + + String data = aesSecurity.decrypt(qrData); + SyncInformation info = new Gson().fromJson(data, SyncInformation.class); + + Log.i(TAG, info.organisation.toString()); + Log.i(TAG, info.user.toString()); + + break; + } + } + }; + + /** + * Creates the camera fragment used to scan the QR codes. + */ private void enableCameraFragment() { cameraFragment = new CameraFragment(); FragmentManager fragmentManager = getSupportFragmentManager(); @@ -65,23 +153,121 @@ public class SyncActivity extends AppCompatActivity { transaction.commit(); cameraFragment.setReadQrEnabled(true); - cameraFragment.setOnQrAvailableListener(resultCallback); + cameraFragment.setOnQrAvailableListener(onQrCodeScanned); } + /** + * options for this particular sync + */ + private void enableSyncOptionsFragment() { + SyncOptionsFragment syncOptionsFragment = new SyncOptionsFragment(); + + syncOptionsFragment.setOnOptionsReadyCallback(new SyncOptionsFragment.OptionsReadyCallback() { + @Override + public void ready(boolean share_events, boolean push, boolean pull, boolean caching) { + showPublicKeyQr(); + enableCameraFragment(); + } + }); + + FragmentManager fragmentManager = getSupportFragmentManager(); + FragmentTransaction transaction = fragmentManager.beginTransaction(); + transaction.replace(R.id.sync_fragment_container, syncOptionsFragment, syncOptionsFragment.getClass().getSimpleName()); + transaction.commit(); + } + + /** + * Display public key QR code. + */ private void showPublicKeyQr() { - Bitmap bm = generateQrCodeFromString(aesSecurity.getPublicKey().toString()); + QrCodeGenerator qrCodeGenerator = new QrCodeGenerator(this); + Bitmap bm = qrCodeGenerator.generateQrCode(AESSecurity.publicKeyToString(aesSecurity.getPublicKey())); qrCodeView.setImageBitmap(bm); qrCodeView.setVisibility(View.VISIBLE); } - private CameraFragment.QrResultCallback resultCallback = new CameraFragment.QrResultCallback() { - @Override - public void qrDataResult(String qrData) { - // TODO validate data - cameraFragment.setReadQrEnabled(false); - MakeToast(qrData); - } - }; + /** + * Display sync info QR code. + */ + private void showInformationQr() { + PreferenceManager preferenceManager = PreferenceManager.getInstance(this); + QrCodeGenerator qrCodeGenerator = new QrCodeGenerator(this); + Gson gson = new Gson(); + + // my organisation + Organisation org = preferenceManager.getUserOrganisation(); + User user = preferenceManager.getUserInfo(); + MispUser mispUser = new MispUser(user); + + Server server = new Server( + "SyncServer for " + org.name, + preferenceManager.getServerUrl(), + new RandomString(40).nextString(), + -1 + ); + + MispServer mispServer = new MispServer(server, org, null); + + SyncInformation syncInformation = new SyncInformation(user, org, server); + String encrypted = aesSecurity.encrypt(gson.toJson(syncInformation)); + + final Bitmap bm = qrCodeGenerator.generateQrCode(encrypted); + + runOnUiThread(new Runnable() { + @Override + public void run() { + qrCodeView.setImageBitmap(bm); + qrCodeView.setVisibility(View.VISIBLE); + } + }); + } + + private void addPartnerOrg(Organisation organisation) { + restClient.addOrganisation(organisation, new MispRestClient.OrganisationCallback() { + @Override + public void success(Organisation organisation) { + + } + + @Override + public void failure(String error) { + + } + }); + } + + private void addSyncUser(User user) { + restClient.addUser(user, new MispRestClient.UserCallback() { + @Override + public void success(User user) { + + } + + @Override + public void failure(String error) { + + } + }); + } + + private void addServer(MispServer server) { + restClient.addServer(server, new MispRestClient.ServerCallback() { + @Override + public void success(List servers) { + + } + + @Override + public void success(MispServer server) { + + } + + @Override + public void failure(String error) { + + } + }); + } private void MakeToast(final String message) { this.runOnUiThread(new Runnable() { @@ -92,120 +278,6 @@ public class SyncActivity extends AppCompatActivity { }); } - private Bitmap generateQrCodeFromString(String content) { - - Point displaySize = new Point(); - this.getWindowManager().getDefaultDisplay().getSize(displaySize); - - int size = displaySize.x; - - if (displaySize.x > displaySize.y) { - size = displaySize.y; - } - - size = (int)(size * 0.8); - - try { - MultiFormatWriter multiFormatWriter = new MultiFormatWriter(); - - Map hints = new HashMap<>(); - hints.put(EncodeHintType.MARGIN, 0); - - BitMatrix bitMatrix = multiFormatWriter.encode(content, BarcodeFormat.QR_CODE, size, size, hints); - return createBitmap(bitMatrix); - } catch (WriterException e) { - e.printStackTrace(); - } - - return null; - } - - private Bitmap createBitmap(BitMatrix matrix) { - int width = matrix.getWidth(); - int height = matrix.getHeight(); - int[] pixels = new int[width * height]; - for (int y = 0; y < height; y++) { - int offset = y * width; - for (int x = 0; x < width; x++) { - pixels[offset + x] = matrix.get(x, y) ? 0xFF000000 : 0x55FFFFFF; - } - } - - Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - bitmap.setPixels(pixels, 0, width, 0, 0, width, height); - return bitmap; - } - -// private View.OnClickListener onAddUser = new View.OnClickListener() { -// @Override -// public void onClick(View v) { -// -// User user = new User(1, "felixpk@outlook.de", MispRestClient.roleId.SYNC_USER.value()); -// -// restClient.addUser(user, new MispRestClient.UserCallback() { -// @Override -// public void success(User user) { -// resultView.setText(user.toString()); -// } -// -// @Override -// public void failure(String error) { -// resultView.setText(error); -// } -// }); -// } -// }; -// -// private View.OnClickListener onAddOrganisation = new View.OnClickListener() { -// @Override -// public void onClick(View v) { -// Organisation organisation = new Organisation("API Organisation 2", "API Generated Organisation"); -// organisation.local = true; -// organisation.nationality = "Buxdehude"; -// -// restClient.addOrganisation(organisation, new MispRestClient.OrganisationCallback() { -// @Override -// public void success(Organisation organisation) { -// resultView.setText(organisation.toString()); -// } -// -// @Override -// public void failure(String error) { -// resultView.setText(error); -// } -// }); -// } -// }; -// -// private View.OnClickListener onAddServer = new View.OnClickListener() { -// @Override -// public void onClick(View v) { -// -// Organisation organisation = new Organisation("", ""); -// Server server = new Server("API Remote Server", "https://127.0.0.1", "0000000000000000000000000000000000000000", 1); -// -// MispServer mispServer = new MispServer(server, organisation, organisation); -// -// -// restClient.addServer(mispServer, new MispRestClient.ServerCallback() { -// @Override -// public void success(List servers) { -// -// } -// -// @Override -// public void success(MispServer server) { -// -// } -// -// @Override -// public void failure(String error) { -// -// } -// }); -// } -// }; -// // private View.OnClickListener onGetServers = new View.OnClickListener() { // @Override // public void onClick(View v) { diff --git a/app/src/main/java/lu/circl/mispbump/SyncOptionsFragment.java b/app/src/main/java/lu/circl/mispbump/SyncOptionsFragment.java new file mode 100644 index 0000000..d192f76 --- /dev/null +++ b/app/src/main/java/lu/circl/mispbump/SyncOptionsFragment.java @@ -0,0 +1,46 @@ +package lu.circl.mispbump; + +import android.os.Bundle; +import android.support.design.widget.FloatingActionButton; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Switch; + +public class SyncOptionsFragment extends Fragment { + + private Switch share, push, pull, cache; + private OptionsReadyCallback readyCallback; + + public interface OptionsReadyCallback { + void ready(boolean share_events, boolean push, boolean pull, boolean caching); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View v = inflater.inflate(R.layout.fragment_sync_options, container, false); + + share = v.findViewById(R.id.share_events_switch); + push = v.findViewById(R.id.push_switch); + pull = v.findViewById(R.id.pull_switch); + cache = v.findViewById(R.id.cache_switch); + + FloatingActionButton fab = v.findViewById(R.id.sync_options_fab); + fab.setOnClickListener(fabListener); + + return v; + } + + private View.OnClickListener fabListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + readyCallback.ready(share.isChecked(), push.isChecked(), pull.isChecked(), cache.isChecked()); + } + }; + + public void setOnOptionsReadyCallback(OptionsReadyCallback callback) { + readyCallback = callback; + } + +} \ No newline at end of file diff --git a/app/src/main/java/lu/circl/mispbump/auxiliary/QrCodeGenerator.java b/app/src/main/java/lu/circl/mispbump/auxiliary/QrCodeGenerator.java new file mode 100644 index 0000000..b8a58c6 --- /dev/null +++ b/app/src/main/java/lu/circl/mispbump/auxiliary/QrCodeGenerator.java @@ -0,0 +1,66 @@ +package lu.circl.mispbump.auxiliary; + +import android.app.Activity; +import android.graphics.Bitmap; +import android.graphics.Point; + +import com.google.zxing.BarcodeFormat; +import com.google.zxing.EncodeHintType; +import com.google.zxing.MultiFormatWriter; +import com.google.zxing.WriterException; +import com.google.zxing.common.BitMatrix; + +import java.util.HashMap; +import java.util.Map; + +public class QrCodeGenerator { + + private Activity callingActivity; + + public QrCodeGenerator(Activity callingActivity) { + this.callingActivity = callingActivity; + } + + public Bitmap generateQrCode(String content) { + Point displaySize = new Point(); + callingActivity.getWindowManager().getDefaultDisplay().getSize(displaySize); + + int size = displaySize.x; + + if (displaySize.x > displaySize.y) { + size = displaySize.y; + } + + size = (int)(size * 0.8); + + try { + MultiFormatWriter multiFormatWriter = new MultiFormatWriter(); + + Map hints = new HashMap<>(); + hints.put(EncodeHintType.MARGIN, 0); + + BitMatrix bitMatrix = multiFormatWriter.encode(content, BarcodeFormat.QR_CODE, size, size, hints); + return createBitmap(bitMatrix); + } catch (WriterException e) { + e.printStackTrace(); + } + + return null; + } + + private Bitmap createBitmap(BitMatrix matrix) { + int width = matrix.getWidth(); + int height = matrix.getHeight(); + int[] pixels = new int[width * height]; + for (int y = 0; y < height; y++) { + int offset = y * width; + for (int x = 0; x < width; x++) { + pixels[offset + x] = matrix.get(x, y) ? 0xFF000000 : 0x99FFFFFF; + } + } + + Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + bitmap.setPixels(pixels, 0, width, 0, 0, width, height); + return bitmap; + } +} diff --git a/app/src/main/java/lu/circl/mispbump/cam/CameraFragment.java b/app/src/main/java/lu/circl/mispbump/cam/CameraFragment.java index c36f236..5979a57 100644 --- a/app/src/main/java/lu/circl/mispbump/cam/CameraFragment.java +++ b/app/src/main/java/lu/circl/mispbump/cam/CameraFragment.java @@ -18,7 +18,6 @@ import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.params.StreamConfigurationMap; import android.media.Image; import android.media.ImageReader; -import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; @@ -94,7 +93,7 @@ public class CameraFragment extends Fragment implements ActivityCompat.OnRequest if (barcodes.size() > 0) { if (qrResultCallback != null) { - qrResultCallback.qrDataResult(barcodes.valueAt(0).rawValue); + qrResultCallback.qrScanResult(barcodes.valueAt(0).rawValue); try { sleep(100); @@ -103,7 +102,7 @@ public class CameraFragment extends Fragment implements ActivityCompat.OnRequest } } else { - Log.i(TAG, "QrResultCallback not attached"); + Log.i(TAG, "QrScanCallback not attached"); } } @@ -121,7 +120,7 @@ public class CameraFragment extends Fragment implements ActivityCompat.OnRequest } } - private QrResultCallback qrResultCallback; + private QrScanCallback qrResultCallback; @Override public void onAttach(Context context) { @@ -783,8 +782,8 @@ public class CameraFragment extends Fragment implements ActivityCompat.OnRequest } - public interface QrResultCallback { - void qrDataResult(String qrData); + public interface QrScanCallback { + void qrScanResult(String qrData); } private boolean readQrEnabled = true; @@ -854,7 +853,7 @@ public class CameraFragment extends Fragment implements ActivityCompat.OnRequest } } - public void setOnQrAvailableListener(QrResultCallback callback) { + public void setOnQrAvailableListener(QrScanCallback callback) { qrResultCallback = callback; } } diff --git a/app/src/main/java/lu/circl/mispbump/restful_client/MispRestClient.java b/app/src/main/java/lu/circl/mispbump/restful_client/MispRestClient.java index 92ec224..7c781e1 100644 --- a/app/src/main/java/lu/circl/mispbump/restful_client/MispRestClient.java +++ b/app/src/main/java/lu/circl/mispbump/restful_client/MispRestClient.java @@ -31,18 +31,6 @@ import retrofit2.converter.gson.GsonConverterFactory; */ public class MispRestClient { - // callbacks and interfaces - - public enum roleId { - ADMIN(1), ORG_ADMIN(2), USER(3), PUBLISHER(4), SYNC_USER(5), READ_ONLY(6); - - private final int id; - - roleId(int id) { this.id = id; } - - public int value() { return id; } - } - public interface UserCallback { void success(User user); void failure(String error); @@ -59,7 +47,6 @@ public class MispRestClient { void failure(String error); } - // fields private PreferenceManager preferenceManager; private MispRestService mispRestService; diff --git a/app/src/main/java/lu/circl/mispbump/restful_client/MispUser.java b/app/src/main/java/lu/circl/mispbump/restful_client/MispUser.java index 0c278a1..4bcb275 100644 --- a/app/src/main/java/lu/circl/mispbump/restful_client/MispUser.java +++ b/app/src/main/java/lu/circl/mispbump/restful_client/MispUser.java @@ -9,4 +9,7 @@ public class MispUser { @Expose public User user; + public MispUser(User user) { + this.user = user; + } } \ No newline at end of file diff --git a/app/src/main/java/lu/circl/mispbump/restful_client/User.java b/app/src/main/java/lu/circl/mispbump/restful_client/User.java index 43873fd..1458bc1 100644 --- a/app/src/main/java/lu/circl/mispbump/restful_client/User.java +++ b/app/src/main/java/lu/circl/mispbump/restful_client/User.java @@ -5,6 +5,13 @@ import com.google.gson.annotations.SerializedName; public class User { + public static final int ROLE_ADMIN = 1; + public static final int ROLE_ORG_ADMIN = 2; + public static final int ROLE_USER = 3; + public static final int ROLE_PUBLISHER = 4; + public static final int ROLE_SYNC_USER = 5; + public static final int ROLE_READ_ONLY = 6; + public User(Integer org_id, String email, Integer role_id) { this.org_id = org_id; this.email = email; diff --git a/app/src/main/java/lu/circl/mispbump/security/AESSecurity.java b/app/src/main/java/lu/circl/mispbump/security/AESSecurity.java index 052b599..32b7b8e 100644 --- a/app/src/main/java/lu/circl/mispbump/security/AESSecurity.java +++ b/app/src/main/java/lu/circl/mispbump/security/AESSecurity.java @@ -5,6 +5,8 @@ import android.util.Base64; import javax.crypto.*; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; + +import java.nio.charset.StandardCharsets; import java.security.*; import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec; @@ -14,7 +16,7 @@ public class AESSecurity { private static final String TAG = "MISP_LOGGING"; - private static final String ENCRYPT_ALGORITHM = "AES/CBC/PKCS5Padding"; + private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding"; private static final String KEY_PAIR_ALGORITHM = "EC"; private static final int KEY_SIZE = 521; // 224 | 256 | 384 | 521 private static final String KEY_AGREEMENT_ALGORITHM = "ECDH"; @@ -28,16 +30,22 @@ public class AESSecurity { private byte[] sharedSecret; private IvParameterSpec ivParameterSpec; - public AESSecurity() { + private AESSecurity() { initialize(); } + public static AESSecurity getInstance() { + if(instance == null) { + instance = new AESSecurity(); + } + return instance; + } + /** * Generates a public and a private key using an elliptic curve algorithm. * The private key is fed into the key agreement instance. */ private void initialize() { - try { KeyPairGenerator kpg = KeyPairGenerator.getInstance(KEY_PAIR_ALGORITHM); kpg.initialize(KEY_SIZE); @@ -58,7 +66,6 @@ public class AESSecurity { * @param publickey public key of the sync partner */ public void setForeignPublicKey(PublicKey publickey) { - try { keyAgreement.doPhase(publickey, true); @@ -74,53 +81,43 @@ public class AESSecurity { } /** - * - * @param data - * @return + * Encrypts data. + * @param data data to encrypt + * @return To String converted and encrypted data */ public String encrypt(String data) { try { + Cipher c = Cipher.getInstance(CIPHER_ALGORITHM); + c.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(sharedSecret, CIPHER_ALGORITHM), ivParameterSpec); - Key key = generateKey(); - Cipher c = Cipher.getInstance(ENCRYPT_ALGORITHM); - - try { - c.init(Cipher.ENCRYPT_MODE, key, ivParameterSpec); - } catch (InvalidAlgorithmParameterException e) { - e.printStackTrace(); - } - - byte[] encVal = c.doFinal(data.getBytes()); - return Base64.encodeToString(encVal, 0); + byte[] cipherText = c.doFinal(data.getBytes(StandardCharsets.UTF_8)); + return Base64.encodeToString(cipherText, Base64.NO_WRAP); } catch (BadPaddingException | InvalidKeyException | NoSuchPaddingException | IllegalBlockSizeException | NoSuchAlgorithmException e) { e.printStackTrace(); + } catch (InvalidAlgorithmParameterException e) { + e.printStackTrace(); } return data; } /** - * - * @param data - * @return + * Decrypts data. + * @param data data to decrypt + * @return To String converted and decrypted data */ public String decrypt(String data) { try { - Key key = generateKey(); + Cipher c = Cipher.getInstance(CIPHER_ALGORITHM); + c.init(Cipher.DECRYPT_MODE, new SecretKeySpec(sharedSecret, CIPHER_ALGORITHM), ivParameterSpec); - Cipher c = Cipher.getInstance(ENCRYPT_ALGORITHM); + byte[] cipherText = Base64.decode(data, Base64.NO_WRAP); + return new String(c.doFinal(cipherText), StandardCharsets.UTF_8); - try { - c.init(Cipher.DECRYPT_MODE, key, ivParameterSpec); - } catch (InvalidAlgorithmParameterException e) { - e.printStackTrace(); - } - - byte[] decoded = Base64.decode(data, 0); - byte[] decValue = c.doFinal(decoded); - return new String(decValue); } catch (BadPaddingException | InvalidKeyException | NoSuchPaddingException | IllegalBlockSizeException | NoSuchAlgorithmException e) { e.printStackTrace(); + } catch (InvalidAlgorithmParameterException e) { + e.printStackTrace(); } return data; } @@ -129,31 +126,12 @@ public class AESSecurity { return publickey; } - private Key generateKey() { - return new SecretKeySpec(sharedSecret, ENCRYPT_ALGORITHM); - } - public static String publicKeyToString(PublicKey key) { return Base64.encodeToString(key.getEncoded(), Base64.DEFAULT); } - public static PublicKey publicKeyFromString(String key) { - try { - - byte[] input = Base64.decode(key, Base64.DEFAULT); - return KeyFactory.getInstance(KEY_FACTORY_ALGORITHM).generatePublic(new X509EncodedKeySpec(input)); - - } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { - e.printStackTrace(); - } - - return null; - } - - public static AESSecurity getInstance() { - if(instance == null) { - instance = new AESSecurity(); - } - return instance; + public static PublicKey publicKeyFromString(String key) throws NoSuchAlgorithmException, InvalidKeySpecException { + byte[] input = Base64.decode(key, Base64.DEFAULT); + return KeyFactory.getInstance(KEY_FACTORY_ALGORITHM).generatePublic(new X509EncodedKeySpec(input)); } } diff --git a/app/src/main/java/lu/circl/mispbump/security/KeyStoreWrapper.java b/app/src/main/java/lu/circl/mispbump/security/KeyStoreWrapper.java index 2af8db5..01e5d67 100644 --- a/app/src/main/java/lu/circl/mispbump/security/KeyStoreWrapper.java +++ b/app/src/main/java/lu/circl/mispbump/security/KeyStoreWrapper.java @@ -72,7 +72,6 @@ public class KeyStoreWrapper { return false; } - /** * * @return SecretKey associated with the given alias. @@ -155,11 +154,11 @@ public class KeyStoreWrapper { * Encrypt data with given algorithm and key associated with alias. * @param data data to encrypt. * @return encrypted data as String. - * @throws NoSuchPaddingException - * @throws NoSuchAlgorithmException - * @throws InvalidKeyException - * @throws BadPaddingException - * @throws IllegalBlockSizeException + * @throws NoSuchPaddingException padding not found + * @throws NoSuchAlgorithmException algorithm not found + * @throws InvalidKeyException invalid key + * @throws BadPaddingException bad padding + * @throws IllegalBlockSizeException illegal block size */ public String encrypt(String data) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { SecretKey secretKey; @@ -184,12 +183,12 @@ public class KeyStoreWrapper { * Decrypts data with given algorithm and key associated with alias. * @param input encrypted data. * @return decrypted data as String. - * @throws NoSuchPaddingException - * @throws NoSuchAlgorithmException - * @throws InvalidAlgorithmParameterException - * @throws InvalidKeyException - * @throws BadPaddingException - * @throws IllegalBlockSizeException + * @throws NoSuchPaddingException padding not found + * @throws NoSuchAlgorithmException algorithm not found + * @throws InvalidAlgorithmParameterException invalid algorithm parameters + * @throws InvalidKeyException invalid key + * @throws BadPaddingException bad padding + * @throws IllegalBlockSizeException illegal block size */ public String decrypt(String input) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { @@ -236,4 +235,18 @@ public class KeyStoreWrapper { e.printStackTrace(); } } + + /** + * Combine IV and encrypted data. + * @param iv initialisation vector + * @param encryptedData encrypted data + * @return combination of iv and encrypted data + */ + private static byte[] getCombinedArray(byte[] iv, byte[] encryptedData) { + byte[] combined = new byte[iv.length + encryptedData.length]; + for (int i = 0; i < combined.length; ++i) { + combined[i] = i < iv.length ? iv[i] : encryptedData[i - iv.length]; + } + return combined; + } } \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_check.xml b/app/src/main/res/drawable/ic_check.xml new file mode 100644 index 0000000..17aca2a --- /dev/null +++ b/app/src/main/res/drawable/ic_check.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/activity_aes.xml b/app/src/main/res/layout/activity_aes.xml deleted file mode 100644 index 798d209..0000000 --- a/app/src/main/res/layout/activity_aes.xml +++ /dev/null @@ -1,59 +0,0 @@ - - - - - -