uploadInformationList = preferenceManager.getUploadInformation();
+ Log.i(TAG, "Size: " + uploadInformationList.size());
+ SyncAdapter syncAdapter = new SyncAdapter(uploadInformationList);
+ recyclerView.setAdapter(syncAdapter);
}
private void clearDeviceAndLogOut() {
@@ -108,4 +190,9 @@ public class HomeActivity extends AppCompatActivity {
builder.create().show();
}
+ @Override
+ protected void onResume() {
+ super.onResume();
+ populateRecyclerView();
+ }
}
diff --git a/app/src/main/java/lu/circl/mispbump/activities/LoginActivity.java b/app/src/main/java/lu/circl/mispbump/activities/LoginActivity.java
index 734784c..344624e 100644
--- a/app/src/main/java/lu/circl/mispbump/activities/LoginActivity.java
+++ b/app/src/main/java/lu/circl/mispbump/activities/LoginActivity.java
@@ -1,7 +1,5 @@
package lu.circl.mispbump.activities;
-import android.app.AlertDialog;
-import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.support.constraint.ConstraintLayout;
@@ -26,14 +24,66 @@ import lu.circl.mispbump.restful_client.MispRestClient;
import lu.circl.mispbump.restful_client.Organisation;
import lu.circl.mispbump.restful_client.User;
+/**
+ * This activity is shown when the current device has no misp user associated with it.
+ * Takes care of downloading all information necessary for a sync with other misp instances.
+ */
public class LoginActivity extends AppCompatActivity {
+ private PreferenceManager preferenceManager;
private ConstraintLayout constraintLayout;
- private TextInputLayout serverUrl;
private TextInputLayout serverAutomationKey;
+ private TextInputLayout serverUrl;
private ProgressBar progressBar;
- private PreferenceManager preferenceManager;
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_login);
+
+ // populate Toolbar (Actionbar)
+ Toolbar myToolbar = findViewById(R.id.appbar);
+ setSupportActionBar(myToolbar);
+
+ ActionBar ab = getSupportActionBar();
+ if (ab != null) {
+ ab.setDisplayHomeAsUpEnabled(false);
+ }
+
+ constraintLayout = findViewById(R.id.layout);
+ progressBar = findViewById(R.id.login_progressbar);
+ serverUrl = findViewById(R.id.login_server_url);
+ serverAutomationKey = findViewById(R.id.login_automation_key);
+ Button downloadInfoButton = findViewById(R.id.login_download_button);
+
+ downloadInfoButton.setOnClickListener(onClickDownload);
+
+ preferenceManager = PreferenceManager.getInstance(this);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.menu_login, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.menu_login_help:
+ DialogManager.loginHelpDialog(LoginActivity.this);
+ return true;
+
+ default:
+ // invoke superclass to handle unrecognized item (eg. homeAsUp)
+ return super.onOptionsItemSelected(item);
+
+ }
+ }
+
+ /**
+ * Is called when the user clicks on the login button.
+ */
private View.OnClickListener onClickDownload = new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -60,8 +110,9 @@ public class LoginActivity extends AppCompatActivity {
return;
}
- // save authkey and url for login
+ // save authkey
preferenceManager.setAutomationKey(authkey);
+ // save url
preferenceManager.setServerUrl(url);
// instance of MispRestClient with given URL
@@ -104,59 +155,22 @@ public class LoginActivity extends AppCompatActivity {
}
};
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_login);
-
- // populate Toolbar (Actionbar)
- Toolbar myToolbar = findViewById(R.id.toolbar);
- setSupportActionBar(myToolbar);
-
- ActionBar ab = getSupportActionBar();
- if (ab != null) {
- ab.setDisplayHomeAsUpEnabled(false);
- }
-
- constraintLayout = findViewById(R.id.login_root);
- progressBar = findViewById(R.id.login_progressbar);
- serverUrl = findViewById(R.id.login_server_url);
- serverAutomationKey = findViewById(R.id.login_automation_key);
- Button downloadInfoButton = findViewById(R.id.login_download_button);
-
- downloadInfoButton.setOnClickListener(onClickDownload);
-
- preferenceManager = PreferenceManager.getInstance(this);
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.menu_login, menu);
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.menu_login_help:
- showHelpDialog();
- return true;
-
- default:
- // invoke superclass to handle unrecognized item (eg. homeAsUp)
- return super.onOptionsItemSelected(item);
-
- }
- }
-
- private void showHelpDialog() {
- DialogManager.loginHelpDialog(LoginActivity.this);
- }
-
+ /**
+ * Check if url is valid.
+ *
+ * @param url url to check
+ * @return true or false
+ */
private boolean isValidUrl(String url) {
return url.startsWith("https://") || url.startsWith("http://");
}
+ /**
+ * Check if automation key is valid.
+ *
+ * @param automationKey the key to check
+ * @return true or false
+ */
private boolean isValidAutomationKey(String automationKey) {
return !TextUtils.isEmpty(automationKey);
}
diff --git a/app/src/main/java/lu/circl/mispbump/activities/MainActivity.java b/app/src/main/java/lu/circl/mispbump/activities/MainActivity.java
new file mode 100644
index 0000000..42eac6a
--- /dev/null
+++ b/app/src/main/java/lu/circl/mispbump/activities/MainActivity.java
@@ -0,0 +1,24 @@
+package lu.circl.mispbump.activities;
+
+import android.os.Bundle;
+import android.support.design.widget.BottomNavigationView;
+import android.support.v7.app.AppCompatActivity;
+
+import lu.circl.mispbump.R;
+
+public class MainActivity extends AppCompatActivity {
+
+ private BottomNavigationView bottomNavigationView;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ initializeViews();
+ }
+
+ private void initializeViews() {
+// bottomNavigationView = findViewById(R.id.bottom_navigation);
+ }
+}
diff --git a/app/src/main/java/lu/circl/mispbump/activities/SyncActivity.java b/app/src/main/java/lu/circl/mispbump/activities/SyncActivity.java
index 67fb392..38517b5 100644
--- a/app/src/main/java/lu/circl/mispbump/activities/SyncActivity.java
+++ b/app/src/main/java/lu/circl/mispbump/activities/SyncActivity.java
@@ -2,7 +2,9 @@ package lu.circl.mispbump.activities;
import android.graphics.Bitmap;
import android.os.Bundle;
+import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.FloatingActionButton;
+import android.support.design.widget.Snackbar;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.ActionBar;
@@ -11,7 +13,6 @@ import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
-import android.widget.Toast;
import com.google.gson.Gson;
@@ -28,41 +29,72 @@ import lu.circl.mispbump.auxiliary.RandomString;
import lu.circl.mispbump.cam.CameraFragment;
import lu.circl.mispbump.fragments.SyncOptionsFragment;
import lu.circl.mispbump.models.SyncInformation;
+import lu.circl.mispbump.models.UploadInformation;
import lu.circl.mispbump.restful_client.MispRestClient;
import lu.circl.mispbump.restful_client.MispServer;
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;
+import lu.circl.mispbump.security.DiffieHellman;
/**
- * 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
+ * This class provides the sync functionality.
+ * It collects the necessary information, guides through the process and finally completes with
+ * the upload to the misp instance.
*/
public class SyncActivity extends AppCompatActivity {
private static final String TAG = "SyncActivity";
- private AESSecurity aesSecurity;
- private MispRestClient restClient;
- private CameraFragment cameraFragment;
+ private CoordinatorLayout layout;
private ImageView qrCodeView;
private FloatingActionButton continueButton;
+ private CameraFragment cameraFragment;
+ private DiffieHellman diffieHellman;
+ private MispRestClient restClient;
+
+ private UploadInformation uploadInformation;
+
private SyncState currentSyncState = SyncState.publicKeyExchange;
+
+ private PreferenceManager preferenceManager;
+
private enum SyncState {
publicKeyExchange,
dataExchange
}
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_sync);
+
+ Toolbar myToolbar = findViewById(R.id.appbar);
+ setSupportActionBar(myToolbar);
+
+ ActionBar ab = getSupportActionBar();
+ if (ab != null) {
+ ab.setDisplayHomeAsUpEnabled(true);
+ }
+
+ layout = findViewById(R.id.layout);
+
+ qrCodeView = findViewById(R.id.qrcode);
+ continueButton = findViewById(R.id.continue_fab);
+ continueButton.setOnClickListener(onContinueClicked);
+ continueButton.hide();
+
+ diffieHellman = DiffieHellman.getInstance();
+ restClient = new MispRestClient(this);
+
+ preferenceManager = PreferenceManager.getInstance(this);
+
+ enableSyncOptionsFragment();
+ }
+
/**
- * Callback to any
+ * This callback is called at the end of each sync step.
*/
private View.OnClickListener onContinueClicked = new View.OnClickListener() {
@Override
@@ -72,7 +104,6 @@ public class SyncActivity extends AppCompatActivity {
switch (currentSyncState) {
case publicKeyExchange:
-
DialogManager.confirmProceedDialog(SyncActivity.this,
new DialogManager.IDialogFeedback() {
@Override
@@ -85,13 +116,21 @@ public class SyncActivity extends AppCompatActivity {
@Override
public void negative() {
- // do nothing, just wait
}
});
break;
case dataExchange:
- // TODO upload
+ DialogManager.confirmProceedDialog(SyncActivity.this, new DialogManager.IDialogFeedback() {
+ @Override
+ public void positive() {
+ startUpload();
+ }
+
+ @Override
+ public void negative() {
+ }
+ });
break;
}
}
@@ -109,69 +148,151 @@ public class SyncActivity extends AppCompatActivity {
switch (currentSyncState) {
case publicKeyExchange:
try {
- final PublicKey pk = AESSecurity.publicKeyFromString(qrData);
+ final PublicKey pk = DiffieHellman.publicKeyFromString(qrData);
+ diffieHellman.setForeignPublicKey(pk);
- DialogManager.publicKeyDialog(pk.toString(), SyncActivity.this,
- new DialogManager.IDialogFeedback() {
- @Override
- public void positive() {
- aesSecurity.setForeignPublicKey(pk);
- continueButton.show();
- }
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ continueButton.show();
+
+ Snackbar sb = Snackbar.make(continueButton, "Public key received", Snackbar.LENGTH_LONG);
+ sb.setAction("Details", new View.OnClickListener() {
@Override
- public void negative() {
- // enable qr read again to scan another pk
- cameraFragment.setReadQrEnabled(true);
+ public void onClick(View v) {
+ DialogManager.publicKeyDialog(pk, SyncActivity.this, null);
}
});
+ sb.show();
+ }
+ });
} catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
- MakeToast("Invalid key");
+ Snackbar.make(layout, "Invalid key", Snackbar.LENGTH_SHORT).show();
}
-
break;
case dataExchange:
- // disable qr read
cameraFragment.setReadQrEnabled(false);
- String data = aesSecurity.decrypt(qrData);
- SyncInformation info = new Gson().fromJson(data, SyncInformation.class);
+ final SyncInformation remoteSyncInfo = new Gson().fromJson(diffieHellman.decrypt(qrData), SyncInformation.class);
- Log.i(TAG, info.organisation.toString());
- Log.i(TAG, info.user.toString());
+ DialogManager.syncInformationDialog(remoteSyncInfo,
+ SyncActivity.this,
+ new DialogManager.IDialogFeedback() {
+ @Override
+ public void positive() {
+ uploadInformation.remote = remoteSyncInfo;
+ continueButton.show();
+ }
+
+ @Override
+ public void negative() {
+ cameraFragment.setReadQrEnabled(true);
+ }
+ });
break;
}
}
};
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_sync);
+ private void startUpload() {
+ // check if misp instance is available
+ restClient.isAvailable(new MispRestClient.AvailableCallback() {
+ @Override
+ public void unavailable() {
+ Snackbar sb = Snackbar.make(layout, "MISP instance not available", Snackbar.LENGTH_LONG);
+ sb.setAction("Retry", new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ startUpload(); // TODO check if this works
+ }
+ });
+ sb.show();
+ }
- Toolbar myToolbar = findViewById(R.id.toolbar);
- setSupportActionBar(myToolbar);
+ @Override
+ public void available() {
- ActionBar ab = getSupportActionBar();
- if (ab != null) {
- ab.setDisplayHomeAsUpEnabled(true);
- }
+ restClient.addOrganisation(uploadInformation.remote.organisation, new MispRestClient.OrganisationCallback() {
+ @Override
+ public void success(final Organisation organisation) {
+ // create syncUser object from syncInfo
+ User syncUser = new User();
+ syncUser.org_id = organisation.id;
+ syncUser.role_id = User.ROLE_SYNC_USER;
- qrCodeView = findViewById(R.id.qrcode);
- continueButton = findViewById(R.id.continue_fab);
- continueButton.setOnClickListener(onContinueClicked);
- continueButton.hide();
+ // syncuser_ORG@REMOTE_ORG_EMAIL_DOMAIN
+ String emailSaveOrgName = organisation.name.replace(" ", "").toLowerCase();
+ syncUser.email = "syncuser_" + emailSaveOrgName + "@misp.de";
- aesSecurity = AESSecurity.getInstance();
- restClient = new MispRestClient(this);
+ syncUser.password = uploadInformation.remote.syncUserPassword;
+ syncUser.authkey = uploadInformation.remote.syncUserAuthkey;
+ syncUser.termsaccepted = true;
- enableSyncOptionsFragment();
+ // add user to local organisation
+ restClient.addUser(syncUser, new MispRestClient.UserCallback() {
+ @Override
+ public void success(User user) {
+ Server server = new Server();
+ server.name = organisation.name + "'s Sync Server";
+ server.url = uploadInformation.remote.baseUrl;
+ server.remote_org_id = organisation.id;
+ server.authkey = uploadInformation.local.syncUserAuthkey;
+ server.self_signed = true;
+
+ restClient.addServer(server, new MispRestClient.ServerCallback() {
+ @Override
+ public void success(List servers) {
+ }
+
+ @Override
+ public void success(MispServer server) {
+ }
+
+ @Override
+ public void success(Server server) {
+ uploadInformation.currentSyncStatus = UploadInformation.SyncStatus.COMPLETE;
+ preferenceManager.setUploadInformation(uploadInformation);
+ finish();
+ }
+
+ @Override
+ public void failure(String error) {
+ uploadInformation.currentSyncStatus = UploadInformation.SyncStatus.FAILURE;
+ preferenceManager.setUploadInformation(uploadInformation);
+ Snackbar.make(layout, error, Snackbar.LENGTH_LONG).show();
+ Log.e(TAG, error);
+ }
+ });
+ }
+
+ @Override
+ public void failure(String error) {
+ uploadInformation.currentSyncStatus = UploadInformation.SyncStatus.FAILURE;
+ preferenceManager.setUploadInformation(uploadInformation);
+ Snackbar.make(layout, error, Snackbar.LENGTH_LONG).show();
+ Log.e(TAG, error);
+ }
+ });
+ }
+
+ @Override
+ public void failure(String error) {
+ uploadInformation.currentSyncStatus = UploadInformation.SyncStatus.FAILURE;
+ preferenceManager.setUploadInformation(uploadInformation);
+ Snackbar.make(layout, error, Snackbar.LENGTH_LONG).show();
+ Log.e(TAG, error);
+ }
+ });
+ }
+ });
}
/**
* Creates the camera fragment used to scan the QR codes.
+ * Automatically starts processing images (search QR codes).
*/
private void enableCameraFragment() {
cameraFragment = new CameraFragment();
@@ -185,7 +306,7 @@ public class SyncActivity extends AppCompatActivity {
}
/**
- * options for this particular sync
+ * Creates fragment to tweak sync options.
*/
private void enableSyncOptionsFragment() {
SyncOptionsFragment syncOptionsFragment = new SyncOptionsFragment();
@@ -205,39 +326,35 @@ public class SyncActivity extends AppCompatActivity {
}
/**
- * Display public key QR code.
+ * Display QR code that contains the public key .
*/
private void showPublicKeyQr() {
QrCodeGenerator qrCodeGenerator = new QrCodeGenerator(this);
- Bitmap bm = qrCodeGenerator.generateQrCode(AESSecurity.publicKeyToString(aesSecurity.getPublicKey()));
+ Bitmap bm = qrCodeGenerator.generateQrCode(DiffieHellman.publicKeyToString(diffieHellman.getPublicKey()));
qrCodeView.setImageBitmap(bm);
qrCodeView.setVisibility(View.VISIBLE);
}
/**
- * Display sync info QR code.
+ * Display QR code that contains mandatory information for a sync.
*/
private void showInformationQr() {
PreferenceManager preferenceManager = PreferenceManager.getInstance(this);
+
+ SyncInformation syncInformation = new SyncInformation();
+
+ syncInformation.organisation = preferenceManager.getUserOrganisation().syncOrganisation();
+ syncInformation.syncUserAuthkey = new RandomString(40).nextString();
+ syncInformation.baseUrl = preferenceManager.getServerUrl();
+ syncInformation.syncUserPassword = "abcdefghijklmnop";
+
+ uploadInformation = new UploadInformation(syncInformation);
+
+ // encrypt serialized content
+ String encrypted = diffieHellman.encrypt(new Gson().toJson(syncInformation));
+
+ // Generate QR code
QrCodeGenerator qrCodeGenerator = new QrCodeGenerator(this);
- Gson gson = new Gson();
-
- // my organisation
- Organisation org = preferenceManager.getUserOrganisation();
- User user = preferenceManager.getUserInfo();
-
- 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() {
@@ -249,61 +366,19 @@ public class SyncActivity extends AppCompatActivity {
});
}
- 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() {
- @Override
- public void run() {
- Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
- }
- });
- }
+// /**
+// * Display toast on UI thread.
+// *
+// * @param message message to display
+// */
+// private void MakeToast(final String message) {
+// this.runOnUiThread(new Runnable() {
+// @Override
+// public void run() {
+// Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
+// }
+// });
+// }
// private View.OnClickListener onGetServers = new View.OnClickListener() {
// @Override
diff --git a/app/src/main/java/lu/circl/mispbump/adapters/SyncAdapter.java b/app/src/main/java/lu/circl/mispbump/adapters/SyncAdapter.java
new file mode 100644
index 0000000..e513ad2
--- /dev/null
+++ b/app/src/main/java/lu/circl/mispbump/adapters/SyncAdapter.java
@@ -0,0 +1,76 @@
+package lu.circl.mispbump.adapters;
+
+import android.support.annotation.NonNull;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.ImageButton;
+import android.widget.TextView;
+
+import java.util.List;
+
+import lu.circl.mispbump.R;
+import lu.circl.mispbump.models.UploadInformation;
+
+public class SyncAdapter extends RecyclerView.Adapter {
+
+ private List uploadInformationList;
+
+ static class SyncViewHolder extends RecyclerView.ViewHolder {
+ TextView title, status;
+ ImageButton retry, delete;
+
+ SyncViewHolder(View v) {
+ super(v);
+
+ title = v.findViewById(R.id.title);
+ status = v.findViewById(R.id.syncStatus);
+
+ retry = v.findViewById(R.id.retry_button);
+ delete = v.findViewById(R.id.delete_button);
+ }
+ }
+
+ public SyncAdapter(List uploadInformationList) {
+ this.uploadInformationList = uploadInformationList;
+ }
+
+ @NonNull
+ @Override
+ public SyncViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
+ View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.viewholder_sync, viewGroup, false);
+ return new SyncViewHolder(v);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull SyncViewHolder syncViewHolder, int i) {
+ syncViewHolder.title.setText(uploadInformationList.get(i).remote.organisation.name);
+
+ switch (uploadInformationList.get(i).currentSyncStatus) {
+ case COMPLETE:
+ syncViewHolder.status.setText("Synced");
+ syncViewHolder.retry.setVisibility(View.GONE);
+ break;
+ case FAILURE:
+ syncViewHolder.status.setText("Error");
+ syncViewHolder.retry.setVisibility(View.VISIBLE);
+ break;
+ case PENDING:
+ syncViewHolder.status.setText("Pending");
+ syncViewHolder.retry.setVisibility(View.GONE);
+ break;
+ }
+ }
+
+ @Override
+ public int getItemCount() {
+ if (uploadInformationList == null) {
+ return 0;
+ }
+
+ return uploadInformationList.size();
+ }
+
+}
diff --git a/app/src/main/java/lu/circl/mispbump/auxiliary/DialogManager.java b/app/src/main/java/lu/circl/mispbump/auxiliary/DialogManager.java
index f8f1833..b511039 100644
--- a/app/src/main/java/lu/circl/mispbump/auxiliary/DialogManager.java
+++ b/app/src/main/java/lu/circl/mispbump/auxiliary/DialogManager.java
@@ -5,7 +5,11 @@ import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
+import java.security.PublicKey;
+
import lu.circl.mispbump.R;
+import lu.circl.mispbump.models.SyncInformation;
+import lu.circl.mispbump.security.DiffieHellman;
/**
* Creates and show dialogs.
@@ -20,21 +24,60 @@ public class DialogManager {
* @param context needed to build and show the dialog
* @param callback {@link IDialogFeedback}
*/
- public static void publicKeyDialog(String publicKey, Context context, final IDialogFeedback callback) {
+ public static void publicKeyDialog(PublicKey publicKey, Context context, final IDialogFeedback callback) {
final AlertDialog.Builder adb = new AlertDialog.Builder(context);
- adb.setTitle("Public Key Received");
- adb.setMessage(publicKey);
+ adb.setTitle("Public Key");
+
+ String message = "Algorithm: " + publicKey.getAlgorithm() + "\n" +
+ "Format: " + publicKey.getFormat() + "\n" +
+ "Content: \n" + DiffieHellman.publicKeyToString(publicKey);
+
+ adb.setMessage(message);
+ adb.setPositiveButton("Okay", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (callback != null) {
+ callback.positive();
+ }
+ }
+ });
+
+ Activity act = (Activity) context;
+
+ act.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ adb.create().show();
+ }
+ });
+ }
+
+ /**
+ * Dialog to display a received public key.
+ *
+ * @param syncInformation {@link SyncInformation}
+ * @param context needed to build and show the dialog
+ * @param callback {@link IDialogFeedback}
+ */
+ public static void syncInformationDialog(SyncInformation syncInformation, Context context, final IDialogFeedback callback) {
+ final AlertDialog.Builder adb = new AlertDialog.Builder(context);
+ adb.setTitle("Sync information received");
+ adb.setMessage(syncInformation.organisation.name);
adb.setPositiveButton("Accept", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- callback.positive();
+ if (callback != null) {
+ callback.positive();
+ }
}
});
adb.setNegativeButton("Reject", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- callback.negative();
+ if (callback != null) {
+ callback.negative();
+ }
}
});
@@ -56,24 +99,27 @@ public class DialogManager {
*/
public static void confirmProceedDialog(Context context, final IDialogFeedback callback) {
final AlertDialog.Builder adb = new AlertDialog.Builder(context);
- adb.setTitle("Really continue?");
- adb.setMessage("Was this QR Code already scanned by your partner?");
- adb.setPositiveButton("Yes, continue", new DialogInterface.OnClickListener() {
+ adb.setTitle("Continue?");
+ adb.setMessage("Only continue if your partner already scanned this QR code");
+ adb.setPositiveButton("Continue", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- callback.positive();
+ if (callback != null) {
+ callback.positive();
+ }
}
});
- adb.setNegativeButton("No, show QR Code", new DialogInterface.OnClickListener() {
+ adb.setNegativeButton("Show QR code again", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- callback.negative();
+ if (callback != null) {
+ callback.negative();
+ }
}
});
Activity act = (Activity) context;
-
act.runOnUiThread(new Runnable() {
@Override
public void run() {
diff --git a/app/src/main/java/lu/circl/mispbump/auxiliary/PreferenceManager.java b/app/src/main/java/lu/circl/mispbump/auxiliary/PreferenceManager.java
index 1963166..c13c28d 100644
--- a/app/src/main/java/lu/circl/mispbump/auxiliary/PreferenceManager.java
+++ b/app/src/main/java/lu/circl/mispbump/auxiliary/PreferenceManager.java
@@ -2,10 +2,13 @@ package lu.circl.mispbump.auxiliary;
import android.content.Context;
import android.content.SharedPreferences;
+import android.util.Log;
import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
import java.io.IOException;
+import java.lang.reflect.Type;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
@@ -13,17 +16,23 @@ import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
+import java.util.ArrayList;
+import java.util.List;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
+import lu.circl.mispbump.models.UploadInformation;
import lu.circl.mispbump.restful_client.Organisation;
import lu.circl.mispbump.restful_client.User;
import lu.circl.mispbump.security.KeyStoreWrapper;
+import static android.support.constraint.Constraints.TAG;
+
public class PreferenceManager {
+ private static final String TAG = "PreferenceManager";
private static final String PREFERENCES_FILE = "user_settings";
@@ -34,6 +43,8 @@ public class PreferenceManager {
private static final String USER_INFOS = "user_infos";
private static final String USER_ORG_INFOS = "user_org_infos";
+ private static final String UPLOAD_INFO = "upload_info";
+
private SharedPreferences preferences;
private static PreferenceManager instance;
@@ -59,7 +70,7 @@ public class PreferenceManager {
/**
* Saves user infos from "users/view/me" (encrypted)
*
- * @param user
+ * @param user {@link User}
*/
public void setUserInfo(User user) {
try {
@@ -178,7 +189,7 @@ public class PreferenceManager {
/**
* Encrypts the automation key and stores it in preferences.
*
- * @param automationKey
+ * @param automationKey key entered in {@link lu.circl.mispbump.activities.LoginActivity}
*/
public void setAutomationKey(String automationKey) {
try {
@@ -202,7 +213,8 @@ public class PreferenceManager {
/**
* Decrypts the stored automation key and returns it.
*
- * @return the decr
+ * @return decrypted automation key associated with the current user. If no user exists an empty
+ * String is returned.
*/
public String getAutomationKey() {
@@ -316,6 +328,37 @@ public class PreferenceManager {
}
+ public void setUploadInformation(UploadInformation uploadInformation) {
+ String storedUploadInfoString = preferences.getString(UPLOAD_INFO, "");
+
+ Type type = new TypeToken>() {}.getType();
+ List dataList;
+
+ if (storedUploadInfoString.isEmpty()) {
+ dataList = new ArrayList<>();
+ } else {
+ dataList = new Gson().fromJson(storedUploadInfoString, type);
+ }
+
+ dataList.add(uploadInformation);
+
+ SharedPreferences.Editor editor = preferences.edit();
+ editor.putString(UPLOAD_INFO, new Gson().toJson(dataList));
+ editor.apply();
+ }
+
+ public List getUploadInformation() {
+ String storedUploadInfoString = preferences.getString(UPLOAD_INFO, "");
+
+ if (storedUploadInfoString.isEmpty()) {
+ return null;
+ }
+
+ Type type = new TypeToken>() {}.getType();
+ return new Gson().fromJson(storedUploadInfoString, type);
+ }
+
+
/**
* Set if credentials (authkey & server url) should be saved locally.
*
diff --git a/app/src/main/java/lu/circl/mispbump/models/SyncInformation.java b/app/src/main/java/lu/circl/mispbump/models/SyncInformation.java
index fb83f3f..abb3501 100644
--- a/app/src/main/java/lu/circl/mispbump/models/SyncInformation.java
+++ b/app/src/main/java/lu/circl/mispbump/models/SyncInformation.java
@@ -1,8 +1,6 @@
package lu.circl.mispbump.models;
import lu.circl.mispbump.restful_client.Organisation;
-import lu.circl.mispbump.restful_client.Server;
-import lu.circl.mispbump.restful_client.User;
/**
* A Class that holds the information needed synchronize two misp instances.
@@ -10,13 +8,10 @@ import lu.circl.mispbump.restful_client.User;
*/
public class SyncInformation {
- public User user;
public Organisation organisation;
- public Server server;
+ public String syncUserPassword;
+ public String syncUserAuthkey;
+ public String baseUrl;
- public SyncInformation(User user, Organisation organisation, Server server) {
- this.user = user;
- this.organisation = organisation;
- this.server = server;
- }
+ public SyncInformation() {}
}
diff --git a/app/src/main/java/lu/circl/mispbump/models/UploadInformation.java b/app/src/main/java/lu/circl/mispbump/models/UploadInformation.java
new file mode 100644
index 0000000..b61b4ae
--- /dev/null
+++ b/app/src/main/java/lu/circl/mispbump/models/UploadInformation.java
@@ -0,0 +1,27 @@
+package lu.circl.mispbump.models;
+
+public class UploadInformation {
+
+ public enum SyncStatus {
+ COMPLETE,
+ FAILURE,
+ PENDING
+ }
+
+ public SyncStatus currentSyncStatus = SyncStatus.PENDING;
+ public SyncInformation local;
+ public SyncInformation remote;
+
+ public UploadInformation() {
+ }
+
+ public UploadInformation(SyncInformation local) {
+ this.local = local;
+ }
+
+ public UploadInformation(SyncInformation local, SyncInformation remote) {
+ this.local = local;
+ this.remote = remote;
+ }
+
+}
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 7c781e1..9c89081 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
@@ -2,9 +2,18 @@ package lu.circl.mispbump.restful_client;
import android.annotation.SuppressLint;
import android.content.Context;
+import android.util.Log;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonObject;
+
+import org.json.JSONException;
+import org.json.JSONObject;
import java.io.IOException;
import java.security.cert.CertificateException;
+import java.util.Iterator;
import java.util.List;
import javax.net.ssl.HostnameVerifier;
@@ -18,6 +27,7 @@ import lu.circl.mispbump.auxiliary.PreferenceManager;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
+import okhttp3.ResponseBody;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Call;
import retrofit2.Callback;
@@ -31,6 +41,13 @@ import retrofit2.converter.gson.GsonConverterFactory;
*/
public class MispRestClient {
+ private static final String TAG = "restClient";
+
+ public interface AvailableCallback {
+ void available();
+ void unavailable();
+ }
+
public interface UserCallback {
void success(User user);
void failure(String error);
@@ -44,6 +61,7 @@ public class MispRestClient {
public interface ServerCallback {
void success(List servers);
void success(MispServer server);
+ void success(Server server);
void failure(String error);
}
@@ -58,9 +76,13 @@ public class MispRestClient {
public MispRestClient(Context context) {
preferenceManager = PreferenceManager.getInstance(context);
+ String url = preferenceManager.getServerUrl();
+
+ Log.i(TAG, "URL: " + url);
+
try {
Retrofit retrofit = new Retrofit.Builder()
- .baseUrl(preferenceManager.getServerUrl())
+ .baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.client(getUnsafeOkHttpClient())
.build();
@@ -137,11 +159,41 @@ public class MispRestClient {
}
}
+ // status routes
+
+ /**
+ * Check via pyMispRoute if server is available
+ * @param callback {@link AvailableCallback}
+ */
+ public void isAvailable(final AvailableCallback callback) {
+ Call call = mispRestService.pyMispVersion();
+ call.enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ if (!response.isSuccessful()) {
+ if (response.code() == 403) {
+ callback.available();
+ return;
+ }
+
+ callback.unavailable();
+ } else {
+ callback.available();
+ }
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ callback.unavailable();
+ }
+ });
+ }
+
// user routes
/**
* Fetches information about the user that is associated with saved auth key.
- * @param callback wrapper to return a user directly
+ * @param callback {@link UserCallback} wrapper to return user directly
*/
public void getMyUser(final UserCallback callback) {
Call call = mispRestService.getMyUserInformation();
@@ -150,7 +202,7 @@ public class MispRestClient {
@Override
public void onResponse(Call call, Response response) {
if(!response.isSuccessful()) {
- callback.failure("" + response.code());
+ callback.failure(extractError(response));
} else {
if (response.body() != null) {
callback.success(response.body().user);
@@ -171,7 +223,7 @@ public class MispRestClient {
/**
* Get an user with specific ID.
* @param userId user identifier
- * @param callback wrapper to return user directly
+ * @param callback {@link UserCallback} wrapper to return user directly
*/
public void getUser(int userId, final UserCallback callback) {
Call call = mispRestService.getUser(userId);
@@ -180,7 +232,7 @@ public class MispRestClient {
@Override
public void onResponse(Call call, Response response) {
if(!response.isSuccessful()) {
- callback.failure("" + response.code());
+ callback.failure(extractError(response));
} else {
if (response.body() != null) {
callback.success(response.body().user);
@@ -201,7 +253,7 @@ public class MispRestClient {
/**
* Add a given user to the MISP instance referenced by url in preferences.
* @param user user to add
- * @param callback wrapper to return the created user directly
+ * @param callback {@link UserCallback} wrapper to return the created user directly
*/
public void addUser(User user, final UserCallback callback) {
Call call = mispRestService.addUser(user);
@@ -209,12 +261,11 @@ public class MispRestClient {
call.enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) {
- if (!response.isSuccessful()) {
- callback.failure("" + response.code());
- return;
+ if(!response.isSuccessful()) {
+ callback.failure(extractError(response));
+ } else {
+ callback.success(response.body().user);
}
-
- callback.success(response.body().user);
}
@Override
@@ -230,7 +281,7 @@ public class MispRestClient {
/**
* Get an organisation by a given organisation id.
* @param orgId organisation identifier
- * @param callback wrapper to return a organisation directly
+ * @param callback {@link OrganisationCallback} wrapper to return a organisation directly
*/
public void getOrganisation(int orgId, final OrganisationCallback callback) {
Call call = mispRestService.getOrganisation(orgId);
@@ -239,7 +290,7 @@ public class MispRestClient {
@Override
public void onResponse(Call call, Response response) {
if(!response.isSuccessful()) {
- callback.failure("" + response.code());
+ callback.failure(extractError(response));
} else {
if (response.body() != null) {
callback.success(response.body().organisation);
@@ -259,7 +310,7 @@ public class MispRestClient {
/**
* Add a given organisation to the MISP instance referenced by url in preferences.
* @param organisation organisation to add
- * @param callback wrapper to return the created organisation directly
+ * @param callback {@link OrganisationCallback} wrapper to return the created organisation directly
*/
public void addOrganisation(Organisation organisation, final OrganisationCallback callback) {
Call call = mispRestService.addOrganisation(organisation);
@@ -267,12 +318,11 @@ public class MispRestClient {
call.enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) {
- if (!response.isSuccessful()) {
- callback.failure("" + response.code());
- return;
+ if(!response.isSuccessful()) {
+ callback.failure(extractError(response));
+ } else {
+ callback.success(response.body().organisation);
}
-
- callback.success(response.body().organisation);
}
@Override
@@ -286,7 +336,7 @@ public class MispRestClient {
/**
* Get all servers on MISP instance.
- * @param callback wrapper to return a list of servers directly
+ * @param callback {@link OrganisationCallback} wrapper to return a list of servers directly
*/
public void getServers(final ServerCallback callback) {
Call> call = mispRestService.getServers();
@@ -294,12 +344,11 @@ public class MispRestClient {
call.enqueue(new Callback>() {
@Override
public void onResponse(Call> call, Response> response) {
- if (!response.isSuccessful()) {
- callback.failure("" + response.code());
- return;
+ if(!response.isSuccessful()) {
+ callback.failure(extractError(response));
+ } else {
+ callback.success(response.body());
}
-
- callback.success(response.body());
}
@Override
@@ -312,26 +361,87 @@ public class MispRestClient {
/**
* Add a server to the MISP instance
* @param server the server to create
- * @param callback wrapper to return the created server directly
+ * @param callback {@link ServerCallback} wrapper to return the created server directly
*/
- public void addServer(MispServer server, final ServerCallback callback) {
- Call call = mispRestService.addServer(server);
+// public void addServer(MispServer server, final ServerCallback callback) {
+// Call call = mispRestService.addServer(server);
+//
+// call.enqueue(new Callback() {
+// @Override
+// public void onResponse(Call call, Response response) {
+// if(!response.isSuccessful()) {
+// callback.failure(extractError(response));
+// } else {
+// callback.success(response.body());
+// }
+// }
+//
+// @Override
+// public void onFailure(Call call, Throwable t) {
+// callback.failure(t.getMessage());
+// }
+// });
+// }
- call.enqueue(new Callback() {
+ public void addServer(Server server, final ServerCallback callback) {
+ Call call = mispRestService.addServer(server);
+
+ call.enqueue(new Callback() {
@Override
- public void onResponse(Call call, Response response) {
+ public void onResponse(Call call, Response response) {
if (!response.isSuccessful()) {
- callback.failure("" + response.code());
- return;
+ callback.failure(extractError(response));
+ } else {
+ callback.success(response.body());
}
-
- callback.success(response.body());
}
@Override
- public void onFailure(Call call, Throwable t) {
+ public void onFailure(Call call, Throwable t) {
callback.failure(t.getMessage());
}
});
}
+
+ private String extractError(Response response) {
+ switch (response.code()) {
+ // bad request (malformed)
+ case 400:
+ return "Bad request";
+
+ // unauthorized
+ case 401:
+ return "Unauthorized";
+
+ // forbidden
+ case 403:
+ try {
+ JSONObject jsonError = new JSONObject(response.errorBody().string());
+
+ String name = jsonError.getString("name") + "\n";
+ String reasons = "";
+ JSONObject errorReasons = jsonError.getJSONObject("errors");
+
+ Iterator errorKeys = errorReasons.keys();
+
+ while (errorKeys.hasNext()) {
+ reasons = reasons.concat(errorReasons.getString(errorKeys.next()) + "\n");
+ }
+
+ return name + reasons;
+ } catch (JSONException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ return "Could not parse (403) error";
+
+ // not found
+ case 404:
+ return "Not found";
+ }
+
+ return "Unknown error";
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/lu/circl/mispbump/restful_client/MispRestService.java b/app/src/main/java/lu/circl/mispbump/restful_client/MispRestService.java
index eeb7786..92daf1a 100644
--- a/app/src/main/java/lu/circl/mispbump/restful_client/MispRestService.java
+++ b/app/src/main/java/lu/circl/mispbump/restful_client/MispRestService.java
@@ -13,6 +13,11 @@ import retrofit2.http.Path;
*/
public interface MispRestService {
+ // settings routes
+
+ @GET("servers/getPyMISPVersion")
+ Call pyMispVersion();
+
// user routes
@GET("users/view/me")
@@ -37,6 +42,9 @@ public interface MispRestService {
@GET("servers/index")
Call> getServers();
+// @POST("servers/add")
+// Call addServer(@Body MispServer server);
+
@POST("servers/add")
- Call addServer(@Body MispServer server);
+ Call addServer(@Body Server server);
}
\ No newline at end of file
diff --git a/app/src/main/java/lu/circl/mispbump/restful_client/MispServer.java b/app/src/main/java/lu/circl/mispbump/restful_client/MispServer.java
index d8038b8..1c7b9b3 100644
--- a/app/src/main/java/lu/circl/mispbump/restful_client/MispServer.java
+++ b/app/src/main/java/lu/circl/mispbump/restful_client/MispServer.java
@@ -6,6 +6,8 @@ import com.google.gson.annotations.SerializedName;
public class MispServer {
+ public MispServer() {}
+
public MispServer(Server server, Organisation organisation, Organisation remoteOrganisation) {
this.server = server;
this.organisation = organisation;
diff --git a/app/src/main/java/lu/circl/mispbump/restful_client/Organisation.java b/app/src/main/java/lu/circl/mispbump/restful_client/Organisation.java
index 7ec4fb4..dcf0539 100644
--- a/app/src/main/java/lu/circl/mispbump/restful_client/Organisation.java
+++ b/app/src/main/java/lu/circl/mispbump/restful_client/Organisation.java
@@ -1,13 +1,13 @@
package lu.circl.mispbump.restful_client;
-import com.google.gson.annotations.Expose;
-import com.google.gson.annotations.SerializedName;
-
/**
* Information gathered from Misp API about a organisation.
*/
public class Organisation {
+ public Organisation() {
+ }
+
public Organisation(String name) {
this.name = name;
}
@@ -17,49 +17,35 @@ public class Organisation {
this.description = description;
}
- @SerializedName("id")
- @Expose
public Integer id;
- @SerializedName("name")
- @Expose
public String name;
- @SerializedName("date_created")
- @Expose
public String date_created;
- @SerializedName("date_modified")
- @Expose
public String date_modified;
- @SerializedName("type")
- @Expose
public String type;
- @SerializedName("nationality")
- @Expose
public String nationality;
- @SerializedName("sector")
- @Expose
public String sector;
- @SerializedName("contacts")
- @Expose
public String contacts;
- @SerializedName("description")
- @Expose
public String description;
- @SerializedName("local")
- @Expose
public Boolean local;
- @SerializedName("uuid")
- @Expose
public String uuid;
- @SerializedName("restricted_to_domain")
- @Expose
public String restricted_to_domain;
- @SerializedName("created_by")
- @Expose
public String created_by;
- @SerializedName("user_count")
- @Expose
public Integer user_count;
+ public Organisation syncOrganisation() {
+ Organisation organisation = new Organisation();
+ organisation.local = true;
+ organisation.name = name;
+ organisation.uuid = uuid;
+ organisation.description = description;
+ organisation.nationality = nationality;
+ organisation.sector = sector;
+ organisation.type = "Sync organisation";
+ organisation.contacts = contacts;
+
+ return organisation;
+ }
+
@Override
public String toString() {
return "Organisation{" +
diff --git a/app/src/main/java/lu/circl/mispbump/restful_client/Server.java b/app/src/main/java/lu/circl/mispbump/restful_client/Server.java
index 0ec307c..99ba9ad 100644
--- a/app/src/main/java/lu/circl/mispbump/restful_client/Server.java
+++ b/app/src/main/java/lu/circl/mispbump/restful_client/Server.java
@@ -1,10 +1,11 @@
package lu.circl.mispbump.restful_client;
-import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class Server {
+ public Server() {}
+
public Server(String name, String url, String authkey, Integer remote_org_id) {
this.name = name;
this.url = url;
@@ -13,91 +14,69 @@ public class Server {
}
@SerializedName("id")
- @Expose
public Integer id;
@SerializedName("name")
- @Expose
public String name;
@SerializedName("url")
- @Expose
public String url;
@SerializedName("authkey")
- @Expose
public String authkey;
@SerializedName("org_id")
- @Expose
public Integer org_id;
@SerializedName("push")
- @Expose
public Boolean push;
@SerializedName("pull")
- @Expose
public Boolean pull;
@SerializedName("lastpulledid")
- @Expose
public Object lastpulledid;
@SerializedName("lastpushedid")
- @Expose
public Object lastpushedid;
@SerializedName("organization")
- @Expose
public Object organization;
@SerializedName("remote_org_id")
- @Expose
public Integer remote_org_id;
@SerializedName("publish_without_email")
- @Expose
- public Boolean publish_without_email;
+ public Boolean publish_without_email = false;
@SerializedName("unpublish_event")
- @Expose
public Boolean unpublish_event;
@SerializedName("self_signed")
- @Expose
- public Boolean self_signed;
+ public Boolean self_signed = false;
@SerializedName("pull_rules")
- @Expose
public String pull_rules;
@SerializedName("push_rules")
- @Expose
public String push_rules;
@SerializedName("cert_file")
- @Expose
public Object cert_file;
@SerializedName("client_cert_file")
- @Expose
public Object client_cert_file;
@SerializedName("internal")
- @Expose
public Boolean internal;
@SerializedName("skip_proxy")
- @Expose
- public Boolean skip_proxy;
+ public Boolean skip_proxy = false;
@SerializedName("caching_enabled")
- @Expose
public Boolean caching_enabled;
@SerializedName("cache_timestamp")
- @Expose
public Boolean cache_timestamp;
@Override
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 1458bc1..4535419 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
@@ -12,6 +12,9 @@ public class User {
public static final int ROLE_SYNC_USER = 5;
public static final int ROLE_READ_ONLY = 6;
+ public User() {
+ }
+
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/restful_client/Version.java b/app/src/main/java/lu/circl/mispbump/restful_client/Version.java
new file mode 100644
index 0000000..5ba921e
--- /dev/null
+++ b/app/src/main/java/lu/circl/mispbump/restful_client/Version.java
@@ -0,0 +1,10 @@
+package lu.circl.mispbump.restful_client;
+
+import com.google.gson.annotations.SerializedName;
+
+public class Version {
+
+ @SerializedName("version")
+ public String version;
+
+}
diff --git a/app/src/main/java/lu/circl/mispbump/security/AESSecurity.java b/app/src/main/java/lu/circl/mispbump/security/DiffieHellman.java
similarity index 90%
rename from app/src/main/java/lu/circl/mispbump/security/AESSecurity.java
rename to app/src/main/java/lu/circl/mispbump/security/DiffieHellman.java
index 32b7b8e..6a0ebb7 100644
--- a/app/src/main/java/lu/circl/mispbump/security/AESSecurity.java
+++ b/app/src/main/java/lu/circl/mispbump/security/DiffieHellman.java
@@ -12,9 +12,11 @@ import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
-public class AESSecurity {
-
- private static final String TAG = "MISP_LOGGING";
+/**
+ * This class provides the functionality generate a shared secret key.
+ * Furthermore it contains the encryption/decryption methods.
+ */
+public class DiffieHellman {
private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";
private static final String KEY_PAIR_ALGORITHM = "EC";
@@ -22,7 +24,7 @@ public class AESSecurity {
private static final String KEY_AGREEMENT_ALGORITHM = "ECDH";
private static final String KEY_FACTORY_ALGORITHM = "EC";
- private static AESSecurity instance;
+ private static DiffieHellman instance;
private PublicKey publickey;
private KeyAgreement keyAgreement;
@@ -30,14 +32,19 @@ public class AESSecurity {
private byte[] sharedSecret;
private IvParameterSpec ivParameterSpec;
- private AESSecurity() {
+ private DiffieHellman() {
initialize();
}
- public static AESSecurity getInstance() {
+ /**
+ * Singleton pattern
+ * @return {@link DiffieHellman}
+ */
+ public static DiffieHellman getInstance() {
if(instance == null) {
- instance = new AESSecurity();
+ instance = new DiffieHellman();
}
+
return instance;
}
@@ -102,7 +109,7 @@ public class AESSecurity {
}
/**
- * Decrypts data.
+ * Decrypts data with the current shared secret.
* @param data data to decrypt
* @return To String converted and decrypted data
*/
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 01e5d67..7e044e1 100644
--- a/app/src/main/java/lu/circl/mispbump/security/KeyStoreWrapper.java
+++ b/app/src/main/java/lu/circl/mispbump/security/KeyStoreWrapper.java
@@ -16,6 +16,7 @@ import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
+import java.util.Arrays;
import java.util.Enumeration;
import javax.crypto.BadPaddingException;
@@ -172,11 +173,10 @@ public class KeyStoreWrapper {
final Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
- byte[] encryptedData = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
- String encryptedDataString = Base64.encodeToString(encryptedData, Base64.DEFAULT);
- String ivString = Base64.encodeToString(cipher.getIV(), Base64.DEFAULT);
-
- return ivString + ":::" + encryptedDataString;
+ byte[] byteData = data.getBytes(StandardCharsets.UTF_8);
+// byte[] byteData = Base64.decode(data, Base64.DEFAULT);
+ byte[] combined = getCombinedArray(cipher.getIV(), cipher.doFinal(byteData));
+ return Base64.encodeToString(combined, Base64.NO_WRAP);
}
/**
@@ -193,17 +193,19 @@ public class KeyStoreWrapper {
public String decrypt(String input) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
// extract iv from save data
- String[] parts = input.split(":::");
+// String[] parts = input.split(":::");
+// byte[] iv = Base64.decode(parts[0], Base64.DEFAULT);
+// byte[] data = Base64.decode(parts[1], Base64.DEFAULT);
- byte[] iv = Base64.decode(parts[0], Base64.DEFAULT);
- byte[] data = Base64.decode(parts[1], Base64.DEFAULT);
+ byte[] in = Base64.decode(input, Base64.NO_WRAP);
+ IvAndData ivAndData = splitCombinedArray(in, 12);
final Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
- final GCMParameterSpec gcmSpec = new GCMParameterSpec(128, iv);
+ final GCMParameterSpec gcmSpec = new GCMParameterSpec(128, ivAndData.iv);
cipher.init(Cipher.DECRYPT_MODE, getStoredKey(), gcmSpec);
- return new String(cipher.doFinal(data), StandardCharsets.UTF_8);
+ return new String(cipher.doFinal(ivAndData.data), StandardCharsets.UTF_8);
}
/**
@@ -242,11 +244,33 @@ public class KeyStoreWrapper {
* @param encryptedData encrypted data
* @return combination of iv and encrypted data
*/
- private static byte[] getCombinedArray(byte[] iv, byte[] encryptedData) {
+ private byte[] getCombinedArray(byte[] iv, byte[] encryptedData) {
+
+ Log.i(TAG, "iv length = " + iv.length);
+
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;
}
+
+ private IvAndData splitCombinedArray(byte[] input, int ivLength) {
+ byte[] iv = Arrays.copyOfRange(input, 0, ivLength);
+ byte[] data = Arrays.copyOfRange(input, ivLength, input.length);
+ return new IvAndData(iv, data);
+ }
+
+ public class IvAndData {
+
+ public IvAndData() {}
+
+ public IvAndData(byte[] iv, byte[] data) {
+ this.iv = iv;
+ this.data = data;
+ }
+
+ public byte[] iv;
+ public byte[] data;
+ }
}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_add.xml b/app/src/main/res/drawable/ic_add.xml
new file mode 100644
index 0000000..e3979cd
--- /dev/null
+++ b/app/src/main/res/drawable/ic_add.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_autorenew.xml b/app/src/main/res/drawable/ic_autorenew.xml
new file mode 100644
index 0000000..3bdee48
--- /dev/null
+++ b/app/src/main/res/drawable/ic_autorenew.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_domain.xml b/app/src/main/res/drawable/ic_domain.xml
new file mode 100644
index 0000000..b7e6b25
--- /dev/null
+++ b/app/src/main/res/drawable/ic_domain.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_location.xml b/app/src/main/res/drawable/ic_location.xml
new file mode 100644
index 0000000..7ce2348
--- /dev/null
+++ b/app/src/main/res/drawable/ic_location.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_person.xml b/app/src/main/res/drawable/ic_person.xml
index c902e21..d7366bd 100644
--- a/app/src/main/res/drawable/ic_person.xml
+++ b/app/src/main/res/drawable/ic_person.xml
@@ -1,4 +1,4 @@
-
diff --git a/app/src/main/res/drawable/ic_sector.xml b/app/src/main/res/drawable/ic_sector.xml
new file mode 100644
index 0000000..eb94181
--- /dev/null
+++ b/app/src/main/res/drawable/ic_sector.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/layout/actionbar_home.xml b/app/src/main/res/layout/actionbar_home.xml
new file mode 100644
index 0000000..de0f835
--- /dev/null
+++ b/app/src/main/res/layout/actionbar_home.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_home.xml b/app/src/main/res/layout/activity_home.xml
index cd57ba8..09d81f2 100644
--- a/app/src/main/res/layout/activity_home.xml
+++ b/app/src/main/res/layout/activity_home.xml
@@ -1,119 +1,118 @@
-
+ android:layout_height="match_parent">
-
+ android:layout_height="wrap_content">
-
+
+ android:layout_height="wrap_content"
+ android:paddingBottom="16dp">
-
-
-
-
-
-
-
+
+
+ tools:text="Financial" />
+
+
+ app:layout_constraintHorizontal_bias="0.5"
+ app:layout_constraintStart_toEndOf="@+id/sector"
+ app:layout_constraintTop_toTopOf="parent"
+ tools:text="Germany" />
+
-
+
+
+
+ android:layout_gravity="bottom|end"
+ android:layout_margin="16dp"
+ android:src="@drawable/ic_add" />
-
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml
index 25439c7..dbba8f7 100644
--- a/app/src/main/res/layout/activity_login.xml
+++ b/app/src/main/res/layout/activity_login.xml
@@ -1,6 +1,6 @@
+ app:layout_constraintTop_toBottomOf="@+id/appbar" />
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..ced079b
--- /dev/null
+++ b/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_sync.xml b/app/src/main/res/layout/activity_sync.xml
index 8183756..110ebb2 100644
--- a/app/src/main/res/layout/activity_sync.xml
+++ b/app/src/main/res/layout/activity_sync.xml
@@ -1,14 +1,16 @@
-
+ android:layout_width="match_parent">
-
+
-
-
\ No newline at end of file
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/viewholder_sync.xml b/app/src/main/res/layout/viewholder_sync.xml
new file mode 100644
index 0000000..93e8677
--- /dev/null
+++ b/app/src/main/res/layout/viewholder_sync.xml
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/main_menu.xml b/app/src/main/res/menu/main_menu.xml
index 94008b0..7078251 100644
--- a/app/src/main/res/menu/main_menu.xml
+++ b/app/src/main/res/menu/main_menu.xml
@@ -2,15 +2,10 @@