From 9f8307a4fff14e2360f54f7d758a8b81430e482a Mon Sep 17 00:00:00 2001 From: Federico Maccaroni Date: Thu, 9 Mar 2023 15:45:51 -0300 Subject: [PATCH] [EC-770] Implement MessagePack on Watch sync (#2264) * EC-770 Started implementing MessagePack for the iPhone -> Watch communication * EC-770 Removed Pods and installed MessagePack through SPM * EC-770 Implemented MessagePack + Lzfse compression when syncing iPhone -> Watch * EC-770 Added MessagePack as submodule and updated the build to checkout the submodule as well. Also added MessagePack files as reference in the watch project * EC-770 Updated build Updated build.yml to checkout submodules on iOS --- .github/workflows/build.yml | 4 + .gitmodules | 3 + lib/MessagePack | 1 + src/Android/Services/WatchDeviceService.cs | 3 +- src/App/App.csproj | 3 + src/App/Services/BaseWatchDeviceService.cs | 19 ++++- src/Core/Core.csproj | 7 ++ src/Core/Models/View/SimpleCipherView.cs | 22 +++++- src/Core/Models/View/WatchDTO.cs | 20 +++++ src/iOS.Core/Services/WatchDeviceService.cs | 29 +++++--- src/iOS.Core/Utilities/WCSessionManager.cs | 6 +- src/iOS.Core/Utilities/iOSCoreHelpers.cs | 3 +- .../Models/Cipher.swift | 6 ++ .../Utilities/BWState.swift | 1 + .../Utilities/JsonDecoderExtensions.swift | 14 ++-- .../ViewModels/BWStateViewModel.swift | 4 +- .../ViewModels/CipherListViewModel.swift | 3 + .../Views/BWStateView.swift | 6 +- .../Views/CipherListView.swift | 2 +- .../WatchConnectivityManager.swift | 44 ++++++++--- .../bitwarden.xcodeproj/project.pbxproj | 73 +++++++++++++++++++ 21 files changed, 228 insertions(+), 45 deletions(-) create mode 100644 .gitmodules create mode 160000 lib/MessagePack diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 47813e813..362ff1579 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -37,6 +37,8 @@ jobs: steps: - name: Checkout repo uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 + with: + submodules: 'true' - name: Check if special branches exist id: branch-check @@ -514,6 +516,8 @@ jobs: - name: Checkout repo uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 + with: + submodules: 'true' - name: Login to Azure - Prod Subscription uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..e5c10debf --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "lib/MessagePack"] + path = lib/MessagePack + url = https://github.com/bitwarden/MessagePack.git diff --git a/lib/MessagePack b/lib/MessagePack new file mode 160000 index 000000000..1ecb15e31 --- /dev/null +++ b/lib/MessagePack @@ -0,0 +1 @@ +Subproject commit 1ecb15e31176023f6aa0b4948859b197b771d357 diff --git a/src/Android/Services/WatchDeviceService.cs b/src/Android/Services/WatchDeviceService.cs index 0cad001b3..f7273c68f 100644 --- a/src/Android/Services/WatchDeviceService.cs +++ b/src/Android/Services/WatchDeviceService.cs @@ -2,7 +2,6 @@ using System.Threading.Tasks; using Bit.App.Services; using Bit.Core.Abstractions; -using Bit.Core.Models; namespace Bit.Droid.Services { @@ -22,7 +21,7 @@ namespace Bit.Droid.Services protected override bool CanSendData => false; - protected override Task SendDataToWatchAsync(WatchDTO watchDto) => throw new NotImplementedException(); + protected override Task SendDataToWatchAsync(byte[] rawData) => throw new NotImplementedException(); protected override void ConnectToWatch() => throw new NotImplementedException(); } diff --git a/src/App/App.csproj b/src/App/App.csproj index 23b7a7d0e..7901bcca6 100644 --- a/src/App/App.csproj +++ b/src/App/App.csproj @@ -21,6 +21,7 @@ + @@ -436,6 +437,8 @@ + + diff --git a/src/App/Services/BaseWatchDeviceService.cs b/src/App/Services/BaseWatchDeviceService.cs index defd04a35..79acc44c1 100644 --- a/src/App/Services/BaseWatchDeviceService.cs +++ b/src/App/Services/BaseWatchDeviceService.cs @@ -1,11 +1,11 @@ -using System; -using System.Linq; +using System.Linq; using System.Threading.Tasks; using Bit.Core.Abstractions; using Bit.Core.Enums; using Bit.Core.Models; using Bit.Core.Models.View; -using Xamarin.Forms; +using MessagePack; +using MessagePack.Resolvers; namespace Bit.App.Services { @@ -124,7 +124,18 @@ namespace Bit.App.Services await SyncDataToWatchAsync(); } - protected abstract Task SendDataToWatchAsync(WatchDTO watchDto); + protected async Task SendDataToWatchAsync(WatchDTO watchDto) + { + var options = MessagePackSerializerOptions.Standard + .WithResolver(CompositeResolver.Create( + GeneratedResolver.Instance, + StandardResolver.Instance + )); + + await SendDataToWatchAsync(MessagePackSerializer.Serialize(watchDto, options)); + } + + protected abstract Task SendDataToWatchAsync(byte[] rawData); protected abstract void ConnectToWatch(); } diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj index bc2add9b6..bb55f7d47 100644 --- a/src/Core/Core.csproj +++ b/src/Core/Core.csproj @@ -18,6 +18,8 @@ + + @@ -32,6 +34,11 @@ + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + diff --git a/src/Core/Models/View/SimpleCipherView.cs b/src/Core/Models/View/SimpleCipherView.cs index df5262710..cfd078b4a 100644 --- a/src/Core/Models/View/SimpleCipherView.cs +++ b/src/Core/Models/View/SimpleCipherView.cs @@ -1,11 +1,17 @@ using System.Collections.Generic; using System.Linq; using Bit.Core.Enums; +using MessagePack; namespace Bit.Core.Models.View { + [MessagePackObject] public class SimpleCipherView { + public SimpleCipherView() + { + } + public SimpleCipherView(CipherView c) { Id = c.Id; @@ -22,26 +28,40 @@ namespace Bit.Core.Models.View } } + [Key(0)] public string Id { get; set; } + [Key(1)] public string Name { get; set; } - public CipherType Type { get; set; } + [IgnoreMember] + public CipherType Type { get; set; } // ignoring on serialization for now, given that all are going to be of type Login + [Key(2)] public SimpleLoginView Login { get; set; } } + [MessagePackObject] public class SimpleLoginView { + [Key(0)] public string Username { get; set; } + [Key(1)] public string Totp { get; set; } + [Key(2)] public List Uris { get; set; } } + [MessagePackObject] public class SimpleLoginUriView { + public SimpleLoginUriView() + { + } + public SimpleLoginUriView(string uri) { Uri = uri; } + [Key(0)] public string Uri { get; set; } } } diff --git a/src/Core/Models/View/WatchDTO.cs b/src/Core/Models/View/WatchDTO.cs index 33bcae973..10c901b87 100644 --- a/src/Core/Models/View/WatchDTO.cs +++ b/src/Core/Models/View/WatchDTO.cs @@ -1,36 +1,56 @@ using System.Collections.Generic; using Bit.Core.Enums; using Bit.Core.Models.View; +using MessagePack; namespace Bit.Core.Models { + [MessagePackObject] public class WatchDTO { + public WatchDTO() + { + } + public WatchDTO(WatchState state) { State = state; } + [Key(0)] public WatchState State { get; private set; } + [Key(1)] public List Ciphers { get; set; } + [Key(2)] public UserDataDto UserData { get; set; } + [Key(3)] public EnvironmentUrlDataDto EnvironmentData { get; set; } //public SettingsDataDto SettingsData { get; set; } + [MessagePackObject] public class UserDataDto { + [Key(0)] public string Id { get; set; } + + [Key(1)] public string Email { get; set; } + + [Key(2)] public string Name { get; set; } } + [MessagePackObject] public class EnvironmentUrlDataDto { + [Key(0)] public string Base { get; set; } + + [Key(1)] public string Icons { get; set; } } diff --git a/src/iOS.Core/Services/WatchDeviceService.cs b/src/iOS.Core/Services/WatchDeviceService.cs index 250d4788c..099311b0c 100644 --- a/src/iOS.Core/Services/WatchDeviceService.cs +++ b/src/iOS.Core/Services/WatchDeviceService.cs @@ -3,9 +3,8 @@ using System.Collections.Generic; using System.Threading.Tasks; using Bit.App.Services; using Bit.Core.Abstractions; -using Bit.Core.Models; using Bit.Core.Utilities; -using Newtonsoft.Json; +using Foundation; using WatchConnectivity; namespace Bit.iOS.Core.Services @@ -15,12 +14,17 @@ namespace Bit.iOS.Core.Services const string ACTION_MESSAGE_KEY = "actionMessage"; const string TRIGGER_SYNC_ACTION_KEY = "triggerSync"; + private readonly ILogger _logger; + public WatchDeviceService(ICipherService cipherService, IEnvironmentService environmentService, IStateService stateService, - IVaultTimeoutService vaultTimeoutService) + IVaultTimeoutService vaultTimeoutService, + ILogger logger) : base(cipherService, environmentService, stateService, vaultTimeoutService) { + _logger = logger; + WCSessionManager.SharedManager.OnMessagedReceived += OnMessagedReceived; } @@ -30,17 +34,24 @@ namespace Bit.iOS.Core.Services protected override bool IsSupported => WCSession.IsSupported; - protected override Task SendDataToWatchAsync(WatchDTO watchDto) + protected override Task SendDataToWatchAsync(byte[] rawData) { - var serializedData = JsonConvert.SerializeObject(watchDto); + NSError error = null; + // Lzfse is available on iOS 13+ but we're already constraining that by the constraint of watchOS version + // so there's no way this will be executed on lower than iOS 13. So no condition is needed here. + var data = NSData.FromArray(rawData).Compress(NSDataCompressionAlgorithm.Lzfse, out error); + + if (error != null) + { + _logger.Error("Can't compress Lzfse. Error: " + error.LocalizedDescription); + return Task.CompletedTask; + } // Add time to the key to make it change on every message sent so it's delivered faster. // If we use the same key then the OS may defer the delivery of the message because of // resources, reachability and other stuff - WCSessionManager.SharedManager.SendBackgroundHighPriorityMessage(new Dictionary - { - [$"watchDto-{DateTime.UtcNow.ToLongTimeString()}"] = serializedData - }); + var dict = new NSDictionary(new NSString($"watchDto-{DateTime.UtcNow.ToLongTimeString()}"), data); + WCSessionManager.SharedManager.SendBackgroundHighPriorityMessage(dict); return Task.CompletedTask; } diff --git a/src/iOS.Core/Utilities/WCSessionManager.cs b/src/iOS.Core/Utilities/WCSessionManager.cs index 7802019f3..e3e0375cd 100644 --- a/src/iOS.Core/Utilities/WCSessionManager.cs +++ b/src/iOS.Core/Utilities/WCSessionManager.cs @@ -74,7 +74,7 @@ namespace WatchConnectivity Debug.WriteLine($"Watch connectivity Reachable:{(session.Reachable ? '✓' : '✗')}"); } - public void SendBackgroundHighPriorityMessage(Dictionary applicationContext) + public void SendBackgroundHighPriorityMessage(NSDictionary applicationContext) { // Application context doesnt need the watch to be reachable, it will be received when opened if (validSession is null || validSession.ActivationState != WCSessionActivationState.Activated) @@ -84,10 +84,10 @@ namespace WatchConnectivity try { - var sendSuccessfully = validSession.UpdateApplicationContext(applicationContext.ToNSDictionary(), out var error); + var sendSuccessfully = validSession.UpdateApplicationContext(applicationContext, out var error); if (sendSuccessfully) { - Debug.WriteLine($"Sent App Context \nPayLoad: {applicationContext.ToNSDictionary().ToString()} \n"); + Debug.WriteLine($"Sent App Context \nPayLoad: {applicationContext.ToString()} \n"); } else { diff --git a/src/iOS.Core/Utilities/iOSCoreHelpers.cs b/src/iOS.Core/Utilities/iOSCoreHelpers.cs index 0ef4a39b3..e56560b9a 100644 --- a/src/iOS.Core/Utilities/iOSCoreHelpers.cs +++ b/src/iOS.Core/Utilities/iOSCoreHelpers.cs @@ -144,7 +144,8 @@ namespace Bit.iOS.Core.Utilities ServiceContainer.Register(new WatchDeviceService(ServiceContainer.Resolve(), ServiceContainer.Resolve(), ServiceContainer.Resolve(), - ServiceContainer.Resolve())); + ServiceContainer.Resolve(), + ServiceContainer.Resolve())); } public static void Bootstrap(Func postBootstrapFunc = null) diff --git a/src/watchOS/bitwarden/bitwarden WatchKit Extension/Models/Cipher.swift b/src/watchOS/bitwarden/bitwarden WatchKit Extension/Models/Cipher.swift index 24232f2a0..38b42b7ba 100644 --- a/src/watchOS/bitwarden/bitwarden WatchKit Extension/Models/Cipher.swift +++ b/src/watchOS/bitwarden/bitwarden WatchKit Extension/Models/Cipher.swift @@ -2,6 +2,12 @@ import Foundation import CoreData struct Cipher:Identifiable,Codable{ + enum CodingKeys : CodingKey { + case id + case name + case login + } + var id:String var name:String? var userId:String? diff --git a/src/watchOS/bitwarden/bitwarden WatchKit Extension/Utilities/BWState.swift b/src/watchOS/bitwarden/bitwarden WatchKit Extension/Utilities/BWState.swift index cfccda06a..13f87ff97 100644 --- a/src/watchOS/bitwarden/bitwarden WatchKit Extension/Utilities/BWState.swift +++ b/src/watchOS/bitwarden/bitwarden WatchKit Extension/Utilities/BWState.swift @@ -9,6 +9,7 @@ enum BWState : Int, Codable { case syncing = 5 // case needUnlock = 6 case needDeviceOwnerAuth = 7 + case debug = 255 var isDestructive: Bool { return self == .needSetup || self == .needLogin || self == .needPremium || self == .need2FAItem diff --git a/src/watchOS/bitwarden/bitwarden WatchKit Extension/Utilities/JsonDecoderExtensions.swift b/src/watchOS/bitwarden/bitwarden WatchKit Extension/Utilities/JsonDecoderExtensions.swift index c40a0303b..6062ae491 100644 --- a/src/watchOS/bitwarden/bitwarden WatchKit Extension/Utilities/JsonDecoderExtensions.swift +++ b/src/watchOS/bitwarden/bitwarden WatchKit Extension/Utilities/JsonDecoderExtensions.swift @@ -3,9 +3,9 @@ import Foundation extension JSONDecoder.KeyDecodingStrategy { static var upperToLowerCamelCase: JSONDecoder.KeyDecodingStrategy { return .custom { codingKeys in - - var key = AnyCodingKey(codingKeys.last!) - + + var key = JSONAnyCodingKey(codingKeys.last!) + if let firstChar = key.stringValue.first { key.stringValue.replaceSubrange( ...key.stringValue.startIndex, with: String(firstChar).lowercased() @@ -16,10 +16,10 @@ extension JSONDecoder.KeyDecodingStrategy { } } -struct AnyCodingKey : CodingKey { +struct JSONAnyCodingKey : CodingKey { var stringValue: String var intValue: Int? - + init(_ base: CodingKey) { self.init(stringValue: base.stringValue, intValue: base.intValue) } @@ -27,12 +27,12 @@ struct AnyCodingKey : CodingKey { init(stringValue: String) { self.stringValue = stringValue } - + init(intValue: Int) { self.stringValue = "\(intValue)" self.intValue = intValue } - + init(stringValue: String, intValue: Int?) { self.stringValue = stringValue self.intValue = intValue diff --git a/src/watchOS/bitwarden/bitwarden WatchKit Extension/ViewModels/BWStateViewModel.swift b/src/watchOS/bitwarden/bitwarden WatchKit Extension/ViewModels/BWStateViewModel.swift index c55991a1c..9f60d2223 100644 --- a/src/watchOS/bitwarden/bitwarden WatchKit Extension/ViewModels/BWStateViewModel.swift +++ b/src/watchOS/bitwarden/bitwarden WatchKit Extension/ViewModels/BWStateViewModel.swift @@ -4,7 +4,7 @@ class BWStateViewModel : ObservableObject{ @Published var text:String @Published var isLoading:Bool = false - init(_ state: BWState){ + init(_ state: BWState, _ defaultText: String?){ switch state { case .needLogin: text = "LogInToBitwardenOnYourIPhoneToViewVerificationCodes" @@ -22,7 +22,7 @@ class BWStateViewModel : ObservableObject{ case .needDeviceOwnerAuth: text = "SetUpAppleWatchPasscodeInOrderToUseBitwarden" default: - text = "" + text = defaultText ?? "" } } } diff --git a/src/watchOS/bitwarden/bitwarden WatchKit Extension/ViewModels/CipherListViewModel.swift b/src/watchOS/bitwarden/bitwarden WatchKit Extension/ViewModels/CipherListViewModel.swift index ffebd82d0..2a5093d33 100644 --- a/src/watchOS/bitwarden/bitwarden WatchKit Extension/ViewModels/CipherListViewModel.swift +++ b/src/watchOS/bitwarden/bitwarden WatchKit Extension/ViewModels/CipherListViewModel.swift @@ -15,6 +15,8 @@ class CipherListViewModel : ObservableObject { @Published var searchTerm: String = "" + var debugText: String? = nil + private var subscriber: AnyCancellable? init(_ cipherService: CipherServiceProtocol){ @@ -23,6 +25,7 @@ class CipherListViewModel : ObservableObject { subscriber = watchConnectivityManager.watchConnectivitySubject.sink { completion in print("WCM subject: \(completion)") } receiveValue: { value in + self.debugText = value.debugText self.checkStateAndFetch(value.state) } diff --git a/src/watchOS/bitwarden/bitwarden WatchKit Extension/Views/BWStateView.swift b/src/watchOS/bitwarden/bitwarden WatchKit Extension/Views/BWStateView.swift index 05436a17c..3912e2937 100644 --- a/src/watchOS/bitwarden/bitwarden WatchKit Extension/Views/BWStateView.swift +++ b/src/watchOS/bitwarden/bitwarden WatchKit Extension/Views/BWStateView.swift @@ -3,8 +3,8 @@ import SwiftUI struct BWStateView: View { @ObservedObject var viewModel:BWStateViewModel - init(_ state: BWState) { - viewModel = BWStateViewModel(state) + init(_ state: BWState, _ defaultText: String?) { + viewModel = BWStateViewModel(state, defaultText) } var body: some View { @@ -32,6 +32,6 @@ struct BWStateView: View { struct BWStateView_Previews: PreviewProvider { static var previews: some View { - BWStateView(.needSetup) + BWStateView(.needSetup, nil) } } diff --git a/src/watchOS/bitwarden/bitwarden WatchKit Extension/Views/CipherListView.swift b/src/watchOS/bitwarden/bitwarden WatchKit Extension/Views/CipherListView.swift index 596ace963..ab8b720fd 100644 --- a/src/watchOS/bitwarden/bitwarden WatchKit Extension/Views/CipherListView.swift +++ b/src/watchOS/bitwarden/bitwarden WatchKit Extension/Views/CipherListView.swift @@ -62,7 +62,7 @@ struct CipherListView: View { #endif } .fullScreenCover(isPresented: $viewModel.showingSheet) { - BWStateView(viewModel.currentState) + BWStateView(viewModel.currentState, viewModel.debugText) } } diff --git a/src/watchOS/bitwarden/bitwarden WatchKit Extension/WatchConnectivityManager.swift b/src/watchOS/bitwarden/bitwarden WatchKit Extension/WatchConnectivityManager.swift index 405062064..c0f4291fe 100644 --- a/src/watchOS/bitwarden/bitwarden WatchKit Extension/WatchConnectivityManager.swift +++ b/src/watchOS/bitwarden/bitwarden WatchKit Extension/WatchConnectivityManager.swift @@ -4,6 +4,7 @@ import WatchConnectivity struct WatchConnectivityMessage { var state: BWState? + var debugText: String? } final class WatchConnectivityManager: NSObject, ObservableObject { @@ -76,22 +77,41 @@ extension WatchConnectivityManager: WCSessionDelegate { k.starts(with: WATCH_DTO_APP_CONTEXT_KEY) } - guard let dtoKey = watchDtoKey, let serializedDto = applicationContext[dtoKey] as? String else { - return - } - - guard KeychainHelper.standard.hasDeviceOwnerAuth() else { - return - } - do { - guard let json = try! JSONSerialization.jsonObject(with: serializedDto.data(using: .utf8)!, options: [.fragmentsAllowed]) as? String else { + guard let dtoKey = watchDtoKey, + let nsRawData = applicationContext[dtoKey] as? NSData, + KeychainHelper.standard.hasDeviceOwnerAuth() else { return } - let decoder = JSONDecoder() - decoder.keyDecodingStrategy = .upperToLowerCamelCase - let watchDTO = try decoder.decode(WatchDTO.self, from: json.data(using: .utf8)!) + let decoder = MessagePackDecoder() + decoder.userInfo[MessagePackDecoder.dataSpecKey] = DataSpecBuilder() + .append("state") + .appendArray("ciphers", DataSpecBuilder() + .append("id") + .append("name") + .appendObj("login", DataSpecBuilder() + .append("username") + .append("totp") + .appendArray("uris", DataSpecBuilder() + .append("uri") + .build()) + .build()) + .build()) + .appendObj("userData", DataSpecBuilder() + .append("id") + .append("email") + .append("name") + .build()) + .appendObj("environmentData", DataSpecBuilder() + .append("base") + .append("icons") + .build()) + .build() + + let rawData = try nsRawData.decompressed(using: .lzfse) + + let watchDTO = try decoder.decode(WatchDTO.self, from: Data(referencing: rawData)) let previousUserId = StateService.shared.getUser()?.id diff --git a/src/watchOS/bitwarden/bitwarden.xcodeproj/project.pbxproj b/src/watchOS/bitwarden/bitwarden.xcodeproj/project.pbxproj index 0c691510d..8720e755d 100644 --- a/src/watchOS/bitwarden/bitwarden.xcodeproj/project.pbxproj +++ b/src/watchOS/bitwarden/bitwarden.xcodeproj/project.pbxproj @@ -27,6 +27,18 @@ 1B15615B28B7F3D900610B9B /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1B15615A28B7F3D900610B9B /* Preview Assets.xcassets */; }; 1B15616C28B81A2200610B9B /* Cipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B15616B28B81A2200610B9B /* Cipher.swift */; }; 1B15616E28B81A4300610B9B /* WatchConnectivityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B15616D28B81A4300610B9B /* WatchConnectivityManager.swift */; }; + 1B2A484029A90D9B00621E13 /* FixedWidthInteger+Bytes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B2A483229A90D9B00621E13 /* FixedWidthInteger+Bytes.swift */; }; + 1B2A484129A90D9B00621E13 /* UnkeyedEncodingContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B2A483429A90D9B00621E13 /* UnkeyedEncodingContainer.swift */; }; + 1B2A484229A90D9B00621E13 /* SingleValueEncodingContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B2A483529A90D9B00621E13 /* SingleValueEncodingContainer.swift */; }; + 1B2A484329A90D9B00621E13 /* KeyedEncodingContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B2A483629A90D9B00621E13 /* KeyedEncodingContainer.swift */; }; + 1B2A484429A90D9B00621E13 /* MessagePackEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B2A483729A90D9B00621E13 /* MessagePackEncoder.swift */; }; + 1B2A484529A90D9B00621E13 /* KeyedDecodingContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B2A483929A90D9B00621E13 /* KeyedDecodingContainer.swift */; }; + 1B2A484629A90D9B00621E13 /* SingleValueDecodingContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B2A483A29A90D9B00621E13 /* SingleValueDecodingContainer.swift */; }; + 1B2A484729A90D9B00621E13 /* MessagePackDecoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B2A483B29A90D9B00621E13 /* MessagePackDecoder.swift */; }; + 1B2A484829A90D9B00621E13 /* UnkeyedDecodingContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B2A483C29A90D9B00621E13 /* UnkeyedDecodingContainer.swift */; }; + 1B2A484929A90D9B00621E13 /* Box.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B2A483D29A90D9B00621E13 /* Box.swift */; }; + 1B2A484A29A90D9B00621E13 /* DataSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B2A483E29A90D9B00621E13 /* DataSpec.swift */; }; + 1B2A484B29A90D9B00621E13 /* AnyCodingKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B2A483F29A90D9B00621E13 /* AnyCodingKey.swift */; }; 1B5849A7294D1C020055286B /* Queue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B5849A6294D1C020055286B /* Queue.swift */; }; 1B59EC5729007DEE00A8718D /* BitwardenDB.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 1B59EC5529007DEE00A8718D /* BitwardenDB.xcdatamodeld */; }; 1B59EC592900801500A8718D /* StringEncryptionTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B59EC582900801500A8718D /* StringEncryptionTransformer.swift */; }; @@ -140,6 +152,18 @@ 1B15615D28B7F3D900610B9B /* PushNotificationPayload.apns */ = {isa = PBXFileReference; lastKnownFileType = text; path = PushNotificationPayload.apns; sourceTree = ""; }; 1B15616B28B81A2200610B9B /* Cipher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cipher.swift; sourceTree = ""; }; 1B15616D28B81A4300610B9B /* WatchConnectivityManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchConnectivityManager.swift; sourceTree = ""; }; + 1B2A483229A90D9B00621E13 /* FixedWidthInteger+Bytes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "FixedWidthInteger+Bytes.swift"; sourceTree = ""; }; + 1B2A483429A90D9B00621E13 /* UnkeyedEncodingContainer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnkeyedEncodingContainer.swift; sourceTree = ""; }; + 1B2A483529A90D9B00621E13 /* SingleValueEncodingContainer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SingleValueEncodingContainer.swift; sourceTree = ""; }; + 1B2A483629A90D9B00621E13 /* KeyedEncodingContainer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyedEncodingContainer.swift; sourceTree = ""; }; + 1B2A483729A90D9B00621E13 /* MessagePackEncoder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessagePackEncoder.swift; sourceTree = ""; }; + 1B2A483929A90D9B00621E13 /* KeyedDecodingContainer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyedDecodingContainer.swift; sourceTree = ""; }; + 1B2A483A29A90D9B00621E13 /* SingleValueDecodingContainer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SingleValueDecodingContainer.swift; sourceTree = ""; }; + 1B2A483B29A90D9B00621E13 /* MessagePackDecoder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessagePackDecoder.swift; sourceTree = ""; }; + 1B2A483C29A90D9B00621E13 /* UnkeyedDecodingContainer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnkeyedDecodingContainer.swift; sourceTree = ""; }; + 1B2A483D29A90D9B00621E13 /* Box.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Box.swift; sourceTree = ""; }; + 1B2A483E29A90D9B00621E13 /* DataSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataSpec.swift; sourceTree = ""; }; + 1B2A483F29A90D9B00621E13 /* AnyCodingKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnyCodingKey.swift; sourceTree = ""; }; 1B5849A6294D1C020055286B /* Queue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Queue.swift; sourceTree = ""; }; 1B5849A92950BC860055286B /* LocalAuthentication.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = LocalAuthentication.framework; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS9.1.sdk/System/Library/Frameworks/LocalAuthentication.framework; sourceTree = DEVELOPER_DIR; }; 1B59EC5629007DEE00A8718D /* BitwardenDB.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = BitwardenDB.xcdatamodel; sourceTree = ""; }; @@ -288,6 +312,7 @@ 1B15614C28B7F3D800610B9B /* bitwarden WatchKit Extension */ = { isa = PBXGroup; children = ( + 1B2A483129A90D9B00621E13 /* MessagePack */, 1B8BF9072919A2BC006F069E /* Controls */, 1B5AFF0629197809004478F9 /* Localization */, 1B59EC5F2900C48300A8718D /* Helpers */, @@ -320,6 +345,42 @@ path = "Preview Content"; sourceTree = ""; }; + 1B2A483129A90D9B00621E13 /* MessagePack */ = { + isa = PBXGroup; + children = ( + 1B2A483229A90D9B00621E13 /* FixedWidthInteger+Bytes.swift */, + 1B2A483329A90D9B00621E13 /* Encoder */, + 1B2A483829A90D9B00621E13 /* Decoder */, + 1B2A483D29A90D9B00621E13 /* Box.swift */, + 1B2A483E29A90D9B00621E13 /* DataSpec.swift */, + 1B2A483F29A90D9B00621E13 /* AnyCodingKey.swift */, + ); + name = MessagePack; + path = ../../../../lib/MessagePack/Sources/MessagePack; + sourceTree = ""; + }; + 1B2A483329A90D9B00621E13 /* Encoder */ = { + isa = PBXGroup; + children = ( + 1B2A483429A90D9B00621E13 /* UnkeyedEncodingContainer.swift */, + 1B2A483529A90D9B00621E13 /* SingleValueEncodingContainer.swift */, + 1B2A483629A90D9B00621E13 /* KeyedEncodingContainer.swift */, + 1B2A483729A90D9B00621E13 /* MessagePackEncoder.swift */, + ); + path = Encoder; + sourceTree = ""; + }; + 1B2A483829A90D9B00621E13 /* Decoder */ = { + isa = PBXGroup; + children = ( + 1B2A483929A90D9B00621E13 /* KeyedDecodingContainer.swift */, + 1B2A483A29A90D9B00621E13 /* SingleValueDecodingContainer.swift */, + 1B2A483B29A90D9B00621E13 /* MessagePackDecoder.swift */, + 1B2A483C29A90D9B00621E13 /* UnkeyedDecodingContainer.swift */, + ); + path = Decoder; + sourceTree = ""; + }; 1B5849A82950BC860055286B /* Frameworks */ = { isa = PBXGroup; children = ( @@ -584,8 +645,12 @@ 1BDBFEAC290B4215009C78C7 /* CipherListViewModel.swift in Sources */, 1BF5F6DB29103066002DDC0C /* CipherService.swift in Sources */, 1B5F5E3E293FBB17009B5FCC /* CipherItemView.swift in Sources */, + 1B2A484629A90D9B00621E13 /* SingleValueDecodingContainer.swift in Sources */, 1B15616C28B81A2200610B9B /* Cipher.swift in Sources */, + 1B2A484A29A90D9B00621E13 /* DataSpec.swift in Sources */, 1B8BF90429199BBB006F069E /* CipherDetailsView.swift in Sources */, + 1B2A484229A90D9B00621E13 /* SingleValueEncodingContainer.swift in Sources */, + 1B2A484929A90D9B00621E13 /* Box.swift in Sources */, 1B8BF9112919CDBB006F069E /* DateExtensions.swift in Sources */, 1BD291BB2927E9B50004F33F /* WatchDTO.swift in Sources */, 1B11C899291BFAB500CE58D8 /* CryptoFunctionService.swift in Sources */, @@ -595,32 +660,40 @@ 1BD291BD292807240004F33F /* User.swift in Sources */, 1BDBFEB3290B5D07009C78C7 /* CoreDataHelper.swift in Sources */, 1B15615028B7F3D800610B9B /* CipherListView.swift in Sources */, + 1B2A484529A90D9B00621E13 /* KeyedDecodingContainer.swift in Sources */, + 1B2A484329A90D9B00621E13 /* KeyedEncodingContainer.swift in Sources */, 1BD291BF292D0E6F0004F33F /* JsonDecoderExtensions.swift in Sources */, 1B59EC632901B1C100A8718D /* LoggerHelper.swift in Sources */, 1BD291C329311E1C0004F33F /* VaultTimeoutAction.swift in Sources */, 1B15615628B7F3D800610B9B /* ComplicationController.swift in Sources */, + 1B2A484129A90D9B00621E13 /* UnkeyedEncodingContainer.swift in Sources */, 1BD291B9292438830004F33F /* StateService.swift in Sources */, 1BC1CD6329227D3C006540DA /* EnvironmentService.swift in Sources */, 1B8BF9092919A2CC006F069E /* CircularProgressView.swift in Sources */, + 1B2A484B29A90D9B00621E13 /* AnyCodingKey.swift in Sources */, 1BD291B7292409410004F33F /* BWState.swift in Sources */, 1B15614E28B7F3D800610B9B /* bitwardenApp.swift in Sources */, 1BC1CD6929228CEB006540DA /* StringExtensions.swift in Sources */, 1BDBFEB1290B5BD3009C78C7 /* DBHelperProtocol.swift in Sources */, + 1B2A484429A90D9B00621E13 /* MessagePackEncoder.swift in Sources */, 1B8BF90629199EC5006F069E /* CipherDetailsViewModel.swift in Sources */, 1BF5F6DE29103B86002DDC0C /* CipherServiceMock.swift in Sources */, 1BC1CD672922871A006540DA /* URLExtensions.swift in Sources */, 1B11C89B291C587600CE58D8 /* UInt64Extensions.swift in Sources */, 1B8453ED290C672E00F921E1 /* CipherEntity+CoreDataProperties.swift in Sources */, + 1B2A484029A90D9B00621E13 /* FixedWidthInteger+Bytes.swift in Sources */, 1BC1CD6E2922B92B006540DA /* ImageView.swift in Sources */, 1BD291C1292E7E690004F33F /* ErrorExtensions.swift in Sources */, 1B14DF37291186D900EA43F1 /* EmptyStateViewModifier.swift in Sources */, 1BD291B52924047C0004F33F /* BWStateViewModel.swift in Sources */, 1B5849A7294D1C020055286B /* Queue.swift in Sources */, 1B15616E28B81A4300610B9B /* WatchConnectivityManager.swift in Sources */, + 1B2A484829A90D9B00621E13 /* UnkeyedDecodingContainer.swift in Sources */, 1B5AFF0329196C81004478F9 /* ColorUtils.swift in Sources */, 1B59EC612900C48E00A8718D /* KeychainHelper.swift in Sources */, 1B5BE453295A08C600E0C323 /* ExtensionDelegate.swift in Sources */, 1B59EC5729007DEE00A8718D /* BitwardenDB.xcdatamodeld in Sources */, + 1B2A484729A90D9B00621E13 /* MessagePackDecoder.swift in Sources */, 1B8BF90D2919BED9006F069E /* Base32.swift in Sources */, 1B8453EC290C672E00F921E1 /* CipherEntity+CoreDataClass.swift in Sources */, 1BC1CD6529227F3C006540DA /* IconImageHelper.swift in Sources */,