diff --git a/Chiota.sln b/Chiota.sln index c11b3ee..b3892b8 100644 --- a/Chiota.sln +++ b/Chiota.sln @@ -11,6 +11,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Chiota.UWP", "Chiota\Chiota EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NTRUEngine", "Chiota\NTRU-NET-master\NTRUEngine.csproj", "{A25C6AB8-531E-4A7E-9968-305003D56332}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UWPRuntimeComponent", "UWPRuntimeComponent\UWPRuntimeComponent.csproj", "{A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -339,6 +341,66 @@ Global {A25C6AB8-531E-4A7E-9968-305003D56332}.Release|x64.Build.0 = Release|Any CPU {A25C6AB8-531E-4A7E-9968-305003D56332}.Release|x86.ActiveCfg = Release|x86 {A25C6AB8-531E-4A7E-9968-305003D56332}.Release|x86.Build.0 = Release|x86 + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Debug|ARM.ActiveCfg = Debug|ARM + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Debug|ARM.Build.0 = Debug|ARM + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Debug|ARM64.Build.0 = Debug|Any CPU + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Debug|x64.ActiveCfg = Debug|x64 + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Debug|x64.Build.0 = Debug|x64 + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Debug|x86.ActiveCfg = Debug|x86 + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Debug|x86.Build.0 = Debug|x86 + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Fast|Any CPU.ActiveCfg = Release|Any CPU + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Fast|Any CPU.Build.0 = Release|Any CPU + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Fast|ARM.ActiveCfg = Release|ARM + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Fast|ARM.Build.0 = Release|ARM + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Fast|ARM64.ActiveCfg = Release|Any CPU + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Fast|ARM64.Build.0 = Release|Any CPU + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Fast|x64.ActiveCfg = Release|x64 + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Fast|x64.Build.0 = Release|x64 + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Fast|x86.ActiveCfg = Release|x86 + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Fast|x86.Build.0 = Release|x86 + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Generic|Any CPU.ActiveCfg = Release|Any CPU + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Generic|Any CPU.Build.0 = Release|Any CPU + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Generic|ARM.ActiveCfg = Release|ARM + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Generic|ARM.Build.0 = Release|ARM + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Generic|ARM64.ActiveCfg = Release|Any CPU + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Generic|ARM64.Build.0 = Release|Any CPU + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Generic|x64.ActiveCfg = Release|x64 + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Generic|x64.Build.0 = Release|x64 + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Generic|x86.ActiveCfg = Release|x86 + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Generic|x86.Build.0 = Release|x86 + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Optimized-fast|Any CPU.ActiveCfg = Release|Any CPU + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Optimized-fast|Any CPU.Build.0 = Release|Any CPU + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Optimized-fast|ARM.ActiveCfg = Release|ARM + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Optimized-fast|ARM.Build.0 = Release|ARM + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Optimized-fast|ARM64.ActiveCfg = Release|Any CPU + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Optimized-fast|ARM64.Build.0 = Release|Any CPU + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Optimized-fast|x64.ActiveCfg = Release|x64 + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Optimized-fast|x64.Build.0 = Release|x64 + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Optimized-fast|x86.ActiveCfg = Release|x86 + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Optimized-fast|x86.Build.0 = Release|x86 + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Optimized-generic|Any CPU.ActiveCfg = Release|Any CPU + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Optimized-generic|Any CPU.Build.0 = Release|Any CPU + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Optimized-generic|ARM.ActiveCfg = Release|ARM + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Optimized-generic|ARM.Build.0 = Release|ARM + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Optimized-generic|ARM64.ActiveCfg = Release|Any CPU + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Optimized-generic|ARM64.Build.0 = Release|Any CPU + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Optimized-generic|x64.ActiveCfg = Release|x64 + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Optimized-generic|x64.Build.0 = Release|x64 + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Optimized-generic|x86.ActiveCfg = Release|x86 + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Optimized-generic|x86.Build.0 = Release|x86 + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Release|Any CPU.Build.0 = Release|Any CPU + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Release|ARM.ActiveCfg = Release|ARM + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Release|ARM.Build.0 = Release|ARM + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Release|ARM64.ActiveCfg = Release|Any CPU + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Release|ARM64.Build.0 = Release|Any CPU + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Release|x64.ActiveCfg = Release|x64 + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Release|x64.Build.0 = Release|x64 + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Release|x86.ActiveCfg = Release|x86 + {A7FCA39F-0FEE-4213-B0D8-EA6B7B228E50}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Chiota/Chiota.Android/Chiota.Android.csproj b/Chiota/Chiota.Android/Chiota.Android.csproj index a06ffcb..70229e0 100644 --- a/Chiota/Chiota.Android/Chiota.Android.csproj +++ b/Chiota/Chiota.Android/Chiota.Android.csproj @@ -59,6 +59,9 @@ + + ..\..\..\..\..\.nuget\packages\zxing.net.mobile\2.4.1\lib\MonoAndroid71\ZXingNetMobile.dll + @@ -80,7 +83,7 @@ 0.6.0 - 9.1.0 + 9.1.1 3.1.3 @@ -94,7 +97,7 @@ 3.0.0-beta14 - + @@ -105,12 +108,14 @@ - - + + - + + + @@ -160,6 +165,10 @@ {a569a7a8-467d-45bd-8848-00f6a0dc34bc} Chiota + + {a25c6ab8-531e-4a7e-9968-305003d56332} + NTRUEngine + @@ -176,5 +185,8 @@ + + + \ No newline at end of file diff --git a/Chiota/Chiota.Android/MainActivity.cs b/Chiota/Chiota.Android/MainActivity.cs index fcfae89..3909f94 100644 --- a/Chiota/Chiota.Android/MainActivity.cs +++ b/Chiota/Chiota.Android/MainActivity.cs @@ -1,17 +1,21 @@ namespace Chiota.Droid { using Android.App; + using Android.Content; using Android.Content.PM; using Android.OS; using Chiota; - + using Chiota.Droid.Services; + using Chiota.Messages; using ImageCircle.Forms.Plugin.Droid; using Plugin.LocalNotifications; using Plugin.Permissions; + using Xamarin.Forms; + [Activity(Label = "FlorenceApp", Icon = "@drawable/icon", Theme = "@style/MainTheme", MainLauncher = false, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)] public class MainActivity : Xamarin.Forms.Platform.Android.FormsAppCompatActivity { @@ -30,9 +34,10 @@ protected override void OnCreate(Bundle bundle) ImageCircleRenderer.Init(); // Changes the notification icon - LocalNotificationsImplementation.NotificationIconId = Resource.Drawable.icon; + LocalNotificationsImplementation.NotificationIconId = Resource.Drawable.reminder; this.LoadApplication(new App()); + this.WireUpLongRunningTask(); } // https://github.com/jamesmontemagno/MediaPlugin#important-permission-information @@ -41,5 +46,26 @@ public override void OnRequestPermissionsResult(int requestCode, string[] permis ZXing.Net.Mobile.Android.PermissionsHandler.OnRequestPermissionsResult(requestCode, permissions, grantResults); PermissionsImplementation.Current.OnRequestPermissionsResult(requestCode, permissions, grantResults); } + + public void WireUpLongRunningTask() + { + MessagingCenter.Subscribe( + this, + "StartLongRunningTaskMessage", + message => + { + var intent = new Intent(this, typeof(PeriodicTaskService)); + this.StartService(intent); + }); + + MessagingCenter.Subscribe( + this, + "StopLongRunningTaskMessage", + message => + { + var intent = new Intent(this, typeof(PeriodicTaskService)); + this.StopService(intent); + }); + } } } \ No newline at end of file diff --git a/Chiota/Chiota.Android/Properties/AndroidManifest.xml b/Chiota/Chiota.Android/Properties/AndroidManifest.xml index f68b9a5..c7bb1ae 100644 --- a/Chiota/Chiota.Android/Properties/AndroidManifest.xml +++ b/Chiota/Chiota.Android/Properties/AndroidManifest.xml @@ -6,5 +6,6 @@ + \ No newline at end of file diff --git a/Chiota/Chiota.Android/FlatButtonRenderer.cs b/Chiota/Chiota.Android/Renderer/FlatButtonRenderer.cs similarity index 100% rename from Chiota/Chiota.Android/FlatButtonRenderer.cs rename to Chiota/Chiota.Android/Renderer/FlatButtonRenderer.cs diff --git a/Chiota/Chiota.Android/Scrollbardisabledrenderer.cs b/Chiota/Chiota.Android/Renderer/Scrollbardisabledrenderer.cs similarity index 100% rename from Chiota/Chiota.Android/Scrollbardisabledrenderer.cs rename to Chiota/Chiota.Android/Renderer/Scrollbardisabledrenderer.cs diff --git a/Chiota/Chiota.Android/Resources/Resource.designer.cs b/Chiota/Chiota.Android/Resources/Resource.designer.cs index d1b80ec..2d99489 100644 --- a/Chiota/Chiota.Android/Resources/Resource.designer.cs +++ b/Chiota/Chiota.Android/Resources/Resource.designer.cs @@ -28,7 +28,12 @@ public static void UpdateIdValues() { global::Plugin.LocalNotifications.Resource.Drawable.plugin_lc_smallicon = global::Chiota.Droid.Resource.Drawable.plugin_lc_smallicon; global::Xamarin.Forms.Platform.Android.Resource.Attribute.actionBarSize = global::Chiota.Droid.Resource.Attribute.actionBarSize; + global::ZXing.Net.Mobile.Forms.Android.Resource.Layout.zxingscanneractivitylayout = global::Chiota.Droid.Resource.Layout.zxingscanneractivitylayout; + global::ZXing.Net.Mobile.Forms.Android.Resource.Layout.zxingscannerfragmentlayout = global::Chiota.Droid.Resource.Layout.zxingscannerfragmentlayout; global::ZXing.Net.Mobile.Forms.Android.Resource.String.library_name = global::Chiota.Droid.Resource.String.library_name; + global::ZXing.Mobile.Resource.Id.contentFrame = global::Chiota.Droid.Resource.Id.contentFrame; + global::ZXing.Mobile.Resource.Layout.zxingscanneractivitylayout = global::Chiota.Droid.Resource.Layout.zxingscanneractivitylayout; + global::ZXing.Mobile.Resource.Layout.zxingscannerfragmentlayout = global::Chiota.Droid.Resource.Layout.zxingscannerfragmentlayout; } public partial class Animation @@ -2350,26 +2355,26 @@ public partial class Drawable // aapt resource value: 0x7f020053 public const int avd_hide_password = 2130837587; - // aapt resource value: 0x7f020135 - public const int avd_hide_password_1 = 2130837813; - // aapt resource value: 0x7f020136 - public const int avd_hide_password_2 = 2130837814; + public const int avd_hide_password_1 = 2130837814; // aapt resource value: 0x7f020137 - public const int avd_hide_password_3 = 2130837815; + public const int avd_hide_password_2 = 2130837815; + + // aapt resource value: 0x7f020138 + public const int avd_hide_password_3 = 2130837816; // aapt resource value: 0x7f020054 public const int avd_show_password = 2130837588; - // aapt resource value: 0x7f020138 - public const int avd_show_password_1 = 2130837816; - // aapt resource value: 0x7f020139 - public const int avd_show_password_2 = 2130837817; + public const int avd_show_password_1 = 2130837817; // aapt resource value: 0x7f02013a - public const int avd_show_password_3 = 2130837818; + public const int avd_show_password_2 = 2130837818; + + // aapt resource value: 0x7f02013b + public const int avd_show_password_3 = 2130837819; // aapt resource value: 0x7f020055 public const int design_bottom_navigation_item_background = 2130837589; @@ -3016,11 +3021,11 @@ public partial class Drawable // aapt resource value: 0x7f02012b public const int notification_icon_background = 2130837803; - // aapt resource value: 0x7f020133 - public const int notification_template_icon_bg = 2130837811; - // aapt resource value: 0x7f020134 - public const int notification_template_icon_low_bg = 2130837812; + public const int notification_template_icon_bg = 2130837812; + + // aapt resource value: 0x7f020135 + public const int notification_template_icon_low_bg = 2130837813; // aapt resource value: 0x7f02012c public const int notification_tile_bg = 2130837804; @@ -3035,13 +3040,16 @@ public partial class Drawable public const int plus = 2130837807; // aapt resource value: 0x7f020130 - public const int splash_screen = 2130837808; + public const int reminder = 2130837808; // aapt resource value: 0x7f020131 - public const int tooltip_frame_dark = 2130837809; + public const int splash_screen = 2130837809; // aapt resource value: 0x7f020132 - public const int tooltip_frame_light = 2130837810; + public const int tooltip_frame_dark = 2130837810; + + // aapt resource value: 0x7f020133 + public const int tooltip_frame_light = 2130837811; static Drawable() { @@ -3194,6 +3202,9 @@ public partial class Id // aapt resource value: 0x7f09008c public const int container = 2131296396; + // aapt resource value: 0x7f0900c8 + public const int contentFrame = 2131296456; + // aapt resource value: 0x7f090065 public const int contentPanel = 2131296357; @@ -3317,8 +3328,8 @@ public partial class Id // aapt resource value: 0x7f090060 public const int list_item = 2131296352; - // aapt resource value: 0x7f0900c9 - public const int masked = 2131296457; + // aapt resource value: 0x7f0900ca + public const int masked = 2131296458; // aapt resource value: 0x7f0900b8 public const int media_actions = 2131296440; @@ -3650,8 +3661,8 @@ public partial class Id // aapt resource value: 0x7f090016 public const int view_offset_helper = 2131296278; - // aapt resource value: 0x7f0900c8 - public const int visible = 2131296456; + // aapt resource value: 0x7f0900c9 + public const int visible = 2131296457; // aapt resource value: 0x7f0900aa public const int volume_item_container = 2131296426; @@ -3953,6 +3964,12 @@ public partial class Layout // aapt resource value: 0x7f030044 public const int tooltip = 2130903108; + // aapt resource value: 0x7f030045 + public const int zxingscanneractivitylayout = 2130903109; + + // aapt resource value: 0x7f030046 + public const int zxingscannerfragmentlayout = 2130903110; + static Layout() { global::Android.Runtime.ResourceIdManager.UpdateIdValues(); diff --git a/Chiota/Chiota.Android/Resources/drawable/iota.png b/Chiota/Chiota.Android/Resources/drawable/iota.png index 7d03b99..4f76f10 100644 Binary files a/Chiota/Chiota.Android/Resources/drawable/iota.png and b/Chiota/Chiota.Android/Resources/drawable/iota.png differ diff --git a/Chiota/Chiota.Android/Resources/drawable/old/reminder.png b/Chiota/Chiota.Android/Resources/drawable/old/reminder.png new file mode 100644 index 0000000..5e3af84 Binary files /dev/null and b/Chiota/Chiota.Android/Resources/drawable/old/reminder.png differ diff --git a/Chiota/Chiota.Android/Resources/drawable/reminder.png b/Chiota/Chiota.Android/Resources/drawable/reminder.png new file mode 100644 index 0000000..9a5cb91 Binary files /dev/null and b/Chiota/Chiota.Android/Resources/drawable/reminder.png differ diff --git a/Chiota/Chiota.Android/Services/BackgroundReceiver.cs b/Chiota/Chiota.Android/Services/BackgroundReceiver.cs new file mode 100644 index 0000000..02bd003 --- /dev/null +++ b/Chiota/Chiota.Android/Services/BackgroundReceiver.cs @@ -0,0 +1,46 @@ +namespace Chiota.Droid.Services +{ + using System.Linq; + + using Android.Content; + using Android.OS; + + using Chiota.Models; + using Chiota.Services; + + using Plugin.LocalNotifications; + + [BroadcastReceiver] + public class BackgroundReceiver : BroadcastReceiver + { + public override async void OnReceive(Context context, Intent intent) + { + var pm = (PowerManager)context.GetSystemService(Context.PowerService); + var wakeLock = pm.NewWakeLock(WakeLockFlags.Partial, "BackgroundReceiver"); + wakeLock.Acquire(); + + // seed needs to be stored on device!! + var secureStorage = new SecureStorage(); + if (secureStorage.CheckUserStored()) + { + var user = await secureStorage.GetUser(); + if (user != null) + { + var contactApprovedList = await user.TangleMessenger.GetJsonMessageAsync>(user.ApprovedAddress); + + // Todo also message for a new contact request + foreach (var contact in contactApprovedList.Where(c => !c.Data.Rejected)) + { + var encryptedMessages = await user.TangleMessenger.GetMessagesAsync(contact.Data.ChatAdress); + foreach (var unused in encryptedMessages.Where(c => !c.Stored)) + { + CrossLocalNotifications.Current.Show(contact.Data.Name, "New Message from " + contact.Data.Name); + } + } + } + } + + wakeLock.Release(); + } + } +} \ No newline at end of file diff --git a/Chiota/Chiota.Android/ClipboardService.cs b/Chiota/Chiota.Android/Services/ClipboardService.cs similarity index 100% rename from Chiota/Chiota.Android/ClipboardService.cs rename to Chiota/Chiota.Android/Services/ClipboardService.cs diff --git a/Chiota/Chiota.Android/Services/PeriodicTaskService.cs b/Chiota/Chiota.Android/Services/PeriodicTaskService.cs new file mode 100644 index 0000000..4295962 --- /dev/null +++ b/Chiota/Chiota.Android/Services/PeriodicTaskService.cs @@ -0,0 +1,78 @@ +namespace Chiota.Droid.Services +{ + using System.Threading; + using System.Threading.Tasks; + + using Android.App; + using Android.Content; + using Android.OS; + using Android.Runtime; + + using Chiota.Messages; + + using Xamarin.Forms; + + //Todo https://blog.xamarin.com/replacing-services-jobs-android-oreo-8-0/ + [Service] + public class PeriodicTaskService : Service + { + private CancellationTokenSource cts; + + public override IBinder OnBind(Intent intent) + { + return null; + } + + public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId) + { + this.cts = new CancellationTokenSource(); + + Task.Run( + () => + { + try + { + // start backgroundreceiver + var alarmIntent = new Intent(this, typeof(BackgroundReceiver)); + + var pending = PendingIntent.GetBroadcast(this, 0, alarmIntent, PendingIntentFlags.UpdateCurrent); + + var alarmManager = this.GetSystemService(AlarmService).JavaCast(); + + // alarmManager.Set(AlarmType.ElapsedRealtime, SystemClock.ElapsedRealtime() + 3 * 1000, pending); + alarmManager.SetRepeating( + AlarmType.RtcWakeup, + SystemClock.ElapsedRealtime(), + 1000 * 60 * 15, // updates every 15 minutes + pending); + } + catch (OperationCanceledException) + { + } + finally + { + if (this.cts.IsCancellationRequested) + { + var message = new CancelledMessage(); + Device.BeginInvokeOnMainThread(() => MessagingCenter.Send(message, "CancelledMessage")); + } + } + }, + this.cts.Token); + + return StartCommandResult.Sticky; + } + + public override void OnDestroy() + { + if (this.cts != null) + { + this.cts.Token.ThrowIfCancellationRequested(); + + this.cts.Cancel(); + } + + base.OnDestroy(); + } + } +} \ No newline at end of file diff --git a/Chiota/Chiota.Android/SplashActivity.cs b/Chiota/Chiota.Android/SplashActivity.cs index 9e10de1..ea6c29d 100644 --- a/Chiota/Chiota.Android/SplashActivity.cs +++ b/Chiota/Chiota.Android/SplashActivity.cs @@ -1,30 +1,16 @@ namespace Chiota.Droid { - using System.Threading.Tasks; - using Android.App; using Android.Content; - using Android.OS; - using Android.Support.V7.App; - - using Chiota.Droid; [Activity(Theme = "@style/SplashTheme", MainLauncher = true, NoHistory = true)] - public class SplashActivity : Activity + public class SplashActivity : Activity + { + // Launches the startup task + protected override void OnResume() { - static readonly string TAG = "X:" + typeof(SplashActivity).Name; - - public override void OnCreate(Bundle savedInstanceState, PersistableBundle persistentState) - { - base.OnCreate(savedInstanceState, persistentState); - } - - // Launches the startup task - protected override void OnResume() - { - base.OnResume(); - this.StartActivity(new Intent(Application.Context, typeof(MainActivity))); - } - + base.OnResume(); + this.StartActivity(new Intent(Application.Context, typeof(MainActivity))); } + } } \ No newline at end of file diff --git a/Chiota/Chiota.UWP/Assets/photothumb.db b/Chiota/Chiota.UWP/Assets/photothumb.db index 9054b9a..3dc5640 100644 Binary files a/Chiota/Chiota.UWP/Assets/photothumb.db and b/Chiota/Chiota.UWP/Assets/photothumb.db differ diff --git a/Chiota/Chiota.UWP/Chiota.UWP.csproj b/Chiota/Chiota.UWP/Chiota.UWP.csproj index 4f194ef..fce6780 100644 --- a/Chiota/Chiota.UWP/Chiota.UWP.csproj +++ b/Chiota/Chiota.UWP/Chiota.UWP.csproj @@ -151,14 +151,21 @@ 2.0.2 + + 3.0.0-beta14 + - 2.5.0.280555 + 2.5.1.444934 2.4.1 + + {a7fca39f-0fee-4213-b0d8-ea6b7b228e50} + UWPRuntimeComponent + {a569a7a8-467d-45bd-8848-00f6a0dc34bc} Chiota diff --git a/Chiota/Chiota.UWP/MainPage.xaml.cs b/Chiota/Chiota.UWP/MainPage.xaml.cs index 76efad0..f50d6ec 100644 --- a/Chiota/Chiota.UWP/MainPage.xaml.cs +++ b/Chiota/Chiota.UWP/MainPage.xaml.cs @@ -1,5 +1,17 @@ namespace Chiota.UWP { + using System; + using System.Linq; + using System.Threading.Tasks; + + using Chiota.Models; + using Chiota.Services; + + using Plugin.LocalNotifications; + + using Windows.ApplicationModel.Background; + using Windows.UI.Xaml; + /// /// An empty page that can be used on its own or navigated to within a Frame. /// @@ -8,8 +20,94 @@ public sealed partial class MainPage public MainPage() { this.InitializeComponent(); - LoadApplication(new Chiota.App()); + this.LoadApplication(new Chiota.App()); + ZXing.Net.Mobile.Forms.WindowsUniversal.ZXingScannerViewRenderer.Init(); + + if (this.IsRegistered()) + { + this.Deregister(); + } + + this.Loaded += this.MainPageLoaded; + } + + private async void MainPageLoaded(object sender, RoutedEventArgs e) + { + await this.BackgroundTask(); + } + + private void Deregister() + { + var taskName = "BackgroundTask"; + + foreach (var task in BackgroundTaskRegistration.AllTasks) + { + if (task.Value.Name == taskName) + { + task.Value.Unregister(true); + } + } + } + + private bool IsRegistered() + { + var taskName = "BackgroundTask"; + + foreach (var task in BackgroundTaskRegistration.AllTasks) + { + if (task.Value.Name == taskName) + { + return true; + } + } + + return false; + } + + private async Task BackgroundTask() + { + BackgroundExecutionManager.RemoveAccess(); + + await BackgroundExecutionManager.RequestAccessAsync(); + + var builder = new BackgroundTaskBuilder + { + Name = "BackgroundTask", + TaskEntryPoint = "UWPRuntimeComponent.BackgroundTask" + }; + + builder.SetTrigger(new TimeTrigger(15, false)); + var task = builder.Register(); + task.Completed += this.TaskCompleted; + } + + private async void TaskCompleted(BackgroundTaskRegistration sender, BackgroundTaskCompletedEventArgs args) + { + var settings = Windows.Storage.ApplicationData.Current.LocalSettings; + var key = "BackgroundTask"; + var message = settings.Values[key].ToString(); + + // Run your background task code here + var secureStorage = new SecureStorage(); + if (secureStorage.CheckUserStored()) + { + var user = await secureStorage.GetUser(); + if (user != null) + { + var contactApprovedList = await user.TangleMessenger.GetJsonMessageAsync>(user.ApprovedAddress); + + // Todo also message for a new contact request + foreach (var contact in contactApprovedList.Where(c => !c.Data.Rejected)) + { + var encryptedMessages = await user.TangleMessenger.GetMessagesAsync(contact.Data.ChatAdress); + foreach (var unused in encryptedMessages.Where(c => !c.Stored)) + { + CrossLocalNotifications.Current.Show(contact.Data.Name, "New Message from " + contact.Data.Name); + } + } + } + } } } } diff --git a/Chiota/Chiota.UWP/Package.appxmanifest b/Chiota/Chiota.UWP/Package.appxmanifest index 2710180..c9d0a1f 100644 --- a/Chiota/Chiota.UWP/Package.appxmanifest +++ b/Chiota/Chiota.UWP/Package.appxmanifest @@ -20,9 +20,17 @@ + + + + + + + + \ No newline at end of file diff --git a/Chiota/Chiota.UWP/iota.png b/Chiota/Chiota.UWP/iota.png index 7d03b99..4f76f10 100644 Binary files a/Chiota/Chiota.UWP/iota.png and b/Chiota/Chiota.UWP/iota.png differ diff --git a/Chiota/Chiota/App.xaml.cs b/Chiota/Chiota/App.xaml.cs index bb572e2..0c4ae10 100644 --- a/Chiota/Chiota/App.xaml.cs +++ b/Chiota/Chiota/App.xaml.cs @@ -4,13 +4,14 @@ namespace Chiota { - using Chiota.CustomCells; + using Chiota.Messages; using Chiota.Services; + using Chiota.Views; using Xamarin.Forms; using ContactPage = Views.ContactPage; - using LoginPage = Chiota.Views.LoginPage; + using LoginPage = Views.LoginPage; /// /// The app. @@ -20,19 +21,24 @@ public partial class App : Application public App() { this.InitializeComponent(); - this.MainPage = new NavigationPage(new LoginPage()); + this.MainPage = new GreyPage(); } public static string AppName => "Chiota"; protected override async void OnStart() { - // Handle when your app starts + // starts listening for messages + var messagestart = new StartLongRunningTaskMessage(); + MessagingCenter.Send(messagestart, "StartLongRunningTaskMessage"); + var secureStorage = new SecureStorage(); if (secureStorage.CheckUserStored()) { var user = await secureStorage.GetUser(); - this.MainPage = new NavigationPage(new ContactPage(user)); + + // user = null => setup probably interrupted + this.MainPage = user != null ? new NavigationPage(new ContactPage(user)) : new NavigationPage(new LoginPage()); } else { diff --git a/Chiota/Chiota/Chiota.csproj b/Chiota/Chiota/Chiota.csproj index a921da6..dad5bc9 100644 --- a/Chiota/Chiota/Chiota.csproj +++ b/Chiota/Chiota/Chiota.csproj @@ -26,12 +26,12 @@ - + - + @@ -52,6 +52,9 @@ ChatPage.xaml + + CheckSeedStoredPage.xaml + SetupPage.xaml @@ -67,6 +70,9 @@ MSBuild:UpdateDesignTimeXaml + + MSBuild:UpdateDesignTimeXaml + MSBuild:UpdateDesignTimeXaml @@ -76,6 +82,9 @@ MSBuild:UpdateDesignTimeXaml + + MSBuild:UpdateDesignTimeXaml + MSBuild:UpdateDesignTimeXaml diff --git a/Chiota/Chiota/IOTAServices/IOTAHelper.cs b/Chiota/Chiota/IOTAServices/IOTAHelper.cs index e8cec65..97d6a31 100644 --- a/Chiota/Chiota/IOTAServices/IOTAHelper.cs +++ b/Chiota/Chiota/IOTAServices/IOTAHelper.cs @@ -7,11 +7,9 @@ using Chiota.Models; using Chiota.Services; - - using Newtonsoft.Json; + using Chiota.ViewModels; using Tangle.Net.Entity; - using Tangle.Net.Mam.Services; using VTDev.Libraries.CEXEngine.Crypto.Cipher.Asymmetric.Encrypt.NTRU; using VTDev.Libraries.CEXEngine.Crypto.Cipher.Asymmetric.Interfaces; @@ -28,58 +26,24 @@ public static bool CorrectSeedAdressChecker(string seed) return seed != string.Empty && seed.All(c => "ABCDEFGHIJKLMNOPQRSTUVWXYZ9".Contains(c)); } - public static Contact FilterRequestInfos(IEnumerable trytes) - { - var contact = new Contact(); - var oneKeyAlreadyFound = false; - foreach (var tryte in trytes) - { - var trytesString = tryte.ToString(); - if (!trytesString.Contains("9CHIOTAYOURIOTACHATAPP9")) - { - continue; - } - - if (oneKeyAlreadyFound) - { - return null; - } - - var index = trytesString.IndexOf("9CHIOTAYOURIOTACHATAPP9", StringComparison.Ordinal); - var publicKeyString = trytesString.Substring(0, index); - var bytesKey = new TryteString(publicKeyString).ToBytes(); - - contact.PublicNtruKey = new NTRUPublicKey(bytesKey); - - contact.ContactAdress = trytesString.Substring(index + 23, 81); - oneKeyAlreadyFound = true; - } - - return contact; - } - - public static List FilterChatMessages(IEnumerable trytes, NtruKex ntruKex, IAsymmetricKeyPair keyPair, DateTime lastPostDate) + public static List FilterChatMessages(IEnumerable trytes, IAsymmetricKeyPair keyPair) { var chatMessages = new List(); foreach (var tryte in trytes) { try { - var trytesString = tryte.ToString(); - var firstBreak = trytesString.IndexOf("9CHIOTAYOUR9", StringComparison.Ordinal); - var secondBreak = trytesString.IndexOf("9IOTACHATAPP9", StringComparison.Ordinal); - var dateTrytes = new TryteString(trytesString.Substring(secondBreak + 13, trytesString.Length - secondBreak - 13)); + var trytesString = tryte.Message.ToString(); + var firstBreak = trytesString.IndexOf(ChiotaIdentifier.FirstBreak, StringComparison.Ordinal); + var secondBreak = trytesString.IndexOf(ChiotaIdentifier.SecondBreak, StringComparison.Ordinal); + var dateTrytes = new TryteString(trytesString.Substring(secondBreak + ChiotaIdentifier.SecondBreak.Length, trytesString.Length - secondBreak - ChiotaIdentifier.SecondBreak.Length)); var date = DateTime.Parse(dateTrytes.ToUtf8String()); - // decrypt only if this is a new message - if (date > lastPostDate) - { - var signature = trytesString.Substring(firstBreak + 12, 30); - var messageTrytes = new TryteString(trytesString.Substring(0, firstBreak)); - var decryptedMessage = ntruKex.Decrypt(keyPair, messageTrytes.ToBytes()); - var chatMessage = new ChatMessage { Message = decryptedMessage, Date = date, Signature = signature }; - chatMessages.Add(chatMessage); - } + var signature = trytesString.Substring(firstBreak + ChiotaIdentifier.FirstBreak.Length, 30); + var messageTrytes = new TryteString(trytesString.Substring(0, firstBreak)); + var decryptedMessage = new NtruKex().Decrypt(keyPair, messageTrytes.ToBytes()); + var chatMessage = new ChatMessage { Message = decryptedMessage, Date = date, Signature = signature }; + chatMessages.Add(chatMessage); } catch { @@ -90,18 +54,27 @@ public static List FilterChatMessages(IEnumerable tryt return chatMessages; } - public static async Task UpdateUserWithTangleInfos(User user, List ownDataWrappers) + public static async Task> GetNewMessages(IAsymmetricKeyPair keyPair, Contact contact, TangleMessenger tangle) { - var trytes = await user.TangleMessenger.GetMessagesAsync(user.PublicKeyAddress, 3); - var contact = FilterRequestInfos(trytes); - var decrypted = new CurlMask().Unmask(ownDataWrappers[0], user.Seed); - var decryptedString = decrypted.ToUtf8String(); - var decryptedUser = JsonConvert.DeserializeObject(decryptedString); - user.Name = decryptedUser.Name; - user.ImageUrl = decryptedUser.ImageUrl; - IAsymmetricKey privateKey = new NTRUPrivateKey(new TryteString(decryptedUser.PrivateKey).ToBytes()); - user.NtruKeyPair = new NTRUKeyPair(contact.PublicNtruKey, privateKey); - return user; + var messages = new List(); + var encryptedMessages = await tangle.GetMessagesAsync(contact.ChatAdress); + var messageList = FilterChatMessages(encryptedMessages, keyPair); + if (messageList != null) + { + var sortedMessageList = messageList.OrderBy(o => o.Date).ToList(); + foreach (var message in sortedMessageList) + { + messages.Add(new MessageViewModel + { + Text = message.Message, + IsIncoming = message.Signature == contact.PublicKeyAdress.Substring(0, 30), + MessagDateTime = message.Date, + ProfileImage = contact.ImageUrl + }); + } + } + + return messages; } public static List GetNewHashes(Tangle.Net.Repository.DataTransfer.TransactionHashList transactions, List storedHashes) @@ -127,5 +100,42 @@ public static List GetNewHashes(Tangle.Net.Repository.DataTransfer.Transac return newHashes; } + + /// + /// Filters Transactions for public key and contact address + /// + /// Transactions in List form + /// null if more than one key + public static List GetPublicKeysAndContactAddresses(IEnumerable trytes) + { + var contacts = new List(); + foreach (var tryte in trytes) + { + var trytesString = tryte.Message.ToString(); + if (!trytesString.Contains(ChiotaIdentifier.LineBreak)) + { + continue; + } + + try + { + var index = trytesString.IndexOf(ChiotaIdentifier.LineBreak, StringComparison.Ordinal); + var publicKeyString = trytesString.Substring(0, index); + var bytesKey = new TryteString(publicKeyString).ToBytes(); + var contact = new Contact + { + PublicNtruKey = new NTRUPublicKey(bytesKey), + ContactAdress = trytesString.Substring(index + ChiotaIdentifier.LineBreak.Length, 81) + }; + contacts.Add(contact); + } + catch + { + // ignored + } + } + + return contacts; + } } } diff --git a/Chiota/Chiota/IOTAServices/TangleMessenger.cs b/Chiota/Chiota/IOTAServices/TangleMessenger.cs index 17f8b64..adb9cb8 100644 --- a/Chiota/Chiota/IOTAServices/TangleMessenger.cs +++ b/Chiota/Chiota/IOTAServices/TangleMessenger.cs @@ -47,10 +47,10 @@ public async Task SendJsonMessageAsync(SentDataWrapper data, string addres await Task.Factory.StartNew(() => this.repository.SendTransfer(this.seed, bundle, SecurityLevel.Medium, 27, 14)); } - public async Task> GetMessagesAsync(string adresse, int retryNumber = 1) + public async Task> GetMessagesAsync(string adresse, int retryNumber = 1) { var roundNumber = 0; - var messagesList = new List(); + var messagesList = new List(); while (roundNumber < retryNumber && messagesList.Count == 0) { // try to update the repository @@ -127,21 +127,25 @@ public async Task> GetJsonMessageAsync(string adresse) return messagesList; } - private async Task MessageFromBundleOrStorage(Hash transactionsHash) + private async Task MessageFromBundleOrStorage(Hash transactionsHash) { // todo upload them again after snapshot - TryteString message; + TryteStringMessage message = new TryteStringMessage(); var hashString = transactionsHash.ToString(); if (Application.Current.Properties.ContainsKey(hashString)) { + // old messages var messageString = Application.Current.Properties[hashString] as string; - message = new TryteString(messageString); + message.Message = new TryteString(messageString); + message.Stored = true; } else { + // new messages var bundle = await this.repository.GetBundleAsync(transactionsHash); - message = this.GetMessages(bundle); - Application.Current.Properties[hashString] = message.ToString(); + message.Message = this.GetMessages(bundle); + message.Stored = false; + Application.Current.Properties[hashString] = message.Message.ToString(); await Application.Current.SavePropertiesAsync(); } diff --git a/Chiota/Chiota/IOTAServices/UserDataOnTangle.cs b/Chiota/Chiota/IOTAServices/UserDataOnTangle.cs new file mode 100644 index 0000000..e3e2f8b --- /dev/null +++ b/Chiota/Chiota/IOTAServices/UserDataOnTangle.cs @@ -0,0 +1,103 @@ +namespace Chiota.IOTAServices +{ + using System; + using System.Collections.Generic; + using System.Threading.Tasks; + + using Chiota.Models; + using Chiota.Services; + + using Newtonsoft.Json; + + using Tangle.Net.Cryptography; + using Tangle.Net.Entity; + using Tangle.Net.Mam.Services; + + using VTDev.Libraries.CEXEngine.Crypto.Cipher.Asymmetric.Encrypt.NTRU; + using VTDev.Libraries.CEXEngine.Crypto.Cipher.Asymmetric.Interfaces; + + public class UserDataOnTangle + { + private readonly NtruKex ntru; + + private readonly User user; + + private IAsymmetricKey privateKey; + + public UserDataOnTangle(User user) + { + this.ntru = new NtruKex(); + this.user = user; + } + + public async Task UpdateUserWithOwnDataAddress() + { + var ownDataWrappers = await this.user.TangleMessenger.GetMessagesAsync(this.user.OwnDataAdress, 3); + if (ownDataWrappers != null && ownDataWrappers.Count > 0) + { + var decryptedString = new CurlMask().Unmask(ownDataWrappers[0].Message, this.user.Seed).ToUtf8String(); + var decryptedUser = JsonConvert.DeserializeObject(decryptedString); + this.privateKey = new NTRUPrivateKey(new TryteString(decryptedUser.PrivateKey).ToBytes()); + this.user.Name = decryptedUser.Name; + this.user.ImageUrl = decryptedUser.ImageUrl; + } + + return this.user; + } + + public async Task UpdateUserWithPublicKeyAddress() + { + var trytes = await this.user.TangleMessenger.GetMessagesAsync(this.user.PublicKeyAddress, 3); + var contacts = IotaHelper.GetPublicKeysAndContactAddresses(trytes); + + // more than one key at this address + if (contacts.Count > 1) + { + contacts = this.FindCorrectPublicKey(contacts); + + // generate a new public key address based on changed seed until you find a unused address + // this way the attacker doesn't know the next public key address + List newContacts; + var addresses = new List
{ new Address(contacts[0].ContactAdress) }; + do + { + var newSeed = this.user.Seed.ToString().Substring(0, 75) + addresses[0].ToString().Substring(0, 6); + addresses = await Task.Factory.StartNew(() => new AddressGenerator(new Seed(newSeed), SecurityLevel.Low).GetAddresses(0, 1)); + var testtrytes = await this.user.TangleMessenger.GetMessagesAsync(addresses[0].ToString(), 3); + newContacts = IotaHelper.GetPublicKeysAndContactAddresses(testtrytes); + + if (newContacts == null) + { + var requestAdressTrytes = new TryteString(contacts[0].PublicNtruKey + ChiotaIdentifier.LineBreak + this.user.RequestAddress + ChiotaIdentifier.End); + await this.user.TangleMessenger.SendMessageAsync(requestAdressTrytes, addresses[0].ToString()); + } + + this.user.PublicKeyAddress = addresses[0].ToString(); + } + while (newContacts?.Count > 1); + } + + this.user.NtruKeyPair = new NTRUKeyPair(contacts[0].PublicNtruKey, this.privateKey); + return this.user; + } + + private List FindCorrectPublicKey(List contacts) + { + const string TestString = "Hello World"; + foreach (var contact in contacts) + { + var keypair = new NTRUKeyPair(contact.PublicNtruKey, this.privateKey); + var encrypt = this.ntru.Encrypt(contact.PublicNtruKey, TestString); + if (TestString == this.ntru.Decrypt(keypair, encrypt)) + { + // Removes all infos except the correct version + contacts.Clear(); + contacts.Add(contact); + break; + } + } + + return contacts; + } + } +} diff --git a/Chiota/Chiota/Messages/CancelledMessage.cs b/Chiota/Chiota/Messages/CancelledMessage.cs new file mode 100644 index 0000000..28f19b3 --- /dev/null +++ b/Chiota/Chiota/Messages/CancelledMessage.cs @@ -0,0 +1,6 @@ +namespace Chiota.Messages +{ + public class CancelledMessage + { + } +} diff --git a/Chiota/Chiota/Messages/StartLongRunningTaskMessage.cs b/Chiota/Chiota/Messages/StartLongRunningTaskMessage.cs new file mode 100644 index 0000000..51b388b --- /dev/null +++ b/Chiota/Chiota/Messages/StartLongRunningTaskMessage.cs @@ -0,0 +1,6 @@ +namespace Chiota.Messages +{ + public class StartLongRunningTaskMessage + { + } +} diff --git a/Chiota/Chiota/Messages/StopLongRunningTaskMessage.cs b/Chiota/Chiota/Messages/StopLongRunningTaskMessage.cs new file mode 100644 index 0000000..c308829 --- /dev/null +++ b/Chiota/Chiota/Messages/StopLongRunningTaskMessage.cs @@ -0,0 +1,6 @@ +namespace Chiota.Messages +{ + public class StopLongRunningTaskMessage + { + } +} diff --git a/Chiota/Chiota/Models/ChiotaIdentifier.cs b/Chiota/Chiota/Models/ChiotaIdentifier.cs new file mode 100644 index 0000000..4d3e111 --- /dev/null +++ b/Chiota/Chiota/Models/ChiotaIdentifier.cs @@ -0,0 +1,13 @@ +namespace Chiota.Models +{ + public class ChiotaIdentifier + { + public static string LineBreak => "9CHIOTAYOURIOTACHATAPP9"; + + public static string End => "9ENDEGUTALLESGUT9"; + + public static string FirstBreak => "9CHIOTAYOUR9"; + + public static string SecondBreak => "9IOTACHATAPP9"; + } +} diff --git a/Chiota/Chiota/Models/DiffieHellmanObject.cs b/Chiota/Chiota/Models/DiffieHellmanObject.cs deleted file mode 100644 index b75023f..0000000 --- a/Chiota/Chiota/Models/DiffieHellmanObject.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Chiota.Models -{ - using Org.BouncyCastle.Crypto; - using Org.BouncyCastle.Math; - - public class DiffieHellmanObject - { - // To be shared - public AsymmetricKeyParameter PublicKey { get; set; } - - public BigInteger PrimInteger { get; set; } - - public BigInteger NaturalInteger { get; set; } - - // To be stored - public AsymmetricKeyParameter PrivateKey { get; set; } - } -} diff --git a/Chiota/Chiota/Models/TryteStringMessage.cs b/Chiota/Chiota/Models/TryteStringMessage.cs new file mode 100644 index 0000000..6161351 --- /dev/null +++ b/Chiota/Chiota/Models/TryteStringMessage.cs @@ -0,0 +1,11 @@ +namespace Chiota.Models +{ + using Tangle.Net.Entity; + + public class TryteStringMessage + { + public TryteString Message { get; set; } + + public bool Stored { get; set; } + } +} diff --git a/Chiota/Chiota/Models/UserFactory.cs b/Chiota/Chiota/Models/UserFactory.cs index 62fcdd5..b45800a 100644 --- a/Chiota/Chiota/Models/UserFactory.cs +++ b/Chiota/Chiota/Models/UserFactory.cs @@ -8,13 +8,13 @@ public class UserFactory { - public User Create(Seed storeSeed, List
addresses, string username = null, string imageUrl = null) + public User Create(Seed storeSeed, List
addresses) { return new User() { - Name = username, + Name = null, Seed = storeSeed, - ImageUrl = imageUrl, + ImageUrl = null, OwnDataAdress = addresses[0].Value, PublicKeyAddress = addresses[1].Value, RequestAddress = addresses[2].Value, diff --git a/Chiota/Chiota/Services/DiffieHellman.cs b/Chiota/Chiota/Services/DiffieHellman.cs deleted file mode 100644 index 494d948..0000000 --- a/Chiota/Chiota/Services/DiffieHellman.cs +++ /dev/null @@ -1,223 +0,0 @@ -namespace Chiota.Services -{ - using System; - using System.Diagnostics; - using System.IO; - - using Chiota.Models; - - using Org.BouncyCastle.Crypto; - using Org.BouncyCastle.Crypto.Engines; - using Org.BouncyCastle.Crypto.Generators; - using Org.BouncyCastle.Crypto.Modes; - using Org.BouncyCastle.Crypto.Parameters; - using Org.BouncyCastle.Math; - using Org.BouncyCastle.Security; - - // https://stackoverflow.com/questions/33813108/bouncycastle-diffie-hellman - // To be replaced by https://en.wikipedia.org/wiki/Supersingular_isogeny_key_exchange - // https://github.com/Microsoft/PQCrypto-SIDH - - public class DiffieHellman - { - private const string Algorithm = "ECDH"; - - private const int KeyBitSize = 256; - - private const int NonceBitSize = 128; - - private const int MacBitSize = 128; - - private const int DefaultPrimeProbability = 30; - - public DiffieHellmanObject CreateAlice() - { - var aliceKeyGen = GeneratorUtilities.GetKeyPairGenerator(Algorithm); - var aliceGenerator = new DHParametersGenerator(); - aliceGenerator.Init(KeyBitSize, DefaultPrimeProbability, new SecureRandom()); - var aliceParameters = aliceGenerator.GenerateParameters(); - - aliceKeyGen.Init(new DHKeyGenerationParameters(new SecureRandom(), aliceParameters)); - - var aliceKeyPair = aliceKeyGen.GenerateKeyPair(); - - return new DiffieHellmanObject - { - PublicKey = aliceKeyPair.Public, - PrimInteger = aliceParameters.P, - NaturalInteger = aliceParameters.G, - PrivateKey = aliceKeyPair.Private - }; - } - - public DiffieHellmanObject CreateBob(DiffieHellmanObject alice) - { - var bobKeyGen = GeneratorUtilities.GetKeyPairGenerator(Algorithm); - var bobParameters = new DHParameters(alice.PrimInteger, alice.NaturalInteger); - - bobKeyGen.Init(new DHKeyGenerationParameters(new SecureRandom(), bobParameters)); - - var bobKeyPair = bobKeyGen.GenerateKeyPair(); - return new DiffieHellmanObject - { - PublicKey = bobKeyPair.Public, - PrimInteger = bobParameters.P, - NaturalInteger = bobParameters.G, - PrivateKey = bobKeyPair.Private - }; - } - - - public static void TestMethod() - { - //BEGIN SETUP ALICE - var aliceKeyGen = GeneratorUtilities.GetKeyPairGenerator(Algorithm); - var aliceGenerator = new DHParametersGenerator(); - aliceGenerator.Init(KeyBitSize, DefaultPrimeProbability, new SecureRandom()); - DHParameters aliceParameters = aliceGenerator.GenerateParameters(); - - var aliceKGP = new DHKeyGenerationParameters(new SecureRandom(), aliceParameters); - aliceKeyGen.Init(aliceKGP); - - AsymmetricCipherKeyPair aliceKeyPair = aliceKeyGen.GenerateKeyPair(); - var aliceKeyAgree = AgreementUtilities.GetBasicAgreement(Algorithm); - aliceKeyAgree.Init(aliceKeyPair.Private); - //END SETUP ALICE - - /////AT THIS POINT, Alice's Public Key, Alice's Parameter P and Alice's Parameter G are sent unsecure to BOB - - //BEGIN SETUP BOB - var bobKeyGen = GeneratorUtilities.GetKeyPairGenerator(Algorithm); - DHParameters bobParameters = new DHParameters(aliceParameters.P, aliceParameters.G); - - KeyGenerationParameters bobKGP = new DHKeyGenerationParameters(new SecureRandom(), bobParameters); - bobKeyGen.Init(bobKGP); - - AsymmetricCipherKeyPair bobKeyPair = bobKeyGen.GenerateKeyPair(); - IBasicAgreement bobKeyAgree = AgreementUtilities.GetBasicAgreement(Algorithm); - bobKeyAgree.Init(bobKeyPair.Private); - //END SETUP BOB - - BigInteger aliceAgree = aliceKeyAgree.CalculateAgreement(bobKeyPair.Public); - BigInteger bobAgree = bobKeyAgree.CalculateAgreement(aliceKeyPair.Public); - - if (!aliceAgree.Equals(bobAgree)) - { - throw new Exception("Keys do not match."); - } - - byte[] nonSecretMessage = GetBytes("HeaderMessageForASDF"); - byte[] secretMessage = GetBytes("Secret message contents"); - byte[] decNonSecretBytes; - - KeyParameter sharedKey = new KeyParameter(aliceAgree.ToByteArrayUnsigned()); - - var encMessage = EncryptMessage(sharedKey, nonSecretMessage, secretMessage); - var decMessage = DecryptMessage(sharedKey, encMessage, out decNonSecretBytes); - - var decNonSecretMessage = GetString(decNonSecretBytes); - var decSecretMessage = GetString(decMessage); - - Debug.WriteLine(decNonSecretMessage + " - " + decSecretMessage); - - return; - } - - public static byte[] EncryptMessage(string sharedKey, string nonSecretMessage, string secretMessage) - { - return EncryptMessage(new KeyParameter(Convert.FromBase64String(sharedKey)), GetBytes(nonSecretMessage), GetBytes(secretMessage)); - } - - public static byte[] EncryptMessage(KeyParameter sharedKey, byte[] nonSecretMessage, byte[] secretMessage) - { - if (nonSecretMessage != null && nonSecretMessage.Length > 255) throw new Exception("Non Secret Message Too Long!"); - byte nonSecretLength = nonSecretMessage == null ? (byte)0 : (byte)nonSecretMessage.Length; - - var nonce = new byte[NonceBitSize / 8]; - var rand = new SecureRandom(); - rand.NextBytes(nonce, 0, nonce.Length); - - var cipher = new GcmBlockCipher(new AesFastEngine()); - var aeadParameters = new AeadParameters(sharedKey, MacBitSize, nonce, nonSecretMessage); - cipher.Init(true, aeadParameters); - - //Generate Cipher Text With Auth Tag - var cipherText = new byte[cipher.GetOutputSize(secretMessage.Length)]; - var len = cipher.ProcessBytes(secretMessage, 0, secretMessage.Length, cipherText, 0); - cipher.DoFinal(cipherText, len); - - using (var combinedStream = new MemoryStream()) - { - using (var binaryWriter = new BinaryWriter(combinedStream)) - { - //Prepend Authenticated Payload - binaryWriter.Write(nonSecretLength); - binaryWriter.Write(nonSecretMessage); - - //Prepend Nonce - binaryWriter.Write(nonce); - //Write Cipher Text - binaryWriter.Write(cipherText); - } - return combinedStream.ToArray(); - } - } - - public static string DecryptMessage(string sharedKey, byte[] encryptedMessage, out string nonSecretPayload) - { - byte[] nonSecretPayloadBytes; - byte[] payload = DecryptMessage(new KeyParameter(Convert.FromBase64String(sharedKey)), encryptedMessage, out nonSecretPayloadBytes); - - nonSecretPayload = GetString(nonSecretPayloadBytes); - return GetString(payload); - } - - public static byte[] DecryptMessage(KeyParameter sharedKey, byte[] encryptedMessage, out byte[] nonSecretPayloadBytes) - { - using (var cipherStream = new MemoryStream(encryptedMessage)) - using (var cipherReader = new BinaryReader(cipherStream)) - { - //Grab Payload - int nonSecretLength = (int)cipherReader.ReadByte(); - nonSecretPayloadBytes = cipherReader.ReadBytes(nonSecretLength); - - //Grab Nonce - var nonce = cipherReader.ReadBytes(NonceBitSize / 8); - - var cipher = new GcmBlockCipher(new AesFastEngine()); - var parameters = new AeadParameters(sharedKey, MacBitSize, nonce, nonSecretPayloadBytes); - cipher.Init(false, parameters); - - //Decrypt Cipher Text - var cipherText = cipherReader.ReadBytes(encryptedMessage.Length - nonSecretLength - nonce.Length); - var plainText = new byte[cipher.GetOutputSize(cipherText.Length)]; - - try - { - var len = cipher.ProcessBytes(cipherText, 0, cipherText.Length, plainText, 0); - cipher.DoFinal(plainText, len); - } - catch (InvalidCipherTextException) - { - //Return null if it doesn't authenticate - return null; - } - - return plainText; - } - } - - static byte[] GetBytes(string str) - { - if (str == null) return null; - return System.Text.Encoding.Unicode.GetBytes(str); - } - - static string GetString(byte[] bytes) - { - if (bytes == null) return null; - return System.Text.Encoding.Unicode.GetString(bytes, 0, bytes.Length); - } - } - -} diff --git a/Chiota/Chiota/Services/NTRUKex.cs b/Chiota/Chiota/Services/NTRUKex.cs index 041fe30..be81772 100644 --- a/Chiota/Chiota/Services/NTRUKex.cs +++ b/Chiota/Chiota/Services/NTRUKex.cs @@ -4,6 +4,8 @@ using System.Linq; using System.Text; + using Tangle.Net.Utils; + using VTDev.Libraries.CEXEngine.Crypto.Cipher.Asymmetric.Encrypt.NTRU; using VTDev.Libraries.CEXEngine.Crypto.Cipher.Asymmetric.Interfaces; @@ -18,7 +20,15 @@ public class NtruKex public IAsymmetricKeyPair CreateAsymmetricKeyPair() { var keyGen = new NTRUKeyGenerator(this.encParams); - return keyGen.GenerateKeyPair(); + var keyPair = keyGen.GenerateKeyPair(); + + // publicKey sometimes has only 1025 bytes instead of 1026 after conversation?! + while (keyPair.PublicKey.ToBytes().ToTrytes().ToBytes().Length != 1026) + { + keyPair = keyGen.GenerateKeyPair(); + } + + return keyPair; } /// diff --git a/Chiota/Chiota/Services/SecureStorage.cs b/Chiota/Chiota/Services/SecureStorage.cs index 9c1ee53..8c6a11b 100644 --- a/Chiota/Chiota/Services/SecureStorage.cs +++ b/Chiota/Chiota/Services/SecureStorage.cs @@ -43,9 +43,18 @@ public async Task GetUser() TangleMessenger = new TangleMessenger(storedSeed) }; - var ownDataWrappers = await user.TangleMessenger.GetMessagesAsync(user.OwnDataAdress, 3); - user = await IotaHelper.UpdateUserWithTangleInfos(user, ownDataWrappers); - return user; + try + { + var tangleData = new UserDataOnTangle(user); + await tangleData.UpdateUserWithOwnDataAddress(); + user = await tangleData.UpdateUserWithPublicKeyAddress(); + return user; + } + catch + { + // incomplete => setup interrupted or not yet finished + return null; + } } public bool StoreUser(User user) diff --git a/Chiota/Chiota/ViewModels/AddContactViewModel.cs b/Chiota/Chiota/ViewModels/AddContactViewModel.cs index 2bfdfda..0fb6025 100644 --- a/Chiota/Chiota/ViewModels/AddContactViewModel.cs +++ b/Chiota/Chiota/ViewModels/AddContactViewModel.cs @@ -93,36 +93,34 @@ private async Task AddContact() { this.IsBusy = true; this.AlreadyClicke = true; - try - { - // get information from receiver adress - var trytes = await this.user.TangleMessenger.GetMessagesAsync(this.ReceiverAdress, 3); - var contact = IotaHelper.FilterRequestInfos(trytes); - if (contact?.PublicNtruKey != null && contact.ContactAdress != null) - { - // faster than generating adresses - var requestContact = new Contact() - { - ChatAdress = Seed.Random().ToString(), - Name = this.user.Name, - ImageUrl = this.user.ImageUrl, - ContactAdress = this.user.ApprovedAddress, - Request = true, - Rejected = false, - PublicNtruKey = null, - PublicKeyAdress = this.user.PublicKeyAddress - }; - - // encrypt contact request? too much infos needed here for one message needs to get request adress plus chatadress - await this.user.TangleMessenger.SendJsonMessageAsync(new SentDataWrapper { Data = requestContact, Sender = this.user.Name }, contact.ContactAdress); - this.SuccessfulRequestPrompt(); - } - } - catch + // get information from receiver adress + var trytes = await this.user.TangleMessenger.GetMessagesAsync(this.ReceiverAdress, 3); + var contacts = IotaHelper.GetPublicKeysAndContactAddresses(trytes); + + if (contacts == null || contacts.Count > 1) { this.DisplayInvalidAdressPrompt(); } + else if (contacts[0]?.PublicNtruKey != null && contacts[0].ContactAdress != null) + { + // faster than generating adresses + var requestContact = new Contact() + { + ChatAdress = Seed.Random().ToString(), + Name = this.user.Name, + ImageUrl = this.user.ImageUrl, + ContactAdress = this.user.ApprovedAddress, + Request = true, + Rejected = false, + PublicNtruKey = null, + PublicKeyAdress = this.user.PublicKeyAddress + }; + + // encrypt contact request? too much infos needed here for one message needs to get request adress plus chatadress + await this.user.TangleMessenger.SendJsonMessageAsync(new SentDataWrapper { Data = requestContact, Sender = this.user.Name }, contacts[0].ContactAdress); + this.SuccessfulRequestPrompt(); + } } else { diff --git a/Chiota/Chiota/ViewModels/ChatViewModel.cs b/Chiota/Chiota/ViewModels/ChatViewModel.cs index a0ea9b3..0427727 100644 --- a/Chiota/Chiota/ViewModels/ChatViewModel.cs +++ b/Chiota/Chiota/ViewModels/ChatViewModel.cs @@ -9,6 +9,7 @@ using System.Windows.Input; using Chiota.IOTAServices; + using Chiota.Messages; using Chiota.Models; using Chiota.Services; @@ -23,7 +24,7 @@ public class ChatViewModel : BaseViewModel { public Action DisplayMessageTooLong; - private readonly string profileImageUrl; + public Action DisplayInvalidPublicKeyPrompt; private readonly User user; @@ -33,8 +34,6 @@ public class ChatViewModel : BaseViewModel private readonly ListView messagesListView; - private DateTime lastPostDate; - private string outgoingText; private ObservableCollection messagesList; @@ -43,11 +42,10 @@ public ChatViewModel(ListView messagesListView, Contact contact, User user) { this.ntruKex = new NtruKex(); this.Messages = new ObservableCollection(); - this.profileImageUrl = contact.ImageUrl; this.user = user; this.contact = contact; this.messagesListView = messagesListView; - this.MessageLoop = true; + this.PageIsShown = true; this.OutGoingText = null; // reset hash short storage, because it's different for every chat @@ -56,7 +54,7 @@ public ChatViewModel(ListView messagesListView, Contact contact, User user) this.SendCommand = new Command(async () => { await this.SendMessage(); }); } - public bool MessageLoop { get; set; } + public bool PageIsShown { get; set; } public string OutGoingText { @@ -82,15 +80,39 @@ public ObservableCollection Messages public async void OnAppearing() { + // cancel if there is no interent this.contact.PublicNtruKey = await this.GetContactPublicKey(); - this.GetMessagesAsync(this.Messages); + if (this.contact.PublicNtruKey == null) + { + // todo: delete contact + this.DisplayInvalidPublicKeyPrompt(); + await this.Navigation.PopAsync(); + } + else + { + this.GetMessagesAsync(this.Messages); + } + + var messagestart = new StartLongRunningTaskMessage(); + MessagingCenter.Send(messagestart, "StopLongRunningTaskMessage"); + } + + public void OnDisappearing() + { + var messagestart = new StartLongRunningTaskMessage(); + MessagingCenter.Send(messagestart, "StartLongRunningTaskMessage"); } private async Task GetContactPublicKey() { - var trytes = await this.user.TangleMessenger.GetMessagesAsync(this.contact.PublicKeyAdress); - var contactInfos = IotaHelper.FilterRequestInfos(trytes); - return contactInfos.PublicNtruKey; + var trytes = await this.user.TangleMessenger.GetMessagesAsync(this.contact.PublicKeyAdress, 3); + var contactInfos = IotaHelper.GetPublicKeysAndContactAddresses(trytes); + if (contactInfos == null || contactInfos.Count > 1) + { + return null; + } + + return contactInfos[0].PublicNtruKey; } private async Task SendMessage() @@ -111,11 +133,11 @@ private async Task SendMessage() // encryption with public key of other user var encryptedForContact = this.ntruKex.Encrypt(this.contact.PublicNtruKey, this.OutGoingText); - var tryteContact = new TryteString(encryptedForContact.ToTrytes() + "9CHIOTAYOUR9" + signature + "9IOTACHATAPP9" + trytesDate + "9ENDEGUTALLESGUT9"); + var tryteContact = new TryteString(encryptedForContact.ToTrytes() + ChiotaIdentifier.FirstBreak + signature + ChiotaIdentifier.SecondBreak + trytesDate + ChiotaIdentifier.End); // encryption with public key of user var encryptedForUser = this.ntruKex.Encrypt(this.user.NtruKeyPair.PublicKey, this.OutGoingText); - var tryteUser = new TryteString(encryptedForUser.ToTrytes() + "9CHIOTAYOUR9" + signature + "9IOTACHATAPP9" + trytesDate + "9ENDEGUTALLESGUT9"); + var tryteUser = new TryteString(encryptedForUser.ToTrytes() + ChiotaIdentifier.FirstBreak + signature + ChiotaIdentifier.SecondBreak + trytesDate + ChiotaIdentifier.End); await this.SendParallelAsync(tryteContact, tryteUser); } @@ -134,7 +156,7 @@ private Task SendParallelAsync(TryteString tryteContact, TryteString tryteUser) private async void GetMessagesAsync(ICollection messages) { - while (this.MessageLoop) + while (this.PageIsShown) { await this.AddNewMessagesAsync(messages); await Task.Delay(9000); @@ -143,28 +165,15 @@ private async void GetMessagesAsync(ICollection messages) private async Task AddNewMessagesAsync(ICollection messages) { - var encryptedMessages = await this.user.TangleMessenger.GetMessagesAsync(this.contact.ChatAdress); - var messageList = IotaHelper.FilterChatMessages(encryptedMessages, this.ntruKex, this.user.NtruKeyPair, this.lastPostDate); - if (messageList != null) + var newMessages = await IotaHelper.GetNewMessages(this.user.NtruKeyPair, this.contact, this.user.TangleMessenger); + if (newMessages.Count > 0) { - var sortedMessageList = messageList.OrderBy(o => o.Date).ToList(); - foreach (var message in sortedMessageList) + foreach (var m in newMessages) { - // might cause problem when two messages send at the exact same time - if (message.Date > this.lastPostDate) - { - messages.Add( - new MessageViewModel - { - Text = message.Message, - IsIncoming = message.Signature == this.contact.PublicKeyAdress.Substring(0, 30), - MessagDateTime = message.Date, - ProfileImage = this.profileImageUrl - }); - this.ScrollToNewMessage(); - this.lastPostDate = message.Date; - } + messages.Add(m); } + + this.ScrollToNewMessage(); } } diff --git a/Chiota/Chiota/ViewModels/CheckSeedStoredViewModel.cs b/Chiota/Chiota/ViewModels/CheckSeedStoredViewModel.cs new file mode 100644 index 0000000..4c0aa43 --- /dev/null +++ b/Chiota/Chiota/ViewModels/CheckSeedStoredViewModel.cs @@ -0,0 +1,66 @@ +namespace Chiota.ViewModels +{ + using System; + using System.Threading.Tasks; + using System.Windows.Input; + + using Chiota.Models; + using Chiota.Views; + + using Xamarin.Forms; + + public class CheckSeedStoredViewModel : BaseViewModel + { + public Action DisplayInvalidSeedPrompt; + + private readonly User user; + + private string seedInput; + + public CheckSeedStoredViewModel(User user) + { + this.user = user; + this.SubmitCommand = new Command(async () => { await this.SeedCheck(); }); + this.BackCommand = new Command(async () => { await this.Back(); }); + } + + public ICommand SubmitCommand { get; protected set; } + + public ICommand BackCommand { get; protected set; } + + public string SeedInput + { + get => this.seedInput; + set + { + this.seedInput = value; + this.RaisePropertyChanged(); + } + } + + private async Task SeedCheck() + { + this.SeedInput = this.SeedInput?.Trim(); + if (this.user.Seed.ToString() != this.SeedInput) + { + this.DisplayInvalidSeedPrompt(); + } + else if (!this.AlreadyClicke) + { + this.AlreadyClicke = true; + await this.Navigation.PushModalAsync(new NavigationPage(new SetupPage(this.user))); + this.AlreadyClicke = false; + } + } + + private async Task Back() + { + if (!this.AlreadyClicke) + { + this.AlreadyClicke = true; + await this.Navigation.PopModalAsync(); + this.AlreadyClicke = false; + } + } + } +} \ No newline at end of file diff --git a/Chiota/Chiota/ViewModels/ContactViewModel.cs b/Chiota/Chiota/ViewModels/ContactViewModel.cs index 4677014..e09d2bf 100644 --- a/Chiota/Chiota/ViewModels/ContactViewModel.cs +++ b/Chiota/Chiota/ViewModels/ContactViewModel.cs @@ -4,13 +4,10 @@ using System.Linq; using System.Threading.Tasks; - using Chiota.CustomCells; using Chiota.Models; using Chiota.Services; - using Xamarin.Forms; - - using ChatPage = Chiota.Views.ChatPage; + using ChatPage = Views.ChatPage; public class ContactViewModel : BaseViewModel { @@ -85,6 +82,7 @@ private async Task UpdateContacts() } } + // Todo store contacts on device private async Task> GetConctacts(string searchText = null) { var contacts = new ObservableCollection(); diff --git a/Chiota/Chiota/ViewModels/LoginViewModel.cs b/Chiota/Chiota/ViewModels/LoginViewModel.cs index 105f33d..55020d2 100644 --- a/Chiota/Chiota/ViewModels/LoginViewModel.cs +++ b/Chiota/Chiota/ViewModels/LoginViewModel.cs @@ -7,6 +7,7 @@ using Chiota.IOTAServices; using Chiota.Models; using Chiota.Services; + using Chiota.Views; using Tangle.Net.Cryptography; using Tangle.Net.Entity; @@ -14,23 +15,25 @@ using Xamarin.Forms; using ContactPage = Views.ContactPage; - using SetupPage = Chiota.Views.SetupPage; public class LoginViewModel : BaseViewModel { + public Action DisplayInvalidLoginPrompt; + private string randomSeed = Seed.Random().Value; private string username; private bool storeSeed; + private UserDataOnTangle dataOnTangle; + public LoginViewModel() { + this.StoreSeed = true; this.SubmitCommand = new Command(async () => { await this.Login(); }); } - public Action DisplayInvalidLoginPrompt; - public bool StoreSeed { get => this.storeSeed; @@ -65,8 +68,8 @@ public string Username private async Task Login() { - this.randomSeed = this.randomSeed?.Trim(); - if (!IotaHelper.CorrectSeedAdressChecker(this.randomSeed)) + this.RandomSeed = this.RandomSeed?.Trim(); + if (!IotaHelper.CorrectSeedAdressChecker(this.RandomSeed)) { this.DisplayInvalidLoginPrompt(); } @@ -74,7 +77,7 @@ private async Task Login() { this.IsBusy = true; this.AlreadyClicke = true; - var seed = new Seed(this.randomSeed); + var seed = new Seed(this.RandomSeed); // 4 addresses needed: // 0. own user data address (encrypted, MAM or private key) @@ -89,17 +92,18 @@ private async Task Login() new SecureStorage().StoreUser(user); } - var ownDataWrappers = await user.TangleMessenger.GetMessagesAsync(user.OwnDataAdress, 3); + this.dataOnTangle = new UserDataOnTangle(user); + user = await this.dataOnTangle.UpdateUserWithOwnDataAddress(); - if (ownDataWrappers == null || ownDataWrappers.Count == 0) + if (user.Name == null) { this.IsBusy = false; this.AlreadyClicke = false; - await this.Navigation.PushModalAsync(new NavigationPage(new SetupPage(user))); + await this.Navigation.PushModalAsync(new NavigationPage(new CheckSeedStoredPage(user))); } else { - user = await IotaHelper.UpdateUserWithTangleInfos(user, ownDataWrappers); + user = await this.dataOnTangle.UpdateUserWithPublicKeyAddress(); this.IsBusy = false; if (user.NtruKeyPair != null) { diff --git a/Chiota/Chiota/ViewModels/SetupViewModel.cs b/Chiota/Chiota/ViewModels/SetupViewModel.cs index fbee5a4..84f4ba3 100644 --- a/Chiota/Chiota/ViewModels/SetupViewModel.cs +++ b/Chiota/Chiota/ViewModels/SetupViewModel.cs @@ -108,14 +108,6 @@ private async Task StoreKeysAndRequestAdress(User user) var publicKeyTrytes = user.NtruKeyPair.PublicKey.ToBytes().ToTrytes(); var privateKeyTrytes = user.NtruKeyPair.PrivateKey.ToBytes().ToTrytes(); - // publicKey sometimes has only 1025 bytes instead of 1026?! - while (publicKeyTrytes.ToBytes().Length != 1026) - { - user.NtruKeyPair = new NtruKex().CreateAsymmetricKeyPair(); - publicKeyTrytes = user.NtruKeyPair.PublicKey.ToBytes().ToTrytes(); - privateKeyTrytes = user.NtruKeyPair.PrivateKey.ToBytes().ToTrytes(); - } - var userData = new UserFactory().CreateUploadUser(user, privateKeyTrytes.ToString()); var serializeObject = JsonConvert.SerializeObject(userData); @@ -127,14 +119,10 @@ private async Task StoreKeysAndRequestAdress(User user) private Task SendParallelAsync(User user, TryteString publicKeyTrytes, TryteString mamEncrypted) { - // not sure this is necessary - const string LineBreak = "9CHIOTAYOURIOTACHATAPP9"; - const string End = "9ENDEGUTALLESGUT9"; - - var firstTransaction = user.TangleMessenger.SendMessageAsync(new TryteString(mamEncrypted + End), user.OwnDataAdress); + var firstTransaction = user.TangleMessenger.SendMessageAsync(new TryteString(mamEncrypted + ChiotaIdentifier.End), user.OwnDataAdress); // only way to store it with one transaction, json to big - var requestAdressTrytes = new TryteString(publicKeyTrytes + LineBreak + user.RequestAddress + End); + var requestAdressTrytes = new TryteString(publicKeyTrytes + ChiotaIdentifier.LineBreak + user.RequestAddress + ChiotaIdentifier.End); var secondTransaction = user.TangleMessenger.SendMessageAsync(requestAdressTrytes, user.PublicKeyAddress); return Task.WhenAll(firstTransaction, secondTransaction); diff --git a/Chiota/Chiota/Views/AddContact.xaml.cs b/Chiota/Chiota/Views/AddContact.xaml.cs index 0a85a43..e4a8b1e 100644 --- a/Chiota/Chiota/Views/AddContact.xaml.cs +++ b/Chiota/Chiota/Views/AddContact.xaml.cs @@ -8,6 +8,9 @@ using Xamarin.Forms; using Xamarin.Forms.Xaml; + /// + /// The add contact. + /// [XamlCompilation(XamlCompilationOptions.Compile)] public partial class AddContact : ContentPage { @@ -18,7 +21,7 @@ public AddContact(User user) var vm = new AddContactViewModel(user) { Navigation = this.Navigation }; - vm.DisplayInvalidAdressPrompt += () => this.DisplayAlert("Error", "Invalid adress, try again", "OK"); + vm.DisplayInvalidAdressPrompt += () => this.DisplayAlert("Error", "Invalid address, try again", "OK"); vm.SuccessfulRequestPrompt += () => this.DisplayAlert("Successful Request", "Your new contact needs to accept the request before you can chat with him!", "OK"); this.ReceiverAdress.Completed += (object sender, EventArgs e) => diff --git a/Chiota/Chiota/Views/ChatPage.xaml b/Chiota/Chiota/Views/ChatPage.xaml index f8dc162..8dcd225 100644 --- a/Chiota/Chiota/Views/ChatPage.xaml +++ b/Chiota/Chiota/Views/ChatPage.xaml @@ -46,11 +46,12 @@ + Command="{Binding SendCommand}"> + diff --git a/Chiota/Chiota/Views/ChatPage.xaml.cs b/Chiota/Chiota/Views/ChatPage.xaml.cs index 9a49cdd..b95a6ff 100644 --- a/Chiota/Chiota/Views/ChatPage.xaml.cs +++ b/Chiota/Chiota/Views/ChatPage.xaml.cs @@ -6,7 +6,12 @@ using Chiota.ViewModels; using Xamarin.Forms; + using Xamarin.Forms.Xaml; + /// + /// The chat page. + /// + [XamlCompilation(XamlCompilationOptions.Compile)] public partial class ChatPage : ContentPage { private ChatViewModel vm; @@ -20,8 +25,9 @@ public ChatPage(Contact contact, User user) } this.Title = contact.Name; - this.vm = new ChatViewModel(MessagesListView, contact, user); + this.vm = new ChatViewModel(MessagesListView, contact, user) { Navigation = this.Navigation }; this.vm.DisplayMessageTooLong += () => this.DisplayAlert("Error", "Sorry, only 105 characters per message are allowed!", "OK"); + this.vm.DisplayInvalidPublicKeyPrompt += () => this.DisplayAlert("Error", "Invalid public key! You contact needs to give you a new contact address.", "OK"); this.BindingContext = this.vm; } @@ -33,7 +39,8 @@ protected override void OnAppearing() protected override void OnDisappearing() { - this.vm.MessageLoop = false; + this.vm.OnDisappearing(); + this.vm.PageIsShown = false; this.vm = null; this.Navigation.PopAsync(); } diff --git a/Chiota/Chiota/Views/CheckSeedStoredPage.xaml b/Chiota/Chiota/Views/CheckSeedStoredPage.xaml new file mode 100644 index 0000000..50cd9fc --- /dev/null +++ b/Chiota/Chiota/Views/CheckSeedStoredPage.xaml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + +