mirror of
https://github.com/bitwarden/mobile
synced 2025-12-05 23:53:33 +00:00
Compare commits
3 Commits
feature/pm
...
feature/PM
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
95ccf1da08 | ||
|
|
ea44a84596 | ||
|
|
62f3fd5b5c |
5
.github/workflows/build.yml
vendored
5
.github/workflows/build.yml
vendored
@@ -71,11 +71,6 @@ jobs:
|
||||
with:
|
||||
nuget-version: 5.9.0
|
||||
|
||||
- name: Set up .NET
|
||||
uses: actions/setup-dotnet@3447fd6a9f9e57506b15f895c5b76d3b197dc7c2 # v3.2.0
|
||||
with:
|
||||
dotnet-version: '3.1.x'
|
||||
|
||||
- name: Set up MSBuild
|
||||
uses: microsoft/setup-msbuild@1ff57057b5cfdc39105cd07a01d78e9b0ea0c14c # v1.3.1
|
||||
|
||||
|
||||
18
.github/workflows/version-auto-bump.yml
vendored
18
.github/workflows/version-auto-bump.yml
vendored
@@ -32,10 +32,14 @@ jobs:
|
||||
echo "new-version=$NEW_VER" >> $GITHUB_OUTPUT
|
||||
|
||||
trigger_version_bump:
|
||||
name: Bump version to ${{ needs.setup.outputs.version_number }}
|
||||
needs: setup
|
||||
uses: ./.github/workflows/version-bump.yml
|
||||
secrets:
|
||||
AZURE_PROD_KV_CREDENTIALS: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
|
||||
with:
|
||||
version_number: ${{ needs.setup.outputs.version_number }}
|
||||
name: "Version bump"
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- setup
|
||||
steps:
|
||||
- name: Bump version to ${{ needs.setup.outputs.version_number }}
|
||||
uses: ./.github/workflows/version-bump.yml
|
||||
secrets:
|
||||
AZURE_PROD_KV_CREDENTIALS: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
|
||||
with:
|
||||
version_number: ${{ needs.setup.outputs.version_number }}
|
||||
|
||||
@@ -46,6 +46,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.ShareExtension", "src\i
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.Autofill", "src\iOS.Autofill\iOS.Autofill.csproj", "{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XamariniOS17CredentialProviderBinding", "lib\ios\iOS17CredentialProvider\XamariniOS17CredentialProvider\XamariniOS17CredentialProviderBinding\XamariniOS17CredentialProviderBinding.csproj", "{63938BA1-0F72-4BFD-B7A4-823C146E43B4}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Ad-Hoc|Any CPU = Ad-Hoc|Any CPU
|
||||
@@ -446,6 +448,36 @@ Global
|
||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhone.Build.0 = Release|iPhone
|
||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
|
||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
|
||||
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
|
||||
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
|
||||
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
|
||||
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.AppStore|Any CPU.Build.0 = Release|Any CPU
|
||||
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.AppStore|iPhone.ActiveCfg = Release|Any CPU
|
||||
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.AppStore|iPhone.Build.0 = Release|Any CPU
|
||||
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.FDroid|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.FDroid|Any CPU.Build.0 = Release|Any CPU
|
||||
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.FDroid|iPhone.ActiveCfg = Release|Any CPU
|
||||
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.FDroid|iPhone.Build.0 = Release|Any CPU
|
||||
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.FDroid|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.FDroid|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -464,6 +496,7 @@ Global
|
||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0} = {8904C536-C67D-420F-9971-51B26574C3AA}
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655} = {D10CA4A9-F866-40E1-B658-F69051236C71}
|
||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A} = {D10CA4A9-F866-40E1-B658-F69051236C71}
|
||||
{63938BA1-0F72-4BFD-B7A4-823C146E43B4} = {D10CA4A9-F866-40E1-B658-F69051236C71}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {7D436EA3-8B7E-45D2-8D14-0730BD2E0410}
|
||||
|
||||
12
crowdin.yml
12
crowdin.yml
@@ -38,15 +38,3 @@ files:
|
||||
pt-PT: pt-PT
|
||||
en-GB: en-GB
|
||||
en-IN: en-IN
|
||||
- source: "/src/watchOS/bitwarden/bitwarden WatchKit Extension/Localization/en.lproj/Localizable.strings"
|
||||
dest: "/src/watchOS/bitwarden/bitwarden WatchKit Extension/Localization/en.lproj/%original_file_name%"
|
||||
translation: "/src/watchOS/bitwarden/bitwarden WatchKit Extension/Localization//%two_letters_code%.lproj/%original_file_name%"
|
||||
update_option: update_as_unapproved
|
||||
languages_mapping:
|
||||
two_letters_code:
|
||||
zh-CN: zh-Hans
|
||||
zh-TW: zh-Hant
|
||||
pt-BR: pt-BR
|
||||
pt-PT: pt-PT
|
||||
en-GB: en-GB
|
||||
en-IN: en-IN
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 25.0.1706.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XamariniOS17CredentialProviderBinding", "XamariniOS17CredentialProviderBinding\XamariniOS17CredentialProviderBinding.csproj", "{63938BA1-0F72-4BFD-B7A4-823C146E43B4}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {5EE0FEF7-C01A-4CAF-BCF0-3A1B818D056A}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,141 @@
|
||||
using System;
|
||||
using AuthenticationServices;
|
||||
using Foundation;
|
||||
using ObjCRuntime;
|
||||
|
||||
namespace XamariniOS17CredentialProviderBinding
|
||||
{
|
||||
interface IASCredentialProviderCompatDelegate { }
|
||||
|
||||
// @protocol ASCredentialProviderCompatDelegate
|
||||
[Protocol, Model(AutoGeneratedName = true)]
|
||||
interface ASCredentialProviderCompatDelegate
|
||||
{
|
||||
// @required -(void)prepareInterfaceToProvideCredentialCompatFor:(ASCredentialRequestCompat * _Nonnull)credentialRequest;
|
||||
[iOS(17, 0)]
|
||||
[Abstract]
|
||||
[Export("prepareInterfaceToProvideCredentialCompatFor:")]
|
||||
void PrepareInterfaceToProvideCredentialCompatFor(ASCredentialRequestCompat credentialRequest);
|
||||
|
||||
// @required -(void)provideCredentialWithoutUserInteractionCompatFor:(ASCredentialRequestCompat * _Nonnull)credentialRequest;
|
||||
[iOS(17, 0)]
|
||||
[Abstract]
|
||||
[Export("provideCredentialWithoutUserInteractionCompatFor:")]
|
||||
void ProvideCredentialWithoutUserInteractionCompatFor(ASCredentialRequestCompat credentialRequest);
|
||||
|
||||
// @required -(void)prepareInterfaceCompatForPasskeyRegistration:(ASCredentialRequestCompat * _Nonnull)registrationRequest;
|
||||
[iOS(17, 0)]
|
||||
[Abstract]
|
||||
[Export("prepareInterfaceCompatForPasskeyRegistration:")]
|
||||
void PrepareInterfaceCompatForPasskeyRegistration(ASCredentialRequestCompat registrationRequest);
|
||||
}
|
||||
|
||||
// @interface ASCredentialRequestCompat : NSObject
|
||||
[iOS(17, 0)]
|
||||
[BaseType (typeof(NSObject))]
|
||||
[DisableDefaultCtor]
|
||||
interface ASCredentialRequestCompat
|
||||
{
|
||||
// @property (nonatomic) enum ASCredentialRequestCompatType type;
|
||||
[Export("type", ArgumentSemantic.Assign)]
|
||||
ASCredentialRequestCompatType Type { get; set; }
|
||||
|
||||
// @property (nonatomic, strong) id<ASCredentialIdentity> _Nonnull credentialIdentity;
|
||||
//[Export("credentialIdentity", ArgumentSemantic.Strong)]
|
||||
//ASCredentialIdentity CredentialIdentity { get; set; }
|
||||
|
||||
|
||||
// @property (readonly, nonatomic, strong) ASPasswordCredentialIdentity * _Nullable passwordCredentialIdentity;
|
||||
[iOS(17, 0)]
|
||||
[NullAllowed, Export("passwordCredentialIdentity", ArgumentSemantic.Strong)]
|
||||
ASPasswordCredentialIdentity PasswordCredentialIdentity { get; }
|
||||
|
||||
// @property (readonly, nonatomic, strong) ASPasskeyCredentialIdentityCompat * _Nullable passkeyCredentialIdentity;
|
||||
[iOS(17, 0)]
|
||||
[NullAllowed, Export("passkeyCredentialIdentity", ArgumentSemantic.Strong)]
|
||||
ASPasskeyCredentialIdentityCompat PasskeyCredentialIdentity { get; }
|
||||
}
|
||||
|
||||
// @interface ASPasskeyCredentialIdentityCompat : NSObject
|
||||
[BaseType(typeof(NSObject))]
|
||||
[DisableDefaultCtor]
|
||||
interface ASPasskeyCredentialIdentityCompat
|
||||
{
|
||||
// @property (copy, nonatomic) NSString * _Nonnull relyingPartyIdentifier;
|
||||
[Export("relyingPartyIdentifier")]
|
||||
string RelyingPartyIdentifier { get; set; }
|
||||
|
||||
// @property (copy, nonatomic) NSString * _Nonnull userName;
|
||||
[Export("userName")]
|
||||
string UserName { get; set; }
|
||||
|
||||
// @property (copy, nonatomic) NSData * _Nonnull credentialID;
|
||||
[Export("credentialID", ArgumentSemantic.Copy)]
|
||||
NSData CredentialID { get; set; }
|
||||
|
||||
// @property (copy, nonatomic) NSData * _Nonnull userHandle;
|
||||
[Export("userHandle", ArgumentSemantic.Copy)]
|
||||
NSData UserHandle { get; set; }
|
||||
|
||||
// @property (copy, nonatomic) NSString * _Nullable recordIdentifier;
|
||||
[NullAllowed, Export("recordIdentifier")]
|
||||
string RecordIdentifier { get; set; }
|
||||
|
||||
// @property (nonatomic) NSInteger rank;
|
||||
[Export("rank")]
|
||||
nint Rank { get; set; }
|
||||
}
|
||||
|
||||
// @interface BaseASCredentialProviderViewController : ASCredentialProviderViewController
|
||||
[BaseType (typeof(ASCredentialProviderViewController))]
|
||||
interface BaseASCredentialProviderViewController
|
||||
{
|
||||
// -(void)prepareInterfaceToProvideCredentialForRequest:(id<ASCredentialRequest> _Nonnull)credentialRequest;
|
||||
//[Export ("prepareInterfaceToProvideCredentialForRequest:")]
|
||||
//void PrepareInterfaceToProvideCredentialForRequest (ASCredentialRequest credentialRequest);
|
||||
|
||||
//// -(void)provideCredentialWithoutUserInteractionForRequest:(id<ASCredentialRequest> _Nonnull)credentialRequest;
|
||||
//[Export ("provideCredentialWithoutUserInteractionForRequest:")]
|
||||
//void ProvideCredentialWithoutUserInteractionForRequest (ASCredentialRequest credentialRequest);
|
||||
|
||||
//// -(void)prepareInterfaceForPasskeyRegistration:(id<ASCredentialRequest> _Nonnull)registrationRequest;
|
||||
//[Export ("prepareInterfaceForPasskeyRegistration:")]
|
||||
//void PrepareInterfaceForPasskeyRegistration (ASCredentialRequest registrationRequest);
|
||||
|
||||
// -(void)prepareInterfaceToProvideCredentialCompatFor:(ASCredentialRequestCompat * _Nonnull)credentialRequest;
|
||||
//[Export ("prepareInterfaceToProvideCredentialCompatFor:")]
|
||||
// [Abstract]
|
||||
// void PrepareInterfaceToProvideCredentialCompatFor (ASCredentialRequestCompat credentialRequest);
|
||||
|
||||
//// -(void)provideCredentialWithoutUserInteractionCompatFor:(ASCredentialRequestCompat * _Nonnull)credentialRequest;
|
||||
//[Export ("provideCredentialWithoutUserInteractionCompatFor:")]
|
||||
// [Abstract]
|
||||
// void ProvideCredentialWithoutUserInteractionCompatFor (ASCredentialRequestCompat credentialRequest);
|
||||
|
||||
//// -(void)prepareInterfaceCompatForPasskeyRegistration:(ASCredentialRequestCompat * _Nonnull)registrationRequest;
|
||||
//[Export ("prepareInterfaceCompatForPasskeyRegistration:")]
|
||||
// [Abstract]
|
||||
// void PrepareInterfaceCompatForPasskeyRegistration (ASCredentialRequestCompat registrationRequest);
|
||||
|
||||
[iOS(17, 0)]
|
||||
[Export("SetCompatDelegate:")]
|
||||
void SetCompatDelegate(IASCredentialProviderCompatDelegate @delegate);
|
||||
|
||||
// -(instancetype _Nonnull)initWithNibName:(NSString * _Nullable)nibNameOrNil bundle:(NSBundle * _Nullable)nibBundleOrNil __attribute__((objc_designated_initializer));
|
||||
[Export ("initWithNibName:bundle:")]
|
||||
[DesignatedInitializer]
|
||||
IntPtr Constructor ([NullAllowed] string nibNameOrNil, [NullAllowed] NSBundle nibBundleOrNil);
|
||||
|
||||
//// -(instancetype _Nullable)initWithCoder:(NSCoder * _Nonnull)coder __attribute__((objc_designated_initializer));
|
||||
//[Export ("initWithCoder:")]
|
||||
//[DesignatedInitializer]
|
||||
// IntPtr Constructor (NSCoder coder);
|
||||
}
|
||||
|
||||
// @interface MyTest : NSObject
|
||||
[BaseType(typeof(NSObject))]
|
||||
//[DisableDefaultCtor]
|
||||
interface MyTest
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
using Foundation;
|
||||
|
||||
// This attribute allows you to mark your assemblies as “safe to link”.
|
||||
// When the attribute is present, the linker—if enabled—will process the assembly
|
||||
// even if you’re using the “Link SDK assemblies only” option, which is the default for device builds.
|
||||
|
||||
[assembly: LinkerSafe]
|
||||
|
||||
// Information about this assembly is defined by the following attributes.
|
||||
// Change them to the values specific to your project.
|
||||
|
||||
[assembly: AssemblyTitle("NativeLibrary")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("NativeLibrary")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2017")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
|
||||
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
|
||||
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
|
||||
|
||||
[assembly: AssemblyVersion("1.0.*")]
|
||||
|
||||
// The following attributes are used to specify the signing key for the assembly,
|
||||
// if desired. See the Mono documentation for more information about signing.
|
||||
|
||||
//[assembly: AssemblyDelaySign(false)]
|
||||
//[assembly: AssemblyKeyFile("")]
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
using ObjCRuntime;
|
||||
|
||||
namespace XamariniOS17CredentialProviderBinding
|
||||
{
|
||||
[Native]
|
||||
public enum ASCredentialRequestCompatType : long
|
||||
{
|
||||
word = 0,
|
||||
keyAssertion = 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectTypeGuids>{8FFB629D-F513-41CE-95D2-7ECE97B6EEEC};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<ProjectGuid>{63938BA1-0F72-4BFD-B7A4-823C146E43B4}</ProjectGuid>
|
||||
<TemplateGuid>{b6f3ff35-79b2-4f25-a2fc-60a7cf61013b}</TemplateGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<RootNamespace>NativeLibrary</RootNamespace>
|
||||
<IPhoneResourcePrefix>Resources</IPhoneResourcePrefix>
|
||||
<AssemblyName>NativeLibrary</AssemblyName>
|
||||
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>portable</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug</OutputPath>
|
||||
<DefineConstants>DEBUG;</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>portable</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release</OutputPath>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="Xamarin.iOS" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Resources\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ObjcBindingApiDefinition Include="ApiDefinitions.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ObjcBindingCoreSource Include="StructsAndEnums.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<NativeReference Include="..\..\iOS17CredentialProvider\build\Release-fat\iOS17CredentialProvider.framework">
|
||||
<Kind>Framework</Kind>
|
||||
<SmartLink>True</SmartLink>
|
||||
<ForceLoad>False</ForceLoad>
|
||||
<WeakFrameworks>Foundation AuthenticationServices</WeakFrameworks>
|
||||
</NativeReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.ObjCBinding.CSharp.targets" />
|
||||
</Project>
|
||||
@@ -0,0 +1,14 @@
|
||||
echo "Building framework"
|
||||
|
||||
xcodebuild -sdk iphonesimulator17.0 -project "iOS17CredentialProvider.xcodeproj" -configuration Release -arch x86_64
|
||||
xcodebuild -sdk iphoneos17.0 -project "iOS17CredentialProvider.xcodeproj" -configuration Release
|
||||
cd build
|
||||
cp -R "Release-iphoneos" "Release-fat"
|
||||
cp -R "Release-iphonesimulator/iOS17CredentialProvider.framework/Modules/iOS17CredentialProvider.swiftmodule/" "Release-fat/iOS17CredentialProvider.framework/Modules/iOS17CredentialProvider.swiftmodule/"
|
||||
lipo -create -output "Release-fat/iOS17CredentialProvider.framework/iOS17CredentialProvider" "Release-iphoneos/iOS17CredentialProvider.framework/iOS17CredentialProvider" "Release-iphonesimulator/iOS17CredentialProvider.framework/iOS17CredentialProvider"
|
||||
|
||||
echo "Sharpie creating binding definitions"
|
||||
|
||||
sharpie bind --sdk=iphoneos17.0 --output="XamarinApiDef" --namespace="Binding" --scope="Release-iphoneos/iOS17CredentialProvider.framework/Headers/" "Release-iphoneos/iOS17CredentialProvider.framework/Headers/iOS17CredentialProvider-Swift.h"
|
||||
|
||||
echo "Done!"
|
||||
@@ -0,0 +1,357 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 56;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
B72040A62A4E421C00E62648 /* iOS17CredentialProvider.docc in Sources */ = {isa = PBXBuildFile; fileRef = B72040A52A4E421C00E62648 /* iOS17CredentialProvider.docc */; };
|
||||
B72040A72A4E421C00E62648 /* iOS17CredentialProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = B72040A42A4E421C00E62648 /* iOS17CredentialProvider.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
B72040AE2A535D6500E62648 /* BaseASCredentialProviderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B72040AD2A535D6500E62648 /* BaseASCredentialProviderViewController.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
B72040A12A4E421C00E62648 /* iOS17CredentialProvider.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = iOS17CredentialProvider.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
B72040A42A4E421C00E62648 /* iOS17CredentialProvider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = iOS17CredentialProvider.h; sourceTree = "<group>"; };
|
||||
B72040A52A4E421C00E62648 /* iOS17CredentialProvider.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = iOS17CredentialProvider.docc; sourceTree = "<group>"; };
|
||||
B72040AD2A535D6500E62648 /* BaseASCredentialProviderViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseASCredentialProviderViewController.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
B720409E2A4E421C00E62648 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
B72040972A4E421C00E62648 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B72040A32A4E421C00E62648 /* iOS17CredentialProvider */,
|
||||
B72040A22A4E421C00E62648 /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B72040A22A4E421C00E62648 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B72040A12A4E421C00E62648 /* iOS17CredentialProvider.framework */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B72040A32A4E421C00E62648 /* iOS17CredentialProvider */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B72040A42A4E421C00E62648 /* iOS17CredentialProvider.h */,
|
||||
B72040A52A4E421C00E62648 /* iOS17CredentialProvider.docc */,
|
||||
B72040AD2A535D6500E62648 /* BaseASCredentialProviderViewController.swift */,
|
||||
);
|
||||
path = "iOS17CredentialProvider";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
B720409C2A4E421C00E62648 /* Headers */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
B72040A72A4E421C00E62648 /* iOS17CredentialProvider.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXHeadersBuildPhase section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
B72040A02A4E421C00E62648 /* iOS17CredentialProvider */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = B72040AA2A4E421C00E62648 /* Build configuration list for PBXNativeTarget "iOS17CredentialProvider" */;
|
||||
buildPhases = (
|
||||
B720409C2A4E421C00E62648 /* Headers */,
|
||||
B720409D2A4E421C00E62648 /* Sources */,
|
||||
B720409E2A4E421C00E62648 /* Frameworks */,
|
||||
B720409F2A4E421C00E62648 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = "iOS17CredentialProvider";
|
||||
productName = "iOS17CredentialProvider";
|
||||
productReference = B72040A12A4E421C00E62648 /* iOS17CredentialProvider.framework */;
|
||||
productType = "com.apple.product-type.framework";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
B72040982A4E421C00E62648 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
BuildIndependentTargetsInParallel = 1;
|
||||
LastUpgradeCheck = 1500;
|
||||
TargetAttributes = {
|
||||
B72040A02A4E421C00E62648 = {
|
||||
CreatedOnToolsVersion = 15.0;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = B720409B2A4E421C00E62648 /* Build configuration list for PBXProject "iOS17CredentialProvider" */;
|
||||
compatibilityVersion = "Xcode 14.0";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = B72040972A4E421C00E62648;
|
||||
productRefGroup = B72040A22A4E421C00E62648 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
B72040A02A4E421C00E62648 /* iOS17CredentialProvider */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
B720409F2A4E421C00E62648 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
B720409D2A4E421C00E62648 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
B72040A62A4E421C00E62648 /* iOS17CredentialProvider.docc in Sources */,
|
||||
B72040AE2A535D6500E62648 /* BaseASCredentialProviderViewController.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
B72040A82A4E421C00E62648 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
B72040A92A4E421C00E62648 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
B72040AB2A4E421C00E62648 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = LTZ2PFU5D6;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
ENABLE_MODULE_VERIFIER = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
|
||||
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.8bit.bitwarden.iOS17CredentialProvider";
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
B72040AC2A4E421C00E62648 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = LTZ2PFU5D6;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
ENABLE_MODULE_VERIFIER = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
|
||||
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.8bit.bitwarden.iOS17CredentialProvider";
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
B720409B2A4E421C00E62648 /* Build configuration list for PBXProject "iOS17CredentialProvider" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
B72040A82A4E421C00E62648 /* Debug */,
|
||||
B72040A92A4E421C00E62648 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
B72040AA2A4E421C00E62648 /* Build configuration list for PBXNativeTarget "iOS17CredentialProvider" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
B72040AB2A4E421C00E62648 /* Debug */,
|
||||
B72040AC2A4E421C00E62648 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = B72040982A4E421C00E62648 /* Project object */;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,66 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1500"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B72040A02A4E421C00E62648"
|
||||
BuildableName = "iOS17CredentialProvider.framework"
|
||||
BlueprintName = "iOS17CredentialProvider"
|
||||
ReferencedContainer = "container:iOS17CredentialProvider.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
shouldAutocreateTestPlan = "YES">
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Release"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B72040A02A4E421C00E62648"
|
||||
BuildableName = "iOS17CredentialProvider.framework"
|
||||
BlueprintName = "iOS17CredentialProvider"
|
||||
ReferencedContainer = "container:iOS17CredentialProvider.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -0,0 +1,188 @@
|
||||
//
|
||||
// BaseASCredentialProviderViewController.swift
|
||||
// iOS17CredentialProvider
|
||||
//
|
||||
// Created by Federico Maccaroni on 03/07/2023.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import AuthenticationServices
|
||||
|
||||
@objc(ASCredentialRequestCompatType)
|
||||
public enum ASCredentialRequestCompatType : Int, @unchecked Sendable {
|
||||
case password = 0
|
||||
|
||||
case passkeyAssertion = 1
|
||||
}
|
||||
|
||||
@objc(ASPasskeyCredentialIdentityCompat)
|
||||
public class ASPasskeyCredentialIdentityCompat : NSObject {
|
||||
|
||||
init(relyingPartyIdentifier: String, userName: String, credentialID: Data, userHandle: Data, recordIdentifier: String? = nil, rank: Int) {
|
||||
self.relyingPartyIdentifier = relyingPartyIdentifier
|
||||
self.userName = userName
|
||||
self.credentialID = credentialID
|
||||
self.userHandle = userHandle
|
||||
self.recordIdentifier = recordIdentifier
|
||||
self.rank = rank
|
||||
}
|
||||
|
||||
/** The relying party identifier of this passkey credential.
|
||||
@discussion This field is reported as the serviceIdentifier property of ASCredentialIdentity.
|
||||
*/
|
||||
@objc
|
||||
public var relyingPartyIdentifier: String
|
||||
|
||||
|
||||
/** The user name of this passkey credential.
|
||||
@discussion This field is reported as the user property of ASCredentialIdentity.
|
||||
*/
|
||||
@objc
|
||||
public var userName: String
|
||||
|
||||
|
||||
/** The credential ID of this passkey credential.
|
||||
@discussion This field is used to identify the correct credential to use based on relying party request parameters.
|
||||
*/
|
||||
@objc
|
||||
public var credentialID: Data
|
||||
|
||||
|
||||
/** The user handle of this passkey credential.
|
||||
@discussion This field is used to identify the correct credential to use based on relying party request parameters.
|
||||
*/
|
||||
@objc
|
||||
public var userHandle: Data
|
||||
|
||||
|
||||
/** Get the record identifier.
|
||||
@result The record identifier.
|
||||
@discussion You can utilize the record identifier to uniquely identify the credential identity in your local database.
|
||||
*/
|
||||
@objc
|
||||
public var recordIdentifier: String?
|
||||
|
||||
|
||||
/** Get or set the rank of the credential identity object.
|
||||
@discussion The system may utilize the rank to decide which credential identity precedes the other
|
||||
if two identities have the same service identifier. A credential identity with a larger rank value
|
||||
precedes one with a smaller value if both credential identities have the same service identifier.
|
||||
The default value of this property is 0.
|
||||
*/
|
||||
@objc
|
||||
public var rank: Int
|
||||
}
|
||||
|
||||
@available(iOS 17.0, *)
|
||||
@objc(ASCredentialRequestCompat)
|
||||
public class ASCredentialRequestCompat : NSObject {
|
||||
|
||||
/** The type of credential used for this request.
|
||||
*/
|
||||
@objc
|
||||
public var type: ASCredentialRequestCompatType
|
||||
|
||||
|
||||
/** The credential identity selected by the user to authenticate.
|
||||
*/
|
||||
@available(iOS 17.0, *)
|
||||
var credentialIdentity: ASCredentialIdentity
|
||||
|
||||
@available(iOS 17.0, *)
|
||||
@objc
|
||||
public var passwordCredentialIdentity: ASPasswordCredentialIdentity? {
|
||||
guard type == .password, let password = credentialIdentity as? ASPasswordCredentialIdentity else {
|
||||
return nil
|
||||
}
|
||||
return password
|
||||
}
|
||||
|
||||
@available(iOS 17.0, *)
|
||||
@objc
|
||||
public var passkeyCredentialIdentity: ASPasskeyCredentialIdentityCompat? {
|
||||
guard type == .passkeyAssertion, let passkey = credentialIdentity as? ASPasskeyCredentialIdentity else {
|
||||
return nil
|
||||
}
|
||||
return ASPasskeyCredentialIdentityCompat(relyingPartyIdentifier: passkey.relyingPartyIdentifier, userName: passkey.userName, credentialID: passkey.credentialID, userHandle: passkey.userHandle, recordIdentifier: passkey.recordIdentifier, rank: passkey.rank)
|
||||
}
|
||||
|
||||
@available(iOS 17.0, *)
|
||||
init(type: ASCredentialRequestCompatType, credentialIdentity: ASCredentialIdentity) {
|
||||
self.type = type
|
||||
self.credentialIdentity = credentialIdentity
|
||||
}
|
||||
}
|
||||
|
||||
@objc(BaseASCredentialProviderViewController)
|
||||
open class BaseASCredentialProviderViewController : ASCredentialProviderViewController
|
||||
{
|
||||
// MARK: iOS 17 new methods
|
||||
|
||||
@available(iOS 17.0, *)
|
||||
override final public func prepareInterfaceToProvideCredential(for credentialRequest: ASCredentialRequest) {
|
||||
guard let compatDelegate = compatDelegate else {
|
||||
return
|
||||
}
|
||||
compatDelegate.prepareInterfaceToProvideCredentialCompat(for: convertRequestToCompat(from: credentialRequest))
|
||||
}
|
||||
|
||||
@available(iOS 17.0, *)
|
||||
override final public func provideCredentialWithoutUserInteraction(for credentialRequest: ASCredentialRequest) {
|
||||
guard let compatDelegate = compatDelegate else {
|
||||
return
|
||||
}
|
||||
compatDelegate.provideCredentialWithoutUserInteractionCompat(for: convertRequestToCompat(from: credentialRequest))
|
||||
}
|
||||
|
||||
@available(iOS 17.0, *)
|
||||
override final public func prepareInterface(forPasskeyRegistration registrationRequest: ASCredentialRequest) {
|
||||
guard let compatDelegate = compatDelegate else {
|
||||
return
|
||||
}
|
||||
compatDelegate.prepareInterfaceCompat(forPasskeyRegistration: convertRequestToCompat(from: registrationRequest))
|
||||
}
|
||||
|
||||
// MARK: Compat
|
||||
|
||||
var compatDelegate: ASCredentialProviderCompatDelegate? = nil;
|
||||
|
||||
@objc
|
||||
@available(iOS 17.0, *)
|
||||
public func SetCompatDelegate(_ delegate: ASCredentialProviderCompatDelegate)
|
||||
{
|
||||
compatDelegate = delegate
|
||||
}
|
||||
|
||||
@available(iOS 17.0, *)
|
||||
func convertRequestToCompat(from credentialRequest: ASCredentialRequest) -> ASCredentialRequestCompat {
|
||||
return ASCredentialRequestCompat(
|
||||
type: credentialRequest.type == .password ? .password : .passkeyAssertion,
|
||||
credentialIdentity: credentialRequest.credentialIdentity)
|
||||
}
|
||||
}
|
||||
|
||||
@objc(ASCredentialProviderCompatDelegate)
|
||||
public protocol ASCredentialProviderCompatDelegate
|
||||
{
|
||||
@available(iOS 17.0, *)
|
||||
@objc
|
||||
func prepareInterfaceToProvideCredentialCompat(for credentialRequest: ASCredentialRequestCompat)
|
||||
|
||||
@available(iOS 17.0, *)
|
||||
@objc
|
||||
func provideCredentialWithoutUserInteractionCompat(for credentialRequest: ASCredentialRequestCompat)
|
||||
|
||||
@available(iOS 17.0, *)
|
||||
@objc
|
||||
func prepareInterfaceCompat(forPasskeyRegistration registrationRequest: ASCredentialRequestCompat)
|
||||
}
|
||||
|
||||
@objc(MyTest)
|
||||
public class MyTest : NSObject
|
||||
{
|
||||
var a: String
|
||||
|
||||
init(a: String) {
|
||||
self.a = a
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
# ``iOS17CredentialProvider``
|
||||
|
||||
<!--@START_MENU_TOKEN@-->Summary<!--@END_MENU_TOKEN@-->
|
||||
|
||||
## Overview
|
||||
|
||||
<!--@START_MENU_TOKEN@-->Text<!--@END_MENU_TOKEN@-->
|
||||
|
||||
## Topics
|
||||
|
||||
### <!--@START_MENU_TOKEN@-->Group<!--@END_MENU_TOKEN@-->
|
||||
|
||||
- <!--@START_MENU_TOKEN@-->``Symbol``<!--@END_MENU_TOKEN@-->
|
||||
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// iOS17CredentialProvider.h
|
||||
// iOS17CredentialProvider
|
||||
//
|
||||
// Created by Federico Maccaroni on 29/06/2023.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
//! Project version number for iOS17CredentialProvider.
|
||||
FOUNDATION_EXPORT double iOS17CredentialProviderVersionNumber;
|
||||
|
||||
//! Project version string for iOS17CredentialProvider.
|
||||
FOUNDATION_EXPORT const unsigned char iOS17CredentialProviderVersionString[];
|
||||
|
||||
// In this header, you should import all the public headers of your framework using statements like #import <iOS17CredentialProvider/PublicHeader.h>
|
||||
|
||||
|
||||
@@ -233,18 +233,6 @@
|
||||
<SubType></SubType>
|
||||
<Generator></Generator>
|
||||
</AndroidResource>
|
||||
<AndroidResource Include="Resources\layout\validatable_input_dialog_layout.xml">
|
||||
<SubType></SubType>
|
||||
<Generator></Generator>
|
||||
</AndroidResource>
|
||||
<AndroidResource Include="Resources\drawable\empty_uris_placeholder.xml">
|
||||
<SubType></SubType>
|
||||
<Generator></Generator>
|
||||
</AndroidResource>
|
||||
<AndroidResource Include="Resources\drawable\empty_uris_placeholder_dark.xml">
|
||||
<SubType></SubType>
|
||||
<Generator></Generator>
|
||||
</AndroidResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable\splash_screen.xml" />
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:versionCode="1" android:versionName="2023.7.1" android:installLocation="internalOnly" package="com.x8bit.bitwarden">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:versionCode="1" android:versionName="2023.5.1" android:installLocation="internalOnly" package="com.x8bit.bitwarden">
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="33" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.NFC" />
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
using System.ComponentModel;
|
||||
using Android.Content;
|
||||
using Android.OS;
|
||||
using System;
|
||||
using Bit.App.Controls;
|
||||
using Bit.Droid.Renderers;
|
||||
using Xamarin.Forms;
|
||||
using System.ComponentModel;
|
||||
using Xamarin.Forms.Platform.Android;
|
||||
using Android.Content;
|
||||
using Xamarin.Forms;
|
||||
using Bit.Droid.Renderers;
|
||||
|
||||
[assembly: ExportRenderer(typeof(CustomLabel), typeof(CustomLabelRenderer))]
|
||||
namespace Bit.Droid.Renderers
|
||||
@@ -15,19 +15,6 @@ namespace Bit.Droid.Renderers
|
||||
: base(context)
|
||||
{ }
|
||||
|
||||
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
|
||||
{
|
||||
base.OnElementChanged(e);
|
||||
|
||||
if (Control != null && e.NewElement is CustomLabel label)
|
||||
{
|
||||
if (label.FontWeight.HasValue && Build.VERSION.SdkInt >= BuildVersionCodes.P)
|
||||
{
|
||||
Control.Typeface = Android.Graphics.Typeface.Create(null, label.FontWeight.Value, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
var label = sender as CustomLabel;
|
||||
@@ -41,3 +28,4 @@ namespace Bit.Droid.Renderers
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:viewportWidth="129"
|
||||
android:viewportHeight="124"
|
||||
android:width="129dp"
|
||||
android:height="124dp">
|
||||
<path
|
||||
android:pathData="M126.8227 61.9441A59.6843 59.6843 0 0 1 7.4541 61.9441A59.6843 59.6843 0 0 1 126.8227 61.9441Z"
|
||||
android:fillColor="#F0F0F0"
|
||||
android:strokeColor="#89929F"
|
||||
android:strokeWidth="3" />
|
||||
<path
|
||||
android:pathData="M21.6167 100.851C52.597 103.31 79.6937 80.3264 82.1391 49.5156C83.6205 30.8497 76.0789 14.8844 62.7275 3.63385"
|
||||
android:strokeColor="#89929F"
|
||||
android:strokeWidth="1.5"
|
||||
android:strokeLineCap="round" />
|
||||
<path
|
||||
android:pathData="M14.5633 34.2845C12.2035 66.7711 38.5225 96.3429 72.6666 98.8232C74.2596 98.9389 78.629 98.9975 80.1951 99C84.6245 98.8232 97.8063 96.593 106.813 91.8485C113.439 88.3581 119.745 84.6984 124.644 79.1121"
|
||||
android:strokeColor="#89929F"
|
||||
android:strokeWidth="1.5"
|
||||
android:strokeLineCap="round" />
|
||||
<path
|
||||
android:pathData="M124.502 48.5051C106.554 24.3817 68.8237 21.6709 41.4178 42.0617C24.8146 54.4149 14.7327 72.4183 13.9255 90.1427"
|
||||
android:strokeColor="#89929F"
|
||||
android:strokeWidth="1.5"
|
||||
android:strokeLineCap="round" />
|
||||
<path
|
||||
android:pathData="M83.4034 28.3934A5 5 0 0 1 73.4034 28.3934A5 5 0 0 1 83.4034 28.3934Z"
|
||||
android:fillColor="#89929F" />
|
||||
<path
|
||||
android:pathData="M24.7698 66.5518A5 5 0 0 1 14.7698 66.5518A5 5 0 0 1 24.7698 66.5518Z"
|
||||
android:fillColor="#89929F" />
|
||||
<path
|
||||
android:pathData="M57.344 94.4726A5 5 0 0 1 47.344 94.4726A5 5 0 0 1 57.344 94.4726Z"
|
||||
android:fillColor="#89929F" />
|
||||
</vector>
|
||||
@@ -1,35 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:viewportWidth="129"
|
||||
android:viewportHeight="124"
|
||||
android:width="129dp"
|
||||
android:height="124dp">
|
||||
<path
|
||||
android:pathData="M126.8227 61.9441A59.6843 59.6843 0 0 1 7.4541 61.9441A59.6843 59.6843 0 0 1 126.8227 61.9441Z"
|
||||
android:fillColor="@android:color/transparent"
|
||||
android:strokeColor="#A3A3A3"
|
||||
android:strokeWidth="3" />
|
||||
<path
|
||||
android:pathData="M21.6167 100.851C52.597 103.31 79.6937 80.3264 82.1391 49.5156C83.6205 30.8497 76.0789 14.8844 62.7275 3.63385"
|
||||
android:strokeColor="#A3A3A3"
|
||||
android:strokeWidth="1.5"
|
||||
android:strokeLineCap="round" />
|
||||
<path
|
||||
android:pathData="M14.5633 34.2845C12.2035 66.7711 38.5225 96.3429 72.6666 98.8232C74.2596 98.9389 78.629 98.9975 80.1951 99C84.6245 98.8232 97.8063 96.593 106.813 91.8485C113.439 88.3581 119.745 84.6984 124.644 79.1121"
|
||||
android:strokeColor="#A3A3A3"
|
||||
android:strokeWidth="1.5"
|
||||
android:strokeLineCap="round" />
|
||||
<path
|
||||
android:pathData="M124.502 48.5051C106.554 24.3817 68.8237 21.6709 41.4178 42.0617C24.8146 54.4149 14.7327 72.4183 13.9255 90.1427"
|
||||
android:strokeColor="#A3A3A3"
|
||||
android:strokeWidth="1.5"
|
||||
android:strokeLineCap="round" />
|
||||
<path
|
||||
android:pathData="M83.4034 28.3934A5 5 0 0 1 73.4034 28.3934A5 5 0 0 1 83.4034 28.3934Z"
|
||||
android:fillColor="#A3A3A3" />
|
||||
<path
|
||||
android:pathData="M24.7698 66.5518A5 5 0 0 1 14.7698 66.5518A5 5 0 0 1 24.7698 66.5518Z"
|
||||
android:fillColor="#A3A3A3" />
|
||||
<path
|
||||
android:pathData="M57.344 94.4726A5 5 0 0 1 47.344 94.4726A5 5 0 0 1 57.344 94.4726Z"
|
||||
android:fillColor="#A3A3A3" />
|
||||
</vector>
|
||||
@@ -1,27 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="30dp"
|
||||
android:paddingRight="30dp">
|
||||
<TextView
|
||||
android:id="@+id/lblHeader"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="@dimen/dialog_header_text_size"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginBottom="-3dp"
|
||||
android:labelFor="@+id/txtValue"/>
|
||||
<EditText
|
||||
android:id="@id/txtValue"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="@dimen/dialog_input_text_size"/>
|
||||
<TextView
|
||||
android:id="@+id/lblValueSubinfo"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="@dimen/dialog_sub_value_info_text_size"/>
|
||||
</LinearLayout>
|
||||
|
||||
@@ -2,7 +2,4 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<dimen name="design_bottom_navigation_text_size" tools:override="true">15sp</dimen>
|
||||
<dimen name="design_bottom_navigation_active_text_size" tools:override="true">15sp</dimen>
|
||||
<dimen name="dialog_input_text_size">16sp</dimen>
|
||||
<dimen name="dialog_header_text_size">12sp</dimen>
|
||||
<dimen name="dialog_sub_value_info_text_size">12sp</dimen>
|
||||
</resources>
|
||||
|
||||
@@ -3,7 +3,6 @@ using System.Threading.Tasks;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.Content.PM;
|
||||
using Android.Media;
|
||||
using Android.Nfc;
|
||||
using Android.OS;
|
||||
using Android.Provider;
|
||||
@@ -14,17 +13,11 @@ using Android.Views.InputMethods;
|
||||
using Android.Widget;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.App.Utilities.Prompts;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Utilities;
|
||||
using Bit.Droid.Utilities;
|
||||
using Plugin.CurrentActivity;
|
||||
using Xamarin.Forms.Platform.Android;
|
||||
using static Android.Icu.Text.CaseMap;
|
||||
using static Android.Renderscripts.ScriptGroup;
|
||||
using static Android.Util.EventLogTags;
|
||||
using static Bit.App.Pages.SettingsPageViewModel;
|
||||
|
||||
namespace Bit.Droid.Services
|
||||
@@ -216,7 +209,10 @@ namespace Bit.Droid.Services
|
||||
}
|
||||
if (numericKeyboard)
|
||||
{
|
||||
SetNumericKeyboardTo(input);
|
||||
input.InputType = InputTypes.ClassNumber | InputTypes.NumberFlagDecimal | InputTypes.NumberFlagSigned;
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
input.KeyListener = DigitsKeyListener.GetInstance(false, false);
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
}
|
||||
if (password)
|
||||
{
|
||||
@@ -252,82 +248,6 @@ namespace Bit.Droid.Services
|
||||
return result.Task;
|
||||
}
|
||||
|
||||
public Task<ValidatablePromptResponse?> DisplayValidatablePromptAsync(ValidatablePromptConfig config)
|
||||
{
|
||||
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
||||
if (activity == null)
|
||||
{
|
||||
return Task.FromResult<ValidatablePromptResponse?>(null);
|
||||
}
|
||||
|
||||
var alertBuilder = new AlertDialog.Builder(activity);
|
||||
alertBuilder.SetTitle(config.Title);
|
||||
var view = activity.LayoutInflater.Inflate(Resource.Layout.validatable_input_dialog_layout, null);
|
||||
alertBuilder.SetView(view);
|
||||
|
||||
var result = new TaskCompletionSource<ValidatablePromptResponse?>();
|
||||
|
||||
alertBuilder.SetPositiveButton(config.OkButtonText ?? AppResources.Ok, listener: null);
|
||||
alertBuilder.SetNegativeButton(config.CancelButtonText ?? AppResources.Cancel, (sender, args) => result.TrySetResult(null));
|
||||
if (!string.IsNullOrEmpty(config.ThirdButtonText))
|
||||
{
|
||||
alertBuilder.SetNeutralButton(config.ThirdButtonText, (sender, args) => result.TrySetResult(new ValidatablePromptResponse(null, true)));
|
||||
}
|
||||
|
||||
var alert = alertBuilder.Create();
|
||||
|
||||
var input = view.FindViewById<EditText>(Resource.Id.txtValue);
|
||||
var lblHeader = view.FindViewById<TextView>(Resource.Id.lblHeader);
|
||||
var lblValueSubinfo = view.FindViewById<TextView>(Resource.Id.lblValueSubinfo);
|
||||
|
||||
lblHeader.Text = config.Subtitle;
|
||||
lblValueSubinfo.Text = config.ValueSubInfo;
|
||||
|
||||
var defaultSubInfoColor = lblValueSubinfo.TextColors;
|
||||
|
||||
input.InputType = InputTypes.ClassText;
|
||||
|
||||
if (config.NumericKeyboard)
|
||||
{
|
||||
SetNumericKeyboardTo(input);
|
||||
}
|
||||
|
||||
input.ImeOptions = input.ImeOptions | (ImeAction)ImeFlags.NoPersonalizedLearning | (ImeAction)ImeFlags.NoExtractUi;
|
||||
input.Text = config.Text ?? string.Empty;
|
||||
input.SetSelection(config.Text?.Length ?? 0);
|
||||
input.AfterTextChanged += (sender, args) =>
|
||||
{
|
||||
if (lblValueSubinfo.Text != config.ValueSubInfo)
|
||||
{
|
||||
lblValueSubinfo.Text = config.ValueSubInfo;
|
||||
lblHeader.SetTextColor(defaultSubInfoColor);
|
||||
lblValueSubinfo.SetTextColor(defaultSubInfoColor);
|
||||
}
|
||||
};
|
||||
|
||||
alert.Window.SetSoftInputMode(SoftInput.StateVisible);
|
||||
alert.Show();
|
||||
|
||||
var positiveButton = alert.GetButton((int)DialogButtonType.Positive);
|
||||
positiveButton.Click += (sender, args) =>
|
||||
{
|
||||
var error = config.ValidateText(input.Text);
|
||||
if (error != null)
|
||||
{
|
||||
lblHeader.SetTextColor(ThemeManager.GetResourceColor("DangerColor").ToAndroid());
|
||||
lblValueSubinfo.SetTextColor(ThemeManager.GetResourceColor("DangerColor").ToAndroid());
|
||||
lblValueSubinfo.Text = error;
|
||||
lblValueSubinfo.SendAccessibilityEvent(Android.Views.Accessibility.EventTypes.ViewFocused);
|
||||
return;
|
||||
}
|
||||
|
||||
result.TrySetResult(new ValidatablePromptResponse(input.Text, false));
|
||||
alert.Dismiss();
|
||||
};
|
||||
|
||||
return result.Task;
|
||||
}
|
||||
|
||||
public void RateApp()
|
||||
{
|
||||
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
||||
@@ -605,13 +525,5 @@ namespace Bit.Droid.Services
|
||||
// only used by iOS
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private void SetNumericKeyboardTo(EditText editText)
|
||||
{
|
||||
editText.InputType = InputTypes.ClassNumber | InputTypes.NumberFlagDecimal | InputTypes.NumberFlagSigned;
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
editText.KeyListener = DigitsKeyListener.GetInstance(false, false);
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using Android.Content;
|
||||
using Android.OS;
|
||||
using Java.Lang;
|
||||
|
||||
namespace Bit.Droid.Utilities
|
||||
{
|
||||
@@ -14,12 +13,7 @@ namespace Bit.Droid.Utilities
|
||||
// Note: getting the bundle like this will cause to call unparcel() internally
|
||||
var b = intent?.Extras?.GetBundle("trashstringwhichhasnousebuttocheckunparcel");
|
||||
}
|
||||
catch (Exception ex) when
|
||||
(
|
||||
ex is BadParcelableException ||
|
||||
ex is ClassNotFoundException ||
|
||||
ex is RuntimeException
|
||||
)
|
||||
catch (BadParcelableException)
|
||||
{
|
||||
intent.ReplaceExtras((Bundle)null);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Utilities.Prompts;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models;
|
||||
|
||||
@@ -19,7 +18,6 @@ namespace Bit.App.Abstractions
|
||||
Task<string> DisplayPromptAync(string title = null, string description = null, string text = null,
|
||||
string okButtonText = null, string cancelButtonText = null, bool numericKeyboard = false,
|
||||
bool autofocus = true, bool password = false);
|
||||
Task<ValidatablePromptResponse?> DisplayValidatablePromptAsync(ValidatablePromptConfig config);
|
||||
Task<string> DisplayAlertAsync(string title, string message, string cancel, params string[] buttons);
|
||||
Task<string> DisplayActionSheetAsync(string title, string cancel, string destruction, params string[] buttons);
|
||||
|
||||
|
||||
@@ -146,7 +146,6 @@
|
||||
<Folder Include="Controls\IconLabelButton\" />
|
||||
<Folder Include="Controls\PasswordStrengthProgressBar\" />
|
||||
<Folder Include="Utilities\Automation\" />
|
||||
<Folder Include="Utilities\Prompts\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -443,6 +442,5 @@
|
||||
<None Remove="MessagePack.MSBuild.Tasks" />
|
||||
<None Remove="Controls\PasswordStrengthProgressBar\" />
|
||||
<None Remove="Utilities\Automation\" />
|
||||
<None Remove="Utilities\Prompts\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Xamarin.Forms;
|
||||
using System;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Controls
|
||||
{
|
||||
@@ -7,7 +8,6 @@ namespace Bit.App.Controls
|
||||
public CustomLabel()
|
||||
{
|
||||
}
|
||||
|
||||
public int? FontWeight { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
<StackLayout StyleClass="box">
|
||||
<Grid
|
||||
StyleClass="box-row"
|
||||
IsVisible="{Binding PinEnabled}"
|
||||
IsVisible="{Binding PinLock}"
|
||||
Padding="0, 10, 0, 0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
@@ -89,7 +89,7 @@
|
||||
<Grid
|
||||
x:Name="_passwordGrid"
|
||||
StyleClass="box-row"
|
||||
IsVisible="{Binding PinEnabled, Converter={StaticResource inverseBool}}"
|
||||
IsVisible="{Binding PinLock, Converter={StaticResource inverseBool}}"
|
||||
Padding="0, 10, 0, 0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_vm?.PinEnabled ?? false)
|
||||
if (_vm?.PinLock ?? false)
|
||||
{
|
||||
return _pin;
|
||||
}
|
||||
@@ -54,7 +54,7 @@ namespace Bit.App.Pages
|
||||
|
||||
public async Task PromptBiometricAfterResumeAsync()
|
||||
{
|
||||
if (_vm.BiometricEnabled)
|
||||
if (_vm.BiometricLock)
|
||||
{
|
||||
await Task.Delay(500);
|
||||
if (!_promptedAfterResume)
|
||||
@@ -91,13 +91,13 @@ namespace Bit.App.Pages
|
||||
|
||||
_vm.FocusSecretEntry += PerformFocusSecretEntry;
|
||||
|
||||
if (!_vm.BiometricEnabled)
|
||||
if (!_vm.BiometricLock)
|
||||
{
|
||||
RequestFocus(SecretEntry);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_vm.UsingKeyConnector && !_vm.PinEnabled)
|
||||
if (_vm.UsingKeyConnector && !_vm.PinLock)
|
||||
{
|
||||
_passwordGrid.IsVisible = false;
|
||||
_unlockButton.IsVisible = false;
|
||||
|
||||
@@ -33,21 +33,21 @@ namespace Bit.App.Pages
|
||||
private readonly WeakEventManager<int?> _secretEntryFocusWeakEventManager = new WeakEventManager<int?>();
|
||||
private readonly IPolicyService _policyService;
|
||||
private readonly IPasswordGenerationService _passwordGenerationService;
|
||||
private IDeviceTrustCryptoService _deviceTrustCryptoService;
|
||||
private readonly ISyncService _syncService;
|
||||
|
||||
private string _email;
|
||||
private string _masterPassword;
|
||||
private string _pin;
|
||||
private bool _showPassword;
|
||||
private PinLockEnum _pinStatus;
|
||||
private bool _pinEnabled;
|
||||
private bool _biometricEnabled;
|
||||
private bool _pinLock;
|
||||
private bool _biometricLock;
|
||||
private bool _biometricIntegrityValid = true;
|
||||
private bool _biometricButtonVisible;
|
||||
private bool _usingKeyConnector;
|
||||
private string _biometricButtonText;
|
||||
private string _loggedInAsText;
|
||||
private string _lockedVerifyText;
|
||||
private bool _isPinProtected;
|
||||
private bool _isPinProtectedWithKey;
|
||||
|
||||
public LockPageViewModel()
|
||||
{
|
||||
@@ -65,8 +65,6 @@ namespace Bit.App.Pages
|
||||
_watchDeviceService = ServiceContainer.Resolve<IWatchDeviceService>();
|
||||
_policyService = ServiceContainer.Resolve<IPolicyService>();
|
||||
_passwordGenerationService = ServiceContainer.Resolve<IPasswordGenerationService>();
|
||||
_deviceTrustCryptoService = ServiceContainer.Resolve<IDeviceTrustCryptoService>();
|
||||
_syncService = ServiceContainer.Resolve<ISyncService>();
|
||||
|
||||
PageTitle = AppResources.VerifyMasterPassword;
|
||||
TogglePasswordCommand = new Command(TogglePassword);
|
||||
@@ -102,10 +100,10 @@ namespace Bit.App.Pages
|
||||
});
|
||||
}
|
||||
|
||||
public bool PinEnabled
|
||||
public bool PinLock
|
||||
{
|
||||
get => _pinEnabled;
|
||||
set => SetProperty(ref _pinEnabled, value);
|
||||
get => _pinLock;
|
||||
set => SetProperty(ref _pinLock, value);
|
||||
}
|
||||
|
||||
public bool UsingKeyConnector
|
||||
@@ -113,10 +111,10 @@ namespace Bit.App.Pages
|
||||
get => _usingKeyConnector;
|
||||
}
|
||||
|
||||
public bool BiometricEnabled
|
||||
public bool BiometricLock
|
||||
{
|
||||
get => _biometricEnabled;
|
||||
set => SetProperty(ref _biometricEnabled, value);
|
||||
get => _biometricLock;
|
||||
set => SetProperty(ref _biometricLock, value);
|
||||
}
|
||||
|
||||
public bool BiometricIntegrityValid
|
||||
@@ -164,18 +162,14 @@ namespace Bit.App.Pages
|
||||
|
||||
public async Task InitAsync()
|
||||
{
|
||||
_pinStatus = await _vaultTimeoutService.IsPinLockSetAsync();
|
||||
|
||||
var ephemeralPinSet = await _stateService.GetUserKeyPinEphemeralAsync()
|
||||
?? await _stateService.GetPinProtectedKeyAsync();
|
||||
PinEnabled = (_pinStatus == PinLockEnum.Transient && ephemeralPinSet != null) ||
|
||||
_pinStatus == PinLockEnum.Persistent;
|
||||
|
||||
BiometricEnabled = await _vaultTimeoutService.IsBiometricLockSetAsync() && await _cryptoService.HasEncryptedUserKeyAsync();
|
||||
(_isPinProtected, _isPinProtectedWithKey) = await _vaultTimeoutService.IsPinLockSetAsync();
|
||||
PinLock = (_isPinProtected && await _stateService.GetPinProtectedKeyAsync() != null) ||
|
||||
_isPinProtectedWithKey;
|
||||
BiometricLock = await _vaultTimeoutService.IsBiometricLockSetAsync() && await _cryptoService.HasKeyAsync();
|
||||
|
||||
// Users with key connector and without biometric or pin has no MP to unlock with
|
||||
_usingKeyConnector = await _keyConnectorService.GetUsesKeyConnector();
|
||||
if (_usingKeyConnector && !(BiometricEnabled || PinEnabled))
|
||||
if (_usingKeyConnector && !(BiometricLock || PinLock))
|
||||
{
|
||||
await _vaultTimeoutService.LogOutAsync();
|
||||
return;
|
||||
@@ -194,7 +188,7 @@ namespace Bit.App.Pages
|
||||
}
|
||||
var webVaultHostname = CoreHelpers.GetHostname(webVault);
|
||||
LoggedInAsText = string.Format(AppResources.LoggedInAsOn, _email, webVaultHostname);
|
||||
if (PinEnabled)
|
||||
if (PinLock)
|
||||
{
|
||||
PageTitle = AppResources.VerifyPIN;
|
||||
LockedVerifyText = AppResources.VaultLockedPIN;
|
||||
@@ -213,7 +207,7 @@ namespace Bit.App.Pages
|
||||
}
|
||||
}
|
||||
|
||||
if (BiometricEnabled)
|
||||
if (BiometricLock)
|
||||
{
|
||||
BiometricIntegrityValid = await _platformUtilsService.IsBiometricIntegrityValidAsync();
|
||||
if (!_biometricIntegrityValid)
|
||||
@@ -235,14 +229,14 @@ namespace Bit.App.Pages
|
||||
|
||||
public async Task SubmitAsync()
|
||||
{
|
||||
if (PinEnabled && string.IsNullOrWhiteSpace(Pin))
|
||||
if (PinLock && string.IsNullOrWhiteSpace(Pin))
|
||||
{
|
||||
await Page.DisplayAlert(AppResources.AnErrorHasOccurred,
|
||||
string.Format(AppResources.ValidationFieldRequired, AppResources.PIN),
|
||||
AppResources.Ok);
|
||||
return;
|
||||
}
|
||||
if (!PinEnabled && string.IsNullOrWhiteSpace(MasterPassword))
|
||||
if (!PinLock && string.IsNullOrWhiteSpace(MasterPassword))
|
||||
{
|
||||
await Page.DisplayAlert(AppResources.AnErrorHasOccurred,
|
||||
string.Format(AppResources.ValidationFieldRequired, AppResources.MasterPassword),
|
||||
@@ -253,54 +247,34 @@ namespace Bit.App.Pages
|
||||
ShowPassword = false;
|
||||
var kdfConfig = await _stateService.GetActiveUserCustomDataAsync(a => new KdfConfig(a?.Profile));
|
||||
|
||||
if (PinEnabled)
|
||||
if (PinLock)
|
||||
{
|
||||
var failed = true;
|
||||
try
|
||||
{
|
||||
EncString userKeyPin = null;
|
||||
EncString oldPinProtected = null;
|
||||
if (_pinStatus == PinLockEnum.Persistent)
|
||||
if (_isPinProtected)
|
||||
{
|
||||
userKeyPin = await _stateService.GetUserKeyPinAsync();
|
||||
var oldEncryptedKey = await _stateService.GetPinProtectedAsync();
|
||||
oldPinProtected = oldEncryptedKey != null ? new EncString(oldEncryptedKey) : null;
|
||||
}
|
||||
else if (_pinStatus == PinLockEnum.Transient)
|
||||
{
|
||||
userKeyPin = await _stateService.GetUserKeyPinEphemeralAsync();
|
||||
oldPinProtected = await _stateService.GetPinProtectedKeyAsync();
|
||||
}
|
||||
|
||||
UserKey userKey;
|
||||
if (oldPinProtected != null)
|
||||
{
|
||||
userKey = await _cryptoService.DecryptAndMigrateOldPinKeyAsync(
|
||||
_pinStatus == PinLockEnum.Transient,
|
||||
Pin,
|
||||
_email,
|
||||
var key = await _cryptoService.MakeKeyFromPinAsync(Pin, _email,
|
||||
kdfConfig,
|
||||
oldPinProtected
|
||||
);
|
||||
await _stateService.GetPinProtectedKeyAsync());
|
||||
var encKey = await _cryptoService.GetEncKeyAsync(key);
|
||||
var protectedPin = await _stateService.GetProtectedPinAsync();
|
||||
var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey);
|
||||
failed = decPin != Pin;
|
||||
if (!failed)
|
||||
{
|
||||
Pin = string.Empty;
|
||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||
await SetKeyAndContinueAsync(key);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
userKey = await _cryptoService.DecryptUserKeyWithPinAsync(
|
||||
Pin,
|
||||
_email,
|
||||
kdfConfig,
|
||||
userKeyPin
|
||||
);
|
||||
}
|
||||
|
||||
var protectedPin = await _stateService.GetProtectedPinAsync();
|
||||
var decryptedPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), userKey);
|
||||
failed = decryptedPin != Pin;
|
||||
if (!failed)
|
||||
{
|
||||
var key = await _cryptoService.MakeKeyFromPinAsync(Pin, _email, kdfConfig);
|
||||
failed = false;
|
||||
Pin = string.Empty;
|
||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||
await SetKeyAndContinueAsync(userKey);
|
||||
await SetKeyAndContinueAsync(key);
|
||||
}
|
||||
}
|
||||
catch
|
||||
@@ -321,21 +295,19 @@ namespace Bit.App.Pages
|
||||
}
|
||||
else
|
||||
{
|
||||
var masterKey = await _cryptoService.MakeMasterKeyAsync(MasterPassword, _email, kdfConfig);
|
||||
var storedKeyHash = await _cryptoService.GetPasswordHashAsync();
|
||||
var key = await _cryptoService.MakeKeyAsync(MasterPassword, _email, kdfConfig);
|
||||
var storedKeyHash = await _cryptoService.GetKeyHashAsync();
|
||||
var passwordValid = false;
|
||||
MasterPasswordPolicyOptions enforcedMasterPasswordOptions = null;
|
||||
|
||||
if (storedKeyHash != null)
|
||||
{
|
||||
// Offline unlock possible
|
||||
passwordValid = await _cryptoService.CompareAndUpdatePasswordHashAsync(MasterPassword, masterKey);
|
||||
passwordValid = await _cryptoService.CompareAndUpdateKeyHashAsync(MasterPassword, key);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Online unlock required
|
||||
await _deviceActionService.ShowLoadingAsync(AppResources.Loading);
|
||||
var keyHash = await _cryptoService.HashPasswordAsync(MasterPassword, masterKey, HashPurpose.ServerAuthorization);
|
||||
var keyHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.ServerAuthorization);
|
||||
var request = new PasswordVerificationRequest();
|
||||
request.MasterPasswordHash = keyHash;
|
||||
|
||||
@@ -344,8 +316,8 @@ namespace Bit.App.Pages
|
||||
var response = await _apiService.PostAccountVerifyPasswordAsync(request);
|
||||
enforcedMasterPasswordOptions = response.MasterPasswordPolicy;
|
||||
passwordValid = true;
|
||||
var localKeyHash = await _cryptoService.HashPasswordAsync(MasterPassword, masterKey, HashPurpose.LocalAuthorization);
|
||||
await _cryptoService.SetPasswordHashAsync(localKeyHash);
|
||||
var localKeyHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.LocalAuthorization);
|
||||
await _cryptoService.SetKeyHashAsync(localKeyHash);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -355,6 +327,15 @@ namespace Bit.App.Pages
|
||||
}
|
||||
if (passwordValid)
|
||||
{
|
||||
if (_isPinProtected)
|
||||
{
|
||||
var protectedPin = await _stateService.GetProtectedPinAsync();
|
||||
var encKey = await _cryptoService.GetEncKeyAsync(key);
|
||||
var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey);
|
||||
var pinKey = await _cryptoService.MakePinKeyAysnc(decPin, _email, kdfConfig);
|
||||
await _stateService.SetPinProtectedKeyAsync(await _cryptoService.EncryptAsync(key.Key, pinKey));
|
||||
}
|
||||
|
||||
if (await RequirePasswordChangeAsync(enforcedMasterPasswordOptions))
|
||||
{
|
||||
// Save the ForcePasswordResetReason to force a password reset after unlock
|
||||
@@ -364,13 +345,10 @@ namespace Bit.App.Pages
|
||||
|
||||
MasterPassword = string.Empty;
|
||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||
|
||||
var userKey = await _cryptoService.DecryptUserKeyWithMasterKeyAsync(masterKey);
|
||||
await _cryptoService.SetMasterKeyAsync(masterKey);
|
||||
await SetKeyAndContinueAsync(userKey);
|
||||
await SetKeyAndContinueAsync(key);
|
||||
|
||||
// Re-enable biometrics
|
||||
if (BiometricEnabled & !BiometricIntegrityValid)
|
||||
if (BiometricLock & !BiometricIntegrityValid)
|
||||
{
|
||||
await _biometricService.SetupBiometricAsync();
|
||||
}
|
||||
@@ -447,7 +425,7 @@ namespace Bit.App.Pages
|
||||
public void TogglePassword()
|
||||
{
|
||||
ShowPassword = !ShowPassword;
|
||||
var secret = PinEnabled ? Pin : MasterPassword;
|
||||
var secret = PinLock ? Pin : MasterPassword;
|
||||
_secretEntryFocusWeakEventManager.RaiseEvent(string.IsNullOrEmpty(secret) ? 0 : secret.Length, nameof(FocusSecretEntry));
|
||||
}
|
||||
|
||||
@@ -455,12 +433,12 @@ namespace Bit.App.Pages
|
||||
{
|
||||
BiometricIntegrityValid = await _platformUtilsService.IsBiometricIntegrityValidAsync();
|
||||
BiometricButtonVisible = BiometricIntegrityValid;
|
||||
if (!BiometricEnabled || !BiometricIntegrityValid)
|
||||
if (!BiometricLock || !BiometricIntegrityValid)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var success = await _platformUtilsService.AuthenticateBiometricAsync(null,
|
||||
PinEnabled ? AppResources.PIN : AppResources.MasterPassword,
|
||||
PinLock ? AppResources.PIN : AppResources.MasterPassword,
|
||||
() => _secretEntryFocusWeakEventManager.RaiseEvent((int?)null, nameof(FocusSecretEntry)));
|
||||
await _stateService.SetBiometricLockedAsync(!success);
|
||||
if (success)
|
||||
@@ -469,20 +447,18 @@ namespace Bit.App.Pages
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SetKeyAndContinueAsync(UserKey key)
|
||||
private async Task SetKeyAndContinueAsync(SymmetricCryptoKey key)
|
||||
{
|
||||
var hasKey = await _cryptoService.HasUserKeyAsync();
|
||||
var hasKey = await _cryptoService.HasKeyAsync();
|
||||
if (!hasKey)
|
||||
{
|
||||
await _cryptoService.SetUserKeyAsync(key);
|
||||
await _cryptoService.SetKeyAsync(key);
|
||||
}
|
||||
await _deviceTrustCryptoService.TrustDeviceIfNeededAsync();
|
||||
await DoContinueAsync();
|
||||
}
|
||||
|
||||
private async Task DoContinueAsync()
|
||||
{
|
||||
_syncService.FullSyncAsync(false).FireAndForget();
|
||||
await _stateService.SetBiometricLockedAsync(false);
|
||||
_watchDeviceService.SyncDataToWatchAsync().FireAndForget();
|
||||
_messagingService.Send("unlocked");
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<pages:BaseContentPage
|
||||
xmlns="http://xamarin.com/schemas/2014/forms"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
x:Class="Bit.App.Pages.LoginApproveDevicePage"
|
||||
xmlns:pages="clr-namespace:Bit.App.Pages"
|
||||
xmlns:u="clr-namespace:Bit.App.Utilities"
|
||||
x:DataType="pages:LoginApproveDeviceViewModel"
|
||||
x:Name="_page"
|
||||
Title="{Binding PageTitle}">
|
||||
|
||||
<ContentPage.BindingContext>
|
||||
<pages:LoginApproveDeviceViewModel />
|
||||
</ContentPage.BindingContext>
|
||||
|
||||
<StackLayout Padding="10, 10">
|
||||
<StackLayout Padding="5, 10" Orientation="Horizontal">
|
||||
<StackLayout HorizontalOptions="FillAndExpand">
|
||||
<Label
|
||||
StyleClass="text-md"
|
||||
Text="{u:I18n RememberThisDevice}"/>
|
||||
<Label
|
||||
StyleClass="box-sub-label"
|
||||
Text="{u:I18n TurnOffUsingPublicDevice}"/>
|
||||
</StackLayout>
|
||||
<Switch
|
||||
Scale="0.8"
|
||||
IsToggled="{Binding RememberThisDevice}"
|
||||
VerticalOptions="Center"/>
|
||||
</StackLayout>
|
||||
<StackLayout Margin="0, 20, 0, 0">
|
||||
<Button
|
||||
x:Name="_continue"
|
||||
Text="{u:I18n Continue}"
|
||||
StyleClass="btn-primary"
|
||||
Command="{Binding ContinueCommand}"
|
||||
IsVisible="{Binding ContinueEnabled}"/>
|
||||
<Button
|
||||
x:Name="_approveWithMyOtherDevice"
|
||||
Text="{u:I18n ApproveWithMyOtherDevice}"
|
||||
StyleClass="btn-primary"
|
||||
Command="{Binding ApproveWithMyOtherDeviceCommand}"
|
||||
IsVisible="{Binding ApproveWithMyOtherDeviceEnabled}"/>
|
||||
<Button
|
||||
x:Name="_requestAdminApproval"
|
||||
Text="{u:I18n RequestAdminApproval}"
|
||||
StyleClass="box-button-row"
|
||||
Command="{Binding RequestAdminApprovalCommand}"
|
||||
IsVisible="{Binding RequestAdminApprovalEnabled}"/>
|
||||
<Button
|
||||
x:Name="_approveWithMasterPassword"
|
||||
Text="{u:I18n ApproveWithMasterPassword}"
|
||||
StyleClass="box-button-row"
|
||||
Command="{Binding ApproveWithMasterPasswordCommand}"
|
||||
IsVisible="{Binding ApproveWithMasterPasswordEnabled}"/>
|
||||
<Label
|
||||
Text="{Binding LoggingInAsText}"
|
||||
StyleClass="text-sm"
|
||||
Margin="0,40,0,0"
|
||||
AutomationId="LoggingInAsLabel"
|
||||
/>
|
||||
<Label
|
||||
Text="{u:I18n NotYou}"
|
||||
StyleClass="text-md"
|
||||
HorizontalOptions="Start"
|
||||
TextColor="{DynamicResource HyperlinkColor}"
|
||||
AutomationId="NotYouLabel">
|
||||
<Label.GestureRecognizers>
|
||||
<TapGestureRecognizer Tapped="Cancel_Clicked" />
|
||||
</Label.GestureRecognizers>
|
||||
</Label>
|
||||
</StackLayout>
|
||||
</StackLayout>
|
||||
</pages:BaseContentPage>
|
||||
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Models;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Utilities;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
{
|
||||
public partial class LoginApproveDevicePage : BaseContentPage
|
||||
{
|
||||
|
||||
private readonly LoginApproveDeviceViewModel _vm;
|
||||
private readonly AppOptions _appOptions;
|
||||
|
||||
public LoginApproveDevicePage(AppOptions appOptions = null)
|
||||
{
|
||||
InitializeComponent();
|
||||
_vm = BindingContext as LoginApproveDeviceViewModel;
|
||||
_vm.LogInWithMasterPasswordAction = () => StartLogInWithMasterPassword().FireAndForget();
|
||||
_vm.LogInWithDeviceAction = () => StartLoginWithDeviceAsync().FireAndForget();
|
||||
_vm.RequestAdminApprovalAction = () => RequestAdminApprovalAsync().FireAndForget();
|
||||
_vm.CloseAction = () => { Navigation.PopModalAsync(); };
|
||||
_vm.Page = this;
|
||||
_appOptions = appOptions;
|
||||
}
|
||||
|
||||
protected override void OnAppearing()
|
||||
{
|
||||
_vm.InitAsync();
|
||||
}
|
||||
|
||||
private void Cancel_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
if (DoOnce())
|
||||
{
|
||||
_vm.CloseAction();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task StartLogInWithMasterPassword()
|
||||
{
|
||||
var page = new LockPage(_appOptions);
|
||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||
}
|
||||
|
||||
private async Task StartLoginWithDeviceAsync()
|
||||
{
|
||||
var page = new LoginPasswordlessRequestPage(_vm.Email, AuthRequestType.AuthenticateAndUnlock, _appOptions);
|
||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||
}
|
||||
|
||||
private async Task RequestAdminApprovalAsync()
|
||||
{
|
||||
var page = new LoginPasswordlessRequestPage(_vm.Email, AuthRequestType.AdminApproval, _appOptions);
|
||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,132 +0,0 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.App.Utilities.AccountManagement;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Request;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Utilities;
|
||||
using Xamarin.CommunityToolkit.ObjectModel;
|
||||
using Xamarin.Essentials;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
{
|
||||
public class LoginApproveDeviceViewModel : BaseViewModel
|
||||
{
|
||||
private bool _rememberThisDevice;
|
||||
private bool _approveWithMyOtherDeviceEnabled;
|
||||
private bool _requestAdminApprovalEnabled;
|
||||
private bool _approveWithMasterPasswordEnabled;
|
||||
private bool _continueEnabled;
|
||||
private string _email;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IApiService _apiService;
|
||||
private IDeviceTrustCryptoService _deviceTrustCryptoService;
|
||||
|
||||
public ICommand ApproveWithMyOtherDeviceCommand { get; }
|
||||
public ICommand RequestAdminApprovalCommand { get; }
|
||||
public ICommand ApproveWithMasterPasswordCommand { get; }
|
||||
public ICommand ContinueCommand { get; }
|
||||
|
||||
public Action LogInWithMasterPasswordAction { get; set; }
|
||||
public Action LogInWithDeviceAction { get; set; }
|
||||
public Action RequestAdminApprovalAction { get; set; }
|
||||
public Action CloseAction { get; set; }
|
||||
|
||||
public LoginApproveDeviceViewModel()
|
||||
{
|
||||
_stateService = ServiceContainer.Resolve<IStateService>();
|
||||
_apiService = ServiceContainer.Resolve<IApiService>();
|
||||
_deviceTrustCryptoService = ServiceContainer.Resolve<IDeviceTrustCryptoService>();
|
||||
|
||||
PageTitle = AppResources.LoggedIn;
|
||||
|
||||
ApproveWithMyOtherDeviceCommand = new AsyncCommand(() => SetDeviceTrustAndInvokeAsync(LogInWithDeviceAction),
|
||||
onException: ex => HandleException(ex),
|
||||
allowsMultipleExecutions: false);
|
||||
|
||||
RequestAdminApprovalCommand = new AsyncCommand(() => SetDeviceTrustAndInvokeAsync(RequestAdminApprovalAction),
|
||||
onException: ex => HandleException(ex),
|
||||
allowsMultipleExecutions: false);
|
||||
|
||||
ApproveWithMasterPasswordCommand = new AsyncCommand(() => SetDeviceTrustAndInvokeAsync(LogInWithMasterPasswordAction),
|
||||
onException: ex => HandleException(ex),
|
||||
allowsMultipleExecutions: false);
|
||||
|
||||
ContinueCommand = new AsyncCommand(InitAsync,
|
||||
onException: ex => HandleException(ex),
|
||||
allowsMultipleExecutions: false);
|
||||
}
|
||||
|
||||
public string LoggingInAsText => string.Format(AppResources.LoggingInAsX, Email);
|
||||
|
||||
public bool RememberThisDevice
|
||||
{
|
||||
get => _rememberThisDevice;
|
||||
set => SetProperty(ref _rememberThisDevice, value);
|
||||
}
|
||||
|
||||
public bool ApproveWithMyOtherDeviceEnabled
|
||||
{
|
||||
get => _approveWithMyOtherDeviceEnabled;
|
||||
set => SetProperty(ref _approveWithMyOtherDeviceEnabled, value);
|
||||
}
|
||||
|
||||
public bool RequestAdminApprovalEnabled
|
||||
{
|
||||
get => _requestAdminApprovalEnabled;
|
||||
set => SetProperty(ref _requestAdminApprovalEnabled, value);
|
||||
}
|
||||
|
||||
public bool ApproveWithMasterPasswordEnabled
|
||||
{
|
||||
get => _approveWithMasterPasswordEnabled;
|
||||
set => SetProperty(ref _approveWithMasterPasswordEnabled, value);
|
||||
}
|
||||
|
||||
public bool ContinueEnabled
|
||||
{
|
||||
get => _continueEnabled;
|
||||
set => SetProperty(ref _continueEnabled, value);
|
||||
}
|
||||
|
||||
public string Email
|
||||
{
|
||||
get => _email;
|
||||
set => SetProperty(ref _email, value, additionalPropertyNames:
|
||||
new string[] {
|
||||
nameof(LoggingInAsText)
|
||||
});
|
||||
}
|
||||
|
||||
public async Task InitAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
Email = await _stateService.GetRememberedEmailAsync();
|
||||
var decryptOptions = await _stateService.GetAccountDecryptionOptions();
|
||||
RequestAdminApprovalEnabled = decryptOptions?.TrustedDeviceOption?.HasAdminApproval ?? false;
|
||||
ApproveWithMasterPasswordEnabled = decryptOptions?.HasMasterPassword ?? false;
|
||||
ApproveWithMyOtherDeviceEnabled = decryptOptions?.TrustedDeviceOption?.HasLoginApprovingDevice ?? false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
HandleException(ex);
|
||||
}
|
||||
|
||||
// TODO: Change this expression to, Appear if the browser is trusted and shared the key with the app
|
||||
ContinueEnabled = !RequestAdminApprovalEnabled && !ApproveWithMasterPasswordEnabled && !ApproveWithMyOtherDeviceEnabled;
|
||||
}
|
||||
|
||||
private async Task SetDeviceTrustAndInvokeAsync(Action action)
|
||||
{
|
||||
await _deviceTrustCryptoService.SetShouldTrustDeviceAsync(RememberThisDevice);
|
||||
await Device.InvokeOnMainThreadAsync(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ using Bit.App.Models;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Utilities;
|
||||
using Xamarin.CommunityToolkit.ObjectModel;
|
||||
@@ -136,7 +135,7 @@ namespace Bit.App.Pages
|
||||
|
||||
private async Task StartLoginWithDeviceAsync()
|
||||
{
|
||||
var page = new LoginPasswordlessRequestPage(_vm.Email, AuthRequestType.AuthenticateAndUnlock, _appOptions);
|
||||
var page = new LoginPasswordlessRequestPage(_vm.Email, _appOptions);
|
||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||
}
|
||||
|
||||
|
||||
@@ -21,17 +21,17 @@
|
||||
<StackLayout
|
||||
Padding="7, 0, 7, 20">
|
||||
<Label
|
||||
Text="{Binding Tittle}"
|
||||
Text="{u:I18n LogInInitiated}"
|
||||
FontSize="Title"
|
||||
FontAttributes="Bold"
|
||||
Margin="0,14,0,21"
|
||||
AutomationId="LogInInitiatedLabel" />
|
||||
<Label
|
||||
Text="{Binding SubTittle}"
|
||||
Text="{u:I18n ANotificationHasBeenSentToYourDevice}"
|
||||
FontSize="Small"
|
||||
Margin="0,0,0,10"/>
|
||||
<Label
|
||||
Text="{Binding Description}"
|
||||
Text="{u:I18n PleaseMakeSureYourVaultIsUnlockedAndTheFingerprintPhraseMatchesOnTheOtherDevice}"
|
||||
FontSize="Small"
|
||||
Margin="0,0,0,24"/>
|
||||
<Label
|
||||
@@ -45,7 +45,6 @@
|
||||
AutomationId="FingerprintPhraseValue" />
|
||||
<Label
|
||||
Text="{u:I18n ResendNotification}"
|
||||
IsVisible="{Binding ResendNotificationVisible}"
|
||||
StyleClass="text-md"
|
||||
HorizontalOptions="Start"
|
||||
Margin="0,40,0,0"
|
||||
@@ -59,7 +58,7 @@
|
||||
Orientation="Horizontal"
|
||||
Margin="0,30,0,0">
|
||||
<Label
|
||||
Text="{Binding OtherOptions}"
|
||||
Text="{u:I18n NeedAnotherOption}"
|
||||
FontSize="Small"
|
||||
VerticalTextAlignment="End"/>
|
||||
<Label
|
||||
|
||||
@@ -3,7 +3,6 @@ using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Models;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core.Enums;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
@@ -13,14 +12,13 @@ namespace Bit.App.Pages
|
||||
private LoginPasswordlessRequestViewModel _vm;
|
||||
private readonly AppOptions _appOptions;
|
||||
|
||||
public LoginPasswordlessRequestPage(string email, AuthRequestType authRequestType, AppOptions appOptions = null)
|
||||
public LoginPasswordlessRequestPage(string email, AppOptions appOptions = null)
|
||||
{
|
||||
InitializeComponent();
|
||||
_appOptions = appOptions;
|
||||
_vm = BindingContext as LoginPasswordlessRequestViewModel;
|
||||
_vm.Page = this;
|
||||
_vm.Email = email;
|
||||
_vm.AuthRequestType = authRequestType;
|
||||
_vm.StartTwoFactorAction = () => Device.BeginInvokeOnMainThread(async () => await StartTwoFactorAsync());
|
||||
_vm.LogInSuccessAction = () => Device.BeginInvokeOnMainThread(async () => await LogInSuccessAsync());
|
||||
_vm.UpdateTempPasswordAction = () => Device.BeginInvokeOnMainThread(async () => await UpdateTempPasswordAsync());
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -13,7 +12,6 @@ using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Domain;
|
||||
using Bit.Core.Models.Response;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Utilities;
|
||||
using Xamarin.CommunityToolkit.ObjectModel;
|
||||
@@ -34,9 +32,6 @@ namespace Bit.App.Pages
|
||||
private IPlatformUtilsService _platformUtilsService;
|
||||
private IEnvironmentService _environmentService;
|
||||
private ILogger _logger;
|
||||
private IDeviceTrustCryptoService _deviceTrustCryptoService;
|
||||
private readonly ICryptoFunctionService _cryptoFunctionService;
|
||||
private readonly ICryptoService _cryptoService;
|
||||
|
||||
protected override II18nService i18nService => _i18nService;
|
||||
protected override IEnvironmentService environmentService => _environmentService;
|
||||
@@ -49,7 +44,6 @@ namespace Bit.App.Pages
|
||||
private string _email;
|
||||
private string _requestId;
|
||||
private string _requestAccessCode;
|
||||
private AuthRequestType _authRequestType;
|
||||
// Item1 publicKey, Item2 privateKey
|
||||
private Tuple<byte[], byte[]> _requestKeyPair;
|
||||
|
||||
@@ -63,9 +57,6 @@ namespace Bit.App.Pages
|
||||
_i18nService = ServiceContainer.Resolve<II18nService>();
|
||||
_stateService = ServiceContainer.Resolve<IStateService>();
|
||||
_logger = ServiceContainer.Resolve<ILogger>();
|
||||
_deviceTrustCryptoService = ServiceContainer.Resolve<IDeviceTrustCryptoService>();
|
||||
_cryptoFunctionService = ServiceContainer.Resolve<ICryptoFunctionService>();
|
||||
_cryptoService = ServiceContainer.Resolve<ICryptoService>();
|
||||
|
||||
PageTitle = AppResources.LogInWithAnotherDevice;
|
||||
|
||||
@@ -86,70 +77,6 @@ namespace Bit.App.Pages
|
||||
public ICommand CreatePasswordlessLoginCommand { get; }
|
||||
public ICommand CloseCommand { get; }
|
||||
|
||||
public string Tittle
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (_authRequestType)
|
||||
{
|
||||
case AuthRequestType.AuthenticateAndUnlock:
|
||||
return AppResources.LogInInitiated;
|
||||
case AuthRequestType.AdminApproval:
|
||||
return AppResources.AdminApprovalRequested;
|
||||
default:
|
||||
return string.Empty;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public string SubTittle
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (_authRequestType)
|
||||
{
|
||||
case AuthRequestType.AuthenticateAndUnlock:
|
||||
return AppResources.ANotificationHasBeenSentToYourDevice;
|
||||
case AuthRequestType.AdminApproval:
|
||||
return AppResources.YourRequestHasBeenSentToYourAdmin;
|
||||
default:
|
||||
return string.Empty;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public string Description
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (_authRequestType)
|
||||
{
|
||||
case AuthRequestType.AuthenticateAndUnlock:
|
||||
return AppResources.PleaseMakeSureYourVaultIsUnlockedAndTheFingerprintPhraseMatchesOnTheOtherDevice;
|
||||
case AuthRequestType.AdminApproval:
|
||||
return AppResources.YouWillBeNotifiedOnceApproved;
|
||||
default:
|
||||
return string.Empty;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public string OtherOptions
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (_authRequestType)
|
||||
{
|
||||
case AuthRequestType.AuthenticateAndUnlock:
|
||||
return AppResources.NeedAnotherOption;
|
||||
case AuthRequestType.AdminApproval:
|
||||
return AppResources.TroubleLoggingIn;
|
||||
default:
|
||||
return string.Empty;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public string FingerprintPhrase
|
||||
{
|
||||
get => _fingerprintPhrase;
|
||||
@@ -162,21 +89,6 @@ namespace Bit.App.Pages
|
||||
set => SetProperty(ref _email, value);
|
||||
}
|
||||
|
||||
public AuthRequestType AuthRequestType
|
||||
{
|
||||
get => _authRequestType;
|
||||
set => SetProperty(ref _authRequestType, value, additionalPropertyNames: new string[]
|
||||
{
|
||||
nameof(Tittle),
|
||||
nameof(SubTittle),
|
||||
nameof(Description),
|
||||
nameof(OtherOptions),
|
||||
nameof(ResendNotificationVisible)
|
||||
});
|
||||
}
|
||||
|
||||
public bool ResendNotificationVisible => AuthRequestType == AuthRequestType.AuthenticateAndUnlock;
|
||||
|
||||
public void StartCheckLoginRequestStatus()
|
||||
{
|
||||
try
|
||||
@@ -207,22 +119,14 @@ namespace Bit.App.Pages
|
||||
|
||||
private async Task CheckLoginRequestStatus()
|
||||
{
|
||||
if (string.IsNullOrEmpty(_requestId))
|
||||
if (string.IsNullOrEmpty(_requestId) || string.IsNullOrEmpty(_requestAccessCode))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
PasswordlessLoginResponse response = null;
|
||||
if (await _stateService.IsAuthenticatedAsync())
|
||||
{
|
||||
response = await _authService.GetPasswordlessLoginRequestByIdAsync(_requestId);
|
||||
}
|
||||
else
|
||||
{
|
||||
response = await _authService.GetPasswordlessLoginResquestAsync(_requestId, _requestAccessCode);
|
||||
}
|
||||
var response = await _authService.GetPasswordlessLoginResponseAsync(_requestId, _requestAccessCode);
|
||||
|
||||
if (response.RequestApproved == null || !response.RequestApproved.Value)
|
||||
{
|
||||
@@ -234,12 +138,6 @@ namespace Bit.App.Pages
|
||||
var authResult = await _authService.LogInPasswordlessAsync(Email, _requestAccessCode, _requestId, _requestKeyPair.Item2, response.Key, response.MasterPasswordHash);
|
||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||
|
||||
if (authResult == null && await _stateService.IsAuthenticatedAsync())
|
||||
{
|
||||
await HandleLoginCompleteAsync();
|
||||
return;
|
||||
}
|
||||
|
||||
if (await HandleCaptchaAsync(authResult.CaptchaSiteKey, authResult.CaptchaNeeded, CheckLoginRequestStatus))
|
||||
{
|
||||
return;
|
||||
@@ -255,7 +153,8 @@ namespace Bit.App.Pages
|
||||
}
|
||||
else
|
||||
{
|
||||
await HandleLoginCompleteAsync();
|
||||
_syncService.FullSyncAsync(true).FireAndForget();
|
||||
LogInSuccessAction?.Invoke();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -265,66 +164,22 @@ namespace Bit.App.Pages
|
||||
}
|
||||
}
|
||||
|
||||
private async Task HandleLoginCompleteAsync()
|
||||
{
|
||||
await _stateService.SetPendingAdminAuthRequestAsync(null);
|
||||
_syncService.FullSyncAsync(true).FireAndForget();
|
||||
LogInSuccessAction?.Invoke();
|
||||
}
|
||||
|
||||
private async Task CreatePasswordlessLoginAsync()
|
||||
{
|
||||
await Device.InvokeOnMainThreadAsync(() => _deviceActionService.ShowLoadingAsync(AppResources.Loading));
|
||||
|
||||
PasswordlessLoginResponse response = null;
|
||||
var pendingRequest = await _stateService.GetPendingAdminAuthRequestAsync();
|
||||
if (pendingRequest != null && _authRequestType == AuthRequestType.AdminApproval)
|
||||
var response = await _authService.PasswordlessCreateLoginRequestAsync(_email);
|
||||
if (response != null)
|
||||
{
|
||||
response = await _authService.GetPasswordlessLoginRequestByIdAsync(pendingRequest.Id);
|
||||
if (response == null || (response.IsAnswered && !response.RequestApproved.Value))
|
||||
{
|
||||
// handle pending auth request not valid remove it from state
|
||||
await _stateService.SetPendingAdminAuthRequestAsync(null);
|
||||
pendingRequest = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Derive pubKey from privKey in state to avoid MITM attacks
|
||||
// Also generate FingerprintPhrase locally for the same reason
|
||||
var derivedPublicKey = await _cryptoFunctionService.RsaExtractPublicKeyAsync(pendingRequest.PrivateKey);
|
||||
response.FingerprintPhrase = string.Join("-", await _cryptoService.GetFingerprintAsync(Email, derivedPublicKey));
|
||||
response.RequestKeyPair = new Tuple<byte[], byte[]>(derivedPublicKey, pendingRequest.PrivateKey);
|
||||
}
|
||||
FingerprintPhrase = response.FingerprintPhrase;
|
||||
_requestId = response.Id;
|
||||
_requestAccessCode = response.RequestAccessCode;
|
||||
_requestKeyPair = response.RequestKeyPair;
|
||||
}
|
||||
|
||||
if (response == null)
|
||||
{
|
||||
response = await _authService.PasswordlessCreateLoginRequestAsync(_email, AuthRequestType);
|
||||
}
|
||||
|
||||
await HandlePasswordlessLoginAsync(response, pendingRequest == null && _authRequestType == AuthRequestType.AdminApproval);
|
||||
await _deviceActionService.HideLoadingAsync();
|
||||
}
|
||||
|
||||
private async Task HandlePasswordlessLoginAsync(PasswordlessLoginResponse response, bool createPendingAdminRequest)
|
||||
{
|
||||
if (response == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(response));
|
||||
}
|
||||
|
||||
if (createPendingAdminRequest)
|
||||
{
|
||||
var pendingAuthRequest = new PendingAdminAuthRequest { Id = response.Id, PrivateKey = response.RequestKeyPair.Item2 };
|
||||
await _stateService.SetPendingAdminAuthRequestAsync(pendingAuthRequest);
|
||||
}
|
||||
|
||||
FingerprintPhrase = response.FingerprintPhrase;
|
||||
_requestId = response.Id;
|
||||
_requestAccessCode = response.RequestAccessCode;
|
||||
_requestKeyPair = response.RequestKeyPair;
|
||||
}
|
||||
|
||||
private void HandleException(Exception ex)
|
||||
{
|
||||
Xamarin.Essentials.MainThread.InvokeOnMainThreadAsync(async () =>
|
||||
|
||||
@@ -29,8 +29,6 @@ namespace Bit.App.Pages
|
||||
_vm.SsoAuthSuccessAction = () => Device.BeginInvokeOnMainThread(async () => await SsoAuthSuccessAsync());
|
||||
_vm.UpdateTempPasswordAction =
|
||||
() => Device.BeginInvokeOnMainThread(async () => await UpdateTempPasswordAsync());
|
||||
_vm.StartDeviceApprovalOptionsAction =
|
||||
() => Device.BeginInvokeOnMainThread(async () => await StartDeviceApprovalOptionsAsync());
|
||||
_vm.CloseAction = async () =>
|
||||
{
|
||||
await Navigation.PopModalAsync();
|
||||
@@ -108,17 +106,10 @@ namespace Bit.App.Pages
|
||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||
}
|
||||
|
||||
private async Task StartDeviceApprovalOptionsAsync()
|
||||
{
|
||||
var page = new LoginApproveDevicePage();
|
||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||
}
|
||||
|
||||
private async Task SsoAuthSuccessAsync()
|
||||
{
|
||||
RestoreAppOptionsFromCopy();
|
||||
await AppHelpers.ClearPreviousPage();
|
||||
|
||||
if (await _vaultTimeoutService.IsLockedAsync())
|
||||
{
|
||||
Application.Current.MainPage = new NavigationPage(new LockPage(_appOptions));
|
||||
|
||||
@@ -9,7 +9,6 @@ using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.Domain;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Utilities;
|
||||
using Xamarin.CommunityToolkit.ObjectModel;
|
||||
using Xamarin.Essentials;
|
||||
@@ -30,8 +29,6 @@ namespace Bit.App.Pages
|
||||
private readonly IStateService _stateService;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IOrganizationService _organizationService;
|
||||
private readonly IDeviceTrustCryptoService _deviceTrustCryptoService;
|
||||
private readonly ICryptoService _cryptoService;
|
||||
|
||||
private string _orgIdentifier;
|
||||
|
||||
@@ -48,8 +45,7 @@ namespace Bit.App.Pages
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_logger = ServiceContainer.Resolve<ILogger>("logger");
|
||||
_organizationService = ServiceContainer.Resolve<IOrganizationService>();
|
||||
_deviceTrustCryptoService = ServiceContainer.Resolve<IDeviceTrustCryptoService>();
|
||||
_cryptoService = ServiceContainer.Resolve<ICryptoService>();
|
||||
|
||||
|
||||
PageTitle = AppResources.Bitwarden;
|
||||
LogInCommand = new AsyncCommand(LogInAsync, allowsMultipleExecutions: false);
|
||||
@@ -65,7 +61,6 @@ namespace Bit.App.Pages
|
||||
public Action StartTwoFactorAction { get; set; }
|
||||
public Action StartSetPasswordAction { get; set; }
|
||||
public Action SsoAuthSuccessAction { get; set; }
|
||||
public Action StartDeviceApprovalOptionsAction { get; set; }
|
||||
public Action CloseAction { get; set; }
|
||||
public Action UpdateTempPasswordAction { get; set; }
|
||||
|
||||
@@ -202,7 +197,6 @@ namespace Bit.App.Pages
|
||||
try
|
||||
{
|
||||
var response = await _authService.LogInSsoAsync(code, codeVerifier, REDIRECT_URI, orgId);
|
||||
var decryptOptions = await _stateService.GetAccountDecryptionOptions();
|
||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||
await _stateService.SetRememberedOrgIdentifierAsync(OrgIdentifier);
|
||||
await _deviceActionService.HideLoadingAsync();
|
||||
@@ -218,31 +212,9 @@ namespace Bit.App.Pages
|
||||
{
|
||||
UpdateTempPasswordAction?.Invoke();
|
||||
}
|
||||
else if (decryptOptions?.TrustedDeviceOption != null)
|
||||
{
|
||||
// If user doesn't have a MP, but has reset password permission, they must set a MP
|
||||
if (!decryptOptions.HasMasterPassword &&
|
||||
decryptOptions.TrustedDeviceOption.HasManageResetPasswordPermission)
|
||||
{
|
||||
StartSetPasswordAction?.Invoke();
|
||||
}
|
||||
else if (response.ForcePasswordReset)
|
||||
{
|
||||
UpdateTempPasswordAction?.Invoke();
|
||||
}
|
||||
else if (await _deviceTrustCryptoService.IsDeviceTrustedAsync())
|
||||
{
|
||||
_syncService.FullSyncAsync(true).FireAndForget();
|
||||
SsoAuthSuccessAction?.Invoke();
|
||||
}
|
||||
else
|
||||
{
|
||||
StartDeviceApprovalOptionsAction?.Invoke();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_syncService.FullSyncAsync(true).FireAndForget();
|
||||
var task = Task.Run(async () => await _syncService.FullSyncAsync(true));
|
||||
SsoAuthSuccessAction?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,28 +177,25 @@ namespace Bit.App.Pages
|
||||
Name = string.IsNullOrWhiteSpace(Name) ? null : Name;
|
||||
Email = Email.Trim().ToLower();
|
||||
var kdfConfig = new KdfConfig(KdfType.PBKDF2_SHA256, Constants.Pbkdf2Iterations, null, null);
|
||||
var newMasterKey = await _cryptoService.MakeMasterKeyAsync(MasterPassword, Email, kdfConfig);
|
||||
var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(
|
||||
newMasterKey,
|
||||
await _cryptoService.MakeUserKeyAsync()
|
||||
);
|
||||
var hashedPassword = await _cryptoService.HashPasswordAsync(MasterPassword, newMasterKey);
|
||||
var (newPublicKey, newProtectedPrivateKey) = await _cryptoService.MakeKeyPairAsync(newUserKey);
|
||||
var key = await _cryptoService.MakeKeyAsync(MasterPassword, Email, kdfConfig);
|
||||
var encKey = await _cryptoService.MakeEncKeyAsync(key);
|
||||
var hashedPassword = await _cryptoService.HashPasswordAsync(MasterPassword, key);
|
||||
var keys = await _cryptoService.MakeKeyPairAsync(encKey.Item1);
|
||||
var request = new RegisterRequest
|
||||
{
|
||||
Email = Email,
|
||||
Name = Name,
|
||||
MasterPasswordHash = hashedPassword,
|
||||
MasterPasswordHint = Hint,
|
||||
Key = newProtectedUserKey.EncryptedString,
|
||||
Key = encKey.Item2.EncryptedString,
|
||||
Kdf = kdfConfig.Type,
|
||||
KdfIterations = kdfConfig.Iterations,
|
||||
KdfMemory = kdfConfig.Memory,
|
||||
KdfParallelism = kdfConfig.Parallelism,
|
||||
Keys = new KeysRequest
|
||||
{
|
||||
PublicKey = newPublicKey,
|
||||
EncryptedPrivateKey = newProtectedPrivateKey.EncryptedString
|
||||
PublicKey = keys.Item1,
|
||||
EncryptedPrivateKey = keys.Item2.EncryptedString
|
||||
},
|
||||
CaptchaResponse = _captchaToken,
|
||||
};
|
||||
|
||||
@@ -165,18 +165,26 @@ namespace Bit.App.Pages
|
||||
|
||||
var kdfConfig = new KdfConfig(KdfType.PBKDF2_SHA256, Constants.Pbkdf2Iterations, null, null);
|
||||
var email = await _stateService.GetEmailAsync();
|
||||
var newMasterKey = await _cryptoService.MakeMasterKeyAsync(MasterPassword, email, kdfConfig);
|
||||
var masterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, newMasterKey, HashPurpose.ServerAuthorization);
|
||||
var localMasterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, newMasterKey, HashPurpose.LocalAuthorization);
|
||||
var key = await _cryptoService.MakeKeyAsync(MasterPassword, email, kdfConfig);
|
||||
var masterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.ServerAuthorization);
|
||||
var localMasterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.LocalAuthorization);
|
||||
|
||||
var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(newMasterKey,
|
||||
await _cryptoService.GetUserKeyAsync() ?? await _cryptoService.MakeUserKeyAsync());
|
||||
Tuple<SymmetricCryptoKey, EncString> encKey;
|
||||
var existingEncKey = await _cryptoService.GetEncKeyAsync();
|
||||
if (existingEncKey == null)
|
||||
{
|
||||
encKey = await _cryptoService.MakeEncKeyAsync(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
encKey = await _cryptoService.RemakeEncKeyAsync(key);
|
||||
}
|
||||
|
||||
var keys = await _cryptoService.MakeKeyPairAsync(newUserKey);
|
||||
var keys = await _cryptoService.MakeKeyPairAsync(encKey.Item1);
|
||||
var request = new SetPasswordRequest
|
||||
{
|
||||
MasterPasswordHash = masterPasswordHash,
|
||||
Key = newProtectedUserKey.EncryptedString,
|
||||
Key = encKey.Item2.EncryptedString,
|
||||
MasterPasswordHint = Hint,
|
||||
Kdf = kdfConfig.Type.GetValueOrDefault(KdfType.PBKDF2_SHA256),
|
||||
KdfIterations = kdfConfig.Iterations.GetValueOrDefault(Constants.Pbkdf2Iterations),
|
||||
@@ -196,19 +204,19 @@ namespace Bit.App.Pages
|
||||
// Set Password and relevant information
|
||||
await _apiService.SetPasswordAsync(request);
|
||||
await _stateService.SetKdfConfigurationAsync(kdfConfig);
|
||||
await _cryptoService.SetMasterKeyAsync(newMasterKey);
|
||||
await _cryptoService.SetPasswordHashAsync(localMasterPasswordHash);
|
||||
await _cryptoService.SetMasterKeyEncryptedUserKeyAsync(newProtectedUserKey.EncryptedString);
|
||||
await _cryptoService.SetPrivateKeyAsync(keys.Item2.EncryptedString);
|
||||
await _cryptoService.SetKeyAsync(key);
|
||||
await _cryptoService.SetKeyHashAsync(localMasterPasswordHash);
|
||||
await _cryptoService.SetEncKeyAsync(encKey.Item2.EncryptedString);
|
||||
await _cryptoService.SetEncPrivateKeyAsync(keys.Item2.EncryptedString);
|
||||
|
||||
if (ResetPasswordAutoEnroll)
|
||||
{
|
||||
// Grab Organization Keys
|
||||
var response = await _apiService.GetOrganizationKeysAsync(OrgId);
|
||||
var publicKey = CoreHelpers.Base64UrlDecode(response.PublicKey);
|
||||
// Grab User Key and encrypt with Org Public Key
|
||||
var userKey = await _cryptoService.GetUserKeyAsync();
|
||||
var encryptedKey = await _cryptoService.RsaEncryptAsync(userKey.Key, publicKey);
|
||||
// Grab user's Encryption Key and encrypt with Org Public Key
|
||||
var userEncKey = await _cryptoService.GetEncKeyAsync();
|
||||
var encryptedKey = await _cryptoService.RsaEncryptAsync(userEncKey.Key, publicKey);
|
||||
// Request
|
||||
var resetRequest = new OrganizationUserResetPasswordEnrollmentRequest
|
||||
{
|
||||
|
||||
@@ -37,8 +37,6 @@ namespace Bit.App.Pages
|
||||
Device.BeginInvokeOnMainThread(async () => await TwoFactorAuthSuccessAsync());
|
||||
_vm.UpdateTempPasswordAction =
|
||||
() => Device.BeginInvokeOnMainThread(async () => await UpdateTempPasswordAsync());
|
||||
_vm.StartDeviceApprovalOptionsAction =
|
||||
() => Device.BeginInvokeOnMainThread(async () => await StartDeviceApprovalOptionsAsync());
|
||||
_vm.CloseAction = async () => await Navigation.PopModalAsync();
|
||||
DuoWebView = _duoWebView;
|
||||
if (Device.RuntimePlatform == Device.Android)
|
||||
@@ -182,12 +180,6 @@ namespace Bit.App.Pages
|
||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||
}
|
||||
|
||||
private async Task StartDeviceApprovalOptionsAsync()
|
||||
{
|
||||
var page = new LoginApproveDevicePage();
|
||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||
}
|
||||
|
||||
private async Task TwoFactorAuthSuccessAsync()
|
||||
{
|
||||
if (_authingWithSso)
|
||||
|
||||
@@ -11,7 +11,6 @@ using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.Request;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Utilities;
|
||||
using Newtonsoft.Json;
|
||||
using Xamarin.CommunityToolkit.ObjectModel;
|
||||
@@ -34,7 +33,7 @@ namespace Bit.App.Pages
|
||||
private readonly II18nService _i18nService;
|
||||
private readonly IAppIdService _appIdService;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IDeviceTrustCryptoService _deviceTrustCryptoService;
|
||||
|
||||
private TwoFactorProviderType? _selectedProviderType;
|
||||
private string _totpInstruction;
|
||||
private string _webVaultUrl = "https://vault.bitwarden.com";
|
||||
@@ -56,7 +55,6 @@ namespace Bit.App.Pages
|
||||
_i18nService = ServiceContainer.Resolve<II18nService>("i18nService");
|
||||
_appIdService = ServiceContainer.Resolve<IAppIdService>("appIdService");
|
||||
_logger = ServiceContainer.Resolve<ILogger>();
|
||||
_deviceTrustCryptoService = ServiceContainer.Resolve<IDeviceTrustCryptoService>();
|
||||
|
||||
PageTitle = AppResources.TwoStepLogin;
|
||||
SubmitCommand = new Command(async () => await SubmitAsync());
|
||||
@@ -120,7 +118,6 @@ namespace Bit.App.Pages
|
||||
public Command SubmitCommand { get; }
|
||||
public ICommand MoreCommand { get; }
|
||||
public Action TwoFactorAuthSuccessAction { get; set; }
|
||||
public Action StartDeviceApprovalOptionsAction { get; set; }
|
||||
public Action StartSetPasswordAction { get; set; }
|
||||
public Action CloseAction { get; set; }
|
||||
public Action UpdateTempPasswordAction { get; set; }
|
||||
@@ -318,7 +315,6 @@ namespace Bit.App.Pages
|
||||
|
||||
var task = Task.Run(() => _syncService.FullSyncAsync(true));
|
||||
await _deviceActionService.HideLoadingAsync();
|
||||
var decryptOptions = await _stateService.GetAccountDecryptionOptions();
|
||||
_messagingService.Send("listenYubiKeyOTP", false);
|
||||
_broadcasterService.Unsubscribe(nameof(TwoFactorPage));
|
||||
|
||||
@@ -330,27 +326,6 @@ namespace Bit.App.Pages
|
||||
{
|
||||
UpdateTempPasswordAction?.Invoke();
|
||||
}
|
||||
else if (decryptOptions?.TrustedDeviceOption != null)
|
||||
{
|
||||
// If user doesn't have a MP, but has reset password permission, they must set a MP
|
||||
if (!decryptOptions.HasMasterPassword &&
|
||||
decryptOptions.TrustedDeviceOption.HasManageResetPasswordPermission)
|
||||
{
|
||||
StartSetPasswordAction?.Invoke();
|
||||
}
|
||||
else if (result.ForcePasswordReset)
|
||||
{
|
||||
UpdateTempPasswordAction?.Invoke();
|
||||
}
|
||||
else if (await _deviceTrustCryptoService.IsDeviceTrustedAsync())
|
||||
{
|
||||
TwoFactorAuthSuccessAction?.Invoke();
|
||||
}
|
||||
else
|
||||
{
|
||||
StartDeviceApprovalOptionsAction?.Invoke();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TwoFactorAuthSuccessAction?.Invoke();
|
||||
|
||||
@@ -93,12 +93,12 @@ namespace Bit.App.Pages
|
||||
var kdfConfig = await _stateService.GetActiveUserCustomDataAsync(a => new KdfConfig(a?.Profile));
|
||||
var email = await _stateService.GetEmailAsync();
|
||||
|
||||
// Create new master key and hash new password
|
||||
var masterKey = await _cryptoService.MakeMasterKeyAsync(MasterPassword, email, kdfConfig);
|
||||
var masterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, masterKey);
|
||||
// Create new key and hash new password
|
||||
var key = await _cryptoService.MakeKeyAsync(MasterPassword, email, kdfConfig);
|
||||
var masterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, key);
|
||||
|
||||
// Encrypt user key with new master key
|
||||
var (userKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(masterKey);
|
||||
// Create new encKey for the User
|
||||
var newEncKey = await _cryptoService.RemakeEncKeyAsync(key);
|
||||
|
||||
// Initiate API action
|
||||
try
|
||||
@@ -108,10 +108,10 @@ namespace Bit.App.Pages
|
||||
switch (_reason)
|
||||
{
|
||||
case ForcePasswordResetReason.AdminForcePasswordReset:
|
||||
await UpdateTempPasswordAsync(masterPasswordHash, newProtectedUserKey.EncryptedString);
|
||||
await UpdateTempPasswordAsync(masterPasswordHash, newEncKey.Item2.EncryptedString);
|
||||
break;
|
||||
case ForcePasswordResetReason.WeakMasterPasswordOnLogin:
|
||||
await UpdatePasswordAsync(masterPasswordHash, newProtectedUserKey.EncryptedString);
|
||||
await UpdatePasswordAsync(masterPasswordHash, newEncKey.Item2.EncryptedString);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<pages:BaseContentPage xmlns="http://xamarin.com/schemas/2014/forms"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
x:Class="Bit.App.Pages.BlockAutofillUrisPage"
|
||||
xmlns:pages="clr-namespace:Bit.App.Pages"
|
||||
xmlns:controls="clr-namespace:Bit.App.Controls"
|
||||
xmlns:u="clr-namespace:Bit.App.Utilities"
|
||||
xmlns:core="clr-namespace:Bit.Core;assembly=BitwardenCore"
|
||||
x:DataType="pages:BlockAutofillUrisPageViewModel"
|
||||
Title="{u:I18n BlockAutoFill}">
|
||||
<ContentPage.BindingContext>
|
||||
<pages:BlockAutofillUrisPageViewModel />
|
||||
</ContentPage.BindingContext>
|
||||
<ContentPage.Resources>
|
||||
<ResourceDictionary>
|
||||
<u:InverseBoolConverter x:Key="inverseBool" />
|
||||
</ResourceDictionary>
|
||||
</ContentPage.Resources>
|
||||
<StackLayout Orientation="Vertical">
|
||||
<Image
|
||||
x:Name="_emptyUrisPlaceholder"
|
||||
HorizontalOptions="Center"
|
||||
WidthRequest="120"
|
||||
HeightRequest="120"
|
||||
Margin="0,100,0,0"
|
||||
IsVisible="{Binding ShowList, Converter={StaticResource inverseBool}}"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n ThereAreNoBlockedURIs}" />
|
||||
<controls:CustomLabel
|
||||
StyleClass="box-label-regular"
|
||||
Text="{u:I18n AutoFillWillNotBeOfferedForTheseURIs}"
|
||||
FontWeight="500"
|
||||
HorizontalTextAlignment="Center"
|
||||
Margin="14,10,14,0"/>
|
||||
<controls:ExtendedCollectionView
|
||||
ItemsSource="{Binding BlockedUris}"
|
||||
IsVisible="{Binding ShowList}"
|
||||
VerticalOptions="FillAndExpand"
|
||||
Margin="0,5,0,0"
|
||||
SelectionMode="None"
|
||||
StyleClass="list, list-platform"
|
||||
ExtraDataForLogging="Blocked Autofill Uris"
|
||||
AutomationId="BlockedUrisCellList">
|
||||
<CollectionView.ItemTemplate>
|
||||
<DataTemplate x:DataType="pages:BlockAutofillUriItemViewModel">
|
||||
<StackLayout
|
||||
Orientation="Vertical"
|
||||
AutomationId="BlockedUriCell">
|
||||
<StackLayout
|
||||
Orientation="Horizontal">
|
||||
<controls:CustomLabel
|
||||
VerticalOptions="Center"
|
||||
StyleClass="box-label-regular"
|
||||
Text="{Binding Uri}"
|
||||
MaxLines="2"
|
||||
LineBreakMode="TailTruncation"
|
||||
FontWeight="500"
|
||||
Margin="15,0,0,0"
|
||||
HorizontalOptions="StartAndExpand"/>
|
||||
<controls:IconButton
|
||||
StyleClass="box-row-button-muted, box-row-button-platform"
|
||||
Text="{Binding Source={x:Static core:BitwardenIcons.PencilSquare}}"
|
||||
Command="{Binding EditUriCommand}"
|
||||
Margin="5,0,15,0"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n EditURI}"
|
||||
AutomationId="EditUriButton" />
|
||||
</StackLayout>
|
||||
<BoxView StyleClass="box-row-separator" />
|
||||
</StackLayout>
|
||||
</DataTemplate>
|
||||
</CollectionView.ItemTemplate>
|
||||
</controls:ExtendedCollectionView>
|
||||
<Button
|
||||
Text="{u:I18n NewBlockedURI}"
|
||||
Command="{Binding AddUriCommand}"
|
||||
VerticalOptions="End"
|
||||
HeightRequest="40"
|
||||
Opacity="0.8"
|
||||
Margin="14,5,14,10"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n NewBlockedURI}"
|
||||
AutomationId="NewBlockedUriButton" />
|
||||
</StackLayout>
|
||||
</pages:BaseContentPage>
|
||||
@@ -1,44 +0,0 @@
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Styles;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core.Utilities;
|
||||
using Xamarin.Essentials;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
{
|
||||
public partial class BlockAutofillUrisPage : BaseContentPage, IThemeDirtablePage
|
||||
{
|
||||
private readonly BlockAutofillUrisPageViewModel _vm;
|
||||
|
||||
public BlockAutofillUrisPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
_vm = BindingContext as BlockAutofillUrisPageViewModel;
|
||||
_vm.Page = this;
|
||||
}
|
||||
|
||||
protected override void OnAppearing()
|
||||
{
|
||||
base.OnAppearing();
|
||||
|
||||
_vm.InitAsync().FireAndForget(_ => Navigation.PopAsync());
|
||||
|
||||
UpdatePlaceholder();
|
||||
}
|
||||
|
||||
public override async Task UpdateOnThemeChanged()
|
||||
{
|
||||
await base.UpdateOnThemeChanged();
|
||||
|
||||
UpdatePlaceholder();
|
||||
}
|
||||
|
||||
private void UpdatePlaceholder()
|
||||
{
|
||||
MainThread.BeginInvokeOnMainThread(() =>
|
||||
_emptyUrisPlaceholder.Source = ImageSource.FromFile(ThemeManager.UsingLightTheme ? "empty_uris_placeholder" : "empty_uris_placeholder_dark"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,186 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using Xamarin.CommunityToolkit.ObjectModel;
|
||||
using Xamarin.Essentials;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
{
|
||||
public class BlockAutofillUrisPageViewModel : BaseViewModel
|
||||
{
|
||||
private const char URI_SEPARARTOR = ',';
|
||||
private const string URI_FORMAT = "https://domain.com";
|
||||
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
|
||||
public BlockAutofillUrisPageViewModel()
|
||||
{
|
||||
_stateService = ServiceContainer.Resolve<IStateService>();
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>();
|
||||
|
||||
AddUriCommand = new AsyncCommand(AddUriAsync,
|
||||
onException: ex => HandleException(ex),
|
||||
allowsMultipleExecutions: false);
|
||||
|
||||
EditUriCommand = new AsyncCommand<BlockAutofillUriItemViewModel>(EditUriAsync,
|
||||
onException: ex => HandleException(ex),
|
||||
allowsMultipleExecutions: false);
|
||||
}
|
||||
|
||||
public ObservableRangeCollection<BlockAutofillUriItemViewModel> BlockedUris { get; set; } = new ObservableRangeCollection<BlockAutofillUriItemViewModel>();
|
||||
|
||||
public bool ShowList => BlockedUris.Any();
|
||||
|
||||
public ICommand AddUriCommand { get; }
|
||||
|
||||
public ICommand EditUriCommand { get; }
|
||||
|
||||
public async Task InitAsync()
|
||||
{
|
||||
var blockedUrisList = await _stateService.GetAutofillBlacklistedUrisAsync();
|
||||
if (blockedUrisList?.Any() != true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
await MainThread.InvokeOnMainThreadAsync(() =>
|
||||
{
|
||||
BlockedUris.AddRange(blockedUrisList.OrderBy(uri => uri).Select(u => new BlockAutofillUriItemViewModel(u, EditUriCommand)).ToList());
|
||||
TriggerPropertyChanged(nameof(ShowList));
|
||||
});
|
||||
}
|
||||
|
||||
private async Task AddUriAsync()
|
||||
{
|
||||
var response = await _deviceActionService.DisplayValidatablePromptAsync(new Utilities.Prompts.ValidatablePromptConfig
|
||||
{
|
||||
Title = AppResources.NewUri,
|
||||
Subtitle = AppResources.EnterURI,
|
||||
ValueSubInfo = string.Format(AppResources.FormatXSeparateMultipleURIsWithAComma, URI_FORMAT),
|
||||
OkButtonText = AppResources.Save,
|
||||
ValidateText = text => ValidateUris(text, true)
|
||||
});
|
||||
if (response?.Text is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await MainThread.InvokeOnMainThreadAsync(() =>
|
||||
{
|
||||
foreach (var uri in response.Value.Text.Split(URI_SEPARARTOR).Where(s => !string.IsNullOrEmpty(s)))
|
||||
{
|
||||
var cleanedUri = uri.Replace(Environment.NewLine, string.Empty).Trim();
|
||||
BlockedUris.Add(new BlockAutofillUriItemViewModel(cleanedUri, EditUriCommand));
|
||||
}
|
||||
|
||||
BlockedUris = new ObservableRangeCollection<BlockAutofillUriItemViewModel>(BlockedUris.OrderBy(b => b.Uri));
|
||||
TriggerPropertyChanged(nameof(BlockedUris));
|
||||
TriggerPropertyChanged(nameof(ShowList));
|
||||
});
|
||||
await UpdateAutofillBlacklistedUrisAsync();
|
||||
_deviceActionService.Toast(AppResources.URISaved);
|
||||
}
|
||||
|
||||
private async Task EditUriAsync(BlockAutofillUriItemViewModel uriItemViewModel)
|
||||
{
|
||||
var response = await _deviceActionService.DisplayValidatablePromptAsync(new Utilities.Prompts.ValidatablePromptConfig
|
||||
{
|
||||
Title = AppResources.EditURI,
|
||||
Subtitle = AppResources.EnterURI,
|
||||
Text = uriItemViewModel.Uri,
|
||||
ValueSubInfo = string.Format(AppResources.FormatX, URI_FORMAT),
|
||||
OkButtonText = AppResources.Save,
|
||||
ThirdButtonText = AppResources.Remove,
|
||||
ValidateText = text => ValidateUris(text, false)
|
||||
});
|
||||
if (response is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.Value.ExecuteThirdAction)
|
||||
{
|
||||
await MainThread.InvokeOnMainThreadAsync(() =>
|
||||
{
|
||||
BlockedUris.Remove(uriItemViewModel);
|
||||
TriggerPropertyChanged(nameof(ShowList));
|
||||
});
|
||||
await UpdateAutofillBlacklistedUrisAsync();
|
||||
_deviceActionService.Toast(AppResources.URIRemoved);
|
||||
return;
|
||||
}
|
||||
|
||||
var cleanedUri = response.Value.Text.Replace(Environment.NewLine, string.Empty).Trim();
|
||||
await MainThread.InvokeOnMainThreadAsync(() =>
|
||||
{
|
||||
BlockedUris.Remove(uriItemViewModel);
|
||||
BlockedUris.Add(new BlockAutofillUriItemViewModel(cleanedUri, EditUriCommand));
|
||||
BlockedUris = new ObservableRangeCollection<BlockAutofillUriItemViewModel>(BlockedUris.OrderBy(b => b.Uri));
|
||||
TriggerPropertyChanged(nameof(BlockedUris));
|
||||
TriggerPropertyChanged(nameof(ShowList));
|
||||
});
|
||||
await UpdateAutofillBlacklistedUrisAsync();
|
||||
_deviceActionService.Toast(AppResources.URISaved);
|
||||
}
|
||||
|
||||
private string ValidateUris(string uris, bool allowMultipleUris)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(uris))
|
||||
{
|
||||
return string.Format(AppResources.FormatX, URI_FORMAT);
|
||||
}
|
||||
|
||||
if (!allowMultipleUris && uris.Contains(URI_SEPARARTOR))
|
||||
{
|
||||
return AppResources.CannotEditMultipleURIsAtOnce;
|
||||
}
|
||||
|
||||
foreach (var uri in uris.Split(URI_SEPARARTOR).Where(u => !string.IsNullOrWhiteSpace(u)))
|
||||
{
|
||||
var cleanedUri = uri.Replace(Environment.NewLine, string.Empty).Trim();
|
||||
if (!cleanedUri.StartsWith("http://") && !cleanedUri.StartsWith("https://") &&
|
||||
!cleanedUri.StartsWith(Constants.AndroidAppProtocol))
|
||||
{
|
||||
return AppResources.InvalidFormatUseHttpsHttpOrAndroidApp;
|
||||
}
|
||||
|
||||
if (!Uri.TryCreate(cleanedUri, UriKind.Absolute, out var _))
|
||||
{
|
||||
return AppResources.InvalidURI;
|
||||
}
|
||||
|
||||
if (BlockedUris.Any(uriItem => uriItem.Uri == cleanedUri))
|
||||
{
|
||||
return string.Format(AppResources.TheURIXIsAlreadyBlocked, cleanedUri);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task UpdateAutofillBlacklistedUrisAsync()
|
||||
{
|
||||
await _stateService.SetAutofillBlacklistedUrisAsync(BlockedUris.Any() ? BlockedUris.Select(bu => bu.Uri).ToList() : null);
|
||||
}
|
||||
}
|
||||
|
||||
public class BlockAutofillUriItemViewModel : ExtendedViewModel
|
||||
{
|
||||
public BlockAutofillUriItemViewModel(string uri, ICommand editUriCommand)
|
||||
{
|
||||
Uri = uri;
|
||||
EditUriCommand = new Command(() => editUriCommand.Execute(this));
|
||||
}
|
||||
|
||||
public string Uri { get; }
|
||||
|
||||
public ICommand EditUriCommand { get; }
|
||||
}
|
||||
}
|
||||
@@ -153,14 +153,22 @@
|
||||
StyleClass="box-footer-label, box-footer-label-switch" />
|
||||
</StackLayout>
|
||||
<StackLayout StyleClass="box" IsVisible="{Binding ShowAndroidAutofillSettings}">
|
||||
<StackLayout.GestureRecognizers>
|
||||
<TapGestureRecognizer Command="{Binding GoToBlockAutofillUrisCommand}" />
|
||||
</StackLayout.GestureRecognizers>
|
||||
<StackLayout StyleClass="box-row, box-row-input">
|
||||
<Label
|
||||
Text="{u:I18n AutofillBlockedUris}"
|
||||
StyleClass="box-label" />
|
||||
<Editor
|
||||
x:Name="_autofillBlockedUrisEditor"
|
||||
Text="{Binding AutofillBlockedUris}"
|
||||
StyleClass="box-value"
|
||||
AutoSize="TextChanges"
|
||||
IsSpellCheckEnabled="False"
|
||||
IsTextPredictionEnabled="False"
|
||||
Keyboard="Url"
|
||||
Unfocused="AutofillBlockedUrisEditor_Unfocused" />
|
||||
</StackLayout>
|
||||
<Label
|
||||
Text="{u:I18n BlockAutoFill}"
|
||||
StyleClass="box-label-regular" />
|
||||
<Label
|
||||
Text="{u:I18n AutoFillWillNotBeOfferedForTheseURIs}"
|
||||
Text="{u:I18n AutofillBlockedUrisDescription}"
|
||||
StyleClass="box-footer-label" />
|
||||
</StackLayout>
|
||||
</StackLayout>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.PlatformConfiguration;
|
||||
@@ -42,6 +44,17 @@ namespace Bit.App.Pages
|
||||
await _vm.InitAsync();
|
||||
}
|
||||
|
||||
protected async override void OnDisappearing()
|
||||
{
|
||||
base.OnDisappearing();
|
||||
await _vm.UpdateAutofillBlockedUris();
|
||||
}
|
||||
|
||||
private async void AutofillBlockedUrisEditor_Unfocused(object sender, FocusEventArgs e)
|
||||
{
|
||||
await _vm.UpdateAutofillBlockedUris();
|
||||
}
|
||||
|
||||
private async void Close_Clicked(object sender, System.EventArgs e)
|
||||
{
|
||||
if (DoOnce())
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Bit.App.Resources;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Utilities;
|
||||
using Xamarin.CommunityToolkit.ObjectModel;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
@@ -20,6 +19,7 @@ namespace Bit.App.Pages
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
|
||||
private bool _autofillSavePrompt;
|
||||
private string _autofillBlockedUris;
|
||||
private bool _favicon;
|
||||
private bool _autoTotpCopy;
|
||||
private int _clearClipboardSelectedIndex;
|
||||
@@ -84,10 +84,6 @@ namespace Bit.App.Pages
|
||||
new KeyValuePair<string, string>(null, AppResources.DefaultSystem)
|
||||
};
|
||||
LocalesOptions.AddRange(_i18nService.LocaleNames.ToList());
|
||||
|
||||
GoToBlockAutofillUrisCommand = new AsyncCommand(() => Page.Navigation.PushAsync(new BlockAutofillUrisPage()),
|
||||
onException: ex => HandleException(ex),
|
||||
allowsMultipleExecutions: false);
|
||||
}
|
||||
|
||||
public List<KeyValuePair<int?, string>> ClearClipboardOptions { get; set; }
|
||||
@@ -196,18 +192,25 @@ namespace Bit.App.Pages
|
||||
}
|
||||
}
|
||||
|
||||
public string AutofillBlockedUris
|
||||
{
|
||||
get => _autofillBlockedUris;
|
||||
set => SetProperty(ref _autofillBlockedUris, value);
|
||||
}
|
||||
|
||||
public bool ShowAndroidAutofillSettings
|
||||
{
|
||||
get => _showAndroidAutofillSettings;
|
||||
set => SetProperty(ref _showAndroidAutofillSettings, value);
|
||||
}
|
||||
|
||||
public ICommand GoToBlockAutofillUrisCommand { get; }
|
||||
|
||||
public async Task InitAsync()
|
||||
{
|
||||
AutofillSavePrompt = !(await _stateService.GetAutofillDisableSavePromptAsync()).GetValueOrDefault();
|
||||
|
||||
var blockedUrisList = await _stateService.GetAutofillBlacklistedUrisAsync();
|
||||
AutofillBlockedUris = blockedUrisList != null ? string.Join(", ", blockedUrisList) : null;
|
||||
|
||||
AutoTotpCopy = !(await _stateService.GetDisableAutoTotpCopyAsync() ?? false);
|
||||
|
||||
Favicon = !(await _stateService.GetDisableFaviconAsync()).GetValueOrDefault();
|
||||
@@ -285,6 +288,41 @@ namespace Bit.App.Pages
|
||||
}
|
||||
}
|
||||
|
||||
public async Task UpdateAutofillBlockedUris()
|
||||
{
|
||||
if (_inited)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(AutofillBlockedUris))
|
||||
{
|
||||
await _stateService.SetAutofillBlacklistedUrisAsync(null);
|
||||
AutofillBlockedUris = null;
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
var csv = AutofillBlockedUris;
|
||||
var urisList = new List<string>();
|
||||
foreach (var uri in csv.Split(','))
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(uri))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var cleanedUri = uri.Replace(System.Environment.NewLine, string.Empty).Trim();
|
||||
if (!cleanedUri.StartsWith("http://") && !cleanedUri.StartsWith("https://") &&
|
||||
!cleanedUri.StartsWith(Constants.AndroidAppProtocol))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
urisList.Add(cleanedUri);
|
||||
}
|
||||
await _stateService.SetAutofillBlacklistedUrisAsync(urisList);
|
||||
AutofillBlockedUris = string.Join(", ", urisList);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UpdateCurrentLocaleAsync()
|
||||
{
|
||||
if (!_inited)
|
||||
|
||||
@@ -48,7 +48,6 @@ namespace Bit.App.Pages
|
||||
private bool _reportLoggingEnabled;
|
||||
private bool _approvePasswordlessLoginRequests;
|
||||
private bool _shouldConnectToWatch;
|
||||
private bool _hasMasterPassword;
|
||||
private readonly static List<KeyValuePair<string, int?>> VaultTimeoutOptions =
|
||||
new List<KeyValuePair<string, int?>>
|
||||
{
|
||||
@@ -101,17 +100,12 @@ namespace Bit.App.Pages
|
||||
ExecuteSettingItemCommand = new AsyncCommand<SettingsPageListItem>(item => item.ExecuteAsync(), onException: _loggerService.Exception, allowsMultipleExecutions: false);
|
||||
}
|
||||
|
||||
private bool IsVaultTimeoutActionLockAllowed => _hasMasterPassword || _biometric || _pin;
|
||||
|
||||
public ObservableRangeCollection<ISettingsPageListItem> GroupedItems { get; set; }
|
||||
|
||||
public IAsyncCommand<SettingsPageListItem> ExecuteSettingItemCommand { get; }
|
||||
|
||||
public async Task InitAsync()
|
||||
{
|
||||
var decryptionOptions = await _stateService.GetAccountDecryptionOptions();
|
||||
// set has true for backwards compatibility
|
||||
_hasMasterPassword = decryptionOptions?.HasMasterPassword ?? true;
|
||||
_supportsBiometric = await _platformUtilsService.SupportsBiometricAsync();
|
||||
var lastSync = await _syncService.GetLastSyncAsync();
|
||||
if (lastSync != null)
|
||||
@@ -130,13 +124,7 @@ namespace Bit.App.Pages
|
||||
_vaultTimeoutDisplayValue = _vaultTimeoutOptions.FirstOrDefault(o => o.Value == _vaultTimeout).Key;
|
||||
_vaultTimeoutDisplayValue ??= _vaultTimeoutOptions.Where(o => o.Value == CustomVaultTimeoutValue).First().Key;
|
||||
|
||||
var savedVaultTimeoutAction = await _vaultTimeoutService.GetVaultTimeoutAction();
|
||||
var action = savedVaultTimeoutAction ?? VaultTimeoutAction.Lock;
|
||||
if (!_hasMasterPassword && savedVaultTimeoutAction == null)
|
||||
{
|
||||
action = VaultTimeoutAction.Logout;
|
||||
await _vaultTimeoutService.SetVaultTimeoutOptionsAsync(_vaultTimeout, VaultTimeoutAction.Logout);
|
||||
}
|
||||
var action = await _vaultTimeoutService.GetVaultTimeoutAction() ?? VaultTimeoutAction.Lock;
|
||||
_vaultTimeoutActionDisplayValue = _vaultTimeoutActionOptions.FirstOrDefault(o => o.Value == action).Key;
|
||||
|
||||
if (await _policyService.PolicyAppliesToUser(PolicyType.MaximumVaultTimeout))
|
||||
@@ -151,7 +139,7 @@ namespace Bit.App.Pages
|
||||
}
|
||||
|
||||
var pinSet = await _vaultTimeoutService.IsPinLockSetAsync();
|
||||
_pin = pinSet != PinLockEnum.Disabled;
|
||||
_pin = pinSet.Item1 || pinSet.Item2;
|
||||
_biometric = await _vaultTimeoutService.IsBiometricLockSetAsync();
|
||||
_screenCaptureAllowed = await _stateService.GetScreenCaptureAllowedAsync();
|
||||
|
||||
@@ -399,11 +387,8 @@ namespace Bit.App.Pages
|
||||
// do nothing if we have a policy set
|
||||
return;
|
||||
}
|
||||
|
||||
var options = IsVaultTimeoutActionLockAllowed
|
||||
? _vaultTimeoutActionOptions.Select(o => CreateSelectableOption(o.Key, _vaultTimeoutActionDisplayValue == o.Key)).ToArray()
|
||||
: _vaultTimeoutActionOptions.Where(o => o.Value == VaultTimeoutAction.Logout).Select(v => ToSelectedOption(v.Key)).ToArray();
|
||||
|
||||
var options = _vaultTimeoutActionOptions.Select(o =>
|
||||
o.Key == _vaultTimeoutActionDisplayValue ? $"✓ {o.Key}" : o.Key).ToArray();
|
||||
var selection = await Page.DisplayActionSheet(AppResources.VaultTimeoutAction,
|
||||
AppResources.Cancel, null, options);
|
||||
if (selection == null || selection == AppResources.Cancel)
|
||||
@@ -452,20 +437,19 @@ namespace Bit.App.Pages
|
||||
|
||||
var kdfConfig = await _stateService.GetActiveUserCustomDataAsync(a => new KdfConfig(a?.Profile));
|
||||
var email = await _stateService.GetEmailAsync();
|
||||
var pinKey = await _cryptoService.MakePinKeyAsync(pin, email, kdfConfig);
|
||||
var userKey = await _cryptoService.GetUserKeyAsync();
|
||||
var pinProtectedKey = await _cryptoService.EncryptAsync(userKey.Key, pinKey);
|
||||
|
||||
var encPin = await _cryptoService.EncryptAsync(pin);
|
||||
await _stateService.SetProtectedPinAsync(encPin.EncryptedString);
|
||||
var pinKey = await _cryptoService.MakePinKeyAysnc(pin, email, kdfConfig);
|
||||
var key = await _cryptoService.GetKeyAsync();
|
||||
var pinProtectedKey = await _cryptoService.EncryptAsync(key.Key, pinKey);
|
||||
|
||||
if (masterPassOnRestart)
|
||||
{
|
||||
await _stateService.SetUserKeyPinEphemeralAsync(pinProtectedKey);
|
||||
var encPin = await _cryptoService.EncryptAsync(pin);
|
||||
await _stateService.SetProtectedPinAsync(encPin.EncryptedString);
|
||||
await _stateService.SetPinProtectedKeyAsync(pinProtectedKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _stateService.SetUserKeyPinAsync(pinProtectedKey);
|
||||
await _stateService.SetPinProtectedAsync(pinProtectedKey.EncryptedString);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -475,6 +459,7 @@ namespace Bit.App.Pages
|
||||
}
|
||||
if (!_pin)
|
||||
{
|
||||
await _cryptoService.ClearPinProtectedKeyAsync();
|
||||
await _vaultTimeoutService.ClearAsync();
|
||||
}
|
||||
BuildList();
|
||||
@@ -504,10 +489,9 @@ namespace Bit.App.Pages
|
||||
else
|
||||
{
|
||||
await _stateService.SetBiometricUnlockAsync(null);
|
||||
await UpdateVaultTimeoutActionIfNeededAsync();
|
||||
}
|
||||
await _stateService.SetBiometricLockedAsync(false);
|
||||
await _cryptoService.ToggleKeysAsync();
|
||||
await _cryptoService.ToggleKeyAsync();
|
||||
BuildList();
|
||||
}
|
||||
|
||||
@@ -851,11 +835,9 @@ namespace Bit.App.Pages
|
||||
return _vaultTimeoutOptions.FirstOrDefault(o => o.Key == key).Value;
|
||||
}
|
||||
|
||||
private string CreateSelectableOption(string option, bool selected) => selected ? ToSelectedOption(option) : option;
|
||||
private string CreateSelectableOption(string option, bool selected) => selected ? $"✓ {option}" : option;
|
||||
|
||||
private bool CompareSelection(string selection, string compareTo) => selection == compareTo || selection == ToSelectedOption(compareTo);
|
||||
|
||||
private string ToSelectedOption(string option) => $"✓ {option}";
|
||||
private bool CompareSelection(string selection, string compareTo) => selection == compareTo || selection == $"✓ {compareTo}";
|
||||
|
||||
public async Task SetScreenCaptureAllowedAsync()
|
||||
{
|
||||
@@ -887,17 +869,5 @@ namespace Bit.App.Pages
|
||||
await _watchDeviceService.SetShouldConnectToWatchAsync(_shouldConnectToWatch);
|
||||
BuildList();
|
||||
}
|
||||
|
||||
private async Task UpdateVaultTimeoutActionIfNeededAsync()
|
||||
{
|
||||
if (IsVaultTimeoutActionLockAllowed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_vaultTimeoutActionDisplayValue = _vaultTimeoutActionOptions.First(o => o.Value == VaultTimeoutAction.Logout).Key;
|
||||
await _vaultTimeoutService.SetVaultTimeoutOptionsAsync(_vaultTimeout, VaultTimeoutAction.Logout);
|
||||
_deviceActionService.Toast(AppResources.VaultTimeoutActionChangedToLogOut);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ namespace Bit.App.Pages
|
||||
_cipherDomain = await _cipherService.GetAsync(CipherId);
|
||||
Cipher = await _cipherDomain.DecryptAsync();
|
||||
LoadAttachments();
|
||||
_hasUpdatedKey = await _cryptoService.HasUserKeyAsync();
|
||||
_hasUpdatedKey = await _cryptoService.HasEncKeyAsync();
|
||||
var canAccessPremium = await _stateService.CanAccessPremiumAsync();
|
||||
_canAccessAttachments = canAccessPremium || Cipher.OrganizationId != null;
|
||||
if (!_canAccessAttachments)
|
||||
|
||||
@@ -179,7 +179,7 @@
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}"
|
||||
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}"
|
||||
IsVisible="{Binding Cipher.ViewPassword}"
|
||||
AutomationId="ShowValueButton" />
|
||||
AutomationId="ViewValueButton" />
|
||||
<controls:IconButton
|
||||
StyleClass="box-row-button, box-row-button-platform"
|
||||
Text="{Binding Source={x:Static core:BitwardenIcons.Clone}}"
|
||||
|
||||
@@ -208,11 +208,6 @@ namespace Bit.App.Pages
|
||||
}
|
||||
else if (selection == AppResources.Autofill || selection == AppResources.AutofillAndSave)
|
||||
{
|
||||
if (cipher.Reprompt != CipherRepromptType.None && !await _passwordRepromptService.ShowPasswordPromptAsync())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (selection == AppResources.AutofillAndSave)
|
||||
{
|
||||
var uris = cipher.Login?.Uris?.ToList();
|
||||
|
||||
@@ -330,16 +330,13 @@ namespace Bit.App.Pages
|
||||
items.AddRange(itemGroup);
|
||||
}
|
||||
|
||||
Device.BeginInvokeOnMainThread(() =>
|
||||
if (Device.RuntimePlatform == Device.iOS)
|
||||
{
|
||||
if (Device.RuntimePlatform == Device.iOS)
|
||||
{
|
||||
// HACK: [PS-536] Fix to avoid blank list after back navigation on unlocking with previous page info
|
||||
// because of update to XF v5.0.0.2401
|
||||
GroupedItems.Clear();
|
||||
}
|
||||
GroupedItems.ReplaceRange(items);
|
||||
});
|
||||
// HACK: [PS-536] Fix to avoid blank list after back navigation on unlocking with previous page info
|
||||
// because of update to XF v5.0.0.2401
|
||||
GroupedItems.Clear();
|
||||
}
|
||||
GroupedItems.ReplaceRange(items);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -359,24 +356,21 @@ namespace Bit.App.Pages
|
||||
items.AddRange(itemGroup);
|
||||
}
|
||||
|
||||
Device.BeginInvokeOnMainThread(() =>
|
||||
if (groupedItems.Any())
|
||||
{
|
||||
if (groupedItems.Any())
|
||||
{
|
||||
if (Device.RuntimePlatform == Device.iOS)
|
||||
{
|
||||
// HACK: [PS-536] Fix to avoid blank list after back navigation on unlocking with previous page info
|
||||
// because of update to XF v5.0.0.2401
|
||||
GroupedItems.Clear();
|
||||
}
|
||||
GroupedItems.ReplaceRange(new List<IGroupingsPageListItem> { new GroupingsPageHeaderListItem(groupedItems[0].Name, groupedItems[0].ItemCount) });
|
||||
GroupedItems.AddRange(items);
|
||||
}
|
||||
else
|
||||
if (Device.RuntimePlatform == Device.iOS)
|
||||
{
|
||||
// HACK: [PS-536] Fix to avoid blank list after back navigation on unlocking with previous page info
|
||||
// because of update to XF v5.0.0.2401
|
||||
GroupedItems.Clear();
|
||||
}
|
||||
});
|
||||
GroupedItems.ReplaceRange(new List<IGroupingsPageListItem> { new GroupingsPageHeaderListItem(groupedItems[0].Name, groupedItems[0].ItemCount) });
|
||||
GroupedItems.AddRange(items);
|
||||
}
|
||||
else
|
||||
{
|
||||
GroupedItems.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
@@ -384,12 +378,9 @@ namespace Bit.App.Pages
|
||||
_doingLoad = false;
|
||||
Loaded = true;
|
||||
Loading = false;
|
||||
Device.BeginInvokeOnMainThread(() =>
|
||||
{
|
||||
ShowNoData = (MainPage && !HasCiphers) || !groupedItems.Any();
|
||||
ShowList = !ShowNoData;
|
||||
DisableRefreshing();
|
||||
});
|
||||
ShowNoData = (MainPage && !HasCiphers) || !groupedItems.Any();
|
||||
ShowList = !ShowNoData;
|
||||
DisableRefreshing();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
243
src/App/Resources/AppResources.Designer.cs
generated
243
src/App/Resources/AppResources.Designer.cs
generated
@@ -418,15 +418,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Admin approval requested.
|
||||
/// </summary>
|
||||
public static string AdminApprovalRequested {
|
||||
get {
|
||||
return ResourceManager.GetString("AdminApprovalRequested", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to All.
|
||||
/// </summary>
|
||||
@@ -571,24 +562,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Approve with master password.
|
||||
/// </summary>
|
||||
public static string ApproveWithMasterPassword {
|
||||
get {
|
||||
return ResourceManager.GetString("ApproveWithMasterPassword", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Approve with my other device.
|
||||
/// </summary>
|
||||
public static string ApproveWithMyOtherDevice {
|
||||
get {
|
||||
return ResourceManager.GetString("ApproveWithMyOtherDevice", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to April.
|
||||
/// </summary>
|
||||
@@ -823,6 +796,15 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Auto-fill will not be offered for blocked URIs. Separate multiple URIs with a comma. For example: "https://twitter.com, androidapp://com.twitter.android"..
|
||||
/// </summary>
|
||||
public static string AutofillBlockedUrisDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("AutofillBlockedUrisDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Do you want to auto-fill or view this item?.
|
||||
/// </summary>
|
||||
@@ -958,15 +940,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Auto-fill will not be offered for these URIs..
|
||||
/// </summary>
|
||||
public static string AutoFillWillNotBeOfferedForTheseURIs {
|
||||
get {
|
||||
return ResourceManager.GetString("AutoFillWillNotBeOfferedForTheseURIs", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Auto-fill with Bitwarden.
|
||||
/// </summary>
|
||||
@@ -1255,15 +1228,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Block auto-fill.
|
||||
/// </summary>
|
||||
public static string BlockAutoFill {
|
||||
get {
|
||||
return ResourceManager.GetString("BlockAutoFill", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Brand.
|
||||
/// </summary>
|
||||
@@ -1300,15 +1264,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Cannot edit multiple URIs at once.
|
||||
/// </summary>
|
||||
public static string CannotEditMultipleURIsAtOnce {
|
||||
get {
|
||||
return ResourceManager.GetString("CannotEditMultipleURIsAtOnce", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Cannot open the app "{0}"..
|
||||
/// </summary>
|
||||
@@ -2191,15 +2146,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Edit URI.
|
||||
/// </summary>
|
||||
public static string EditURI {
|
||||
get {
|
||||
return ResourceManager.GetString("EditURI", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Email.
|
||||
/// </summary>
|
||||
@@ -2353,15 +2299,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Enter URI.
|
||||
/// </summary>
|
||||
public static string EnterURI {
|
||||
get {
|
||||
return ResourceManager.GetString("EnterURI", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Enter the 6 digit verification code from your authenticator app..
|
||||
/// </summary>
|
||||
@@ -3010,24 +2947,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Format: {0}.
|
||||
/// </summary>
|
||||
public static string FormatX {
|
||||
get {
|
||||
return ResourceManager.GetString("FormatX", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Format: {0}. Separate multiple URIs with a comma..
|
||||
/// </summary>
|
||||
public static string FormatXSeparateMultipleURIsWithAComma {
|
||||
get {
|
||||
return ResourceManager.GetString("FormatXSeparateMultipleURIsWithAComma", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Forwarded email alias.
|
||||
/// </summary>
|
||||
@@ -3370,15 +3289,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Invalid format. Use https://, http://, or androidapp://.
|
||||
/// </summary>
|
||||
public static string InvalidFormatUseHttpsHttpOrAndroidApp {
|
||||
get {
|
||||
return ResourceManager.GetString("InvalidFormatUseHttpsHttpOrAndroidApp", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Invalid master password. Try again..
|
||||
/// </summary>
|
||||
@@ -3397,15 +3307,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Invalid URI.
|
||||
/// </summary>
|
||||
public static string InvalidURI {
|
||||
get {
|
||||
return ResourceManager.GetString("InvalidURI", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Invalid verification code.
|
||||
/// </summary>
|
||||
@@ -3685,15 +3586,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Logged in!.
|
||||
/// </summary>
|
||||
public static string LoggedIn {
|
||||
get {
|
||||
return ResourceManager.GetString("LoggedIn", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Logged in as {0} on {1}..
|
||||
/// </summary>
|
||||
@@ -3712,15 +3604,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Logging in as {0}.
|
||||
/// </summary>
|
||||
public static string LoggingInAsX {
|
||||
get {
|
||||
return ResourceManager.GetString("LoggingInAsX", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Logging in as {0} on {1}.
|
||||
/// </summary>
|
||||
@@ -4335,15 +4218,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to New blocked URI.
|
||||
/// </summary>
|
||||
public static string NewBlockedURI {
|
||||
get {
|
||||
return ResourceManager.GetString("NewBlockedURI", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to New custom field.
|
||||
/// </summary>
|
||||
@@ -5318,15 +5192,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Remember this device.
|
||||
/// </summary>
|
||||
public static string RememberThisDevice {
|
||||
get {
|
||||
return ResourceManager.GetString("RememberThisDevice", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Remove.
|
||||
/// </summary>
|
||||
@@ -5399,15 +5264,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Request admin approval.
|
||||
/// </summary>
|
||||
public static string RequestAdminApproval {
|
||||
get {
|
||||
return ResourceManager.GetString("RequestAdminApproval", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Request one-time password.
|
||||
/// </summary>
|
||||
@@ -6263,15 +6119,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to There are no blocked URIs.
|
||||
/// </summary>
|
||||
public static string ThereAreNoBlockedURIs {
|
||||
get {
|
||||
return ResourceManager.GetString("ThereAreNoBlockedURIs", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to There are no items in your vault that match "{0}".
|
||||
/// </summary>
|
||||
@@ -6290,15 +6137,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The URI {0} is already blocked.
|
||||
/// </summary>
|
||||
public static string TheURIXIsAlreadyBlocked {
|
||||
get {
|
||||
return ResourceManager.GetString("TheURIXIsAlreadyBlocked", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to 30 days.
|
||||
/// </summary>
|
||||
@@ -6416,15 +6254,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Trouble logging in?.
|
||||
/// </summary>
|
||||
public static string TroubleLoggingIn {
|
||||
get {
|
||||
return ResourceManager.GetString("TroubleLoggingIn", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Try again.
|
||||
/// </summary>
|
||||
@@ -6434,15 +6263,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Turn off using a public device.
|
||||
/// </summary>
|
||||
public static string TurnOffUsingPublicDevice {
|
||||
get {
|
||||
return ResourceManager.GetString("TurnOffUsingPublicDevice", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to 20 seconds.
|
||||
/// </summary>
|
||||
@@ -6767,15 +6587,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to URI removed.
|
||||
/// </summary>
|
||||
public static string URIRemoved {
|
||||
get {
|
||||
return ResourceManager.GetString("URIRemoved", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to URIs.
|
||||
/// </summary>
|
||||
@@ -6785,15 +6596,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to URI saved.
|
||||
/// </summary>
|
||||
public static string URISaved {
|
||||
get {
|
||||
return ResourceManager.GetString("URISaved", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to US.
|
||||
/// </summary>
|
||||
@@ -6974,15 +6776,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Vault timeout action changed to log out.
|
||||
/// </summary>
|
||||
public static string VaultTimeoutActionChangedToLogOut {
|
||||
get {
|
||||
return ResourceManager.GetString("VaultTimeoutActionChangedToLogOut", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Your organization policies have set your vault timeout action to {0}..
|
||||
/// </summary>
|
||||
@@ -7352,24 +7145,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Your request has been sent to your admin..
|
||||
/// </summary>
|
||||
public static string YourRequestHasBeenSentToYourAdmin {
|
||||
get {
|
||||
return ResourceManager.GetString("YourRequestHasBeenSentToYourAdmin", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to You will be notified once approved. .
|
||||
/// </summary>
|
||||
public static string YouWillBeNotifiedOnceApproved {
|
||||
get {
|
||||
return ResourceManager.GetString("YouWillBeNotifiedOnceApproved", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to To continue, hold your YubiKey NEO against the back of the device or insert your YubiKey into your device's USB port, then touch its button..
|
||||
/// </summary>
|
||||
|
||||
@@ -2638,9 +2638,9 @@
|
||||
<value>باز کردن قفل ممکن است به دلیل حافظه ناکافی انجام شود. تنظیمات حافظه KDF خود را کاهش دهید تا حل شود.</value>
|
||||
</data>
|
||||
<data name="InvalidAPIKey" xml:space="preserve">
|
||||
<value>کلید API نامعتبر</value>
|
||||
<value>Invalid API key</value>
|
||||
</data>
|
||||
<data name="InvalidAPIToken" xml:space="preserve">
|
||||
<value>کلید توکن API نامعتبر</value>
|
||||
<value>Invalid API token</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -1098,7 +1098,7 @@ A leitura será efetuada automaticamente.</value>
|
||||
<value>Sr.</value>
|
||||
</data>
|
||||
<data name="Mrs" xml:space="preserve">
|
||||
<value>Sr.ª</value>
|
||||
<value>Sra.</value>
|
||||
</data>
|
||||
<data name="Ms" xml:space="preserve">
|
||||
<value>Menina</value>
|
||||
@@ -1579,7 +1579,7 @@ A leitura será efetuada automaticamente.</value>
|
||||
<comment>'Nord' is the name of a specific color scheme. It should not be translated.</comment>
|
||||
</data>
|
||||
<data name="SolarizedDark" xml:space="preserve">
|
||||
<value>Solarized (escuro)</value>
|
||||
<value>Solarized Dark</value>
|
||||
<comment>'Solarized Dark' is the name of a specific color scheme. It should not be translated.</comment>
|
||||
</data>
|
||||
<data name="AutofillBlockedUris" xml:space="preserve">
|
||||
@@ -2233,10 +2233,10 @@ A leitura será efetuada automaticamente.</value>
|
||||
<value>Ajude o Bitwarden a melhorar a estabilidade da aplicação ao enviar relatórios de falhas.</value>
|
||||
</data>
|
||||
<data name="OptionsExpanded" xml:space="preserve">
|
||||
<value>As opções foram expandidas, clique para fechar.</value>
|
||||
<value>Options are expanded, tap to collapse.</value>
|
||||
</data>
|
||||
<data name="OptionsCollapsed" xml:space="preserve">
|
||||
<value>As opções foram fechadas, clique para expandir.</value>
|
||||
<value>Options are collapsed, tap to expand.</value>
|
||||
</data>
|
||||
<data name="UppercaseAtoZ" xml:space="preserve">
|
||||
<value>Maiúsculas (A-Z)</value>
|
||||
@@ -2392,10 +2392,10 @@ A leitura será efetuada automaticamente.</value>
|
||||
<value>Tipo de nome de utilizador</value>
|
||||
</data>
|
||||
<data name="PlusAddressedEmail" xml:space="preserve">
|
||||
<value>E-mail com subendereço</value>
|
||||
<value>Plus addressed email</value>
|
||||
</data>
|
||||
<data name="CatchAllEmail" xml:space="preserve">
|
||||
<value>E-mail de captura geral</value>
|
||||
<value>Catch-all email</value>
|
||||
</data>
|
||||
<data name="ForwardedEmailAlias" xml:space="preserve">
|
||||
<value>Alias de e-mail reencaminhado</value>
|
||||
@@ -2454,10 +2454,10 @@ A leitura será efetuada automaticamente.</value>
|
||||
<value>Ocorreu um erro desconhecido ({0}).</value>
|
||||
</data>
|
||||
<data name="PlusAddressedEmailDescription" xml:space="preserve">
|
||||
<value>Utilize as capacidades de subendereçamento do seu fornecedor de e-mail.</value>
|
||||
<value>Use your email provider's subaddress capabilities</value>
|
||||
</data>
|
||||
<data name="CatchAllEmailDescription" xml:space="preserve">
|
||||
<value>Utilize a caixa de entrada de captura geral configurada para o seu domínio.</value>
|
||||
<value>Use your domain's configured catch-all inbox.</value>
|
||||
</data>
|
||||
<data name="ForwardedEmailDescription" xml:space="preserve">
|
||||
<value>Gerar um alias de e-mail com um serviço de reencaminhamento externo.</value>
|
||||
|
||||
@@ -1585,6 +1585,9 @@ Scanning will happen automatically.</value>
|
||||
<data name="AutofillBlockedUris" xml:space="preserve">
|
||||
<value>Auto-fill blocked URIs</value>
|
||||
</data>
|
||||
<data name="AutofillBlockedUrisDescription" xml:space="preserve">
|
||||
<value>Auto-fill will not be offered for blocked URIs. Separate multiple URIs with a comma. For example: "https://twitter.com, androidapp://com.twitter.android".</value>
|
||||
</data>
|
||||
<data name="AskToAddLogin" xml:space="preserve">
|
||||
<value>Ask to add login</value>
|
||||
</data>
|
||||
@@ -2628,24 +2631,6 @@ Do you want to switch to this account?</value>
|
||||
<data name="CurrentMasterPassword" xml:space="preserve">
|
||||
<value>Current master password</value>
|
||||
</data>
|
||||
<data name="LoggedIn" xml:space="preserve">
|
||||
<value>Logged in!</value>
|
||||
</data>
|
||||
<data name="ApproveWithMyOtherDevice" xml:space="preserve">
|
||||
<value>Approve with my other device</value>
|
||||
</data>
|
||||
<data name="RequestAdminApproval" xml:space="preserve">
|
||||
<value>Request admin approval</value>
|
||||
</data>
|
||||
<data name="ApproveWithMasterPassword" xml:space="preserve">
|
||||
<value>Approve with master password</value>
|
||||
</data>
|
||||
<data name="TurnOffUsingPublicDevice" xml:space="preserve">
|
||||
<value>Turn off using a public device</value>
|
||||
</data>
|
||||
<data name="RememberThisDevice" xml:space="preserve">
|
||||
<value>Remember this device</value>
|
||||
</data>
|
||||
<data name="MasterPasswordRePromptHelp" xml:space="preserve">
|
||||
<value>Master password re-prompt help</value>
|
||||
</data>
|
||||
@@ -2658,68 +2643,4 @@ Do you want to switch to this account?</value>
|
||||
<data name="InvalidAPIToken" xml:space="preserve">
|
||||
<value>Invalid API token</value>
|
||||
</data>
|
||||
<data name="AdminApprovalRequested" xml:space="preserve">
|
||||
<value>Admin approval requested</value>
|
||||
</data>
|
||||
<data name="YourRequestHasBeenSentToYourAdmin" xml:space="preserve">
|
||||
<value>Your request has been sent to your admin.</value>
|
||||
</data>
|
||||
<data name="YouWillBeNotifiedOnceApproved" xml:space="preserve">
|
||||
<value>You will be notified once approved. </value>
|
||||
</data>
|
||||
<data name="TroubleLoggingIn" xml:space="preserve">
|
||||
<value>Trouble logging in?</value>
|
||||
</data>
|
||||
<data name="LoggingInAsX" xml:space="preserve">
|
||||
<value>Logging in as {0}</value>
|
||||
</data>
|
||||
<data name="VaultTimeoutActionChangedToLogOut" xml:space="preserve">
|
||||
<value>Vault timeout action changed to log out</value>
|
||||
</data>
|
||||
<data name="BlockAutoFill" xml:space="preserve">
|
||||
<value>Block auto-fill</value>
|
||||
</data>
|
||||
<data name="AutoFillWillNotBeOfferedForTheseURIs" xml:space="preserve">
|
||||
<value>Auto-fill will not be offered for these URIs.</value>
|
||||
</data>
|
||||
<data name="NewBlockedURI" xml:space="preserve">
|
||||
<value>New blocked URI</value>
|
||||
</data>
|
||||
<data name="URISaved" xml:space="preserve">
|
||||
<value>URI saved</value>
|
||||
</data>
|
||||
<data name="InvalidFormatUseHttpsHttpOrAndroidApp" xml:space="preserve">
|
||||
<value>Invalid format. Use https://, http://, or androidapp://</value>
|
||||
<comment>https://, http://, androidapp:// should not be translated</comment>
|
||||
</data>
|
||||
<data name="EditURI" xml:space="preserve">
|
||||
<value>Edit URI</value>
|
||||
</data>
|
||||
<data name="EnterURI" xml:space="preserve">
|
||||
<value>Enter URI</value>
|
||||
</data>
|
||||
<data name="FormatXSeparateMultipleURIsWithAComma" xml:space="preserve">
|
||||
<value>Format: {0}. Separate multiple URIs with a comma.</value>
|
||||
</data>
|
||||
<data name="FormatX" xml:space="preserve">
|
||||
<value>Format: {0}</value>
|
||||
</data>
|
||||
<data name="InvalidURI" xml:space="preserve">
|
||||
<value>Invalid URI</value>
|
||||
</data>
|
||||
<data name="URISaved" xml:space="preserve">
|
||||
<value>URI saved</value>
|
||||
</data>
|
||||
<data name="URIRemoved" xml:space="preserve">
|
||||
<value>URI removed</value>
|
||||
</data>
|
||||
<data name="ThereAreNoBlockedURIs" xml:space="preserve">
|
||||
<value>There are no blocked URIs</value>
|
||||
</data>
|
||||
<data name="TheURIXIsAlreadyBlocked" xml:space="preserve">
|
||||
<value>The URI {0} is already blocked</value>
|
||||
</data>
|
||||
<data name="CannotEditMultipleURIsAtOnce" xml:space="preserve">
|
||||
<value>Cannot edit multiple URIs at once</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -375,7 +375,7 @@
|
||||
<comment>Validation message for when a form field is left blank and is required to be entered.</comment>
|
||||
</data>
|
||||
<data name="ValueHasBeenCopied" xml:space="preserve">
|
||||
<value>{0} kopirana</value>
|
||||
<value>{0} je bilo kopirano.</value>
|
||||
<comment>Confirmation message after successfully copying a value to the clipboard.</comment>
|
||||
</data>
|
||||
<data name="VerifyFingerprint" xml:space="preserve">
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace Bit.App.Services
|
||||
return false;
|
||||
};
|
||||
|
||||
return await _cryptoService.CompareAndUpdatePasswordHashAsync(password, null);
|
||||
return await _cryptoService.CompareAndUpdateKeyHashAsync(password, null);
|
||||
}
|
||||
|
||||
public async Task<bool> Enabled()
|
||||
|
||||
@@ -428,22 +428,6 @@
|
||||
<Setter Property="VerticalOptions"
|
||||
Value="CenterAndExpand" />
|
||||
</Style>
|
||||
<Style TargetType="Button"
|
||||
ApplyToDerivedTypes="True"
|
||||
Class="box-row-button-muted">
|
||||
<Setter Property="BackgroundColor"
|
||||
Value="Transparent" />
|
||||
<Setter Property="BorderWidth"
|
||||
Value="0" />
|
||||
<Setter Property="Padding"
|
||||
Value="0" />
|
||||
<Setter Property="TextColor"
|
||||
Value="{DynamicResource MutedColor}" />
|
||||
<Setter Property="HorizontalOptions"
|
||||
Value="End" />
|
||||
<Setter Property="VerticalOptions"
|
||||
Value="CenterAndExpand" />
|
||||
</Style>
|
||||
<Style TargetType="Button"
|
||||
ApplyToDerivedTypes="True"
|
||||
Class="box-overlay">
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Bit.App.Utilities.Prompts
|
||||
{
|
||||
public class ValidatablePromptConfig
|
||||
{
|
||||
public string Title { get; set; }
|
||||
public string Subtitle { get; set; }
|
||||
public string Text { get; set; }
|
||||
public Func<string, string> ValidateText { get; set; }
|
||||
public string ValueSubInfo { get; set; }
|
||||
public string OkButtonText { get; set; }
|
||||
public string CancelButtonText { get; set; }
|
||||
public string ThirdButtonText { get; set; }
|
||||
public bool NumericKeyboard { get; set; }
|
||||
}
|
||||
|
||||
public struct ValidatablePromptResponse
|
||||
{
|
||||
public ValidatablePromptResponse(string text, bool executeThirdAction)
|
||||
{
|
||||
Text = text;
|
||||
ExecuteThirdAction = executeThirdAction;
|
||||
}
|
||||
|
||||
public string Text { get; set; }
|
||||
public bool ExecuteThirdAction { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Domain;
|
||||
using Bit.Core.Models.Request;
|
||||
using Bit.Core.Models.Response;
|
||||
@@ -71,7 +70,7 @@ namespace Bit.Core.Abstractions
|
||||
Task<OrganizationAutoEnrollStatusResponse> GetOrganizationAutoEnrollStatusAsync(string identifier);
|
||||
Task PutOrganizationUserResetPasswordEnrollmentAsync(string orgId, string userId,
|
||||
OrganizationUserResetPasswordEnrollmentRequest request);
|
||||
Task<KeyConnectorUserKeyResponse> GetMasterKeyFromKeyConnector(string keyConnectorUrl);
|
||||
Task<KeyConnectorUserKeyResponse> GetUserKeyFromKeyConnector(string keyConnectorUrl);
|
||||
Task PostUserKeyToKeyConnector(string keyConnectorUrl, KeyConnectorUserKeyRequest request);
|
||||
Task PostSetKeyConnectorKey(SetKeyConnectorKeyRequest request);
|
||||
Task PostConvertToKeyConnector();
|
||||
@@ -91,12 +90,9 @@ namespace Bit.Core.Abstractions
|
||||
Task<PasswordlessLoginResponse> GetAuthRequestAsync(string id);
|
||||
Task<PasswordlessLoginResponse> GetAuthResponseAsync(string id, string accessCode);
|
||||
Task<PasswordlessLoginResponse> PutAuthRequestAsync(string id, string key, string masterPasswordHash, string deviceIdentifier, bool requestApproved);
|
||||
Task<PasswordlessLoginResponse> PostCreateRequestAsync(PasswordlessCreateLoginRequest passwordlessCreateLoginRequest, AuthRequestType authRequestType);
|
||||
Task<PasswordlessLoginResponse> PostCreateRequestAsync(PasswordlessCreateLoginRequest passwordlessCreateLoginRequest);
|
||||
Task<bool> GetKnownDeviceAsync(string email, string deviceIdentifier);
|
||||
Task<DeviceResponse> GetDeviceByIdentifierAsync(string deviceIdentifier);
|
||||
Task<DeviceResponse> UpdateTrustedDeviceKeysAsync(string deviceIdentifier, TrustedDeviceKeysRequest deviceRequest);
|
||||
Task<OrganizationDomainSsoDetailsResponse> GetOrgDomainSsoDetailsAsync(string email);
|
||||
Task<bool> GetDevicesExistenceByTypes(DeviceType[] deviceTypes);
|
||||
Task<ConfigResponse> GetConfigsAsync();
|
||||
Task<string> GetFastmailAccountIdAsync(string apiKey);
|
||||
}
|
||||
|
||||
@@ -32,12 +32,9 @@ namespace Bit.Core.Abstractions
|
||||
Task<List<PasswordlessLoginResponse>> GetPasswordlessLoginRequestsAsync();
|
||||
Task<List<PasswordlessLoginResponse>> GetActivePasswordlessLoginRequestsAsync();
|
||||
Task<PasswordlessLoginResponse> GetPasswordlessLoginRequestByIdAsync(string id);
|
||||
/// <summary>
|
||||
/// Gets a passwordless login request by <paramref name="id"/> and <paramref name="accessCode"/>. No authentication required.
|
||||
/// </summary>
|
||||
Task<PasswordlessLoginResponse> GetPasswordlessLoginResquestAsync(string id, string accessCode);
|
||||
Task<PasswordlessLoginResponse> GetPasswordlessLoginResponseAsync(string id, string accessCode);
|
||||
Task<PasswordlessLoginResponse> PasswordlessLoginAsync(string id, string pubKey, bool requestApproved);
|
||||
Task<PasswordlessLoginResponse> PasswordlessCreateLoginRequestAsync(string email, AuthRequestType authRequestType);
|
||||
Task<PasswordlessLoginResponse> PasswordlessCreateLoginRequestAsync(string email);
|
||||
|
||||
void LogOut(Action callback);
|
||||
void Init();
|
||||
|
||||
@@ -9,54 +9,49 @@ namespace Bit.Core.Abstractions
|
||||
{
|
||||
public interface ICryptoService
|
||||
{
|
||||
void ClearCache();
|
||||
Task ToggleKeysAsync();
|
||||
Task SetUserKeyAsync(UserKey userKey, string userId = null);
|
||||
Task<UserKey> GetUserKeyAsync(string userId = null);
|
||||
Task<UserKey> GetUserKeyWithLegacySupportAsync(string userId = null);
|
||||
Task<bool> HasUserKeyAsync(string userId = null);
|
||||
Task<bool> HasEncryptedUserKeyAsync(string userId = null);
|
||||
Task<UserKey> MakeUserKeyAsync();
|
||||
Task ClearUserKeyAsync(string userId = null);
|
||||
Task SetMasterKeyEncryptedUserKeyAsync(string value, string userId = null);
|
||||
Task SetMasterKeyAsync(MasterKey masterKey, string userId = null);
|
||||
Task<MasterKey> GetMasterKeyAsync(string userId = null);
|
||||
Task<MasterKey> MakeMasterKeyAsync(string password, string email, KdfConfig kdfConfig);
|
||||
Task ClearMasterKeyAsync(string userId = null);
|
||||
Task<Tuple<UserKey, EncString>> EncryptUserKeyWithMasterKeyAsync(MasterKey masterKey, UserKey userKey = null);
|
||||
Task<UserKey> DecryptUserKeyWithMasterKeyAsync(MasterKey masterKey, EncString encUserKey = null, string userId = null);
|
||||
Task<Tuple<SymmetricCryptoKey, EncString>> MakeDataEncKeyAsync(UserKey key);
|
||||
Task<Tuple<SymmetricCryptoKey, EncString>> MakeDataEncKeyAsync(OrgKey key);
|
||||
Task<string> HashPasswordAsync(string password, SymmetricCryptoKey key, HashPurpose hashPurpose = HashPurpose.ServerAuthorization);
|
||||
Task SetPasswordHashAsync(string keyHash);
|
||||
Task<string> GetPasswordHashAsync();
|
||||
Task ClearPasswordHashAsync(string userId = null);
|
||||
Task<bool> CompareAndUpdatePasswordHashAsync(string masterPassword, MasterKey key);
|
||||
Task SetOrgKeysAsync(IEnumerable<ProfileOrganizationResponse> orgs);
|
||||
Task<OrgKey> GetOrgKeyAsync(string orgId);
|
||||
Task<Dictionary<string, OrgKey>> GetOrgKeysAsync();
|
||||
Task ClearOrgKeysAsync(bool memoryOnly = false, string userId = null);
|
||||
Task<byte[]> GetPublicKeyAsync();
|
||||
Task SetPrivateKeyAsync(string encPrivateKey);
|
||||
Task<byte[]> GetPrivateKeyAsync();
|
||||
Task<List<string>> GetFingerprintAsync(string userId, byte[] publicKey = null);
|
||||
Task<Tuple<string, EncString>> MakeKeyPairAsync(SymmetricCryptoKey key = null);
|
||||
Task ClearEncKeyAsync(bool memoryOnly = false, string userId = null);
|
||||
Task ClearKeyAsync(string userId = null);
|
||||
Task ClearKeyHashAsync(string userId = null);
|
||||
Task ClearKeyPairAsync(bool memoryOnly = false, string userId = null);
|
||||
Task<PinKey> MakePinKeyAsync(string pin, string salt, KdfConfig config);
|
||||
Task ClearPinKeysAsync(string userId = null);
|
||||
Task<UserKey> DecryptUserKeyWithPinAsync(string pin, string salt, KdfConfig kdfConfig, EncString pinProtectedUserKey = null);
|
||||
Task<MasterKey> DecryptMasterKeyWithPinAsync(string pin, string salt, KdfConfig kdfConfig, EncString pinProtectedMasterKey = null);
|
||||
Task<SymmetricCryptoKey> MakeSendKeyAsync(byte[] keyMaterial);
|
||||
Task<EncString> RsaEncryptAsync(byte[] data, byte[] publicKey = null);
|
||||
Task<byte[]> RsaDecryptAsync(string encValue, byte[] privateKey = null);
|
||||
Task<int> RandomNumberAsync(int min, int max);
|
||||
Task<string> RandomStringAsync(int length);
|
||||
Task ClearKeysAsync(string userId = null);
|
||||
Task ClearOrgKeysAsync(bool memoryOnly = false, string userId = null);
|
||||
Task ClearPinProtectedKeyAsync(string userId = null);
|
||||
void ClearCache();
|
||||
Task<byte[]> DecryptFromBytesAsync(byte[] encBytes, SymmetricCryptoKey key);
|
||||
Task<byte[]> DecryptToBytesAsync(EncString encString, SymmetricCryptoKey key = null);
|
||||
Task<string> DecryptToUtf8Async(EncString encString, SymmetricCryptoKey key = null);
|
||||
Task<EncString> EncryptAsync(byte[] plainValue, SymmetricCryptoKey key = null);
|
||||
Task<EncString> EncryptAsync(string plainValue, SymmetricCryptoKey key = null);
|
||||
Task<EncByteArray> EncryptToBytesAsync(byte[] plainValue, SymmetricCryptoKey key = null);
|
||||
Task<UserKey> DecryptAndMigrateOldPinKeyAsync(bool masterPasswordOnRestart, string pin, string email, KdfConfig kdfConfig, EncString oldPinKey);
|
||||
Task<SymmetricCryptoKey> GetEncKeyAsync(SymmetricCryptoKey key = null);
|
||||
Task<List<string>> GetFingerprintAsync(string userId, byte[] publicKey = null);
|
||||
Task<SymmetricCryptoKey> GetKeyAsync(string userId = null);
|
||||
Task<string> GetKeyHashAsync();
|
||||
Task<SymmetricCryptoKey> GetOrgKeyAsync(string orgId);
|
||||
Task<Dictionary<string, SymmetricCryptoKey>> GetOrgKeysAsync();
|
||||
Task<byte[]> GetPrivateKeyAsync();
|
||||
Task<byte[]> GetPublicKeyAsync();
|
||||
Task<bool> CompareAndUpdateKeyHashAsync(string masterPassword, SymmetricCryptoKey key);
|
||||
Task<bool> HasEncKeyAsync();
|
||||
Task<string> HashPasswordAsync(string password, SymmetricCryptoKey key, HashPurpose hashPurpose = HashPurpose.ServerAuthorization);
|
||||
Task<bool> HasKeyAsync(string userId = null);
|
||||
Task<Tuple<SymmetricCryptoKey, EncString>> MakeEncKeyAsync(SymmetricCryptoKey key);
|
||||
Task<SymmetricCryptoKey> MakeKeyAsync(string password, string salt, KdfConfig config);
|
||||
Task<SymmetricCryptoKey> MakeKeyFromPinAsync(string pin, string salt, KdfConfig config, EncString protectedKeyEs = null);
|
||||
Task<Tuple<string, EncString>> MakeKeyPairAsync(SymmetricCryptoKey key = null);
|
||||
Task<SymmetricCryptoKey> MakePinKeyAysnc(string pin, string salt, KdfConfig config);
|
||||
Task<Tuple<EncString, SymmetricCryptoKey>> MakeShareKeyAsync();
|
||||
Task<SymmetricCryptoKey> MakeSendKeyAsync(byte[] keyMaterial);
|
||||
Task<int> RandomNumberAsync(int min, int max);
|
||||
Task<string> RandomStringAsync(int length);
|
||||
Task<Tuple<SymmetricCryptoKey, EncString>> RemakeEncKeyAsync(SymmetricCryptoKey key);
|
||||
Task<EncString> RsaEncryptAsync(byte[] data, byte[] publicKey = null);
|
||||
Task<byte[]> RsaDecryptAsync(string encValue, byte[] privateKey = null);
|
||||
Task SetEncKeyAsync(string encKey);
|
||||
Task SetEncPrivateKeyAsync(string encPrivateKey);
|
||||
Task SetKeyAsync(SymmetricCryptoKey key);
|
||||
Task SetKeyHashAsync(string keyHash);
|
||||
Task SetOrgKeysAsync(IEnumerable<ProfileOrganizationResponse> orgs);
|
||||
Task ToggleKeyAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Models.Domain;
|
||||
|
||||
namespace Bit.Core.Abstractions
|
||||
{
|
||||
public interface IDeviceTrustCryptoService
|
||||
{
|
||||
Task<SymmetricCryptoKey> GetDeviceKeyAsync();
|
||||
Task<DeviceResponse> TrustDeviceAsync();
|
||||
Task<DeviceResponse> TrustDeviceIfNeededAsync();
|
||||
Task<bool> GetShouldTrustDeviceAsync();
|
||||
Task SetShouldTrustDeviceAsync(bool value);
|
||||
Task<UserKey> DecryptUserKeyWithDeviceKeyAsync(string encryptedDevicePrivateKey, string encryptedUserKey);
|
||||
Task<bool> IsDeviceTrustedAsync();
|
||||
}
|
||||
}
|
||||
@@ -13,12 +13,6 @@ namespace Bit.Core.Abstractions
|
||||
public interface IStateService
|
||||
{
|
||||
List<AccountView> AccountViews { get; }
|
||||
Task<UserKey> GetUserKeyAsync(string userId = null);
|
||||
Task SetUserKeyAsync(UserKey value, string userId = null);
|
||||
Task<MasterKey> GetMasterKeyAsync(string userId = null);
|
||||
Task SetMasterKeyAsync(MasterKey value, string userId = null);
|
||||
Task<string> GetUserKeyMasterKeyAsync(string userId = null);
|
||||
Task SetUserKeyMasterKeyAsync(string value, string userId = null);
|
||||
Task<string> GetActiveUserIdAsync();
|
||||
Task<string> GetActiveUserEmailAsync();
|
||||
Task<T> GetActiveUserCustomDataAsync<T>(Func<Account, T> dataMapper);
|
||||
@@ -42,30 +36,26 @@ namespace Bit.Core.Abstractions
|
||||
Task<bool> IsAccountBiometricIntegrityValidAsync(string bioIntegritySrcKey, string userId = null);
|
||||
Task SetAccountBiometricIntegrityValidAsync(string bioIntegritySrcKey, string userId = null);
|
||||
Task<bool> CanAccessPremiumAsync(string userId = null);
|
||||
Task<string> GetProtectedPinAsync(string userId = null);
|
||||
Task SetPersonalPremiumAsync(bool value, string userId = null);
|
||||
Task<EncString> GetUserKeyPinAsync(string userId = null);
|
||||
Task SetUserKeyPinAsync(EncString value, string userId = null);
|
||||
Task<EncString> GetUserKeyPinEphemeralAsync(string userId = null);
|
||||
Task SetUserKeyPinEphemeralAsync(EncString value, string userId = null);
|
||||
Task<string> GetProtectedPinAsync(string userId = null);
|
||||
Task SetProtectedPinAsync(string value, string userId = null);
|
||||
[Obsolete("Use GetUserKeyPinAsync instead, left for migration purposes")]
|
||||
Task<string> GetPinProtectedAsync(string userId = null);
|
||||
[Obsolete("Use SetUserKeyPinAsync instead")]
|
||||
Task SetPinProtectedAsync(string value, string userId = null);
|
||||
[Obsolete("Use GetUserKeyPinEphemeralAsync instead, left for migration purposes")]
|
||||
Task<EncString> GetPinProtectedKeyAsync(string userId = null);
|
||||
[Obsolete("Use SetUserKeyPinEphemeralAsync instead")]
|
||||
Task SetPinProtectedKeyAsync(EncString value, string userId = null);
|
||||
Task SetKdfConfigurationAsync(KdfConfig config, string userId = null);
|
||||
Task<string> GetKeyEncryptedAsync(string userId = null);
|
||||
Task SetKeyEncryptedAsync(string value, string userId = null);
|
||||
Task<SymmetricCryptoKey> GetKeyDecryptedAsync(string userId = null);
|
||||
Task SetKeyDecryptedAsync(SymmetricCryptoKey value, string userId = null);
|
||||
Task<string> GetKeyHashAsync(string userId = null);
|
||||
Task SetKeyHashAsync(string value, string userId = null);
|
||||
Task<string> GetEncKeyEncryptedAsync(string userId = null);
|
||||
Task SetEncKeyEncryptedAsync(string value, string userId = null);
|
||||
Task<Dictionary<string, string>> GetOrgKeysEncryptedAsync(string userId = null);
|
||||
Task SetOrgKeysEncryptedAsync(Dictionary<string, string> value, string userId = null);
|
||||
Task<string> GetPrivateKeyEncryptedAsync(string userId = null);
|
||||
Task SetPrivateKeyEncryptedAsync(string value, string userId = null);
|
||||
Task<SymmetricCryptoKey> GetDeviceKeyAsync(string userId = null);
|
||||
Task SetDeviceKeyAsync(SymmetricCryptoKey value, string userId = null);
|
||||
Task<List<string>> GetAutofillBlacklistedUrisAsync(string userId = null);
|
||||
Task SetAutofillBlacklistedUrisAsync(List<string> value, string userId = null);
|
||||
Task<bool?> GetAutofillTileAddedAsync();
|
||||
@@ -182,26 +172,9 @@ namespace Bit.Core.Abstractions
|
||||
Task<string> GetAvatarColorAsync(string userId = null);
|
||||
Task<string> GetPreLoginEmailAsync();
|
||||
Task SetPreLoginEmailAsync(string value);
|
||||
Task<AccountDecryptionOptions> GetAccountDecryptionOptions(string userId = null);
|
||||
Task<PendingAdminAuthRequest> GetPendingAdminAuthRequestAsync(string userId = null);
|
||||
Task SetPendingAdminAuthRequestAsync(PendingAdminAuthRequest value, string userId = null);
|
||||
string GetLocale();
|
||||
void SetLocale(string locale);
|
||||
ConfigResponse GetConfigs();
|
||||
void SetConfigs(ConfigResponse value);
|
||||
Task<bool> GetShouldTrustDeviceAsync();
|
||||
Task SetShouldTrustDeviceAsync(bool value);
|
||||
[Obsolete("Use GetUserKeyMasterKey instead")]
|
||||
Task<string> GetEncKeyEncryptedAsync(string userId = null);
|
||||
[Obsolete("Use SetUserKeyMasterKey instead")]
|
||||
Task SetEncKeyEncryptedAsync(string value, string userId = null);
|
||||
[Obsolete]
|
||||
Task<string> GetKeyEncryptedAsync(string userId = null);
|
||||
[Obsolete]
|
||||
Task SetKeyEncryptedAsync(string value, string userId = null);
|
||||
[Obsolete("Use GetMasterKey instead")]
|
||||
Task<SymmetricCryptoKey> GetKeyDecryptedAsync(string userId = null);
|
||||
[Obsolete("Use GetMasterKey instead")]
|
||||
Task SetKeyDecryptedAsync(SymmetricCryptoKey value, string userId = null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Services;
|
||||
|
||||
namespace Bit.Core.Abstractions
|
||||
{
|
||||
@@ -17,7 +16,7 @@ namespace Bit.Core.Abstractions
|
||||
Task<bool> ShouldLockAsync(string userId = null);
|
||||
Task<bool> IsLoggedOutByTimeoutAsync(string userId = null);
|
||||
Task<bool> ShouldLogOutByTimeoutAsync(string userId = null);
|
||||
Task<PinLockEnum> IsPinLockSetAsync(string userId = null);
|
||||
Task<Tuple<bool, bool>> IsPinLockSetAsync(string userId = null);
|
||||
Task<bool> IsBiometricLockSetAsync(string userId = null);
|
||||
Task LockAsync(bool allowSoftLock = false, bool userInitiated = false, string userId = null);
|
||||
Task LogOutAsync(bool userInitiated = true, string userId = null);
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System;
|
||||
|
||||
namespace Bit.Core
|
||||
namespace Bit.Core
|
||||
{
|
||||
public static class Constants
|
||||
{
|
||||
@@ -55,7 +53,6 @@ namespace Bit.Core
|
||||
public const string AppLocaleKey = "appLocale";
|
||||
public const string ClearSensitiveFields = "clearSensitiveFields";
|
||||
public const string ForceUpdatePassword = "forceUpdatePassword";
|
||||
public const string ShouldTrustDevice = "shouldTrustDevice";
|
||||
public const int SelectFileRequestCode = 42;
|
||||
public const int SelectFilePermissionRequestCode = 43;
|
||||
public const int SaveFileRequestCode = 44;
|
||||
@@ -82,7 +79,6 @@ namespace Bit.Core
|
||||
|
||||
public static string VaultTimeoutKey(string userId) => $"vaultTimeout_{userId}";
|
||||
public static string VaultTimeoutActionKey(string userId) => $"vaultTimeoutAction_{userId}";
|
||||
public static string UserKeyKey(string userId) => $"userKey_{userId}";
|
||||
public static string CiphersKey(string userId) => $"ciphers_{userId}";
|
||||
public static string FoldersKey(string userId) => $"folders_{userId}";
|
||||
public static string CollectionsKey(string userId) => $"collections_{userId}";
|
||||
@@ -91,11 +87,12 @@ namespace Bit.Core
|
||||
public static string NeverDomainsKey(string userId) => $"neverDomains_{userId}";
|
||||
public static string SendsKey(string userId) => $"sends_{userId}";
|
||||
public static string PoliciesKey(string userId) => $"policies_{userId}";
|
||||
public static string KeyKey(string userId) => $"key_{userId}";
|
||||
public static string EncOrgKeysKey(string userId) => $"encOrgKeys_{userId}";
|
||||
public static string EncPrivateKeyKey(string userId) => $"encPrivateKey_{userId}";
|
||||
public static string DeviceKeyKey(string userId) => $"deviceKey_{userId}";
|
||||
public static string EncKeyKey(string userId) => $"encKey_{userId}";
|
||||
public static string KeyHashKey(string userId) => $"keyHash_{userId}";
|
||||
public static string UserKeyPinKey(string userId) => $"userKeyPin_{userId}";
|
||||
public static string PinProtectedKey(string userId) => $"pinProtectedKey_{userId}";
|
||||
public static string PassGenOptionsKey(string userId) => $"passwordGenerationOptions_{userId}";
|
||||
public static string PassGenHistoryKey(string userId) => $"generatedPasswordHistory_{userId}";
|
||||
public static string TwoFactorTokenKey(string email) => $"twoFactorToken_{email}";
|
||||
@@ -124,12 +121,5 @@ namespace Bit.Core
|
||||
public static string PushCurrentTokenKey(string userId) => $"pushCurrentToken_{userId}";
|
||||
public static string ShouldConnectToWatchKey(string userId) => $"shouldConnectToWatch_{userId}";
|
||||
public static string ScreenCaptureAllowedKey(string userId) => $"screenCaptureAllowed_{userId}";
|
||||
public static string PendingAdminAuthRequest(string userId) => $"pendingAdminAuthRequest_{userId}";
|
||||
[Obsolete]
|
||||
public static string KeyKey(string userId) => $"key_{userId}";
|
||||
[Obsolete]
|
||||
public static string EncKeyKey(string userId) => $"encKey_{userId}";
|
||||
[Obsolete]
|
||||
public static string PinProtectedKey(string userId) => $"pinProtectedKey_{userId}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
using System;
|
||||
namespace Bit.Core.Enums
|
||||
{
|
||||
public enum AuthRequestType : byte
|
||||
{
|
||||
AuthenticateAndUnlock = 0,
|
||||
Unlock = 1,
|
||||
AdminApproval = 2
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Bit.Core.Enums
|
||||
namespace Bit.Core.Enums
|
||||
{
|
||||
public enum DeviceType : byte
|
||||
{
|
||||
@@ -27,24 +24,4 @@ namespace Bit.Core.Enums
|
||||
VivaldiExtension = 19,
|
||||
SafariExtension = 20
|
||||
}
|
||||
|
||||
public static class DeviceTypeExtensions
|
||||
{
|
||||
public static List<DeviceType> GetMobileTypes() => new List<DeviceType>
|
||||
{
|
||||
DeviceType.Android,
|
||||
DeviceType.AndroidAmazon,
|
||||
DeviceType.iOS
|
||||
};
|
||||
|
||||
public static List<DeviceType> GetDesktopTypes() => new List<DeviceType>
|
||||
{
|
||||
DeviceType.WindowsDesktop,
|
||||
DeviceType.MacOsDesktop,
|
||||
DeviceType.LinuxDesktop,
|
||||
DeviceType.UWP,
|
||||
};
|
||||
|
||||
public static List<DeviceType> GetDesktopAndMobileTypes() => GetMobileTypes().Concat(GetDesktopTypes()).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,6 @@ namespace Bit.Core.Models.Domain
|
||||
HasPremiumPersonally = copy.HasPremiumPersonally;
|
||||
AvatarColor = copy.AvatarColor;
|
||||
ForcePasswordResetReason = copy.ForcePasswordResetReason;
|
||||
UserDecryptionOptions = copy.UserDecryptionOptions;
|
||||
}
|
||||
|
||||
public string UserId;
|
||||
@@ -69,7 +68,6 @@ namespace Bit.Core.Models.Domain
|
||||
public bool? EmailVerified;
|
||||
public bool? HasPremiumPersonally;
|
||||
public ForcePasswordResetReason? ForcePasswordResetReason;
|
||||
public AccountDecryptionOptions UserDecryptionOptions;
|
||||
}
|
||||
|
||||
public class AccountTokens
|
||||
@@ -119,14 +117,9 @@ namespace Bit.Core.Models.Domain
|
||||
|
||||
public class AccountVolatileData
|
||||
{
|
||||
public UserKey UserKey;
|
||||
public MasterKey MasterKey;
|
||||
public EncString UserKeyPinEphemeral;
|
||||
public bool? BiometricLocked;
|
||||
[Obsolete("Jul 6 2023: Key has been deprecated. We will use the User Key in the future. It remains here for migration during app upgrade.")]
|
||||
public SymmetricCryptoKey Key;
|
||||
[Obsolete("Jul 6 2023: PinProtectedKey has been deprecated in favor of UserKeyPinEphemeral. It remains here for migration during app upgrade.")]
|
||||
public EncString PinProtectedKey;
|
||||
public bool? BiometricLocked;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
using System;
|
||||
namespace Bit.Core.Models.Domain
|
||||
{
|
||||
public class AccountDecryptionOptions
|
||||
{
|
||||
public bool HasMasterPassword { get; set; }
|
||||
public TrustedDeviceOption TrustedDeviceOption { get; set; }
|
||||
public KeyConnectorOption KeyConnectorOption { get; set; }
|
||||
}
|
||||
|
||||
public class TrustedDeviceOption
|
||||
{
|
||||
public bool HasAdminApproval { get; set; }
|
||||
public bool HasLoginApprovingDevice { get; set; }
|
||||
public bool HasManageResetPasswordPermission { get; set; }
|
||||
public string EncryptedPrivateKey { get; set; }
|
||||
public string EncryptedUserKey { get; set; }
|
||||
}
|
||||
|
||||
public class KeyConnectorOption
|
||||
{
|
||||
public string KeyConnectorUrl { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
using System;
|
||||
namespace Bit.Core.Models.Domain
|
||||
{
|
||||
public class PendingAdminAuthRequest
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public byte[] PrivateKey { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,32 +74,4 @@ namespace Bit.Core.Models.Domain
|
||||
public string EncKeyB64 { get; set; }
|
||||
public string MacKeyB64 { get; set; }
|
||||
}
|
||||
|
||||
public class UserKey : SymmetricCryptoKey
|
||||
{
|
||||
public UserKey(byte[] key, EncryptionType? encType = null)
|
||||
: base(key, encType)
|
||||
{ }
|
||||
}
|
||||
|
||||
public class MasterKey : SymmetricCryptoKey
|
||||
{
|
||||
public MasterKey(byte[] key, EncryptionType? encType = null)
|
||||
: base(key, encType)
|
||||
{ }
|
||||
}
|
||||
|
||||
public class PinKey : SymmetricCryptoKey
|
||||
{
|
||||
public PinKey(byte[] key, EncryptionType? encType = null)
|
||||
: base(key, encType)
|
||||
{ }
|
||||
}
|
||||
|
||||
public class OrgKey : SymmetricCryptoKey
|
||||
{
|
||||
public OrgKey(byte[] key, EncryptionType? encType = null)
|
||||
: base(key, encType)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System;
|
||||
using Bit.Core.Enums;
|
||||
|
||||
namespace Bit.Core.Models.Request
|
||||
{
|
||||
public class PasswordlessCreateLoginRequest
|
||||
@@ -27,4 +25,10 @@ namespace Bit.Core.Models.Request
|
||||
|
||||
public string FingerprintPhrase { get; set; }
|
||||
}
|
||||
|
||||
public enum AuthRequestType : byte
|
||||
{
|
||||
AuthenticateAndUnlock = 0,
|
||||
Unlock = 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
|
||||
namespace Bit.Core.Models.Request
|
||||
{
|
||||
public class TrustedDeviceKeysRequest
|
||||
{
|
||||
public string EncryptedUserKey { get; set; }
|
||||
public string EncryptedPublicKey { get; set; }
|
||||
public string EncryptedPrivateKey { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
using Bit.Core.Enums;
|
||||
|
||||
public class DeviceResponse
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Identifier { get; set; }
|
||||
public DeviceType Type { get; set; }
|
||||
public string CreationDate { get; set; }
|
||||
public string EncryptedUserKey { get; set; }
|
||||
public string EncryptedPublicKey { get; set; }
|
||||
public string EncryptedPrivateKey { get; set; }
|
||||
}
|
||||
@@ -27,7 +27,6 @@ namespace Bit.Core.Models.Response
|
||||
public bool ForcePasswordReset { get; set; }
|
||||
public string KeyConnectorUrl { get; set; }
|
||||
public MasterPasswordPolicyOptions MasterPasswordPolicy { get; set; }
|
||||
public AccountDecryptionOptions UserDecryptionOptions { get; set; }
|
||||
[JsonIgnore]
|
||||
public KdfConfig KdfConfig => new KdfConfig(Kdf, KdfIterations, KdfMemory, KdfParallelism);
|
||||
}
|
||||
|
||||
@@ -397,38 +397,12 @@ namespace Bit.Core.Services
|
||||
|
||||
#region Device APIs
|
||||
|
||||
|
||||
public Task<bool> GetKnownDeviceAsync(string email, string deviceIdentifier)
|
||||
{
|
||||
return SendAsync<object, bool>(HttpMethod.Get, "/devices/knowndevice", null, false, true, (message) =>
|
||||
{
|
||||
message.Headers.Add("X-Device-Identifier", deviceIdentifier);
|
||||
message.Headers.Add("X-Request-Email", CoreHelpers.Base64UrlEncode(Encoding.UTF8.GetBytes(email)));
|
||||
});
|
||||
}
|
||||
|
||||
public Task PutDeviceTokenAsync(string identifier, DeviceTokenRequest request)
|
||||
{
|
||||
return SendAsync<DeviceTokenRequest, object>(
|
||||
HttpMethod.Put, $"/devices/identifier/{identifier}/token", request, true, false);
|
||||
}
|
||||
|
||||
public Task<bool> GetDevicesExistenceByTypes(DeviceType[] deviceTypes)
|
||||
{
|
||||
return SendAsync<DeviceType[], bool>(
|
||||
HttpMethod.Post, "/devices/exist-by-types", deviceTypes, true, true);
|
||||
}
|
||||
|
||||
public Task<DeviceResponse> GetDeviceByIdentifierAsync(string deviceIdentifier)
|
||||
{
|
||||
return SendAsync<object, DeviceResponse>(HttpMethod.Get, $"/devices/identifier/{deviceIdentifier}", null, true, true);
|
||||
}
|
||||
|
||||
public Task<DeviceResponse> UpdateTrustedDeviceKeysAsync(string deviceIdentifier, TrustedDeviceKeysRequest trustedDeviceKeysRequest)
|
||||
{
|
||||
return SendAsync<TrustedDeviceKeysRequest, DeviceResponse>(HttpMethod.Put, $"/devices/{deviceIdentifier}/keys", trustedDeviceKeysRequest, true, true);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event APIs
|
||||
@@ -511,7 +485,7 @@ namespace Bit.Core.Services
|
||||
|
||||
#region Key Connector
|
||||
|
||||
public async Task<KeyConnectorUserKeyResponse> GetMasterKeyFromKeyConnector(string keyConnectorUrl)
|
||||
public async Task<KeyConnectorUserKeyResponse> GetUserKeyFromKeyConnector(string keyConnectorUrl)
|
||||
{
|
||||
using (var requestMessage = new HttpRequestMessage())
|
||||
{
|
||||
@@ -591,9 +565,9 @@ namespace Bit.Core.Services
|
||||
return SendAsync<object, PasswordlessLoginResponse>(HttpMethod.Get, $"/auth-requests/{id}/response?code={accessCode}", null, false, true);
|
||||
}
|
||||
|
||||
public Task<PasswordlessLoginResponse> PostCreateRequestAsync(PasswordlessCreateLoginRequest passwordlessCreateLoginRequest, AuthRequestType authRequestType)
|
||||
public Task<PasswordlessLoginResponse> PostCreateRequestAsync(PasswordlessCreateLoginRequest passwordlessCreateLoginRequest)
|
||||
{
|
||||
return SendAsync<object, PasswordlessLoginResponse>(HttpMethod.Post, authRequestType == AuthRequestType.AdminApproval ? "/auth-requests/admin-request" : "/auth-requests", passwordlessCreateLoginRequest, authRequestType == AuthRequestType.AdminApproval, true);
|
||||
return SendAsync<object, PasswordlessLoginResponse>(HttpMethod.Post, $"/auth-requests", passwordlessCreateLoginRequest, false, true);
|
||||
}
|
||||
|
||||
public Task<PasswordlessLoginResponse> PutAuthRequestAsync(string id, string encKey, string encMasterPasswordHash, string deviceIdentifier, bool requestApproved)
|
||||
@@ -602,6 +576,15 @@ namespace Bit.Core.Services
|
||||
return SendAsync<object, PasswordlessLoginResponse>(HttpMethod.Put, $"/auth-requests/{id}", request, true, true);
|
||||
}
|
||||
|
||||
public Task<bool> GetKnownDeviceAsync(string email, string deviceIdentifier)
|
||||
{
|
||||
return SendAsync<object, bool>(HttpMethod.Get, "/devices/knowndevice", null, false, true, (message) =>
|
||||
{
|
||||
message.Headers.Add("X-Device-Identifier", deviceIdentifier);
|
||||
message.Headers.Add("X-Request-Email", CoreHelpers.Base64UrlEncode(Encoding.UTF8.GetBytes(email)));
|
||||
});
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Configs
|
||||
|
||||
@@ -27,11 +27,10 @@ namespace Bit.Core.Services
|
||||
private readonly IKeyConnectorService _keyConnectorService;
|
||||
private readonly IPasswordGenerationService _passwordGenerationService;
|
||||
private readonly IPolicyService _policyService;
|
||||
private readonly IDeviceTrustCryptoService _deviceTrustCryptoService;
|
||||
private readonly bool _setCryptoKeys;
|
||||
|
||||
private readonly LazyResolve<IWatchDeviceService> _watchDeviceService = new LazyResolve<IWatchDeviceService>();
|
||||
private MasterKey _masterKey;
|
||||
private SymmetricCryptoKey _key;
|
||||
|
||||
private string _authedUserId;
|
||||
private MasterPasswordPolicyOptions _masterPasswordPolicy;
|
||||
@@ -51,7 +50,6 @@ namespace Bit.Core.Services
|
||||
IKeyConnectorService keyConnectorService,
|
||||
IPasswordGenerationService passwordGenerationService,
|
||||
IPolicyService policyService,
|
||||
IDeviceTrustCryptoService deviceTrustCryptoService,
|
||||
bool setCryptoKeys = true)
|
||||
{
|
||||
_cryptoService = cryptoService;
|
||||
@@ -66,7 +64,6 @@ namespace Bit.Core.Services
|
||||
_keyConnectorService = keyConnectorService;
|
||||
_passwordGenerationService = passwordGenerationService;
|
||||
_policyService = policyService;
|
||||
_deviceTrustCryptoService = deviceTrustCryptoService;
|
||||
_setCryptoKeys = setCryptoKeys;
|
||||
|
||||
TwoFactorProviders = new Dictionary<TwoFactorProviderType, TwoFactorProvider>();
|
||||
@@ -198,34 +195,11 @@ namespace Bit.Core.Services
|
||||
return !await _policyService.EvaluateMasterPassword(strength.Value, masterPassword, _masterPasswordPolicy);
|
||||
}
|
||||
|
||||
public async Task<AuthResult> LogInPasswordlessAsync(string email, string accessCode, string authRequestId, byte[] decryptionKey, string encryptedAuthRequestKey, string masterKeyHash)
|
||||
public async Task<AuthResult> LogInPasswordlessAsync(string email, string accessCode, string authRequestId, byte[] decryptionKey, string userKeyCiphered, string localHashedPasswordCiphered)
|
||||
{
|
||||
var decryptedKey = await _cryptoService.RsaDecryptAsync(encryptedAuthRequestKey, decryptionKey);
|
||||
|
||||
// On SSO flow user is already AuthN
|
||||
if (await _stateService.IsAuthenticatedAsync())
|
||||
{
|
||||
if (string.IsNullOrEmpty(masterKeyHash))
|
||||
{
|
||||
await _cryptoService.SetUserKeyAsync(new UserKey(decryptedKey));
|
||||
}
|
||||
else
|
||||
{
|
||||
var userKey = await _cryptoService.DecryptUserKeyWithMasterKeyAsync(new MasterKey(decryptedKey));
|
||||
await _cryptoService.SetUserKeyAsync(userKey);
|
||||
}
|
||||
await _deviceTrustCryptoService.TrustDeviceIfNeededAsync();
|
||||
return null;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(masterKeyHash) && decryptionKey != null)
|
||||
{
|
||||
await _cryptoService.SetUserKeyAsync(new UserKey(decryptedKey));
|
||||
return null;
|
||||
}
|
||||
|
||||
var decKeyHash = await _cryptoService.RsaDecryptAsync(masterKeyHash, decryptionKey);
|
||||
return await LogInHelperAsync(email, accessCode, Encoding.UTF8.GetString(decKeyHash), null, null, null, new MasterKey(decryptedKey), null, null,
|
||||
var decKey = await _cryptoService.RsaDecryptAsync(userKeyCiphered, decryptionKey);
|
||||
var decPasswordHash = await _cryptoService.RsaDecryptAsync(localHashedPasswordCiphered, decryptionKey);
|
||||
return await LogInHelperAsync(email, accessCode, Encoding.UTF8.GetString(decPasswordHash), null, null, null, new SymmetricCryptoKey(decKey), null, null,
|
||||
null, null, authRequestId: authRequestId);
|
||||
}
|
||||
|
||||
@@ -242,7 +216,7 @@ namespace Bit.Core.Services
|
||||
{
|
||||
CaptchaToken = captchaToken;
|
||||
}
|
||||
var result = await LogInHelperAsync(Email, MasterPasswordHash, LocalMasterPasswordHash, Code, CodeVerifier, SsoRedirectUrl, _masterKey,
|
||||
var result = await LogInHelperAsync(Email, MasterPasswordHash, LocalMasterPasswordHash, Code, CodeVerifier, SsoRedirectUrl, _key,
|
||||
twoFactorProvider, twoFactorToken, remember, CaptchaToken, authRequestId: AuthRequestId);
|
||||
|
||||
// If we successfully authenticated and we have a saved _2faForcePasswordResetReason reason from LogInAsync()
|
||||
@@ -363,7 +337,7 @@ namespace Bit.Core.Services
|
||||
|
||||
// Helpers
|
||||
|
||||
private async Task<MasterKey> MakePreloginKeyAsync(string masterPassword, string email)
|
||||
private async Task<SymmetricCryptoKey> MakePreloginKeyAsync(string masterPassword, string email)
|
||||
{
|
||||
email = email.Trim().ToLower();
|
||||
KdfConfig kdfConfig = KdfConfig.Default;
|
||||
@@ -382,11 +356,11 @@ namespace Bit.Core.Services
|
||||
throw;
|
||||
}
|
||||
}
|
||||
return await _cryptoService.MakeMasterKeyAsync(masterPassword, email, kdfConfig);
|
||||
return await _cryptoService.MakeKeyAsync(masterPassword, email, kdfConfig);
|
||||
}
|
||||
|
||||
private async Task<AuthResult> LogInHelperAsync(string email, string hashedPassword, string localHashedPassword,
|
||||
string code, string codeVerifier, string redirectUrl, MasterKey masterKey,
|
||||
string code, string codeVerifier, string redirectUrl, SymmetricCryptoKey key,
|
||||
TwoFactorProviderType? twoFactorProvider = null, string twoFactorToken = null, bool? remember = null,
|
||||
string captchaToken = null, string orgId = null, string authRequestId = null)
|
||||
{
|
||||
@@ -452,7 +426,7 @@ namespace Bit.Core.Services
|
||||
Code = code;
|
||||
CodeVerifier = codeVerifier;
|
||||
SsoRedirectUrl = redirectUrl;
|
||||
_masterKey = _setCryptoKeys ? masterKey : null;
|
||||
_key = _setCryptoKeys ? key : null;
|
||||
TwoFactorProvidersData = response.TwoFactorResponse.TwoFactorProviders2;
|
||||
result.TwoFactorProviders = response.TwoFactorResponse.TwoFactorProviders2;
|
||||
CaptchaToken = response.TwoFactorResponse.CaptchaToken;
|
||||
@@ -485,7 +459,6 @@ namespace Bit.Core.Services
|
||||
ForcePasswordResetReason = result.ForcePasswordReset
|
||||
? ForcePasswordResetReason.AdminForcePasswordReset
|
||||
: (ForcePasswordResetReason?)null,
|
||||
UserDecryptionOptions = tokenResponse.UserDecryptionOptions,
|
||||
},
|
||||
new Account.AccountTokens()
|
||||
{
|
||||
@@ -497,46 +470,24 @@ namespace Bit.Core.Services
|
||||
_messagingService.Send("accountAdded");
|
||||
if (_setCryptoKeys)
|
||||
{
|
||||
if (key != null)
|
||||
{
|
||||
await _cryptoService.SetKeyAsync(key);
|
||||
}
|
||||
|
||||
if (localHashedPassword != null)
|
||||
{
|
||||
await _cryptoService.SetPasswordHashAsync(localHashedPassword);
|
||||
await _cryptoService.SetMasterKeyAsync(masterKey);
|
||||
var userKey = await _cryptoService.DecryptUserKeyWithMasterKeyAsync(masterKey);
|
||||
await _cryptoService.SetUserKeyAsync(userKey);
|
||||
await _cryptoService.SetKeyHashAsync(localHashedPassword);
|
||||
}
|
||||
|
||||
if (code == null || tokenResponse.Key != null)
|
||||
{
|
||||
var decryptOptions = await _stateService.GetAccountDecryptionOptions();
|
||||
await _cryptoService.SetMasterKeyEncryptedUserKeyAsync(tokenResponse.Key);
|
||||
|
||||
if (decryptOptions?.TrustedDeviceOption != null)
|
||||
if (tokenResponse.KeyConnectorUrl != null)
|
||||
{
|
||||
var key = await _deviceTrustCryptoService.DecryptUserKeyWithDeviceKeyAsync(decryptOptions.TrustedDeviceOption.EncryptedPrivateKey, decryptOptions.TrustedDeviceOption.EncryptedUserKey);
|
||||
if (key != null)
|
||||
{
|
||||
await _cryptoService.SetUserKeyAsync(key);
|
||||
}
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(tokenResponse.KeyConnectorUrl) || !string.IsNullOrEmpty(decryptOptions?.KeyConnectorOption?.KeyConnectorUrl))
|
||||
{
|
||||
|
||||
await _cryptoService.SetMasterKeyEncryptedUserKeyAsync(tokenResponse.Key);
|
||||
if (masterKey != null)
|
||||
{
|
||||
await _cryptoService.SetMasterKeyAsync(masterKey);
|
||||
var userKey = await _cryptoService.DecryptUserKeyWithMasterKeyAsync(masterKey);
|
||||
await _cryptoService.SetUserKeyAsync(userKey);
|
||||
}
|
||||
await _keyConnectorService.GetAndSetKey(tokenResponse.KeyConnectorUrl);
|
||||
}
|
||||
|
||||
// Login with Device
|
||||
if (masterKey != null && !string.IsNullOrEmpty(authRequestId))
|
||||
{
|
||||
await _cryptoService.SetMasterKeyAsync(masterKey);
|
||||
var userKey = await _cryptoService.DecryptUserKeyWithMasterKeyAsync(masterKey);
|
||||
await _cryptoService.SetUserKeyAsync(userKey);
|
||||
}
|
||||
await _cryptoService.SetEncKeyAsync(tokenResponse.Key);
|
||||
|
||||
// User doesn't have a key pair yet (old account), let's generate one for them.
|
||||
if (tokenResponse.PrivateKey == null)
|
||||
@@ -554,22 +505,19 @@ namespace Bit.Core.Services
|
||||
catch { }
|
||||
}
|
||||
|
||||
await _cryptoService.SetPrivateKeyAsync(tokenResponse.PrivateKey);
|
||||
await _cryptoService.SetEncPrivateKeyAsync(tokenResponse.PrivateKey);
|
||||
}
|
||||
else if (tokenResponse.KeyConnectorUrl != null)
|
||||
{
|
||||
// SSO Key Connector Onboarding
|
||||
var password = await _cryptoFunctionService.RandomBytesAsync(64);
|
||||
var newMasterKey = await _cryptoService.MakeMasterKeyAsync(Convert.ToBase64String(password), _tokenService.GetEmail(), tokenResponse.KdfConfig);
|
||||
var keyConnectorRequest = new KeyConnectorUserKeyRequest(newMasterKey.EncKeyB64);
|
||||
await _cryptoService.SetMasterKeyAsync(newMasterKey);
|
||||
var k = await _cryptoService.MakeKeyAsync(Convert.ToBase64String(password), _tokenService.GetEmail(), tokenResponse.KdfConfig);
|
||||
var keyConnectorRequest = new KeyConnectorUserKeyRequest(k.EncKeyB64);
|
||||
await _cryptoService.SetKeyAsync(k);
|
||||
|
||||
var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(
|
||||
newMasterKey,
|
||||
await _cryptoService.MakeUserKeyAsync());
|
||||
|
||||
await _cryptoService.SetUserKeyAsync(newUserKey);
|
||||
var (newPublicKey, newProtectedPrivateKey) = await _cryptoService.MakeKeyPairAsync();
|
||||
var encKey = await _cryptoService.MakeEncKeyAsync(k);
|
||||
await _cryptoService.SetEncKeyAsync(encKey.Item2.EncryptedString);
|
||||
var keyPair = await _cryptoService.MakeKeyPairAsync();
|
||||
|
||||
try
|
||||
{
|
||||
@@ -582,14 +530,15 @@ namespace Bit.Core.Services
|
||||
|
||||
var keys = new KeysRequest
|
||||
{
|
||||
PublicKey = newPublicKey,
|
||||
EncryptedPrivateKey = newProtectedPrivateKey.EncryptedString
|
||||
PublicKey = keyPair.Item1,
|
||||
EncryptedPrivateKey = keyPair.Item2.EncryptedString
|
||||
};
|
||||
var setPasswordRequest = new SetKeyConnectorKeyRequest(
|
||||
newProtectedPrivateKey.EncryptedString, keys, tokenResponse.KdfConfig, orgId
|
||||
encKey.Item2.EncryptedString, keys, tokenResponse.KdfConfig, orgId
|
||||
);
|
||||
await _apiService.PostSetKeyConnectorKey(setPasswordRequest);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_authedUserId = _tokenService.GetUserId();
|
||||
@@ -600,7 +549,7 @@ namespace Bit.Core.Services
|
||||
|
||||
private void ClearState()
|
||||
{
|
||||
_masterKey = null;
|
||||
_key = null;
|
||||
Email = null;
|
||||
CaptchaToken = null;
|
||||
MasterPasswordHash = null;
|
||||
@@ -626,14 +575,14 @@ namespace Bit.Core.Services
|
||||
var activeRequests = requests.Where(r => !r.IsAnswered && !r.IsExpired).OrderByDescending(r => r.CreationDate).ToList();
|
||||
return await PopulateFingerprintPhrasesAsync(activeRequests);
|
||||
}
|
||||
|
||||
public async Task<PasswordlessLoginResponse> GetPasswordlessLoginRequestByIdAsync(string id)
|
||||
{
|
||||
var response = await _apiService.GetAuthRequestAsync(id);
|
||||
return await PopulateFingerprintPhraseAsync(response, await _stateService.GetEmailAsync());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<PasswordlessLoginResponse> GetPasswordlessLoginResquestAsync(string id, string accessCode)
|
||||
public async Task<PasswordlessLoginResponse> GetPasswordlessLoginResponseAsync(string id, string accessCode)
|
||||
{
|
||||
return await _apiService.GetAuthResponseAsync(id, accessCode);
|
||||
}
|
||||
@@ -641,20 +590,15 @@ namespace Bit.Core.Services
|
||||
public async Task<PasswordlessLoginResponse> PasswordlessLoginAsync(string id, string pubKey, bool requestApproved)
|
||||
{
|
||||
var publicKey = CoreHelpers.Base64UrlDecode(pubKey);
|
||||
var masterKey = await _cryptoService.GetMasterKeyAsync();
|
||||
var masterKey = await _cryptoService.GetKeyAsync();
|
||||
var encryptedKey = await _cryptoService.RsaEncryptAsync(masterKey.EncKey, publicKey);
|
||||
var keyHash = await _stateService.GetKeyHashAsync();
|
||||
EncString encryptedMasterPassword = null;
|
||||
if (!string.IsNullOrEmpty(keyHash))
|
||||
{
|
||||
encryptedMasterPassword = await _cryptoService.RsaEncryptAsync(Encoding.UTF8.GetBytes(keyHash), publicKey);
|
||||
}
|
||||
var encryptedMasterPassword = await _cryptoService.RsaEncryptAsync(Encoding.UTF8.GetBytes(await _stateService.GetKeyHashAsync()), publicKey);
|
||||
var deviceId = await _appIdService.GetAppIdAsync();
|
||||
var response = await _apiService.PutAuthRequestAsync(id, encryptedKey.EncryptedString, encryptedMasterPassword?.EncryptedString, deviceId, requestApproved);
|
||||
var response = await _apiService.PutAuthRequestAsync(id, encryptedKey.EncryptedString, encryptedMasterPassword.EncryptedString, deviceId, requestApproved);
|
||||
return await PopulateFingerprintPhraseAsync(response, await _stateService.GetEmailAsync());
|
||||
}
|
||||
|
||||
public async Task<PasswordlessLoginResponse> PasswordlessCreateLoginRequestAsync(string email, AuthRequestType authRequestType)
|
||||
public async Task<PasswordlessLoginResponse> PasswordlessCreateLoginRequestAsync(string email)
|
||||
{
|
||||
var deviceId = await _appIdService.GetAppIdAsync();
|
||||
var keyPair = await _cryptoFunctionService.RsaGenerateKeyPairAsync(2048);
|
||||
@@ -662,8 +606,8 @@ namespace Bit.Core.Services
|
||||
var fingerprintPhrase = string.Join("-", generatedFingerprintPhrase);
|
||||
var publicB64 = Convert.ToBase64String(keyPair.Item1);
|
||||
var accessCode = await _passwordGenerationService.GeneratePasswordAsync(PasswordGenerationOptions.CreateDefault.WithLength(25));
|
||||
var passwordlessCreateLoginRequest = new PasswordlessCreateLoginRequest(email, publicB64, deviceId, accessCode, authRequestType, fingerprintPhrase);
|
||||
var response = await _apiService.PostCreateRequestAsync(passwordlessCreateLoginRequest, authRequestType);
|
||||
var passwordlessCreateLoginRequest = new PasswordlessCreateLoginRequest(email, publicB64, deviceId, accessCode, AuthRequestType.AuthenticateAndUnlock, fingerprintPhrase);
|
||||
var response = await _apiService.PostCreateRequestAsync(passwordlessCreateLoginRequest);
|
||||
|
||||
if (response != null)
|
||||
{
|
||||
|
||||
@@ -249,7 +249,7 @@ namespace Bit.Core.Services
|
||||
{
|
||||
try
|
||||
{
|
||||
var hashKey = await _cryptoService.HasUserKeyAsync();
|
||||
var hashKey = await _cryptoService.HasKeyAsync();
|
||||
if (!hashKey)
|
||||
{
|
||||
throw new Exception("No key.");
|
||||
@@ -557,20 +557,9 @@ namespace Bit.Core.Services
|
||||
|
||||
public async Task<Cipher> SaveAttachmentRawWithServerAsync(Cipher cipher, string filename, byte[] data)
|
||||
{
|
||||
SymmetricCryptoKey attachmentKey;
|
||||
EncString protectedAttachmentKey;
|
||||
var orgKey = await _cryptoService.GetOrgKeyAsync(cipher.OrganizationId);
|
||||
if (orgKey != null)
|
||||
{
|
||||
(attachmentKey, protectedAttachmentKey) = await _cryptoService.MakeDataEncKeyAsync(orgKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
var userKey = await _cryptoService.GetUserKeyWithLegacySupportAsync();
|
||||
(attachmentKey, protectedAttachmentKey) = await _cryptoService.MakeDataEncKeyAsync(userKey);
|
||||
}
|
||||
|
||||
var encFileName = await _cryptoService.EncryptAsync(filename, orgKey);
|
||||
var (attachmentKey, orgEncAttachmentKey) = await _cryptoService.MakeEncKeyAsync(orgKey);
|
||||
var encFileData = await _cryptoService.EncryptToBytesAsync(data, attachmentKey);
|
||||
|
||||
CipherResponse response;
|
||||
@@ -578,7 +567,7 @@ namespace Bit.Core.Services
|
||||
{
|
||||
var request = new AttachmentRequest
|
||||
{
|
||||
Key = protectedAttachmentKey.EncryptedString,
|
||||
Key = orgEncAttachmentKey.EncryptedString,
|
||||
FileName = encFileName.EncryptedString,
|
||||
FileSize = encFileData.Buffer.Length,
|
||||
};
|
||||
@@ -589,7 +578,7 @@ namespace Bit.Core.Services
|
||||
}
|
||||
catch (ApiException e) when (e.Error.StatusCode == System.Net.HttpStatusCode.NotFound || e.Error.StatusCode == System.Net.HttpStatusCode.MethodNotAllowed)
|
||||
{
|
||||
response = await LegacyServerAttachmentFileUploadAsync(cipher.Id, encFileName, encFileData, protectedAttachmentKey);
|
||||
response = await LegacyServerAttachmentFileUploadAsync(cipher.Id, encFileName, encFileData, orgEncAttachmentKey);
|
||||
}
|
||||
|
||||
var userId = await _stateService.GetActiveUserIdAsync();
|
||||
@@ -818,27 +807,14 @@ namespace Bit.Core.Services
|
||||
|
||||
var bytes = await attachmentResponse.Content.ReadAsByteArrayAsync();
|
||||
var decBytes = await _cryptoService.DecryptFromBytesAsync(bytes, null);
|
||||
|
||||
SymmetricCryptoKey attachmentKey;
|
||||
EncString protectedAttachmentKey;
|
||||
var orgKey = await _cryptoService.GetOrgKeyAsync(organizationId);
|
||||
if (orgKey != null)
|
||||
{
|
||||
(attachmentKey, protectedAttachmentKey) = await _cryptoService.MakeDataEncKeyAsync(orgKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
var userKey = await _cryptoService.GetUserKeyWithLegacySupportAsync();
|
||||
(attachmentKey, protectedAttachmentKey) = await _cryptoService.MakeDataEncKeyAsync(userKey);
|
||||
}
|
||||
|
||||
var encFileName = await _cryptoService.EncryptAsync(attachmentView.FileName, orgKey);
|
||||
var encFileData = await _cryptoService.EncryptToBytesAsync(decBytes, attachmentKey);
|
||||
|
||||
var key = await _cryptoService.GetOrgKeyAsync(organizationId);
|
||||
var encFileName = await _cryptoService.EncryptAsync(attachmentView.FileName, key);
|
||||
var dataEncKey = await _cryptoService.MakeEncKeyAsync(key);
|
||||
var encData = await _cryptoService.EncryptToBytesAsync(decBytes, dataEncKey.Item1);
|
||||
var boundary = string.Concat("--BWMobileFormBoundary", DateTime.UtcNow.Ticks);
|
||||
var fd = new MultipartFormDataContent(boundary);
|
||||
fd.Add(new StringContent(protectedAttachmentKey.EncryptedString), "key");
|
||||
fd.Add(new StreamContent(new MemoryStream(encFileData.Buffer)), "data", encFileName.EncryptedString);
|
||||
fd.Add(new StringContent(dataEncKey.Item2.EncryptedString), "key");
|
||||
fd.Add(new StreamContent(new MemoryStream(encData.Buffer)), "data", encFileName.EncryptedString);
|
||||
await _apiService.PostShareCipherAttachmentAsync(cipherId, attachmentView.Id, fd, organizationId);
|
||||
}
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ namespace Bit.Core.Services
|
||||
{
|
||||
return _decryptedCollectionCache;
|
||||
}
|
||||
var hasKey = await _cryptoService.HasUserKeyAsync();
|
||||
var hasKey = await _cryptoService.HasKeyAsync();
|
||||
if (!hasKey)
|
||||
{
|
||||
throw new Exception("No key.");
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,129 +0,0 @@
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Models.Domain;
|
||||
using Bit.Core.Models.Request;
|
||||
|
||||
namespace Bit.Core.Services
|
||||
{
|
||||
public class DeviceTrustCryptoService : IDeviceTrustCryptoService
|
||||
{
|
||||
private readonly IApiService _apiService;
|
||||
private readonly IAppIdService _appIdService;
|
||||
private readonly ICryptoFunctionService _cryptoFunctionService;
|
||||
private readonly ICryptoService _cryptoService;
|
||||
private readonly IStateService _stateService;
|
||||
|
||||
private const int DEVICE_KEY_SIZE = 64;
|
||||
|
||||
public DeviceTrustCryptoService(
|
||||
IApiService apiService,
|
||||
IAppIdService appIdService,
|
||||
ICryptoFunctionService cryptoFunctionService,
|
||||
ICryptoService cryptoService,
|
||||
IStateService stateService)
|
||||
{
|
||||
_apiService = apiService;
|
||||
_appIdService = appIdService;
|
||||
_cryptoFunctionService = cryptoFunctionService;
|
||||
_cryptoService = cryptoService;
|
||||
_stateService = stateService;
|
||||
}
|
||||
|
||||
public async Task<SymmetricCryptoKey> GetDeviceKeyAsync()
|
||||
{
|
||||
return await _stateService.GetDeviceKeyAsync();
|
||||
}
|
||||
|
||||
private async Task SetDeviceKeyAsync(SymmetricCryptoKey deviceKey)
|
||||
{
|
||||
await _stateService.SetDeviceKeyAsync(deviceKey);
|
||||
}
|
||||
|
||||
public async Task<DeviceResponse> TrustDeviceAsync()
|
||||
{
|
||||
// Attempt to get user key
|
||||
var userKey = await _cryptoService.GetUserKeyAsync();
|
||||
if (userKey == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
// Generate deviceKey
|
||||
var deviceKey = await MakeDeviceKeyAsync();
|
||||
|
||||
// Generate asymmetric RSA key pair: devicePrivateKey, devicePublicKey
|
||||
var (devicePublicKey, devicePrivateKey) = await _cryptoFunctionService.RsaGenerateKeyPairAsync(2048);
|
||||
|
||||
// Send encrypted keys to server
|
||||
var deviceIdentifier = await _appIdService.GetAppIdAsync();
|
||||
var deviceRequest = new TrustedDeviceKeysRequest
|
||||
{
|
||||
EncryptedUserKey = (await _cryptoService.RsaEncryptAsync(userKey.Key, devicePublicKey)).EncryptedString,
|
||||
EncryptedPublicKey = (await _cryptoService.EncryptAsync(devicePublicKey, userKey)).EncryptedString,
|
||||
EncryptedPrivateKey = (await _cryptoService.EncryptAsync(devicePrivateKey, deviceKey)).EncryptedString,
|
||||
};
|
||||
|
||||
var deviceResponse = await _apiService.UpdateTrustedDeviceKeysAsync(deviceIdentifier, deviceRequest);
|
||||
|
||||
// Store device key if successful
|
||||
await SetDeviceKeyAsync(deviceKey);
|
||||
return deviceResponse;
|
||||
}
|
||||
|
||||
private async Task<SymmetricCryptoKey> MakeDeviceKeyAsync()
|
||||
{
|
||||
// Create 512-bit device key
|
||||
var randomBytes = await _cryptoFunctionService.RandomBytesAsync(DEVICE_KEY_SIZE);
|
||||
return new SymmetricCryptoKey(randomBytes);
|
||||
}
|
||||
|
||||
public async Task<bool> GetShouldTrustDeviceAsync()
|
||||
{
|
||||
return await _stateService.GetShouldTrustDeviceAsync();
|
||||
}
|
||||
|
||||
public async Task SetShouldTrustDeviceAsync(bool value)
|
||||
{
|
||||
await _stateService.SetShouldTrustDeviceAsync(value);
|
||||
}
|
||||
|
||||
public async Task<DeviceResponse> TrustDeviceIfNeededAsync()
|
||||
{
|
||||
if (!await GetShouldTrustDeviceAsync())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var response = await TrustDeviceAsync();
|
||||
await SetShouldTrustDeviceAsync(false);
|
||||
return response;
|
||||
}
|
||||
|
||||
public async Task<bool> IsDeviceTrustedAsync()
|
||||
{
|
||||
var existingDeviceKey = await GetDeviceKeyAsync();
|
||||
return existingDeviceKey != null;
|
||||
}
|
||||
|
||||
public async Task<UserKey> DecryptUserKeyWithDeviceKeyAsync(string encryptedDevicePrivateKey, string encryptedUserKey)
|
||||
{
|
||||
var existingDeviceKey = await GetDeviceKeyAsync();
|
||||
if (existingDeviceKey == null)
|
||||
{
|
||||
// User doesn't have a device key anymore so device is untrusted
|
||||
return null;
|
||||
}
|
||||
|
||||
// Attempt to decrypt encryptedDevicePrivateKey with device key
|
||||
var devicePrivateKeyBytes = await _cryptoService.DecryptToBytesAsync(
|
||||
new EncString(encryptedDevicePrivateKey),
|
||||
existingDeviceKey
|
||||
);
|
||||
|
||||
// Attempt to decrypt encryptedUserDataKey with devicePrivateKey
|
||||
var userKeyBytes = await _cryptoService.RsaDecryptAsync(encryptedUserKey, devicePrivateKeyBytes);
|
||||
return new UserKey(userKeyBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,7 +77,7 @@ namespace Bit.Core.Services
|
||||
{
|
||||
return _decryptedFolderCache;
|
||||
}
|
||||
var hasKey = await _cryptoService.HasUserKeyAsync();
|
||||
var hasKey = await _cryptoService.HasKeyAsync();
|
||||
if (!hasKey)
|
||||
{
|
||||
throw new Exception("No key.");
|
||||
|
||||
@@ -28,10 +28,10 @@ namespace Bit.Core.Services
|
||||
{
|
||||
try
|
||||
{
|
||||
var masterKeyResponse = await _apiService.GetMasterKeyFromKeyConnector(url);
|
||||
var masterKeyArr = Convert.FromBase64String(masterKeyResponse.Key);
|
||||
var masterKey = new MasterKey(masterKeyArr);
|
||||
await _cryptoService.SetMasterKeyAsync(masterKey);
|
||||
var userKeyResponse = await _apiService.GetUserKeyFromKeyConnector(url);
|
||||
var keyArr = Convert.FromBase64String(userKeyResponse.Key);
|
||||
var k = new SymmetricCryptoKey(keyArr);
|
||||
await _cryptoService.SetKeyAsync(k);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -60,11 +60,11 @@ namespace Bit.Core.Services
|
||||
public async Task MigrateUser()
|
||||
{
|
||||
var organization = await GetManagingOrganization();
|
||||
var masterKey = await _cryptoService.GetMasterKeyAsync();
|
||||
var key = await _cryptoService.GetKeyAsync();
|
||||
|
||||
try
|
||||
{
|
||||
var keyConnectorRequest = new KeyConnectorUserKeyRequest(masterKey.EncKeyB64);
|
||||
var keyConnectorRequest = new KeyConnectorUserKeyRequest(key.EncKeyB64);
|
||||
await _apiService.PostUserKeyToKeyConnector(organization.KeyConnectorUrl, keyConnectorRequest);
|
||||
}
|
||||
catch (Exception e)
|
||||
|
||||
@@ -247,7 +247,7 @@ namespace Bit.Core.Services
|
||||
|
||||
public async Task<List<GeneratedPasswordHistory>> GetHistoryAsync()
|
||||
{
|
||||
var hasKey = await _cryptoService.HasUserKeyAsync();
|
||||
var hasKey = await _cryptoService.HasKeyAsync();
|
||||
if (!hasKey)
|
||||
{
|
||||
return new List<GeneratedPasswordHistory>();
|
||||
@@ -262,7 +262,7 @@ namespace Bit.Core.Services
|
||||
|
||||
public async Task AddHistoryAsync(string password, CancellationToken token = default(CancellationToken))
|
||||
{
|
||||
var hasKey = await _cryptoService.HasUserKeyAsync();
|
||||
var hasKey = await _cryptoService.HasKeyAsync();
|
||||
if (!hasKey)
|
||||
{
|
||||
return;
|
||||
|
||||
@@ -143,7 +143,7 @@ namespace Bit.Core.Services
|
||||
return _decryptedSendsCache;
|
||||
}
|
||||
|
||||
var hasKey = await _cryptoService.HasUserKeyAsync();
|
||||
var hasKey = await _cryptoService.HasKeyAsync();
|
||||
if (!hasKey)
|
||||
{
|
||||
throw new Exception("No Key.");
|
||||
|
||||
@@ -302,52 +302,6 @@ namespace Bit.Core.Services
|
||||
true, reconciledOptions);
|
||||
}
|
||||
|
||||
public async Task<UserKey> GetUserKeyAsync(string userId = null)
|
||||
{
|
||||
return (await GetAccountAsync(
|
||||
ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultInMemoryOptionsAsync())
|
||||
))?.VolatileData?.UserKey;
|
||||
}
|
||||
|
||||
public async Task SetUserKeyAsync(UserKey value, string userId = null)
|
||||
{
|
||||
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||
await GetDefaultInMemoryOptionsAsync());
|
||||
var account = await GetAccountAsync(reconciledOptions);
|
||||
account.VolatileData.UserKey = value;
|
||||
await SaveAccountAsync(account, reconciledOptions);
|
||||
}
|
||||
|
||||
public async Task<MasterKey> GetMasterKeyAsync(string userId = null)
|
||||
{
|
||||
return (await GetAccountAsync(
|
||||
ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultInMemoryOptionsAsync())
|
||||
))?.VolatileData?.MasterKey;
|
||||
}
|
||||
|
||||
public async Task SetMasterKeyAsync(MasterKey value, string userId = null)
|
||||
{
|
||||
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||
await GetDefaultInMemoryOptionsAsync());
|
||||
var account = await GetAccountAsync(reconciledOptions);
|
||||
account.VolatileData.MasterKey = value;
|
||||
await SaveAccountAsync(account, reconciledOptions);
|
||||
}
|
||||
|
||||
public async Task<string> GetUserKeyMasterKeyAsync(string userId = null)
|
||||
{
|
||||
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||
await GetDefaultInMemoryOptionsAsync());
|
||||
return await _storageMediatorService.GetAsync<string>(Constants.UserKeyKey(reconciledOptions.UserId), false);
|
||||
}
|
||||
|
||||
public async Task SetUserKeyMasterKeyAsync(string value, string userId = null)
|
||||
{
|
||||
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||
await GetDefaultInMemoryOptionsAsync());
|
||||
await _storageMediatorService.SaveAsync(Constants.UserKeyKey(reconciledOptions.UserId), value, false);
|
||||
}
|
||||
|
||||
public async Task<bool> CanAccessPremiumAsync(string userId = null)
|
||||
{
|
||||
if (userId == null)
|
||||
@@ -399,34 +353,6 @@ namespace Bit.Core.Services
|
||||
await SetValueAsync(Constants.ProtectedPinKey(reconciledOptions.UserId), value, reconciledOptions);
|
||||
}
|
||||
|
||||
public async Task<EncString> GetUserKeyPinAsync(string userId = null)
|
||||
{
|
||||
var key = await _storageMediatorService.GetAsync<string>(Constants.UserKeyPinKey(userId), false);
|
||||
return key != null ? new EncString(key) : null;
|
||||
}
|
||||
|
||||
public async Task SetUserKeyPinAsync(EncString value, string userId = null)
|
||||
{
|
||||
await _storageMediatorService.SaveAsync(Constants.UserKeyPinKey(userId), value?.EncryptedString, false);
|
||||
}
|
||||
|
||||
public async Task<EncString> GetUserKeyPinEphemeralAsync(string userId = null)
|
||||
{
|
||||
return (await GetAccountAsync(
|
||||
ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultInMemoryOptionsAsync())
|
||||
))?.VolatileData?.UserKeyPinEphemeral;
|
||||
}
|
||||
|
||||
public async Task SetUserKeyPinEphemeralAsync(EncString value, string userId = null)
|
||||
{
|
||||
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||
await GetDefaultInMemoryOptionsAsync());
|
||||
var account = await GetAccountAsync(reconciledOptions);
|
||||
account.VolatileData.UserKeyPinEphemeral = value;
|
||||
await SaveAccountAsync(account, reconciledOptions);
|
||||
}
|
||||
|
||||
[Obsolete("Use GetUserKeyPinAsync instead, left for migration purposes")]
|
||||
public async Task<string> GetPinProtectedAsync(string userId = null)
|
||||
{
|
||||
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||
@@ -434,7 +360,6 @@ namespace Bit.Core.Services
|
||||
return await GetValueAsync<string>(Constants.PinProtectedKey(reconciledOptions.UserId), reconciledOptions);
|
||||
}
|
||||
|
||||
[Obsolete("Use SetUserKeyPinAsync instead")]
|
||||
public async Task SetPinProtectedAsync(string value, string userId = null)
|
||||
{
|
||||
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||
@@ -442,7 +367,6 @@ namespace Bit.Core.Services
|
||||
await SetValueAsync(Constants.PinProtectedKey(reconciledOptions.UserId), value, reconciledOptions);
|
||||
}
|
||||
|
||||
[Obsolete("Use GetUserKeyPinEphemeralAsync instead, left for migration purposes")]
|
||||
public async Task<EncString> GetPinProtectedKeyAsync(string userId = null)
|
||||
{
|
||||
return (await GetAccountAsync(
|
||||
@@ -450,7 +374,6 @@ namespace Bit.Core.Services
|
||||
))?.VolatileData?.PinProtectedKey;
|
||||
}
|
||||
|
||||
[Obsolete("Use SetUserKeyPinEphemeralAsync instead")]
|
||||
public async Task SetPinProtectedKeyAsync(EncString value, string userId = null)
|
||||
{
|
||||
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||
@@ -472,6 +395,35 @@ namespace Bit.Core.Services
|
||||
await SaveAccountAsync(account, reconciledOptions);
|
||||
}
|
||||
|
||||
public async Task<string> GetKeyEncryptedAsync(string userId = null)
|
||||
{
|
||||
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||
await GetDefaultSecureStorageOptionsAsync());
|
||||
return await GetValueAsync<string>(Constants.KeyKey(reconciledOptions.UserId), reconciledOptions);
|
||||
}
|
||||
|
||||
public async Task SetKeyEncryptedAsync(string value, string userId)
|
||||
{
|
||||
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||
await GetDefaultSecureStorageOptionsAsync());
|
||||
await SetValueAsync(Constants.KeyKey(reconciledOptions.UserId), value, reconciledOptions);
|
||||
}
|
||||
|
||||
public async Task<SymmetricCryptoKey> GetKeyDecryptedAsync(string userId = null)
|
||||
{
|
||||
return (await GetAccountAsync(
|
||||
ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultInMemoryOptionsAsync())
|
||||
))?.VolatileData?.Key;
|
||||
}
|
||||
|
||||
public async Task SetKeyDecryptedAsync(SymmetricCryptoKey value, string userId = null)
|
||||
{
|
||||
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||
await GetDefaultInMemoryOptionsAsync());
|
||||
var account = await GetAccountAsync(reconciledOptions);
|
||||
account.VolatileData.Key = value;
|
||||
await SaveAccountAsync(account, reconciledOptions);
|
||||
}
|
||||
|
||||
public async Task<string> GetKeyHashAsync(string userId = null)
|
||||
{
|
||||
@@ -487,6 +439,19 @@ namespace Bit.Core.Services
|
||||
await SetValueAsync(Constants.KeyHashKey(reconciledOptions.UserId), value, reconciledOptions);
|
||||
}
|
||||
|
||||
public async Task<string> GetEncKeyEncryptedAsync(string userId = null)
|
||||
{
|
||||
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||
await GetDefaultStorageOptionsAsync());
|
||||
return await GetValueAsync<string>(Constants.EncKeyKey(reconciledOptions.UserId), reconciledOptions);
|
||||
}
|
||||
|
||||
public async Task SetEncKeyEncryptedAsync(string value, string userId)
|
||||
{
|
||||
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||
await GetDefaultStorageOptionsAsync());
|
||||
await SetValueAsync(Constants.EncKeyKey(reconciledOptions.UserId), value, reconciledOptions);
|
||||
}
|
||||
|
||||
public async Task<Dictionary<string, string>> GetOrgKeysEncryptedAsync(string userId = null)
|
||||
{
|
||||
@@ -517,25 +482,6 @@ namespace Bit.Core.Services
|
||||
await SetValueAsync(Constants.EncPrivateKeyKey(reconciledOptions.UserId), value, reconciledOptions);
|
||||
}
|
||||
|
||||
public async Task<SymmetricCryptoKey> GetDeviceKeyAsync(string userId = null)
|
||||
{
|
||||
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||
await GetDefaultStorageOptionsAsync());
|
||||
var deviceKeyB64 = await _storageMediatorService.GetAsync<string>(Constants.DeviceKeyKey(reconciledOptions.UserId), true);
|
||||
if (string.IsNullOrEmpty(deviceKeyB64))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return new SymmetricCryptoKey(Convert.FromBase64String(deviceKeyB64));
|
||||
}
|
||||
|
||||
public async Task SetDeviceKeyAsync(SymmetricCryptoKey value, string userId = null)
|
||||
{
|
||||
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||
await GetDefaultStorageOptionsAsync());
|
||||
await _storageMediatorService.SaveAsync(Constants.DeviceKeyKey(reconciledOptions.UserId), value.KeyB64, true);
|
||||
}
|
||||
|
||||
public async Task<List<string>> GetAutofillBlacklistedUrisAsync(string userId = null)
|
||||
{
|
||||
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||
@@ -1334,37 +1280,6 @@ namespace Bit.Core.Services
|
||||
await SetValueAsync(Constants.PreLoginEmailKey, value, options);
|
||||
}
|
||||
|
||||
public async Task<AccountDecryptionOptions> GetAccountDecryptionOptions(string userId = null)
|
||||
{
|
||||
return (await GetAccountAsync(
|
||||
ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultStorageOptionsAsync())
|
||||
))?.Profile?.UserDecryptionOptions;
|
||||
}
|
||||
|
||||
public async Task<bool> GetShouldTrustDeviceAsync()
|
||||
{
|
||||
return await _storageMediatorService.GetAsync<bool>(Constants.ShouldTrustDevice);
|
||||
}
|
||||
|
||||
public async Task SetShouldTrustDeviceAsync(bool value)
|
||||
{
|
||||
await _storageMediatorService.SaveAsync(Constants.ShouldTrustDevice, value);
|
||||
}
|
||||
|
||||
public async Task<PendingAdminAuthRequest> GetPendingAdminAuthRequestAsync(string userId = null)
|
||||
{
|
||||
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||
await GetDefaultStorageOptionsAsync());
|
||||
return await _storageMediatorService.GetAsync<PendingAdminAuthRequest>(Constants.PendingAdminAuthRequest(reconciledOptions.UserId), true);
|
||||
}
|
||||
|
||||
public async Task SetPendingAdminAuthRequestAsync(PendingAdminAuthRequest value, string userId = null)
|
||||
{
|
||||
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||
await GetDefaultStorageOptionsAsync());
|
||||
await _storageMediatorService.SaveAsync(Constants.PendingAdminAuthRequest(reconciledOptions.UserId), value, true);
|
||||
}
|
||||
|
||||
public ConfigResponse GetConfigs()
|
||||
{
|
||||
return _storageMediatorService.Get<ConfigResponse>(Constants.ConfigsKey);
|
||||
@@ -1741,55 +1656,5 @@ namespace Bit.Core.Services
|
||||
await SetValueAsync(Constants.LastUserShouldConnectToWatchKey,
|
||||
shouldConnect ?? await GetShouldConnectToWatchAsync(), await GetDefaultStorageOptionsAsync());
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public async Task<string> GetEncKeyEncryptedAsync(string userId = null)
|
||||
{
|
||||
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||
await GetDefaultStorageOptionsAsync());
|
||||
return await GetValueAsync<string>(Constants.EncKeyKey(reconciledOptions.UserId), reconciledOptions);
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public async Task SetEncKeyEncryptedAsync(string value, string userId)
|
||||
{
|
||||
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||
await GetDefaultStorageOptionsAsync());
|
||||
await SetValueAsync(Constants.EncKeyKey(reconciledOptions.UserId), value, reconciledOptions);
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public async Task<string> GetKeyEncryptedAsync(string userId = null)
|
||||
{
|
||||
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||
await GetDefaultSecureStorageOptionsAsync());
|
||||
return await GetValueAsync<string>(Constants.KeyKey(reconciledOptions.UserId), reconciledOptions);
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public async Task SetKeyEncryptedAsync(string value, string userId)
|
||||
{
|
||||
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||
await GetDefaultSecureStorageOptionsAsync());
|
||||
await SetValueAsync(Constants.KeyKey(reconciledOptions.UserId), value, reconciledOptions);
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public async Task<SymmetricCryptoKey> GetKeyDecryptedAsync(string userId = null)
|
||||
{
|
||||
return (await GetAccountAsync(
|
||||
ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultInMemoryOptionsAsync())
|
||||
))?.VolatileData?.Key;
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public async Task SetKeyDecryptedAsync(SymmetricCryptoKey value, string userId = null)
|
||||
{
|
||||
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||
await GetDefaultInMemoryOptionsAsync());
|
||||
var account = await GetAccountAsync(reconciledOptions);
|
||||
account.VolatileData.Key = value;
|
||||
await SaveAccountAsync(account, reconciledOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -327,8 +327,8 @@ namespace Bit.Core.Services
|
||||
}
|
||||
return;
|
||||
}
|
||||
await _cryptoService.SetMasterKeyEncryptedUserKeyAsync(response.Key);
|
||||
await _cryptoService.SetPrivateKeyAsync(response.PrivateKey);
|
||||
await _cryptoService.SetEncKeyAsync(response.Key);
|
||||
await _cryptoService.SetEncPrivateKeyAsync(response.PrivateKey);
|
||||
await _cryptoService.SetOrgKeysAsync(response.Organizations);
|
||||
await _stateService.SetSecurityStampAsync(response.SecurityStamp);
|
||||
var organizations = response.Organizations.ToDictionary(o => o.Id, o => new OrganizationData(o));
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace Bit.Core.Services
|
||||
}
|
||||
else
|
||||
{
|
||||
var passwordValid = await _cryptoService.CompareAndUpdatePasswordHashAsync(secret, null);
|
||||
var passwordValid = await _cryptoService.CompareAndUpdateKeyHashAsync(secret, null);
|
||||
if (!passwordValid)
|
||||
{
|
||||
await InvalidSecretErrorAsync(verificationType);
|
||||
|
||||
@@ -7,13 +7,6 @@ using Bit.Core.Models.Domain;
|
||||
|
||||
namespace Bit.Core.Services
|
||||
{
|
||||
public enum PinLockEnum
|
||||
{
|
||||
Disabled,
|
||||
Persistent,
|
||||
Transient
|
||||
}
|
||||
|
||||
public class VaultTimeoutService : IVaultTimeoutService
|
||||
{
|
||||
private readonly ICryptoService _cryptoService;
|
||||
@@ -61,7 +54,7 @@ namespace Bit.Core.Services
|
||||
|
||||
public async Task<bool> IsLockedAsync(string userId = null)
|
||||
{
|
||||
var hasKey = await _cryptoService.HasUserKeyAsync(userId);
|
||||
var hasKey = await _cryptoService.HasKeyAsync(userId);
|
||||
if (hasKey)
|
||||
{
|
||||
var biometricSet = await IsBiometricLockSetAsync(userId);
|
||||
@@ -172,13 +165,11 @@ namespace Bit.Core.Services
|
||||
|
||||
if (await _keyConnectorService.GetUsesKeyConnector())
|
||||
{
|
||||
var pinStatus = await IsPinLockSetAsync(userId);
|
||||
var ephemeralPinSet = await _stateService.GetUserKeyPinEphemeralAsync()
|
||||
?? await _stateService.GetPinProtectedKeyAsync();
|
||||
var pinEnabled = (pinStatus == PinLockEnum.Transient && ephemeralPinSet != null) ||
|
||||
pinStatus == PinLockEnum.Persistent;
|
||||
var (isPinProtected, isPinProtectedWithKey) = await IsPinLockSetAsync(userId);
|
||||
var pinLock = (isPinProtected && await _stateService.GetPinProtectedKeyAsync(userId) != null) ||
|
||||
isPinProtectedWithKey;
|
||||
|
||||
if (!pinEnabled && !await IsBiometricLockSetAsync())
|
||||
if (!pinLock && !await IsBiometricLockSetAsync())
|
||||
{
|
||||
await LogOutAsync(userInitiated, userId);
|
||||
return;
|
||||
@@ -196,10 +187,10 @@ namespace Bit.Core.Services
|
||||
}
|
||||
}
|
||||
await Task.WhenAll(
|
||||
_cryptoService.ClearUserKeyAsync(userId),
|
||||
_cryptoService.ClearMasterKeyAsync(userId),
|
||||
_cryptoService.ClearKeyAsync(userId),
|
||||
_cryptoService.ClearOrgKeysAsync(true, userId),
|
||||
_cryptoService.ClearKeyPairAsync(true, userId));
|
||||
_cryptoService.ClearKeyPairAsync(true, userId),
|
||||
_cryptoService.ClearEncKeyAsync(true, userId));
|
||||
|
||||
if (isActiveAccount)
|
||||
{
|
||||
@@ -223,30 +214,15 @@ namespace Bit.Core.Services
|
||||
{
|
||||
await _stateService.SetVaultTimeoutAsync(timeout);
|
||||
await _stateService.SetVaultTimeoutActionAsync(action);
|
||||
await _cryptoService.ToggleKeysAsync();
|
||||
await _cryptoService.ToggleKeyAsync();
|
||||
await _tokenService.ToggleTokensAsync();
|
||||
}
|
||||
|
||||
public async Task<PinLockEnum> IsPinLockSetAsync(string userId = null)
|
||||
public async Task<Tuple<bool, bool>> IsPinLockSetAsync(string userId = null)
|
||||
{
|
||||
// we can't depend on only the protected pin being set because old
|
||||
// versions only used it for MP on Restart
|
||||
var pinIsEnabled = await _stateService.GetProtectedPinAsync(userId);
|
||||
var userKeyPin = await _stateService.GetUserKeyPinAsync(userId);
|
||||
var oldUserKeyPin = await _stateService.GetPinProtectedAsync(userId);
|
||||
|
||||
if (userKeyPin != null || oldUserKeyPin != null)
|
||||
{
|
||||
return PinLockEnum.Persistent;
|
||||
}
|
||||
else if (pinIsEnabled != null && userKeyPin == null && oldUserKeyPin == null)
|
||||
{
|
||||
return PinLockEnum.Transient;
|
||||
}
|
||||
else
|
||||
{
|
||||
return PinLockEnum.Disabled;
|
||||
}
|
||||
var protectedPin = await _stateService.GetProtectedPinAsync(userId);
|
||||
var pinProtectedKey = await _stateService.GetPinProtectedAsync(userId);
|
||||
return new Tuple<bool, bool>(protectedPin != null, pinProtectedKey != null);
|
||||
}
|
||||
|
||||
public async Task<bool> IsBiometricLockSetAsync(string userId = null)
|
||||
@@ -257,7 +233,8 @@ namespace Bit.Core.Services
|
||||
|
||||
public async Task ClearAsync(string userId = null)
|
||||
{
|
||||
await _cryptoService.ClearPinKeysAsync(userId);
|
||||
await _stateService.SetPinProtectedKeyAsync(null, userId);
|
||||
await _stateService.SetProtectedPinAsync(null, userId);
|
||||
}
|
||||
|
||||
public async Task<int?> GetVaultTimeout(string userId = null)
|
||||
|
||||
@@ -77,10 +77,9 @@ namespace Bit.Core.Utilities
|
||||
});
|
||||
var passwordGenerationService = new PasswordGenerationService(cryptoService, stateService, cryptoFunctionService, policyService);
|
||||
var totpService = new TotpService(cryptoFunctionService);
|
||||
var deviceTrustCryptoService = new DeviceTrustCryptoService(apiService, appIdService, cryptoFunctionService, cryptoService, stateService);
|
||||
var authService = new AuthService(cryptoService, cryptoFunctionService, apiService, stateService,
|
||||
tokenService, appIdService, i18nService, platformUtilsService, messagingService, vaultTimeoutService,
|
||||
keyConnectorService, passwordGenerationService, policyService, deviceTrustCryptoService);
|
||||
keyConnectorService, passwordGenerationService, policyService);
|
||||
var exportService = new ExportService(folderService, cipherService, cryptoService);
|
||||
var auditService = new AuditService(cryptoFunctionService, apiService);
|
||||
var environmentService = new EnvironmentService(apiService, stateService, conditionedRunner);
|
||||
@@ -115,7 +114,6 @@ namespace Bit.Core.Utilities
|
||||
Register<IUserVerificationService>("userVerificationService", userVerificationService);
|
||||
Register<IUsernameGenerationService>(usernameGenerationService);
|
||||
Register<IConfigService>(configService);
|
||||
Register<IDeviceTrustCryptoService>(deviceTrustCryptoService);
|
||||
}
|
||||
|
||||
public static void Register<T>(string serviceName, T obj)
|
||||
|
||||
@@ -16,13 +16,15 @@ using Bit.iOS.Core.Views;
|
||||
using CoreFoundation;
|
||||
using CoreNFC;
|
||||
using Foundation;
|
||||
using NativeLibrary;
|
||||
using UIKit;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Platform.iOS;
|
||||
using XamariniOS17CredentialProviderBinding;
|
||||
|
||||
namespace Bit.iOS.Autofill
|
||||
{
|
||||
public partial class CredentialProviderViewController : ASCredentialProviderViewController, IAccountsManagerHost
|
||||
public partial class CredentialProviderViewController : BaseASCredentialProviderViewController, IAccountsManagerHost
|
||||
{
|
||||
private Context _context;
|
||||
private NFCNdefReaderSession _nfcSession = null;
|
||||
@@ -49,6 +51,11 @@ namespace Bit.iOS.Autofill
|
||||
{
|
||||
ExtContext = ExtensionContext
|
||||
};
|
||||
if (UIDevice.CurrentDevice.CheckSystemVersion(17, 0))
|
||||
{
|
||||
var del = new ASCredentialProviderDelegate(this);
|
||||
SetCompatDelegate(del);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -498,7 +505,7 @@ namespace Bit.iOS.Autofill
|
||||
vm.StartTwoFactorAction = () => DismissViewController(false, () => LaunchTwoFactorFlow(false));
|
||||
vm.UpdateTempPasswordAction = () => DismissViewController(false, () => LaunchUpdateTempPasswordFlow());
|
||||
vm.StartSsoLoginAction = () => DismissViewController(false, () => LaunchLoginSsoFlow());
|
||||
vm.LogInWithDeviceAction = () => DismissViewController(false, () => LaunchLoginWithDevice(AuthRequestType.AuthenticateAndUnlock, email));
|
||||
vm.LogInWithDeviceAction = () => DismissViewController(false, () => LaunchLoginWithDevice(email));
|
||||
vm.LogInSuccessAction = () => DismissLockAndContinue();
|
||||
vm.CloseAction = () => DismissViewController(false, () => LaunchHomePage());
|
||||
}
|
||||
@@ -511,11 +518,11 @@ namespace Bit.iOS.Autofill
|
||||
LogoutIfAuthed();
|
||||
}
|
||||
|
||||
private void LaunchLoginWithDevice(AuthRequestType authRequestType, string email = null)
|
||||
private void LaunchLoginWithDevice(string email = null)
|
||||
{
|
||||
var appOptions = new AppOptions { IosExtension = true };
|
||||
var app = new App.App(appOptions);
|
||||
var loginWithDevicePage = new LoginPasswordlessRequestPage(email, authRequestType, appOptions);
|
||||
var loginWithDevicePage = new LoginPasswordlessRequestPage(email, appOptions);
|
||||
ThemeManager.SetTheme(app.Resources);
|
||||
ThemeManager.ApplyResourcesTo(loginWithDevicePage);
|
||||
if (loginWithDevicePage.BindingContext is LoginPasswordlessRequestViewModel vm)
|
||||
@@ -545,7 +552,6 @@ namespace Bit.iOS.Autofill
|
||||
vm.StartTwoFactorAction = () => DismissViewController(false, () => LaunchTwoFactorFlow(true));
|
||||
vm.StartSetPasswordAction = () => DismissViewController(false, () => LaunchSetPasswordFlow());
|
||||
vm.UpdateTempPasswordAction = () => DismissViewController(false, () => LaunchUpdateTempPasswordFlow());
|
||||
vm.StartDeviceApprovalOptionsAction = () => DismissViewController(false, () => LaunchDeviceApprovalOptionsFlow());
|
||||
vm.SsoAuthSuccessAction = () => DismissLockAndContinue();
|
||||
vm.CloseAction = () => DismissViewController(false, () => LaunchHomePage());
|
||||
}
|
||||
@@ -568,7 +574,6 @@ namespace Bit.iOS.Autofill
|
||||
{
|
||||
vm.TwoFactorAuthSuccessAction = () => DismissLockAndContinue();
|
||||
vm.StartSetPasswordAction = () => DismissViewController(false, () => LaunchSetPasswordFlow());
|
||||
vm.StartDeviceApprovalOptionsAction = () => DismissViewController(false, () => LaunchDeviceApprovalOptionsFlow());
|
||||
if (authingWithSso)
|
||||
{
|
||||
vm.CloseAction = () => DismissViewController(false, () => LaunchLoginSsoFlow());
|
||||
@@ -623,26 +628,6 @@ namespace Bit.iOS.Autofill
|
||||
PresentViewController(updateTempPasswordController, true, null);
|
||||
}
|
||||
|
||||
private void LaunchDeviceApprovalOptionsFlow()
|
||||
{
|
||||
var loginApproveDevicePage = new LoginApproveDevicePage();
|
||||
var app = new App.App(new AppOptions { IosExtension = true });
|
||||
ThemeManager.SetTheme(app.Resources);
|
||||
ThemeManager.ApplyResourcesTo(loginApproveDevicePage);
|
||||
if (loginApproveDevicePage.BindingContext is LoginApproveDeviceViewModel vm)
|
||||
{
|
||||
vm.LogInWithMasterPasswordAction = () => DismissViewController(false, () => PerformSegue("lockPasswordSegue", this));
|
||||
vm.RequestAdminApprovalAction = () => DismissViewController(false, () => LaunchLoginWithDevice(AuthRequestType.AdminApproval, vm.Email));
|
||||
vm.LogInWithDeviceAction = () => DismissViewController(false, () => LaunchLoginWithDevice(AuthRequestType.AuthenticateAndUnlock, vm.Email));
|
||||
vm.CloseAction = () => DismissViewController(false, () => LaunchHomePage());
|
||||
}
|
||||
|
||||
var navigationPage = new NavigationPage(loginApproveDevicePage);
|
||||
var loginApproveDeviceController = navigationPage.CreateViewController();
|
||||
loginApproveDeviceController.ModalPresentationStyle = UIModalPresentationStyle.FullScreen;
|
||||
PresentViewController(loginApproveDeviceController, true, null);
|
||||
}
|
||||
|
||||
public Task SetPreviousPageInfoAsync() => Task.CompletedTask;
|
||||
public Task UpdateThemeAsync() => Task.CompletedTask;
|
||||
|
||||
@@ -673,4 +658,29 @@ namespace Bit.iOS.Autofill
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ASCredentialProviderDelegate : NSObject, IASCredentialProviderCompatDelegate
|
||||
{
|
||||
public ASCredentialProviderDelegate(CredentialProviderViewController controller)
|
||||
{
|
||||
CredentialProviderViewController = controller;
|
||||
}
|
||||
|
||||
public CredentialProviderViewController CredentialProviderViewController { get; }
|
||||
|
||||
public void PrepareInterfaceToProvideCredentialCompatFor(ASCredentialRequestCompat credentialRequest)
|
||||
{
|
||||
CredentialProviderViewController?.CompleteRequest(null, "lala", "qerqrw");
|
||||
}
|
||||
|
||||
public void ProvideCredentialWithoutUserInteractionCompatFor(ASCredentialRequestCompat credentialRequest)
|
||||
{
|
||||
CredentialProviderViewController?.CompleteRequest(null, "nouserinteraction", "qerqrw");
|
||||
}
|
||||
|
||||
public void PrepareInterfaceCompatForPasskeyRegistration(ASCredentialRequestCompat registrationRequest)
|
||||
{
|
||||
CredentialProviderViewController?.CompleteRequest(null, "passkey", "qerqrw");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.8bit.bitwarden.autofill</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2023.7.1</string>
|
||||
<string>2023.5.1</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>CFBundleLocalizations</key>
|
||||
|
||||
@@ -21,9 +21,9 @@
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<ConsolePause>false</ConsolePause>
|
||||
<MtouchArch>x86_64</MtouchArch>
|
||||
<MtouchLink>None</MtouchLink>
|
||||
<MtouchLink>SdkOnly</MtouchLink>
|
||||
<MtouchDebug>true</MtouchDebug>
|
||||
<MtouchExtraArgs>--nodevcodeshare --http-message-handler=NSUrlSessionHandler -gcc_flags "-L${ProjectDir}/../../lib/ios -largon2 -force_load ${ProjectDir}/../../lib/ios/libargon2.a"</MtouchExtraArgs>
|
||||
<MtouchExtraArgs>--nodevcodeshare --http-message-handler=NSUrlSessionHandler -gcc_flags "-L${ProjectDir}/../../lib/ios -largon2 -force_load ${ProjectDir}/../../lib/ios/libargon2.a" --linkskip=NewsstandKit</MtouchExtraArgs>
|
||||
<OptimizePNGs>true</OptimizePNGs>
|
||||
<MtouchFastDev>false</MtouchFastDev>
|
||||
<MtouchHttpClientHandler>NSUrlSessionHandler</MtouchHttpClientHandler>
|
||||
@@ -216,6 +216,10 @@
|
||||
<IsAppExtension>false</IsAppExtension>
|
||||
<IsWatchApp>false</IsWatchApp>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\lib\ios\iOS17CredentialProvider\XamariniOS17CredentialProvider\XamariniOS17CredentialProviderBinding\XamariniOS17CredentialProviderBinding.csproj">
|
||||
<Project>{63938BA1-0F72-4BFD-B7A4-823C146E43B4}</Project>
|
||||
<Name>XamariniOS17CredentialProviderBinding</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<BundleResource Include="Resources\check.png" />
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user