1
0
mirror of https://github.com/bitwarden/mobile synced 2025-12-27 13:43:32 +00:00

reset for v2

This commit is contained in:
Kyle Spearrin
2019-03-27 16:23:00 -04:00
parent 5a7f106e3e
commit 297beac169
1180 changed files with 0 additions and 126197 deletions

View File

@@ -1,44 +0,0 @@
using System.Net.Http;
using System;
using System.Net.Http.Headers;
using XLabs.Ioc;
using Bit.App.Abstractions;
namespace Bit.App
{
public class ApiHttpClient : HttpClient
{
public ApiHttpClient()
{
Init();
}
public ApiHttpClient(HttpMessageHandler handler)
: base(handler)
{
Init();
}
private void Init()
{
DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var appSettings = Resolver.Resolve<IAppSettingsService>();
if(!string.IsNullOrWhiteSpace(appSettings.BaseUrl))
{
BaseAddress = new Uri($"{appSettings.BaseUrl}/api");
}
else if(!string.IsNullOrWhiteSpace(appSettings.ApiUrl))
{
BaseAddress = new Uri($"{appSettings.ApiUrl}");
}
else
{
//BaseAddress = new Uri("http://169.254.80.80:4000"); // Desktop from VS Android Emulator
//BaseAddress = new Uri("http://192.168.1.3:4000"); // Desktop
//BaseAddress = new Uri("https://preview-api.bitwarden.com"); // Preview
BaseAddress = new Uri("https://api.bitwarden.com"); // Production
}
}
}
}

View File

@@ -1,72 +0,0 @@
using System;
namespace Bit.App.Utilities
{
// ref: https://github.com/aspnet/Identity/blob/dev/src/Microsoft.Extensions.Identity.Core/Base32.cs
// with some modifications for cleaning input
public static class Base32
{
private static readonly string _base32Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
public static byte[] FromBase32(string input)
{
if(input == null)
{
throw new ArgumentNullException(nameof(input));
}
input = input.ToUpperInvariant();
var cleanedInput = string.Empty;
foreach(var c in input)
{
if(_base32Chars.IndexOf(c) < 0)
{
continue;
}
cleanedInput += c;
}
input = cleanedInput;
if(input.Length == 0)
{
return new byte[0];
}
var output = new byte[input.Length * 5 / 8];
var bitIndex = 0;
var inputIndex = 0;
var outputBits = 0;
var outputIndex = 0;
while(outputIndex < output.Length)
{
var byteIndex = _base32Chars.IndexOf(input[inputIndex]);
if(byteIndex < 0)
{
throw new FormatException();
}
var bits = Math.Min(5 - bitIndex, 8 - outputBits);
output[outputIndex] <<= bits;
output[outputIndex] |= (byte)(byteIndex >> (5 - (bitIndex + bits)));
bitIndex += bits;
if(bitIndex >= 5)
{
inputIndex++;
bitIndex = 0;
}
outputBits += bits;
if(outputBits >= 8)
{
outputIndex++;
outputBits = 0;
}
}
return output;
}
}
}

View File

@@ -1,9 +0,0 @@
using Xamarin.Forms;
namespace Bit.App.Utilities
{
public static class Colors
{
public static Color Primary = Color.FromHex("3c8dbc");
}
}

View File

@@ -1,246 +0,0 @@
using Bit.App.Enums;
using Bit.App.Models;
using PCLCrypto;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Bit.App.Utilities
{
public static class Crypto
{
private static string SteamChars = "23456789BCDFGHJKMNPQRTVWXY";
public static CipherString AesCbcEncrypt(byte[] plainBytes, SymmetricCryptoKey key)
{
var parts = AesCbcEncryptToParts(plainBytes, key);
return new CipherString(parts.Item1, Convert.ToBase64String(parts.Item2),
Convert.ToBase64String(parts.Item4), parts.Item3 != null ? Convert.ToBase64String(parts.Item3) : null);
}
public static byte[] AesCbcEncryptToBytes(byte[] plainBytes, SymmetricCryptoKey key)
{
var parts = AesCbcEncryptToParts(plainBytes, key);
var macLength = parts.Item3?.Length ?? 0;
var encBytes = new byte[1 + parts.Item2.Length + macLength + parts.Item4.Length];
encBytes[0] = (byte)parts.Item1;
parts.Item2.CopyTo(encBytes, 1);
if(parts.Item3 != null)
{
parts.Item3.CopyTo(encBytes, 1 + parts.Item2.Length);
}
parts.Item4.CopyTo(encBytes, 1 + parts.Item2.Length + macLength);
return encBytes;
}
private static Tuple<EncryptionType, byte[], byte[], byte[]> AesCbcEncryptToParts(byte[] plainBytes,
SymmetricCryptoKey key)
{
if(key == null)
{
throw new ArgumentNullException(nameof(key));
}
if(plainBytes == null)
{
throw new ArgumentNullException(nameof(plainBytes));
}
var provider = WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithm.AesCbcPkcs7);
var cryptoKey = provider.CreateSymmetricKey(key.EncKey);
var iv = RandomBytes(provider.BlockLength);
var ct = WinRTCrypto.CryptographicEngine.Encrypt(cryptoKey, plainBytes, iv);
var mac = key.MacKey != null ? ComputeMac(ct, iv, key.MacKey) : null;
return new Tuple<EncryptionType, byte[], byte[], byte[]>(key.EncryptionType, iv, mac, ct);
}
public static byte[] AesCbcDecrypt(CipherString encyptedValue, SymmetricCryptoKey key)
{
if(encyptedValue == null)
{
throw new ArgumentNullException(nameof(encyptedValue));
}
return AesCbcDecrypt(encyptedValue.EncryptionType, encyptedValue.CipherTextBytes,
encyptedValue.InitializationVectorBytes, encyptedValue.MacBytes, key);
}
public static byte[] AesCbcDecrypt(EncryptionType type, byte[] ct, byte[] iv, byte[] mac, SymmetricCryptoKey key)
{
if(key == null)
{
throw new ArgumentNullException(nameof(key));
}
if(ct == null)
{
throw new ArgumentNullException(nameof(ct));
}
if(iv == null)
{
throw new ArgumentNullException(nameof(iv));
}
if(key.MacKey != null && mac == null)
{
throw new ArgumentNullException(nameof(mac));
}
if(key.EncryptionType != type)
{
throw new InvalidOperationException(nameof(type));
}
if(key.MacKey != null && mac != null)
{
var computedMacBytes = ComputeMac(ct, iv, key.MacKey);
if(!MacsEqual(computedMacBytes, mac))
{
throw new InvalidOperationException("MAC failed.");
}
}
var provider = WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithm.AesCbcPkcs7);
var cryptoKey = provider.CreateSymmetricKey(key.EncKey);
var decryptedBytes = WinRTCrypto.CryptographicEngine.Decrypt(cryptoKey, ct, iv);
return decryptedBytes;
}
public static byte[] RandomBytes(int length)
{
return WinRTCrypto.CryptographicBuffer.GenerateRandom(length);
}
public static byte[] ComputeMac(byte[] ctBytes, byte[] ivBytes, byte[] macKey)
{
if(ctBytes == null)
{
throw new ArgumentNullException(nameof(ctBytes));
}
if(ivBytes == null)
{
throw new ArgumentNullException(nameof(ivBytes));
}
return ComputeMac(ivBytes.Concat(ctBytes), macKey);
}
public static byte[] ComputeMac(IEnumerable<byte> dataBytes, byte[] macKey)
{
if(macKey == null)
{
throw new ArgumentNullException(nameof(macKey));
}
if(dataBytes == null)
{
throw new ArgumentNullException(nameof(dataBytes));
}
var algorithm = WinRTCrypto.MacAlgorithmProvider.OpenAlgorithm(MacAlgorithm.HmacSha256);
var hasher = algorithm.CreateHash(macKey);
hasher.Append(dataBytes.ToArray());
var mac = hasher.GetValueAndReset();
return mac;
}
// Safely compare two MACs in a way that protects against timing attacks (Double HMAC Verification).
// ref: https://www.nccgroup.trust/us/about-us/newsroom-and-events/blog/2011/february/double-hmac-verification/
// ref: https://paragonie.com/blog/2015/11/preventing-timing-attacks-on-string-comparison-with-double-hmac-strategy
public static bool MacsEqual(byte[] mac1, byte[] mac2)
{
var algorithm = WinRTCrypto.MacAlgorithmProvider.OpenAlgorithm(MacAlgorithm.HmacSha256);
var hasher = algorithm.CreateHash(RandomBytes(32));
hasher.Append(mac1);
mac1 = hasher.GetValueAndReset();
hasher.Append(mac2);
mac2 = hasher.GetValueAndReset();
if(mac1.Length != mac2.Length)
{
return false;
}
for(int i = 0; i < mac2.Length; i++)
{
if(mac1[i] != mac2[i])
{
return false;
}
}
return true;
}
// ref: https://github.com/mirthas/totp-net/blob/master/TOTP/Totp.cs
public static string Totp(string key)
{
var otpParams = new OtpAuth(key);
var b32Key = Base32.FromBase32(otpParams.Secret);
if(b32Key == null || b32Key.Length == 0)
{
return null;
}
var now = Helpers.EpocUtcNow() / 1000;
var sec = now / otpParams.Period;
var secBytes = BitConverter.GetBytes(sec);
if(BitConverter.IsLittleEndian)
{
Array.Reverse(secBytes, 0, secBytes.Length);
}
var algorithm = WinRTCrypto.MacAlgorithmProvider.OpenAlgorithm(otpParams.Algorithm);
var hasher = algorithm.CreateHash(b32Key);
hasher.Append(secBytes);
var hash = hasher.GetValueAndReset();
var offset = (hash[hash.Length - 1] & 0xf);
var binary = ((hash[offset] & 0x7f) << 24) | ((hash[offset + 1] & 0xff) << 16) |
((hash[offset + 2] & 0xff) << 8) | (hash[offset + 3] & 0xff);
string otp = string.Empty;
if(otpParams.Steam)
{
var fullCode = binary & 0x7fffffff;
for(var i = 0; i < otpParams.Digits; i++)
{
otp += SteamChars[fullCode % SteamChars.Length];
fullCode = (int)Math.Truncate(fullCode / (double)SteamChars.Length);
}
}
else
{
var rawOtp = binary % (int)Math.Pow(10, otpParams.Digits);
otp = rawOtp.ToString().PadLeft(otpParams.Digits, '0');
}
return otp;
}
// ref: https://tools.ietf.org/html/rfc5869
public static byte[] HkdfExpand(byte[] prk, byte[] info, int size)
{
var hashLen = 32; // sha256
var okm = new byte[size];
var previousT = new byte[0];
var n = (int)Math.Ceiling((double)size / hashLen);
for(int i = 0; i < n; i++)
{
var t = new byte[previousT.Length + info.Length + 1];
previousT.CopyTo(t, 0);
info.CopyTo(t, previousT.Length);
t[t.Length - 1] = (byte)(i + 1);
previousT = ComputeMac(t, prk);
previousT.CopyTo(okm, i * hashLen);
}
return okm;
}
}
}

View File

@@ -1,33 +0,0 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
namespace Bit.App.Utilities
{
public class ExtendedObservableCollection<T> : ObservableCollection<T>
{
public ExtendedObservableCollection() : base() { }
public ExtendedObservableCollection(IEnumerable<T> collection) : base(collection) { }
public ExtendedObservableCollection(List<T> list) : base(list) { }
public void AddRange(IEnumerable<T> range)
{
foreach(var item in range)
{
Items.Add(item);
}
OnPropertyChanged(new PropertyChangedEventArgs("Count"));
OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
public void ResetWithRange(IEnumerable<T> range)
{
Items.Clear();
AddRange(range);
}
}
}

View File

@@ -1,137 +0,0 @@
using System;
using Bit.App.Abstractions;
using Bit.App.Models;
using Xamarin.Forms;
using XLabs.Ioc;
using System.Threading.Tasks;
using Bit.App.Controls;
namespace Bit.App
{
public static class Extentions
{
public static CipherString Encrypt(this string s, string orgId = null)
{
if(s == null)
{
throw new ArgumentNullException(nameof(s));
}
var cryptoService = Resolver.Resolve<ICryptoService>();
if(!string.IsNullOrWhiteSpace(orgId))
{
return cryptoService.Encrypt(s, cryptoService.GetOrgKey(orgId));
}
return cryptoService.Encrypt(s);
}
public static bool IsPortrait(this Page page)
{
return page.Width < page.Height;
}
public static bool IsLandscape(this Page page)
{
return !page.IsPortrait();
}
public static void FocusWithDelay(this View view, int delay = 1000, bool forceDelay = false)
{
if(Device.RuntimePlatform == Device.Android || forceDelay)
{
Task.Run(async () =>
{
await Task.Delay(delay);
Device.BeginInvokeOnMainThread(() => view.Focus());
});
}
else
{
view.Focus();
}
}
public static async Task PushForDeviceAsync(this INavigation navigation, Page page)
{
if (Device.RuntimePlatform != Device.UWP)
{
await navigation.PushModalAsync(new ExtendedNavigationPage(page), true);
}
else
{
await navigation.PushAsync(page, true);
}
}
public static async Task PopForDeviceAsync(this INavigation navigation)
{
if(navigation.ModalStack.Count < 1)
{
if (navigation.NavigationStack.Count > 0 && Device.RuntimePlatform == Device.UWP)
{
await navigation.PopAsync();
}
return;
}
await navigation.PopModalAsync(true);
}
public static void AdjustMarginsForDevice(this View view)
{
if(Device.RuntimePlatform == Device.Android)
{
var deviceInfo = Resolver.Resolve<IDeviceInfoService>();
if(deviceInfo.Version < 21)
{
view.Margin = new Thickness(-12, -5, -12, -6);
}
else if(deviceInfo.Version == 21)
{
view.Margin = new Thickness(-4, -2, -4, -11);
}
else
{
view.Margin = new Thickness(-4, -7, -4, -11);
}
}
}
public static void AdjustPaddingForDevice(this Layout view)
{
if(Device.RuntimePlatform == Device.Android)
{
var deviceInfo = Resolver.Resolve<IDeviceInfoService>();
if(deviceInfo.Scale == 1) // mdpi
{
view.Padding = new Thickness(22, view.Padding.Top, 22, view.Padding.Bottom);
}
else if(deviceInfo.Scale < 2) // hdpi
{
view.Padding = new Thickness(19, view.Padding.Top, 19, view.Padding.Bottom);
}
else if(deviceInfo.Scale < 3) // xhdpi
{
view.Padding = new Thickness(17, view.Padding.Top, 17, view.Padding.Bottom);
}
else // xxhdpi and xxxhdpi
{
view.Padding = new Thickness(15, view.Padding.Top, 15, view.Padding.Bottom);
}
}
}
public static bool LastActionWasRecent(this DateTime? lastAction, int milliseconds = 1000)
{
if(lastAction.HasValue && (DateTime.UtcNow - lastAction.Value).TotalMilliseconds < milliseconds)
{
System.Diagnostics.Debug.WriteLine("Last action occurred recently.");
return true;
}
return false;
}
}
}

View File

@@ -1,647 +0,0 @@
using Bit.App.Abstractions;
using Bit.App.Controls;
using Bit.App.Enums;
using Bit.App.Models;
using Bit.App.Models.Page;
using Bit.App.Pages;
using Bit.App.Resources;
using Plugin.Settings.Abstractions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Xamarin.Forms;
using XLabs.Ioc;
namespace Bit.App.Utilities
{
public static class Helpers
{
public static readonly DateTime Epoc = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
public static IDictionary<UriMatchType?, string> UriMatchOptionsMap = new Dictionary<UriMatchType?, string>
{
[UriMatchType.Domain] = AppResources.BaseDomain,
[UriMatchType.Host] = AppResources.Host,
[UriMatchType.StartsWith] = AppResources.StartsWith,
[UriMatchType.RegularExpression] = AppResources.RegEx,
[UriMatchType.Exact] = AppResources.Exact,
[UriMatchType.Never] = AppResources.Never
};
public static long EpocUtcNow()
{
return (long)(DateTime.UtcNow - Epoc).TotalMilliseconds;
}
public static T OnPlatform<T>(T iOS = default(T), T Android = default(T),
T WinPhone = default(T), T Windows = default(T), string platform = null)
{
if(platform == null)
{
platform = Device.RuntimePlatform;
}
switch(platform)
{
case Device.iOS:
return iOS;
case Device.Android:
return Android;
case Device.UWP:
return Windows;
default:
throw new Exception("Unsupported platform.");
}
}
public static bool InDebugMode()
{
#if DEBUG
return true;
#else
return false;
#endif
}
public static bool PerformUpdateTasks(ISettings settings,
IAppInfoService appInfoService, IDatabaseService databaseService, ISyncService syncService)
{
var lastBuild = settings.GetValueOrDefault(Constants.LastBuildKey, null);
if(InDebugMode() || lastBuild == null || lastBuild != appInfoService.Build)
{
settings.AddOrUpdateValue(Constants.LastBuildKey, appInfoService.Build);
databaseService.CreateTables();
var task = Task.Run(async () => await syncService.FullSyncAsync(true));
return true;
}
return false;
}
public static string GetEmptyTableSectionTitle()
{
if(Device.RuntimePlatform == Device.iOS)
{
return string.Empty;
}
return " ";
}
public static string ToolbarImage(string image)
{
if(Device.RuntimePlatform == Device.iOS || Device.RuntimePlatform == Device.Android)
{
return null;
}
return image;
}
public static async void CipherMoreClickedAsync(Page page, VaultListPageModel.Cipher cipher, bool autofill)
{
var buttons = new List<string> { AppResources.View, AppResources.Edit };
if(cipher.Type == CipherType.Login)
{
if(!string.IsNullOrWhiteSpace(cipher.LoginPassword.Value))
{
buttons.Add(AppResources.CopyPassword);
}
if(!string.IsNullOrWhiteSpace(cipher.LoginUsername))
{
buttons.Add(AppResources.CopyUsername);
}
if(!autofill && !string.IsNullOrWhiteSpace(cipher.LoginUri) && (cipher.LoginUri.StartsWith("http://")
|| cipher.LoginUri.StartsWith("https://")))
{
buttons.Add(AppResources.GoToWebsite);
}
}
else if(cipher.Type == CipherType.Card)
{
if(!string.IsNullOrWhiteSpace(cipher.CardNumber))
{
buttons.Add(AppResources.CopyNumber);
}
if(!string.IsNullOrWhiteSpace(cipher.CardCode.Value))
{
buttons.Add(AppResources.CopySecurityCode);
}
}
var selection = await page.DisplayActionSheet(cipher.Name, AppResources.Cancel, null, buttons.ToArray());
if(selection == AppResources.View)
{
var p = new VaultViewCipherPage(cipher.Type, cipher.Id);
await page.Navigation.PushForDeviceAsync(p);
}
else if(selection == AppResources.Edit)
{
var p = new VaultEditCipherPage(cipher.Id);
await page.Navigation.PushForDeviceAsync(p);
}
else if(selection == AppResources.CopyPassword)
{
CipherCopy(cipher.LoginPassword.Value, AppResources.Password);
}
else if(selection == AppResources.CopyUsername)
{
CipherCopy(cipher.LoginUsername, AppResources.Username);
}
else if(selection == AppResources.GoToWebsite)
{
Device.OpenUri(new Uri(cipher.LoginUri));
}
else if(selection == AppResources.CopyNumber)
{
CipherCopy(cipher.CardNumber, AppResources.Number);
}
else if(selection == AppResources.CopySecurityCode)
{
CipherCopy(cipher.CardCode.Value, AppResources.SecurityCode);
}
}
public static void CipherCopy(string copyText, string alertLabel)
{
var daService = Resolver.Resolve<IDeviceActionService>();
daService.CopyToClipboard(copyText);
daService.Toast(string.Format(AppResources.ValueHasBeenCopied, alertLabel));
}
public static async void AddCipher(Page page, string folderId)
{
var type = await page.DisplayActionSheet(AppResources.SelectTypeAdd, AppResources.Cancel, null,
AppResources.TypeLogin, AppResources.TypeCard, AppResources.TypeIdentity, AppResources.TypeSecureNote);
var selectedType = CipherType.SecureNote;
if(type == null || type == AppResources.Cancel)
{
return;
}
else if(type == AppResources.TypeLogin)
{
selectedType = CipherType.Login;
}
else if(type == AppResources.TypeCard)
{
selectedType = CipherType.Card;
}
else if(type == AppResources.TypeIdentity)
{
selectedType = CipherType.Identity;
}
else if(type == AppResources.TypeSecureNote)
{
selectedType = CipherType.SecureNote;
}
else
{
return;
}
var addPage = new VaultAddCipherPage(selectedType, defaultFolderId: folderId);
await page.Navigation.PushForDeviceAsync(addPage);
}
public static async Task AddField(Page page, TableSection fieldsSection)
{
var type = await page.DisplayActionSheet(AppResources.SelectTypeField, AppResources.Cancel, null,
AppResources.FieldTypeText, AppResources.FieldTypeHidden, AppResources.FieldTypeBoolean);
FieldType fieldType;
if(type == AppResources.FieldTypeText)
{
fieldType = FieldType.Text;
}
else if(type == AppResources.FieldTypeHidden)
{
fieldType = FieldType.Hidden;
}
else if(type == AppResources.FieldTypeBoolean)
{
fieldType = FieldType.Boolean;
}
else
{
return;
}
var daService = Resolver.Resolve<IDeviceActionService>();
var label = await daService.DisplayPromptAync(AppResources.CustomFieldName);
if(label == null)
{
return;
}
var cell = MakeFieldCell(fieldType, label, string.Empty, fieldsSection, page);
if(cell != null)
{
fieldsSection.Insert(fieldsSection.Count - 1, cell);
if(cell is FormEntryCell feCell)
{
feCell.InitEvents();
}
}
}
public static Cell MakeFieldCell(FieldType type, string label, string value,
TableSection fieldsSection, Page page)
{
Cell cell;
FormEntryCell feCell = null;
FormSwitchCell fsCell = null;
switch(type)
{
case FieldType.Text:
case FieldType.Hidden:
var hidden = type == FieldType.Hidden;
cell = feCell = new FormEntryCell(label, isPassword: hidden,
button1: hidden ? "eye.png" : "cog_alt.png", button2: hidden ? "cog_alt.png" : null);
feCell.Entry.Text = value;
feCell.Entry.DisableAutocapitalize = true;
feCell.Entry.Autocorrect = false;
if(hidden)
{
feCell.Entry.FontFamily = OnPlatform(iOS: "Menlo-Regular", Android: "monospace",
Windows: "Courier");
feCell.Button1.Command = new Command(() =>
{
feCell.Entry.InvokeToggleIsPassword();
feCell.Button1.Image = "eye" +
(!feCell.Entry.IsPasswordFromToggled ? "_slash" : string.Empty) + ".png";
});
}
break;
case FieldType.Boolean:
cell = fsCell = new FormSwitchCell(label, "cog_alt.png");
fsCell.Switch.IsToggled = value == "true";
break;
default:
cell = null;
break;
}
if(cell != null)
{
var optionsButton = feCell != null ? feCell.Button2 ?? feCell.Button1 : fsCell.Button1;
optionsButton.Command = new Command(async () =>
{
var optionsVal = await page.DisplayActionSheet(AppResources.Options, AppResources.Cancel,
null, AppResources.Edit, AppResources.Remove);
if(optionsVal == AppResources.Remove)
{
if(fieldsSection.Contains(cell))
{
fieldsSection.Remove(cell);
}
if(feCell != null)
{
feCell.Dispose();
}
cell = null;
feCell = null;
fsCell = null;
}
else if(optionsVal == AppResources.Edit)
{
var existingLabel = feCell?.Label.Text ?? fsCell?.Label.Text;
var daService = Resolver.Resolve<IDeviceActionService>();
var editLabel = await daService.DisplayPromptAync(AppResources.CustomFieldName,
null, existingLabel);
if(editLabel != null)
{
if(feCell != null)
{
feCell.Label.Text = editLabel;
}
else if(fsCell != null)
{
fsCell.Label.Text = editLabel;
}
}
}
});
}
return cell;
}
public static List<Tuple<string, string>> ProcessFieldsSectionForSave(TableSection fieldsSection, Cipher cipher)
{
var hiddenFieldValues = new List<Tuple<string, string>>();
if(fieldsSection != null && fieldsSection.Count > 0)
{
var fields = new List<Field>();
foreach(var cell in fieldsSection)
{
if(cell is FormEntryCell entryCell)
{
var type = entryCell.Entry.IsPassword || entryCell.Button2 != null ?
FieldType.Hidden : FieldType.Text;
fields.Add(new Field
{
Name = string.IsNullOrWhiteSpace(entryCell.Label.Text) ? null :
entryCell.Label.Text.Encrypt(cipher.OrganizationId),
Value = string.IsNullOrWhiteSpace(entryCell.Entry.Text) ? null :
entryCell.Entry.Text.Encrypt(cipher.OrganizationId),
Type = type
});
if(type == FieldType.Hidden && !string.IsNullOrWhiteSpace(entryCell.Label.Text))
{
hiddenFieldValues.Add(new Tuple<string, string>(entryCell.Label.Text,
entryCell.Entry.Text));
}
}
else if(cell is FormSwitchCell switchCell)
{
var value = switchCell.Switch.IsToggled ? "true" : "false";
fields.Add(new Field
{
Name = string.IsNullOrWhiteSpace(switchCell.Label.Text) ? null :
switchCell.Label.Text.Encrypt(cipher.OrganizationId),
Value = value.Encrypt(cipher.OrganizationId),
Type = FieldType.Boolean
});
}
}
cipher.Fields = fields;
}
if(!cipher.Fields?.Any() ?? true)
{
cipher.Fields = null;
}
return hiddenFieldValues;
}
public static FormEntryCell MakeUriCell(string value, UriMatchType? match, TableSection urisSection, Page page)
{
var label = string.Format(AppResources.URIPosition, urisSection.Count);
var cell = new FormEntryCell(label, entryKeyboard: Keyboard.Url, button1: "cog_alt.png");
cell.Entry.Text = value;
cell.Entry.DisableAutocapitalize = true;
cell.Entry.Autocorrect = false;
cell.MetaData = new Dictionary<string, object> { ["match"] = match };
cell.Button1.Command = new Command(async () =>
{
var optionsVal = await page.DisplayActionSheet(AppResources.Options, AppResources.Cancel,
null, AppResources.MatchDetection, AppResources.Remove);
if(optionsVal == AppResources.MatchDetection)
{
var options = UriMatchOptionsMap.Select(v => v.Value).ToList();
options.Insert(0, AppResources.Default);
var exactingMatchVal = cell.MetaData["match"] as UriMatchType?;
var matchIndex = exactingMatchVal.HasValue ?
Array.IndexOf(UriMatchOptionsMap.Keys.ToArray(), exactingMatchVal) + 1 : 0;
options[matchIndex] = $"✓ {options[matchIndex]}";
var optionsArr = options.ToArray();
var val = await page.DisplayActionSheet(AppResources.URIMatchDetection, AppResources.Cancel,
null, options.ToArray());
UriMatchType? selectedVal = null;
if(val == null || val == AppResources.Cancel)
{
selectedVal = exactingMatchVal;
}
else if(val.Replace("✓ ", string.Empty) != AppResources.Default)
{
selectedVal = UriMatchOptionsMap.ElementAt(Array.IndexOf(optionsArr, val) - 1).Key;
}
cell.MetaData["match"] = selectedVal;
}
else if(optionsVal == AppResources.Remove)
{
if(urisSection.Contains(cell))
{
urisSection.Remove(cell);
if(cell is FormEntryCell feCell)
{
feCell.Dispose();
}
cell = null;
for(int i = 0; i < urisSection.Count; i++)
{
if(urisSection[i] is FormEntryCell uriCell)
{
uriCell.Label.Text = string.Format(AppResources.URIPosition, i + 1);
}
}
}
}
});
return cell;
}
public static void ProcessUrisSectionForSave(TableSection urisSection, Cipher cipher)
{
if(urisSection != null && urisSection.Count > 0)
{
var uris = new List<LoginUri>();
foreach(var cell in urisSection)
{
if(cell is FormEntryCell entryCell && !string.IsNullOrWhiteSpace(entryCell.Entry.Text))
{
var match = entryCell?.MetaData["match"] as UriMatchType?;
uris.Add(new LoginUri
{
Uri = entryCell.Entry.Text.Encrypt(cipher.OrganizationId),
Match = match
});
}
}
cipher.Login.Uris = uris;
}
if(!cipher.Login.Uris?.Any() ?? true)
{
cipher.Login.Uris = null;
}
}
public static void InitSectionEvents(TableSection section)
{
if(section != null && section.Count > 0)
{
foreach(var cell in section)
{
if(cell is FormEntryCell entrycell)
{
entrycell.InitEvents();
}
}
}
}
public static void DisposeSectionEvents(TableSection section)
{
if(section != null && section.Count > 0)
{
foreach(var cell in section)
{
if(cell is FormEntryCell entrycell)
{
entrycell.Dispose();
}
}
}
}
public static string GetUrlHost(string url)
{
if(string.IsNullOrWhiteSpace(url))
{
return null;
}
url = url.Trim();
if(url == string.Empty)
{
return null;
}
if(!url.Contains("://"))
{
url = $"http://{url}";
}
if(!Uri.TryCreate(url, UriKind.Absolute, out Uri u))
{
return null;
}
var host = u.Host;
if(!u.IsDefaultPort)
{
host = $"{host}:{u.Port}";
}
return host;
}
public static void AlertNoConnection(Page page)
{
page.DisplayAlert(AppResources.InternetConnectionRequiredTitle,
AppResources.InternetConnectionRequiredMessage, AppResources.Ok);
}
public static Dictionary<string, string> GetQueryParams(string urlString)
{
var dict = new Dictionary<string, string>();
if(!Uri.TryCreate(urlString, UriKind.Absolute, out var uri) || string.IsNullOrWhiteSpace(uri.Query))
{
return dict;
}
var pairs = uri.Query.Substring(1).Split('&');
foreach(var pair in pairs)
{
var parts = pair.Split('=');
if(parts.Length < 1)
{
continue;
}
var key = System.Net.WebUtility.UrlDecode(parts[0]).ToLower();
if(!dict.ContainsKey(key))
{
dict.Add(key, parts[1] == null ? string.Empty : System.Net.WebUtility.UrlDecode(parts[1]));
}
}
return dict;
}
public static bool CanAccessPremium()
{
var tokenService = Resolver.Resolve<ITokenService>();
if(tokenService?.TokenPremium ?? false)
{
return true;
}
var appSettingsService = Resolver.Resolve<IAppSettingsService>();
return appSettingsService?.OrganizationGivesPremium ?? false;
}
public static void NestedTraverse<T>(List<TreeNode<T>> nodeTree, int partIndex, string[] parts,
T obj, T parent, string delimiter) where T : ITreeNodeObject
{
if(parts.Length <= partIndex)
{
return;
}
var end = partIndex == parts.Length - 1;
var partName = parts[partIndex];
foreach(var n in nodeTree)
{
if(n.Node.Name != parts[partIndex])
{
continue;
}
if(end && n.Node.Id != obj.Id)
{
// Another node with the same name.
nodeTree.Add(new TreeNode<T>(obj, partName, parent));
return;
}
NestedTraverse(n.Children, partIndex + 1, parts, obj, n.Node, delimiter);
return;
}
if(!nodeTree.Any(n => n.Node.Name == partName))
{
if(end)
{
nodeTree.Add(new TreeNode<T>(obj, partName, parent));
return;
}
var newPartName = string.Concat(parts[partIndex], delimiter, parts[partIndex + 1]);
var newParts = new List<string> { newPartName };
var newPartsStartFrom = partIndex + 2;
newParts.AddRange(new ArraySegment<string>(parts, newPartsStartFrom, parts.Length - newPartsStartFrom));
NestedTraverse(nodeTree, 0, newParts.ToArray(), obj, parent, delimiter);
}
}
public static TreeNode<T> GetTreeNodeObject<T>(List<TreeNode<T>> nodeTree, string id) where T : ITreeNodeObject
{
foreach(var n in nodeTree)
{
if(n.Node.Id == id)
{
return n;
}
else if(n.Children != null)
{
var node = GetTreeNodeObject(n.Children, id);
if(node != null)
{
return node;
}
}
}
return null;
}
public static List<TreeNode<T>> GetAllNested<T>(IEnumerable<T> objs) where T : ITreeNodeObject
{
var nodes = new List<TreeNode<T>>();
foreach(var o in objs)
{
NestedTraverse(nodes, 0, o.Name.Split('/'), o, default(T), "/");
}
return nodes;
}
}
}

View File

@@ -1,44 +0,0 @@
using System.Net.Http;
using System;
using System.Net.Http.Headers;
using XLabs.Ioc;
using Bit.App.Abstractions;
namespace Bit.App
{
public class IdentityHttpClient : HttpClient
{
public IdentityHttpClient()
{
Init();
}
public IdentityHttpClient(HttpMessageHandler handler)
: base(handler)
{
Init();
}
private void Init()
{
DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var appSettings = Resolver.Resolve<IAppSettingsService>();
if(!string.IsNullOrWhiteSpace(appSettings.BaseUrl))
{
BaseAddress = new Uri($"{appSettings.BaseUrl}/identity");
}
else if(!string.IsNullOrWhiteSpace(appSettings.IdentityUrl))
{
BaseAddress = new Uri($"{appSettings.IdentityUrl}");
}
else
{
//BaseAddress = new Uri("http://169.254.80.80:33656"); // Desktop from VS Android Emulator
//BaseAddress = new Uri("http://192.168.1.3:33656"); // Desktop
//BaseAddress = new Uri("https://preview-identity.bitwarden.com"); // Preview
BaseAddress = new Uri("https://identity.bitwarden.com"); // Production
}
}
}
}

View File

@@ -1,78 +0,0 @@
using System;
using Xamarin.Forms;
namespace Bit.App.Utilities
{
/**
* Helper class to format a password with numeric encoding to separate
* normal text from numbers and special characters.
*/
class PasswordFormatter
{
/**
* This enum is used for the state machine when building the colorized
* password string.
*/
private enum CharType
{
None,
Normal,
Number,
Special
}
public static FormattedString FormatPassword(String password)
{
var result = new FormattedString();
// Start off with an empty span to prevent possible NPEs. Due to the way the state machine
// works, this will actually always be replaced by a new span anyway.
var currentSpan = new Span();
// Start with an otherwise uncovered case so we will definitely enter the "something changed"
// state.
var currentType = CharType.None;
foreach(var c in password)
{
// First, identify what the current char is.
CharType charType;
if(char.IsLetter(c))
{
charType = CharType.Normal;
}
else if(char.IsDigit(c))
{
charType = CharType.Number;
}
else
{
charType = CharType.Special;
}
// If the char type changed, build a new span to append the text to.
if(charType != currentType)
{
currentSpan = new Span();
result.Spans.Add(currentSpan);
currentType = charType;
// Switch the color if it is not a normal text. Otherwise leave the
// default value.
switch(currentType)
{
case CharType.Number:
currentSpan.TextColor = Color.DodgerBlue;
break;
case CharType.Special:
currentSpan.TextColor = Color.Firebrick;
break;
}
}
currentSpan.Text += c;
}
return result;
}
}
}

View File

@@ -1,39 +0,0 @@
using System.Net.Http;
using System.Text;
using Bit.App.Abstractions;
using Bit.App.Enums;
using Bit.App.Utilities;
using Newtonsoft.Json;
using XLabs.Ioc;
namespace Bit.App
{
public class TokenHttpRequestMessage : HttpRequestMessage
{
public TokenHttpRequestMessage()
{
var tokenService = Resolver.Resolve<ITokenService>();
var appIdService = Resolver.Resolve<IAppIdService>();
var deviceInfoService = Resolver.Resolve<IDeviceInfoService>();
if(!string.IsNullOrWhiteSpace(tokenService.Token))
{
Headers.Add("Authorization", $"Bearer {tokenService.Token}");
}
if(!string.IsNullOrWhiteSpace(appIdService.AppId))
{
Headers.Add("Device-Identifier", appIdService.AppId);
}
Headers.Add("Device-Type", ((int)Helpers.OnPlatform(iOS: DeviceType.iOS,
Android: DeviceType.Android, Windows: DeviceType.UWP, platform: deviceInfoService.Type)).ToString());
}
public TokenHttpRequestMessage(object requestObject)
: this()
{
var stringContent = JsonConvert.SerializeObject(requestObject);
Content = new StringContent(stringContent, Encoding.UTF8, "application/json");
}
}
}