Skip to content
This repository has been archived by the owner on Nov 2, 2021. It is now read-only.

Commit

Permalink
-WARNING: Major Breaking Change! All App Settings now default as Loca…
Browse files Browse the repository at this point in the history
…l, with a boolean Property to make them Roam. This might cause issues if you don't realise it and invert it if set (It use to be all properties roam unless IsLocal = true).

-Removed GetSettingsContainer from Helpers, getting the RoamingSettingsContainer automatically returns Local Settings if not supported.

-You can now find out the Current Services Platform by checking AppServices.ServicePlatform, and UI Platform with AppServices.UI.UIPlatform.

-Default Creation Collision options can now be changed with IOBindings.DefaultFileCreationCollision and IOBindings.DefaultFolderCreationCollision.

-Created IPathResolver as a way for different Layers to check if the provided path from IOBindings.GetFile and IOBindings.GetFolder returns a Valid StorageItem from their service. This is to produce SMBFileContainers from SMB Paths, if a path string is provided. This will also be used to determine Android SAF Files and Folders from a Path.

-Renamed CreateContainer to GetContainer for ISettingsContainer as it will return existing containers.

-Moved Picker Functions out of IOBindings into new Abstract FileSystemPickers Class, as well as FutureAccess Methods into IFutureAccessManager. This reduces the length of the class for Platforms that don't support picking or Future Access. It also makes checking support easier.

-Removed ICredentialManager Update Function, use Store to get back the Platform Credential Container, which will self update.

-CredentialContainers now have platform specific implementations to handle automatic value updating.

-Added more Abstractions to IOBindings, Local/RoamingSettings are now properties instead of methods, renamed OpenFile/Folder to OpenFile/FolderForDisplay as it is more understandable. It is now easier to implement IOBindings due to these changes.

-Added RequestTextFromUserAsync method, a way to request text from the user in a Modal Dialog.

-SMBFile/FolderContainers now use SMB Paths instead of UNC Paths as the path property.

-To use the SMB Extension, you now need to call SMBService.Register().

-Added Type Checking on the UWPSettingsContainer, so that exception don't occur if the value is the wrong type.

-UWP File/Folder Pickers and PromptUser methods are now Background Thread safe, as they will automatically go on the UI thread to work.

-Fixed Android Dialog prompts not working from background threads.

-AndroidSAFFile/Folders now display their paths correctly.

-AndroidSettingsContainer now works properly, creating Settings Containers now produces the Preference Store on the Device, fetching All Subcontainers also works now, GetValue now has Type Safety.

-Created AndroidCredentialManager and AndroidCredentialContainer, Stores credentials in a hidden SettingsContainer, using Encryption. You can replace the IKeyGenerator in AndroidAppSettings to provide a stronger, more secure Credential Encryption.

-Created AndroidFutureAccessManager, Grants permissions and redeemable token.

-Created TestPageGenerators, a cross platform way to generate Test Buttons, and Result Properties quickly.

-Converted most Test Models to TestPages, created Settings and Credential Manager Test Pages.
  • Loading branch information
WilliamABradley committed Nov 19, 2017
1 parent 1b552ee commit 642a259
Show file tree
Hide file tree
Showing 109 changed files with 2,685 additions and 1,125 deletions.
7 changes: 6 additions & 1 deletion Android/AndroidAppServices.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using Android.App;
using PlatformBindings.Enums;
using PlatformBindings.Models.Encryption;
using PlatformBindings.Services;
using System;

Expand All @@ -8,13 +10,15 @@ public class AndroidAppServices : AppServices
{
public AndroidAppServices(bool HasUI, bool UseAppCompatUI) : this(HasUI)
{
KeyGenerator = new DefaultKeyGenerator();
AndroidAppServices.UseAppCompatUI = UseAppCompatUI;
}

public AndroidAppServices(bool HasUI) : base(HasUI)
public AndroidAppServices(bool HasUI) : base(HasUI, Platform.Android)
{
if (HasUI) UI = new AndroidUIBindings();
IO = new AndroidIOBindings();
Credentials = new AndroidCredentialManager();
}

public override Version GetAppVersion()
Expand All @@ -23,6 +27,7 @@ public override Version GetAppVersion()
return new Version(info.VersionName);
}

public static IKeyGenerator KeyGenerator { get; protected set; }
public static bool UseAppCompatUI { get; private set; } = false;
}
}
215 changes: 215 additions & 0 deletions Android/Models/AndroidCredentialContainer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
using System;
using PlatformBindings.Models.Settings;
using Javax.Crypto;
using Java.Security;
using Javax.Crypto.Spec;
using System.Text;
using System.Runtime.InteropServices;
using System.Security;
using Java.IO;

namespace PlatformBindings.Models
{
internal class AndroidCredentialContainer : CredentialContainer
{
public AndroidCredentialContainer(ISettingsContainer Container, CredentialContainer Credentials) : this()
{
this.Container = Container;
var newheader = $"{Credentials.ResourceName}^R^{Credentials.Username}";
EncryptedHeader = Encrypt(newheader, writer);
SetHeaderData(newheader);

Password = Credentials.Password;
}

public AndroidCredentialContainer(ISettingsContainer Container, string Header) : this()
{
this.Container = Container;
EncryptedHeader = Header;
try
{
var decryptedHeader = Decrypt(Header);
SetHeaderData(decryptedHeader);
}
catch
{
Container.RemoveKey(Header);
HeaderData = new string[] { DecryptionError, DecryptionError };
}
}

private void SetHeaderData(string DecryptedHeader)
{
HeaderData = DecryptedHeader.Split(new string[] { "^R^" }, StringSplitOptions.None);
}

public override string ResourceName { get => HeaderData[0]; }
public override string Username { get => HeaderData[1]; }

public override string Password
{
get
{
try
{
var encryptedVal = Container.GetValue<string>(EncryptedHeader);
return Decrypt(encryptedVal);
}
catch { return null; }
}
set
{
var encryptedVal = Encrypt(value, writer);
Container.SetValue(EncryptedHeader, encryptedVal);
}
}

/// <summary>
/// Decrypted HeaderData
/// </summary>
private string[] HeaderData { get; set; }

internal string EncryptedHeader { get; }
internal ISettingsContainer Container { get; }

/*
Copyright (C) 2012 Sveinung Kval Bakken, [email protected]
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
// Converted to C# for PlatformBindings-Android.
#region Encryption

private AndroidCredentialContainer()
{
try
{
writer = Cipher.GetInstance(TRANSFORMATION);
reader = Cipher.GetInstance(TRANSFORMATION);
Encoding = Encoding.GetEncoding(CHARSET);

using (var key = AndroidAppServices.KeyGenerator.GetSecureKey())
{
InitCiphers(key);
}
}
catch (GeneralSecurityException e)
{
throw new SecurityException(e.Message);
}
catch (UnsupportedEncodingException e)
{
throw new SecurityException(e.Message);
}
}

private readonly string TRANSFORMATION = "AES/CBC/PKCS5Padding";
private readonly string SECRET_KEY_HASH_TRANSFORMATION = "SHA-256";
private readonly string CHARSET = "UTF-8";
public static readonly string DecryptionError = "ERR_DECRYPT";

private Encoding Encoding;
private Cipher writer;
private Cipher reader;

protected void InitCiphers(SecureString secureKey)
{
var bstr = Marshal.SecureStringToBSTR(secureKey);
var key = Marshal.PtrToStringBSTR(bstr);

IvParameterSpec ivSpec = GetIv();
SecretKeySpec secretKey = GetSecretKey(key);

writer.Init(CipherMode.EncryptMode, secretKey, ivSpec);
reader.Init(CipherMode.DecryptMode, secretKey, ivSpec);
}

protected IvParameterSpec GetIv()
{
byte[] iv = new byte[writer.BlockSize];
Array.Copy(Encoding.GetBytes("fldsjfodasjifudslfjdsaofshaufihadsf"), 0, iv, 0, writer.BlockSize);
return new IvParameterSpec(iv);
}

protected SecretKeySpec GetSecretKey(string key)
{
byte[] keyBytes = CreateKeyBytes(key);
return new SecretKeySpec(keyBytes, TRANSFORMATION);
}

protected byte[] CreateKeyBytes(string key)
{
MessageDigest md = MessageDigest.GetInstance(SECRET_KEY_HASH_TRANSFORMATION);
md.Reset();
var keyBytes = md.Digest(Encoding.GetBytes(key));
return keyBytes;
}

protected String Encrypt(string value, Cipher writer)
{
byte[] secureValue;
try
{
secureValue = Convert(writer, Encoding.GetEncoding(CHARSET).GetBytes(value));
}
catch (UnsupportedEncodingException e)
{
throw new SecurityException(e.Message);
}
var secureValueEncoded = System.Convert.ToBase64String(secureValue);
return secureValueEncoded;
}

protected string Decrypt(string securedEncodedValue)
{
byte[] securedValue = System.Convert.FromBase64String(securedEncodedValue);
byte[] value = Convert(reader, securedValue);
try
{
return Encoding.GetString(value);
}
catch (UnsupportedEncodingException e)
{
throw new SecurityException(e.Message);
}
}

private static byte[] Convert(Cipher cipher, byte[] bs)
{
try
{
return cipher.DoFinal(bs);
}
catch (Exception e)
{
throw new SecurityException(e.Message);
}
}

~AndroidCredentialContainer()
{
writer?.Dispose();
reader?.Dispose();
}

#endregion Encryption
}
}
6 changes: 5 additions & 1 deletion Android/Models/DialogHandling/AlertDialogBuilderBase.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Android.Content;
using Android.Views;
using Java.Lang;
using PlatformBindings.Common;
using PlatformBindings.Enums;
using System.Threading.Tasks;

Expand Down Expand Up @@ -55,7 +56,10 @@ public void SetSecondaryButton(string text)

public async Task<DialogResult> ShowAsync()
{
Show();
PlatformBindingHelpers.OnUIThread(() =>
{
Show();
});
return await Waiter.Task;
}

Expand Down
22 changes: 22 additions & 0 deletions Android/Models/Encryption/DefaultKeyGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using Android.App;
using PlatformBindings.Common;
using System.Security;

namespace PlatformBindings.Models.Encryption
{
public class DefaultKeyGenerator : IKeyGenerator
{
public SecureString GetSecureKey()
{
var raw = Application.Context.PackageName + Application.Context.ApplicationInfo.Uid.ToString();
var data = PlatformBindingHelpers.ConvertToBase64(raw);

var secure = new SecureString();
foreach (var c in data)
{
secure.AppendChar(c);
}
return secure;
}
}
}
12 changes: 12 additions & 0 deletions Android/Models/Encryption/IKeyGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Security;

namespace PlatformBindings.Models.Encryption
{
/// <summary>
/// Generates a Key to Encrypt/Decrypt the Credential Data, this key must be the same during Encryption/Decryption otherwise you will not be able to read any of the credentials.
/// </summary>
public interface IKeyGenerator
{
SecureString GetSecureKey();
}
}
7 changes: 5 additions & 2 deletions Android/Models/FileSystem/AndroidSAFFileContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
using Android.Content;
using PlatformBindings.Common;
using Android.Support.V4.Provider;
using Android.Net;

namespace PlatformBindings.Models.FileSystem
{
public class AndroidSAFFileContainer : FileContainer
public class AndroidSAFFileContainer : FileContainer, IAndroidSAFContainer
{
public AndroidSAFFileContainer(Android.Net.Uri Uri)
{
Expand Down Expand Up @@ -58,8 +59,10 @@ public override Task<bool> DeleteAsync()
public override bool CanWrite => File.CanWrite();

public override string Name => File.Name;
public override string Path => File.Uri.SchemeSpecificPart;
public override string Path => Uri.ToString();
public DocumentFile File { get; }
private ContentResolver Resolver => AndroidHelpers.GetCurrentActivity().ContentResolver;

public Android.Net.Uri Uri => File.Uri;
}
}
7 changes: 5 additions & 2 deletions Android/Models/FileSystem/AndroidSAFFolderContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
using Java.Net;
using Android.Support.V4.Provider;
using System.Linq;
using Android.Net;

namespace PlatformBindings.Models.FileSystem
{
public class AndroidSAFFolderContainer : FolderContainer
public class AndroidSAFFolderContainer : FolderContainer, IAndroidSAFContainer
{
public AndroidSAFFolderContainer(Android.Net.Uri Uri)
{
Expand Down Expand Up @@ -135,7 +136,9 @@ public override Task<bool> FileExists(string FileName)
public override bool CanWrite => Folder.CanWrite();

public override string Name => Folder.Name;
public override string Path => Folder.Uri.SchemeSpecificPart;
public override string Path => Uri.ToString();
public DocumentFile Folder { get; }

public Android.Net.Uri Uri => Folder.Uri;
}
}
7 changes: 7 additions & 0 deletions Android/Models/FileSystem/IAndroidSAFContainer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace PlatformBindings.Models.FileSystem
{
public interface IAndroidSAFContainer
{
Android.Net.Uri Uri { get; }
}
}
Loading

0 comments on commit 642a259

Please sign in to comment.