mirror of
https://github.com/bitwarden/mobile.git
synced 2024-11-23 11:45:38 +01:00
download, decrypt and open attachment files
This commit is contained in:
parent
0a7ad44d23
commit
ac3fdbc2cd
@ -323,7 +323,7 @@
|
|||||||
<Compile Include="Services\DeviceInfoService.cs" />
|
<Compile Include="Services\DeviceInfoService.cs" />
|
||||||
<Compile Include="Services\GoogleAnalyticsService.cs" />
|
<Compile Include="Services\GoogleAnalyticsService.cs" />
|
||||||
<Compile Include="Services\AppInfoService.cs" />
|
<Compile Include="Services\AppInfoService.cs" />
|
||||||
<Compile Include="Services\ClipboardService.cs" />
|
<Compile Include="Services\DeviceActionService.cs" />
|
||||||
<Compile Include="Services\BouncyCastleKeyDerivationService.cs" />
|
<Compile Include="Services\BouncyCastleKeyDerivationService.cs" />
|
||||||
<Compile Include="Services\KeyStoreStorageService.cs" />
|
<Compile Include="Services\KeyStoreStorageService.cs" />
|
||||||
<Compile Include="MainActivity.cs" />
|
<Compile Include="MainActivity.cs" />
|
||||||
@ -872,6 +872,9 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidResource Include="Resources\drawable-xxhdpi\yubikey.png" />
|
<AndroidResource Include="Resources\drawable-xxhdpi\yubikey.png" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\xml\filepaths.xml" />
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
|
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
|
||||||
<Import Project="..\..\packages\Xamarin.Android.Support.Vector.Drawable.23.3.0\build\Xamarin.Android.Support.Vector.Drawable.targets" Condition="Exists('..\..\packages\Xamarin.Android.Support.Vector.Drawable.23.3.0\build\Xamarin.Android.Support.Vector.Drawable.targets')" />
|
<Import Project="..\..\packages\Xamarin.Android.Support.Vector.Drawable.23.3.0\build\Xamarin.Android.Support.Vector.Drawable.targets" Condition="Exists('..\..\packages\Xamarin.Android.Support.Vector.Drawable.23.3.0\build\Xamarin.Android.Support.Vector.Drawable.targets')" />
|
||||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||||
|
@ -207,7 +207,7 @@ namespace Bit.Android
|
|||||||
container.RegisterSingleton<IFolderService, FolderService>();
|
container.RegisterSingleton<IFolderService, FolderService>();
|
||||||
container.RegisterSingleton<ILoginService, LoginService>();
|
container.RegisterSingleton<ILoginService, LoginService>();
|
||||||
container.RegisterSingleton<ISyncService, SyncService>();
|
container.RegisterSingleton<ISyncService, SyncService>();
|
||||||
container.RegisterSingleton<IClipboardService, ClipboardService>();
|
container.RegisterSingleton<IDeviceActionService, DeviceActionService>();
|
||||||
container.RegisterSingleton<IAppIdService, AppIdService>();
|
container.RegisterSingleton<IAppIdService, AppIdService>();
|
||||||
container.RegisterSingleton<IPasswordGenerationService, PasswordGenerationService>();
|
container.RegisterSingleton<IPasswordGenerationService, PasswordGenerationService>();
|
||||||
container.RegisterSingleton<IReflectionService, ReflectionService>();
|
container.RegisterSingleton<IReflectionService, ReflectionService>();
|
||||||
|
@ -12,5 +12,15 @@
|
|||||||
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
|
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
|
||||||
<uses-permission android:name="com.x8bit.bitwarden.permission.C2D_MESSAGE" />
|
<uses-permission android:name="com.x8bit.bitwarden.permission.C2D_MESSAGE" />
|
||||||
|
|
||||||
<application android:label="bitwarden" android:theme="@style/BitwardenTheme" android:allowBackup="false"></application>
|
<application android:label="bitwarden" android:theme="@style/BitwardenTheme" android:allowBackup="false">
|
||||||
|
<provider
|
||||||
|
android:name="android.support.v4.content.FileProvider"
|
||||||
|
android:authorities="com.x8bit.bitwarden.fileprovider"
|
||||||
|
android:exported="false"
|
||||||
|
android:grantUriPermissions="true">
|
||||||
|
<meta-data
|
||||||
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
|
android:resource="@xml/filepaths" />
|
||||||
|
</provider>
|
||||||
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
3
src/Android/Resources/Resource.Designer.cs
generated
3
src/Android/Resources/Resource.Designer.cs
generated
@ -5296,6 +5296,9 @@ namespace Bit.Android
|
|||||||
// aapt resource value: 0x7f060000
|
// aapt resource value: 0x7f060000
|
||||||
public const int accessibilityservice = 2131099648;
|
public const int accessibilityservice = 2131099648;
|
||||||
|
|
||||||
|
// aapt resource value: 0x7f060001
|
||||||
|
public const int filepaths = 2131099649;
|
||||||
|
|
||||||
static Xml()
|
static Xml()
|
||||||
{
|
{
|
||||||
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
|
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
|
||||||
|
4
src/Android/Resources/xml/filepaths.xml
Normal file
4
src/Android/Resources/xml/filepaths.xml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<cache-path name="cache" path="." />
|
||||||
|
</paths>
|
@ -1,15 +0,0 @@
|
|||||||
using Android.Content;
|
|
||||||
using Bit.App.Abstractions;
|
|
||||||
using Xamarin.Forms;
|
|
||||||
|
|
||||||
namespace Bit.Android.Services
|
|
||||||
{
|
|
||||||
public class ClipboardService : IClipboardService
|
|
||||||
{
|
|
||||||
public void CopyToClipboard(string text)
|
|
||||||
{
|
|
||||||
var clipboardManager = (ClipboardManager)Forms.Context.GetSystemService(Context.ClipboardService);
|
|
||||||
clipboardManager.Text = text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
63
src/Android/Services/DeviceActionService.cs
Normal file
63
src/Android/Services/DeviceActionService.cs
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
using System;
|
||||||
|
using Android.Content;
|
||||||
|
using Bit.App.Abstractions;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
using Java.IO;
|
||||||
|
using Android.Webkit;
|
||||||
|
using Plugin.CurrentActivity;
|
||||||
|
using System.IO;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using Android.Support.V4.Content;
|
||||||
|
|
||||||
|
namespace Bit.Android.Services
|
||||||
|
{
|
||||||
|
public class DeviceActionService : IDeviceActionService
|
||||||
|
{
|
||||||
|
public void CopyToClipboard(string text)
|
||||||
|
{
|
||||||
|
var clipboardManager = (ClipboardManager)Forms.Context.GetSystemService(Context.ClipboardService);
|
||||||
|
clipboardManager.Text = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool OpenFile(byte[] fileData, string id, string fileName)
|
||||||
|
{
|
||||||
|
var extension = MimeTypeMap.GetFileExtensionFromUrl(fileName);
|
||||||
|
if(extension == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var mimeType = MimeTypeMap.Singleton.GetMimeTypeFromExtension(extension.ToLower());
|
||||||
|
if(mimeType == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var cachePath = CrossCurrentActivity.Current.Activity.CacheDir;
|
||||||
|
var filePath = Path.Combine(cachePath.Path, fileName);
|
||||||
|
System.IO.File.WriteAllBytes(filePath, fileData);
|
||||||
|
var file = new Java.IO.File(cachePath, fileName);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var packageManager = CrossCurrentActivity.Current.Activity.PackageManager;
|
||||||
|
var testIntent = new Intent(Intent.ActionView);
|
||||||
|
testIntent.SetType(mimeType);
|
||||||
|
var list = packageManager.QueryIntentActivities(testIntent,
|
||||||
|
global::Android.Content.PM.PackageInfoFlags.MatchDefaultOnly);
|
||||||
|
if(list.Count > 0 && file.IsFile)
|
||||||
|
{
|
||||||
|
var intent = new Intent(Intent.ActionView);
|
||||||
|
var uri = FileProvider.GetUriForFile(CrossCurrentActivity.Current.Activity.ApplicationContext,
|
||||||
|
"com.x8bit.bitwarden.fileprovider", file);
|
||||||
|
intent.SetDataAndType(uri, mimeType);
|
||||||
|
intent.SetFlags(ActivityFlags.GrantReadUriPermission);
|
||||||
|
CrossCurrentActivity.Current.Activity.StartActivity(intent);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +0,0 @@
|
|||||||
namespace Bit.App.Abstractions
|
|
||||||
{
|
|
||||||
public interface IClipboardService
|
|
||||||
{
|
|
||||||
void CopyToClipboard(string text);
|
|
||||||
}
|
|
||||||
}
|
|
@ -19,6 +19,7 @@ namespace Bit.App.Abstractions
|
|||||||
void ClearKeys();
|
void ClearKeys();
|
||||||
string Decrypt(CipherString encyptedValue, SymmetricCryptoKey key = null);
|
string Decrypt(CipherString encyptedValue, SymmetricCryptoKey key = null);
|
||||||
byte[] DecryptToBytes(CipherString encyptedValue, SymmetricCryptoKey key = null);
|
byte[] DecryptToBytes(CipherString encyptedValue, SymmetricCryptoKey key = null);
|
||||||
|
byte[] DecryptToBytes(byte[] encyptedValue, SymmetricCryptoKey key = null);
|
||||||
byte[] RsaDecryptToBytes(CipherString encyptedValue, byte[] privateKey);
|
byte[] RsaDecryptToBytes(CipherString encyptedValue, byte[] privateKey);
|
||||||
CipherString Encrypt(string plaintextValue, SymmetricCryptoKey key = null);
|
CipherString Encrypt(string plaintextValue, SymmetricCryptoKey key = null);
|
||||||
SymmetricCryptoKey MakeKeyFromPassword(string password, string salt);
|
SymmetricCryptoKey MakeKeyFromPassword(string password, string salt);
|
||||||
|
8
src/App/Abstractions/Services/IDeviceActionService.cs
Normal file
8
src/App/Abstractions/Services/IDeviceActionService.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
namespace Bit.App.Abstractions
|
||||||
|
{
|
||||||
|
public interface IDeviceActionService
|
||||||
|
{
|
||||||
|
void CopyToClipboard(string text);
|
||||||
|
bool OpenFile(byte[] fileData, string id, string fileName);
|
||||||
|
}
|
||||||
|
}
|
@ -14,5 +14,6 @@ namespace Bit.App.Abstractions
|
|||||||
Task<Tuple<IEnumerable<Login>, IEnumerable<Login>>> GetAllAsync(string uriString);
|
Task<Tuple<IEnumerable<Login>, IEnumerable<Login>>> GetAllAsync(string uriString);
|
||||||
Task<ApiResult<LoginResponse>> SaveAsync(Login login);
|
Task<ApiResult<LoginResponse>> SaveAsync(Login login);
|
||||||
Task<ApiResult> DeleteAsync(string id);
|
Task<ApiResult> DeleteAsync(string id);
|
||||||
|
Task<byte[]> DownloadAndDecryptAttachmentAsync(SymmetricCryptoKey key, string url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@
|
|||||||
<Compile Include="Abstractions\Services\IAppInfoService.cs" />
|
<Compile Include="Abstractions\Services\IAppInfoService.cs" />
|
||||||
<Compile Include="Abstractions\Services\IAppIdService.cs" />
|
<Compile Include="Abstractions\Services\IAppIdService.cs" />
|
||||||
<Compile Include="Abstractions\Services\IAuthService.cs" />
|
<Compile Include="Abstractions\Services\IAuthService.cs" />
|
||||||
<Compile Include="Abstractions\Services\IClipboardService.cs" />
|
<Compile Include="Abstractions\Services\IDeviceActionService.cs" />
|
||||||
<Compile Include="Abstractions\Services\IKeyDerivationService.cs" />
|
<Compile Include="Abstractions\Services\IKeyDerivationService.cs" />
|
||||||
<Compile Include="Abstractions\Services\ILogService.cs" />
|
<Compile Include="Abstractions\Services\ILogService.cs" />
|
||||||
<Compile Include="Abstractions\Services\IReflectionService.cs" />
|
<Compile Include="Abstractions\Services\IReflectionService.cs" />
|
||||||
|
@ -223,7 +223,8 @@ namespace Bit.App.Models.Page
|
|||||||
{
|
{
|
||||||
Id = attachment.Id,
|
Id = attachment.Id,
|
||||||
Name = attachment.FileName?.Decrypt(login.OrganizationId),
|
Name = attachment.FileName?.Decrypt(login.OrganizationId),
|
||||||
Size = attachment.SizeName
|
Size = attachment.SizeName,
|
||||||
|
Url = attachment.Url
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Attachments = attachments;
|
Attachments = attachments;
|
||||||
@ -239,6 +240,7 @@ namespace Bit.App.Models.Page
|
|||||||
public string Id { get; set; }
|
public string Id { get; set; }
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string Size { get; set; }
|
public string Size { get; set; }
|
||||||
|
public string Url { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ namespace Bit.App.Pages
|
|||||||
private readonly IUserDialogs _userDialogs;
|
private readonly IUserDialogs _userDialogs;
|
||||||
private readonly IPasswordGenerationService _passwordGenerationService;
|
private readonly IPasswordGenerationService _passwordGenerationService;
|
||||||
private readonly ISettings _settings;
|
private readonly ISettings _settings;
|
||||||
private readonly IClipboardService _clipboardService;
|
private readonly IDeviceActionService _clipboardService;
|
||||||
private readonly IGoogleAnalyticsService _googleAnalyticsService;
|
private readonly IGoogleAnalyticsService _googleAnalyticsService;
|
||||||
private readonly Action<string> _passwordValueAction;
|
private readonly Action<string> _passwordValueAction;
|
||||||
private readonly bool _fromAutofill;
|
private readonly bool _fromAutofill;
|
||||||
@ -26,7 +26,7 @@ namespace Bit.App.Pages
|
|||||||
_userDialogs = Resolver.Resolve<IUserDialogs>();
|
_userDialogs = Resolver.Resolve<IUserDialogs>();
|
||||||
_passwordGenerationService = Resolver.Resolve<IPasswordGenerationService>();
|
_passwordGenerationService = Resolver.Resolve<IPasswordGenerationService>();
|
||||||
_settings = Resolver.Resolve<ISettings>();
|
_settings = Resolver.Resolve<ISettings>();
|
||||||
_clipboardService = Resolver.Resolve<IClipboardService>();
|
_clipboardService = Resolver.Resolve<IDeviceActionService>();
|
||||||
_googleAnalyticsService = Resolver.Resolve<IGoogleAnalyticsService>();
|
_googleAnalyticsService = Resolver.Resolve<IGoogleAnalyticsService>();
|
||||||
_passwordValueAction = passwordValueAction;
|
_passwordValueAction = passwordValueAction;
|
||||||
_fromAutofill = fromAutofill;
|
_fromAutofill = fromAutofill;
|
||||||
|
@ -19,7 +19,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
private readonly ILoginService _loginService;
|
private readonly ILoginService _loginService;
|
||||||
private readonly IDeviceInfoService _deviceInfoService;
|
private readonly IDeviceInfoService _deviceInfoService;
|
||||||
private readonly IClipboardService _clipboardService;
|
private readonly IDeviceActionService _clipboardService;
|
||||||
private readonly ISettingsService _settingsService;
|
private readonly ISettingsService _settingsService;
|
||||||
private CancellationTokenSource _filterResultsCancellationTokenSource;
|
private CancellationTokenSource _filterResultsCancellationTokenSource;
|
||||||
private readonly string _name;
|
private readonly string _name;
|
||||||
@ -47,7 +47,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
_loginService = Resolver.Resolve<ILoginService>();
|
_loginService = Resolver.Resolve<ILoginService>();
|
||||||
_deviceInfoService = Resolver.Resolve<IDeviceInfoService>();
|
_deviceInfoService = Resolver.Resolve<IDeviceInfoService>();
|
||||||
_clipboardService = Resolver.Resolve<IClipboardService>();
|
_clipboardService = Resolver.Resolve<IDeviceActionService>();
|
||||||
_settingsService = Resolver.Resolve<ISettingsService>();
|
_settingsService = Resolver.Resolve<ISettingsService>();
|
||||||
UserDialogs = Resolver.Resolve<IUserDialogs>();
|
UserDialogs = Resolver.Resolve<IUserDialogs>();
|
||||||
GoogleAnalyticsService = Resolver.Resolve<IGoogleAnalyticsService>();
|
GoogleAnalyticsService = Resolver.Resolve<IGoogleAnalyticsService>();
|
||||||
|
@ -24,7 +24,7 @@ namespace Bit.App.Pages
|
|||||||
private readonly ILoginService _loginService;
|
private readonly ILoginService _loginService;
|
||||||
private readonly IUserDialogs _userDialogs;
|
private readonly IUserDialogs _userDialogs;
|
||||||
private readonly IConnectivity _connectivity;
|
private readonly IConnectivity _connectivity;
|
||||||
private readonly IClipboardService _clipboardService;
|
private readonly IDeviceActionService _clipboardService;
|
||||||
private readonly ISyncService _syncService;
|
private readonly ISyncService _syncService;
|
||||||
private readonly IPushNotification _pushNotification;
|
private readonly IPushNotification _pushNotification;
|
||||||
private readonly IDeviceInfoService _deviceInfoService;
|
private readonly IDeviceInfoService _deviceInfoService;
|
||||||
@ -41,7 +41,7 @@ namespace Bit.App.Pages
|
|||||||
_loginService = Resolver.Resolve<ILoginService>();
|
_loginService = Resolver.Resolve<ILoginService>();
|
||||||
_connectivity = Resolver.Resolve<IConnectivity>();
|
_connectivity = Resolver.Resolve<IConnectivity>();
|
||||||
_userDialogs = Resolver.Resolve<IUserDialogs>();
|
_userDialogs = Resolver.Resolve<IUserDialogs>();
|
||||||
_clipboardService = Resolver.Resolve<IClipboardService>();
|
_clipboardService = Resolver.Resolve<IDeviceActionService>();
|
||||||
_syncService = Resolver.Resolve<ISyncService>();
|
_syncService = Resolver.Resolve<ISyncService>();
|
||||||
_pushNotification = Resolver.Resolve<IPushNotification>();
|
_pushNotification = Resolver.Resolve<IPushNotification>();
|
||||||
_deviceInfoService = Resolver.Resolve<IDeviceInfoService>();
|
_deviceInfoService = Resolver.Resolve<IDeviceInfoService>();
|
||||||
|
@ -16,14 +16,14 @@ namespace Bit.App.Pages
|
|||||||
private readonly string _loginId;
|
private readonly string _loginId;
|
||||||
private readonly ILoginService _loginService;
|
private readonly ILoginService _loginService;
|
||||||
private readonly IUserDialogs _userDialogs;
|
private readonly IUserDialogs _userDialogs;
|
||||||
private readonly IClipboardService _clipboardService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
|
|
||||||
public VaultViewLoginPage(string loginId)
|
public VaultViewLoginPage(string loginId)
|
||||||
{
|
{
|
||||||
_loginId = loginId;
|
_loginId = loginId;
|
||||||
_loginService = Resolver.Resolve<ILoginService>();
|
_loginService = Resolver.Resolve<ILoginService>();
|
||||||
_userDialogs = Resolver.Resolve<IUserDialogs>();
|
_userDialogs = Resolver.Resolve<IUserDialogs>();
|
||||||
_clipboardService = Resolver.Resolve<IClipboardService>();
|
_deviceActionService = Resolver.Resolve<IDeviceActionService>();
|
||||||
|
|
||||||
Init();
|
Init();
|
||||||
}
|
}
|
||||||
@ -194,9 +194,9 @@ namespace Bit.App.Pages
|
|||||||
AttachmentsSection = new TableSection(AppResources.Attachments);
|
AttachmentsSection = new TableSection(AppResources.Attachments);
|
||||||
foreach(var attachment in Model.Attachments)
|
foreach(var attachment in Model.Attachments)
|
||||||
{
|
{
|
||||||
AttachmentsSection.Add(new AttachmentViewCell(attachment, () =>
|
AttachmentsSection.Add(new AttachmentViewCell(attachment, async () =>
|
||||||
{
|
{
|
||||||
|
await SaveAttachmentAsync(attachment);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
Table.Root.Add(AttachmentsSection);
|
Table.Root.Add(AttachmentsSection);
|
||||||
@ -211,6 +211,23 @@ namespace Bit.App.Pages
|
|||||||
EditItem.Dispose();
|
EditItem.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task SaveAttachmentAsync(VaultViewLoginPageModel.Attachment attachment)
|
||||||
|
{
|
||||||
|
var data = await _loginService.DownloadAndDecryptAttachmentAsync(null, attachment.Url);
|
||||||
|
if(data == null)
|
||||||
|
{
|
||||||
|
await _userDialogs.AlertAsync(AppResources.UnableToDownloadFile, null, AppResources.Ok);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var opened = _deviceActionService.OpenFile(data, attachment.Id, attachment.Name);
|
||||||
|
if(!opened)
|
||||||
|
{
|
||||||
|
await _userDialogs.AlertAsync(AppResources.UnableToOpenFile, null, AppResources.Ok);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void NotesCell_Tapped(object sender, EventArgs e)
|
private void NotesCell_Tapped(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
Copy(Model.Notes, AppResources.Notes);
|
Copy(Model.Notes, AppResources.Notes);
|
||||||
@ -218,7 +235,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
private void Copy(string copyText, string alertLabel)
|
private void Copy(string copyText, string alertLabel)
|
||||||
{
|
{
|
||||||
_clipboardService.CopyToClipboard(copyText);
|
_deviceActionService.CopyToClipboard(copyText);
|
||||||
_userDialogs.Toast(string.Format(AppResources.ValueHasBeenCopied, alertLabel));
|
_userDialogs.Toast(string.Format(AppResources.ValueHasBeenCopied, alertLabel));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
18
src/App/Resources/AppResources.Designer.cs
generated
18
src/App/Resources/AppResources.Designer.cs
generated
@ -1987,6 +1987,24 @@ namespace Bit.App.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Unable to download file..
|
||||||
|
/// </summary>
|
||||||
|
public static string UnableToDownloadFile {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("UnableToDownloadFile", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Unable to open this type of file on your device..
|
||||||
|
/// </summary>
|
||||||
|
public static string UnableToOpenFile {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("UnableToOpenFile", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Unlock with {0}.
|
/// Looks up a localized string similar to Unlock with {0}.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -908,4 +908,10 @@
|
|||||||
<data name="Attachments" xml:space="preserve">
|
<data name="Attachments" xml:space="preserve">
|
||||||
<value>Attachments</value>
|
<value>Attachments</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="UnableToDownloadFile" xml:space="preserve">
|
||||||
|
<value>Unable to download file.</value>
|
||||||
|
</data>
|
||||||
|
<data name="UnableToOpenFile" xml:space="preserve">
|
||||||
|
<value>Unable to open this type of file on your device.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -297,18 +297,61 @@ namespace Bit.App.Services
|
|||||||
// Old encrypt-then-mac scheme, swap out the key
|
// Old encrypt-then-mac scheme, swap out the key
|
||||||
if(_legacyEtmKey == null)
|
if(_legacyEtmKey == null)
|
||||||
{
|
{
|
||||||
_legacyEtmKey = new SymmetricCryptoKey(key.Key, Enums.EncryptionType.AesCbc128_HmacSha256_B64);
|
_legacyEtmKey = new SymmetricCryptoKey(key.Key, EncryptionType.AesCbc128_HmacSha256_B64);
|
||||||
}
|
}
|
||||||
|
|
||||||
key = _legacyEtmKey;
|
key = _legacyEtmKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(encyptedValue.EncryptionType != key.EncryptionType)
|
return Crypto.AesCbcDecrypt(encyptedValue, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] DecryptToBytes(byte[] encyptedValue, SymmetricCryptoKey key = null)
|
||||||
|
{
|
||||||
|
if(key == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentException("encType unavailable.");
|
key = EncKey ?? Key;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Crypto.AesCbcDecrypt(encyptedValue, key);
|
if(key == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(encyptedValue == null || encyptedValue.Length == 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(encyptedValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] ct, iv, mac = null;
|
||||||
|
var encType = (EncryptionType)encyptedValue[0];
|
||||||
|
switch(encType)
|
||||||
|
{
|
||||||
|
case EncryptionType.AesCbc128_HmacSha256_B64:
|
||||||
|
case EncryptionType.AesCbc256_HmacSha256_B64:
|
||||||
|
if(encyptedValue.Length <= 49)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Invalid value length.");
|
||||||
|
}
|
||||||
|
|
||||||
|
iv = new ArraySegment<byte>(encyptedValue, 1, 16).ToArray();
|
||||||
|
mac = new ArraySegment<byte>(encyptedValue, 17, 32).ToArray();
|
||||||
|
ct = new ArraySegment<byte>(encyptedValue, 49, encyptedValue.Length - 49).ToArray();
|
||||||
|
break;
|
||||||
|
case EncryptionType.AesCbc256_B64:
|
||||||
|
if(encyptedValue.Length <= 17)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Invalid value length.");
|
||||||
|
}
|
||||||
|
|
||||||
|
iv = new ArraySegment<byte>(encyptedValue, 1, 16).ToArray();
|
||||||
|
ct = new ArraySegment<byte>(encyptedValue, 17, encyptedValue.Length - 17).ToArray();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new InvalidOperationException("Invalid encryption type.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Crypto.AesCbcDecrypt(encType, ct, iv, mac, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] RsaDecryptToBytes(CipherString encyptedValue, byte[] privateKey)
|
public byte[] RsaDecryptToBytes(CipherString encyptedValue, byte[] privateKey)
|
||||||
|
@ -7,6 +7,7 @@ using Bit.App.Models;
|
|||||||
using Bit.App.Models.Api;
|
using Bit.App.Models.Api;
|
||||||
using Bit.App.Models.Data;
|
using Bit.App.Models.Data;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
using System.Net.Http;
|
||||||
|
|
||||||
namespace Bit.App.Services
|
namespace Bit.App.Services
|
||||||
{
|
{
|
||||||
@ -17,19 +18,22 @@ namespace Bit.App.Services
|
|||||||
private readonly IAuthService _authService;
|
private readonly IAuthService _authService;
|
||||||
private readonly ILoginApiRepository _loginApiRepository;
|
private readonly ILoginApiRepository _loginApiRepository;
|
||||||
private readonly ISettingsService _settingsService;
|
private readonly ISettingsService _settingsService;
|
||||||
|
private readonly ICryptoService _cryptoService;
|
||||||
|
|
||||||
public LoginService(
|
public LoginService(
|
||||||
ILoginRepository loginRepository,
|
ILoginRepository loginRepository,
|
||||||
IAttachmentRepository attachmentRepository,
|
IAttachmentRepository attachmentRepository,
|
||||||
IAuthService authService,
|
IAuthService authService,
|
||||||
ILoginApiRepository loginApiRepository,
|
ILoginApiRepository loginApiRepository,
|
||||||
ISettingsService settingsService)
|
ISettingsService settingsService,
|
||||||
|
ICryptoService cryptoService)
|
||||||
{
|
{
|
||||||
_loginRepository = loginRepository;
|
_loginRepository = loginRepository;
|
||||||
_attachmentRepository = attachmentRepository;
|
_attachmentRepository = attachmentRepository;
|
||||||
_authService = authService;
|
_authService = authService;
|
||||||
_loginApiRepository = loginApiRepository;
|
_loginApiRepository = loginApiRepository;
|
||||||
_settingsService = settingsService;
|
_settingsService = settingsService;
|
||||||
|
_cryptoService = cryptoService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Login> GetByIdAsync(string id)
|
public async Task<Login> GetByIdAsync(string id)
|
||||||
@ -217,6 +221,33 @@ namespace Bit.App.Services
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<byte[]> DownloadAndDecryptAttachmentAsync(SymmetricCryptoKey key, string url)
|
||||||
|
{
|
||||||
|
using(var client = new HttpClient())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var response = await client.GetAsync(new Uri(url)).ConfigureAwait(false);
|
||||||
|
if(!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var data = await response.Content.ReadAsByteArrayAsync();
|
||||||
|
if(data == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _cryptoService.DecryptToBytes(data, key);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private string WebUriFromAndroidAppUri(string androidAppUriString)
|
private string WebUriFromAndroidAppUri(string androidAppUriString)
|
||||||
{
|
{
|
||||||
if(!UriIsAndroidApp(androidAppUriString))
|
if(!UriIsAndroidApp(androidAppUriString))
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using Bit.App.Models;
|
using Bit.App.Enums;
|
||||||
|
using Bit.App.Models;
|
||||||
using PCLCrypto;
|
using PCLCrypto;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -32,21 +33,41 @@ namespace Bit.App.Utilities
|
|||||||
|
|
||||||
public static byte[] AesCbcDecrypt(CipherString encyptedValue, SymmetricCryptoKey key)
|
public static byte[] AesCbcDecrypt(CipherString encyptedValue, SymmetricCryptoKey key)
|
||||||
{
|
{
|
||||||
if(key == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(key));
|
|
||||||
}
|
|
||||||
|
|
||||||
if(encyptedValue == null)
|
if(encyptedValue == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(encyptedValue));
|
throw new ArgumentNullException(nameof(encyptedValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(key.MacKey != null && !string.IsNullOrWhiteSpace(encyptedValue.Mac))
|
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)
|
||||||
{
|
{
|
||||||
var computedMacBytes = ComputeMac(encyptedValue.CipherTextBytes,
|
throw new ArgumentNullException(nameof(key));
|
||||||
encyptedValue.InitializationVectorBytes, key.MacKey);
|
}
|
||||||
if(!MacsEqual(key.MacKey, computedMacBytes, encyptedValue.MacBytes))
|
|
||||||
|
if(ct == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(ct));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(iv == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(iv));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(key.EncryptionType != type)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(nameof(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(key.MacKey != null && mac != null)
|
||||||
|
{
|
||||||
|
var computedMacBytes = ComputeMac(ct, iv, key.MacKey);
|
||||||
|
if(!MacsEqual(key.MacKey, computedMacBytes, mac))
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("MAC failed.");
|
throw new InvalidOperationException("MAC failed.");
|
||||||
}
|
}
|
||||||
@ -54,8 +75,7 @@ namespace Bit.App.Utilities
|
|||||||
|
|
||||||
var provider = WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithm.AesCbcPkcs7);
|
var provider = WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithm.AesCbcPkcs7);
|
||||||
var cryptoKey = provider.CreateSymmetricKey(key.EncKey);
|
var cryptoKey = provider.CreateSymmetricKey(key.EncKey);
|
||||||
var decryptedBytes = WinRTCrypto.CryptographicEngine.Decrypt(cryptoKey, encyptedValue.CipherTextBytes,
|
var decryptedBytes = WinRTCrypto.CryptographicEngine.Decrypt(cryptoKey, ct, iv);
|
||||||
encyptedValue.InitializationVectorBytes);
|
|
||||||
return decryptedBytes;
|
return decryptedBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,7 +254,7 @@ namespace Bit.iOS
|
|||||||
container.RegisterSingleton<IFolderService, FolderService>();
|
container.RegisterSingleton<IFolderService, FolderService>();
|
||||||
container.RegisterSingleton<ILoginService, LoginService>();
|
container.RegisterSingleton<ILoginService, LoginService>();
|
||||||
container.RegisterSingleton<ISyncService, SyncService>();
|
container.RegisterSingleton<ISyncService, SyncService>();
|
||||||
container.RegisterSingleton<IClipboardService, ClipboardService>();
|
container.RegisterSingleton<IDeviceActionService, DeviceActionService>();
|
||||||
container.RegisterSingleton<IAppIdService, AppIdService>();
|
container.RegisterSingleton<IAppIdService, AppIdService>();
|
||||||
container.RegisterSingleton<IPasswordGenerationService, PasswordGenerationService>();
|
container.RegisterSingleton<IPasswordGenerationService, PasswordGenerationService>();
|
||||||
container.RegisterSingleton<IReflectionService, ReflectionService>();
|
container.RegisterSingleton<IReflectionService, ReflectionService>();
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
using Bit.App.Abstractions;
|
|
||||||
using UIKit;
|
|
||||||
|
|
||||||
namespace Bit.iOS.Services
|
|
||||||
{
|
|
||||||
public class ClipboardService : IClipboardService
|
|
||||||
{
|
|
||||||
public void CopyToClipboard(string text)
|
|
||||||
{
|
|
||||||
UIPasteboard clipboard = UIPasteboard.General;
|
|
||||||
clipboard.String = text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
20
src/iOS/Services/DeviceActionService.cs
Normal file
20
src/iOS/Services/DeviceActionService.cs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
using System;
|
||||||
|
using Bit.App.Abstractions;
|
||||||
|
using UIKit;
|
||||||
|
|
||||||
|
namespace Bit.iOS.Services
|
||||||
|
{
|
||||||
|
public class DeviceActionService : IDeviceActionService
|
||||||
|
{
|
||||||
|
public void CopyToClipboard(string text)
|
||||||
|
{
|
||||||
|
UIPasteboard clipboard = UIPasteboard.General;
|
||||||
|
clipboard.String = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool OpenFile(byte[] fileData, string id, string fileName)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -232,7 +232,7 @@
|
|||||||
<Compile Include="Controls\ExtendedEntryRenderer.cs" />
|
<Compile Include="Controls\ExtendedEntryRenderer.cs" />
|
||||||
<Compile Include="Controls\ExtendedTabbedPageRenderer.cs" />
|
<Compile Include="Controls\ExtendedTabbedPageRenderer.cs" />
|
||||||
<Compile Include="Services\AppInfoService.cs" />
|
<Compile Include="Services\AppInfoService.cs" />
|
||||||
<Compile Include="Services\ClipboardService.cs" />
|
<Compile Include="Services\DeviceActionService.cs" />
|
||||||
<Compile Include="Main.cs" />
|
<Compile Include="Main.cs" />
|
||||||
<Compile Include="AppDelegate.cs" />
|
<Compile Include="AppDelegate.cs" />
|
||||||
<Compile Include="Services\ReflectionService.cs" />
|
<Compile Include="Services\ReflectionService.cs" />
|
||||||
|
Loading…
Reference in New Issue
Block a user