internal SharedFolder(SyncDownSharedFolder sf) { Uid = sf.sharedFolderUid; Name = Encoding.UTF8.GetString(CryptoUtils.DecryptAesV1(sf.name.Base64UrlDecode(), sf.unencryptedSharedFolderKey)); ManageRecords = sf.manageRecords ?? DefaultManageRecords; ManageUsers = sf.manageUsers ?? DefaultManageUsers; DefaultManageRecords = sf.defaultManageRecords; DefaultManageUsers = sf.defaultManageUsers; DefaultCanEdit = sf.defaultCanEdit; DefaultCanShare = sf.defaultCanShare; if (sf.records != null) { foreach (var r in sf.records) { Records.Add(new SharedFolderRecord { RecordUid = r.recordUid, CanEdit = r.canEdit, CanShare = r.canShare }); } } if (sf.users != null) { foreach (var u in sf.users) { Users.Add(new SharedFolderUser { Username = u.username, ManageRecords = u.manageRecords, ManageUsers = u.manageUsers }); } } if (sf.teams != null) { foreach (var t in sf.teams) { Teams.Add(new SharedFolderTeam { TeamUid = t.teamUid, Name = t.name, ManageRecords = t.manageRecords, ManageUsers = t.manageUsers }); } } }
private static void ProcessSubFolders(this Vault vault, SyncDownResponse rs) { var comparers = new Comparers(); var folderList = new SortedSet <IFolderNode>(vault.userFolders.Values, comparers); var recordList = new SortedSet <IRecordNode>(vault.userFolderRecords ?? Enumerable.Empty <IRecordNode>(), comparers); if (rs.userFoldersRemoved != null) { foreach (var ufr in rs.userFoldersRemoved) { folderList.RemoveWhere(x => x.FolderUid == ufr.folderUid); recordList.RemoveWhere(x => x.FolderUid == ufr.folderUid); } } if (rs.sharedFolderFolderRemoved != null) { foreach (var sffr in rs.sharedFolderFolderRemoved) { folderList.RemoveWhere(x => x.FolderUid == sffr.folderUid); recordList.RemoveWhere(x => x.FolderUid == sffr.folderUid); } } if (rs.userFolderSharedFoldersRemoved != null) { foreach (var ufsfr in rs.userFolderSharedFoldersRemoved) { folderList.RemoveWhere(x => x.FolderUid == ufsfr.folderUid); recordList.RemoveWhere(x => x.FolderUid == ufsfr.folderUid); } } if (rs.userFoldersRemovedRecords != null) { foreach (var uffr in rs.userFoldersRemovedRecords) { recordList.Remove(uffr); } } if (rs.sharedFolderFolderRecordsRemoved != null) { foreach (var sffrr in rs.sharedFolderFolderRecordsRemoved) { recordList.Remove(sffrr); } } if (rs.userFolders != null) { foreach (var uf in rs.userFolders) { var encryptedKey = uf.userFolderKey.Base64UrlDecode(); uf.unencryptedFolderKey = uf.keyType == 2 ? CryptoUtils.DecryptRsa(encryptedKey, vault.Auth.PrivateKey) : CryptoUtils.DecryptAesV1(encryptedKey, vault.Auth.DataKey); folderList.Remove(uf); folderList.Add(uf); } } if (rs.sharedFolderFolders != null) { foreach (var sff in rs.sharedFolderFolders) { if (vault.sharedFolders.TryGetValue(sff.sharedFolderUid, out SyncDownSharedFolder sf)) { var encryptedKey = sff.sharedFolderFolderKey.Base64UrlDecode(); sff.unencryptedFolderKey = CryptoUtils.DecryptAesV1(encryptedKey, sf.unencryptedSharedFolderKey); folderList.Remove(sff); folderList.Add(sff); } else { Trace.TraceError("Sync_Down: shared_folder_folders: Shared Folder UID {0} not found", sff.sharedFolderUid); } } } if (rs.userFolderSharedFolders != null) { foreach (var ufsf in rs.userFolderSharedFolders) { folderList.Remove(ufsf); folderList.Add(ufsf); } } if (rs.userFolderRecords != null) { foreach (var ufr in rs.userFolderRecords) { recordList.Add(ufr); } } if (rs.sharedFolderFolderRecords != null) { foreach (var sffr in rs.sharedFolderFolderRecords) { recordList.Add(sffr); } } var toDelete = new HashSet <string>(); foreach (var folder in vault.keeperFolders.Values) { toDelete.Add(folder.FolderUid); folder.Children.Clear(); folder.Records.Clear(); } foreach (var folder in folderList) { if (vault.keeperFolders.TryGetValue(folder.FolderUid, out FolderNode node)) { toDelete.Remove(folder.FolderUid); node.Children.Clear(); node.Records.Clear(); node.Name = null; } else { node = new FolderNode { FolderType = folder.Type, FolderUid = folder.FolderUid }; vault.keeperFolders.Add(folder.FolderUid, node); } node.ParentUid = folder.ParentUid; byte[] unencrypted_data = null; switch (folder.Type) { case FolderType.UserFolder: if (folder is SyncDownUserFolder uf) { unencrypted_data = CryptoUtils.DecryptAesV1(uf.data.Base64UrlDecode(), uf.unencryptedFolderKey); } else { Trace.TraceError("Folder UID {0} expected to be User-Folder", folder.FolderUid); } break; case FolderType.SharedFolderForder: if (folder is SyncDownSharedFolderFolder sff) { unencrypted_data = CryptoUtils.DecryptAesV1(sff.data.Base64UrlDecode(), sff.unencryptedFolderKey); } else { Trace.TraceError("Folder UID {0} expected to be Shared-Folder-Folder", folder.FolderUid); } break; case FolderType.SharedFolder: if (vault.sharedFolders.TryGetValue(folder.FolderUid, out SyncDownSharedFolder sf)) { node.Name = Encoding.UTF8.GetString(CryptoUtils.DecryptAesV1(sf.name.Base64UrlDecode(), sf.unencryptedSharedFolderKey)); } else { Trace.TraceError("Folder UID {0} expected to be Shared-Folder", folder.FolderUid); } break; } if (unencrypted_data != null) { var serializer = new DataContractJsonSerializer(typeof(FolderData)); using (var stream = new MemoryStream(unencrypted_data)) { var folderData = serializer.ReadObject(stream) as FolderData; node.Name = folderData.name; } } if (string.IsNullOrEmpty(node.Name)) { node.Name = node.FolderUid; } } foreach (var uid in toDelete) { vault.keeperFolders.Remove(uid); } vault.Root.Children.Clear(); vault.Root.Records.Clear(); foreach (var node in vault.keeperFolders.Values) { if (string.IsNullOrEmpty(node.ParentUid)) { vault.Root.Children.Add(node.FolderUid); } else { if (vault.keeperFolders.TryGetValue(node.ParentUid, out FolderNode parent)) { parent.Children.Add(node.FolderUid); } else { Trace.TraceError("Folder UID {0} was lost", node.FolderUid); } } } foreach (var record in recordList) { if (string.IsNullOrEmpty(record.FolderUid)) { vault.Root.Records.Add(record.RecordUid); } else { if (vault.keeperFolders.TryGetValue(record.FolderUid, out FolderNode node)) { node.Records.Add(record.RecordUid); } else { Trace.TraceError("Folder UID {0} was lost", node.FolderUid); vault.Root.Records.Add(record.RecordUid); } } } vault.userFolders.Clear(); foreach (var folder in folderList) { vault.userFolders.Add(folder.FolderUid, folder); } vault.userFolderRecords = recordList.ToList(); }
internal static void DecryptRecords(this Vault vault) { var uids = new HashSet <string>(); uids.UnionWith(vault.records.Keys); uids.ExceptWith(vault.keeperRecords.Keys); if (uids.Count > 0) { var dataSerializer = new DataContractJsonSerializer(typeof(RecordData)); var extraSerializer = new DataContractJsonSerializer(typeof(RecordExtra)); foreach (var uid in uids) { if (vault.records.TryGetValue(uid, out SyncDownRecord sdr)) { try { var record = new PasswordRecord(uid); var unencrypted_data = CryptoUtils.DecryptAesV1(sdr.data.Base64UrlDecode(), sdr.unencryptedRecordKey); using (var ms = new MemoryStream(unencrypted_data)) { var data = (RecordData)dataSerializer.ReadObject(ms); record.Title = data.title; record.Login = data.secret1; record.Password = data.secret2; record.Link = data.link; record.Notes = data.notes; if (data.custom != null) { foreach (var cr in data.custom) { record.Custom.Add(new CustomField { Name = cr.name, Value = cr.value, Type = cr.type }); } } } if (!string.IsNullOrEmpty(sdr.extra)) { var unencrypted_extra = CryptoUtils.DecryptAesV1(sdr.extra.Base64UrlDecode(), sdr.unencryptedRecordKey); using (var ms = new MemoryStream(unencrypted_extra)) { var extra = (RecordExtra)extraSerializer.ReadObject(ms); if (extra.files != null && extra.files.Length > 0) { foreach (var file in extra.files) { var atta = new AttachmentFile { Id = file.id, Key = file.key, Name = file.name, Title = file.title ?? "", Type = file.type ?? "", Size = file.size ?? 0, LastModified = file.lastModified != null?file.lastModified.Value.FromUnixTimeMilliseconds() : DateTimeOffset.Now }; if (file.thumbs != null) { atta.Thumbnails = file.thumbs .Select(t => new AttachmentFileThumb { Id = t.id, Type = t.type, Size = t.size ?? 0 }) .ToArray(); } record.Attachments.Add(atta); } } } } vault.keeperRecords.Add(uid, record); } catch (Exception e) { Trace.TraceError("Decrypt Record: UID: {0}, {1}: \"{2}\"", uid, e.GetType().Name, e.Message); } } } } }
public static async Task SyncDown(this Vault vault) { var command = new SyncDownCommand { revision = vault.Revision, include = new string[] { "sfheaders", "sfrecords", "sfusers", "teams", "folders" }, deviceName = KeeperEndpoint.DefaultDeviceName }; var rs = await vault.Auth.ExecuteAuthCommand <SyncDownCommand, SyncDownResponse>(command); ISet <string> uids = new HashSet <string>(); if (rs.fullSync) { vault.metaData.Clear(); vault.records.Clear(); vault.sharedFolders.Clear(); vault.teams.Clear(); vault.keeperFolders.Clear(); vault.userFolderRecords = null; vault.keeperRecords.Clear(); } vault.Revision = rs.revision; if (rs.removedRecords != null) { foreach (var uid in rs.removedRecords) { vault.DeleteRecordKey(uid); vault.metaData.Remove(uid); } } if (rs.removedTeams != null) { foreach (var teamUid in rs.removedTeams) { vault.DeleteTeamKey(teamUid); if (vault.teams.TryGetValue(teamUid, out SyncDownTeam sdt)) { if (sdt.sharedFolderKeys != null) { foreach (var sfk in sdt.sharedFolderKeys) { if (vault.sharedFolders.TryGetValue(sfk.sharedFolderUid, out SyncDownSharedFolder sdsf)) { if (sdsf.teams != null) { sdsf.teams = sdsf.teams.Where(x => x.teamUid != teamUid).ToArray(); } } } } vault.teams.Remove(teamUid); } } } if (rs.removedSharedFolders != null) { foreach (var sharedFolderUid in rs.removedSharedFolders) { vault.DeleteSharedFolderKey(sharedFolderUid); if (vault.sharedFolders.TryGetValue(sharedFolderUid, out SyncDownSharedFolder sdsf)) { sdsf.sharedFolderKey = null; sdsf.keyType = null; if (sdsf.users != null) { sdsf.users = sdsf.users.Where(x => string.Compare(x.username, vault.Auth.Username, true) != 0).ToArray(); } } } } if (rs.teams != null) { foreach (var t in rs.teams) { if (vault.teams.TryGetValue(t.teamUid, out SyncDownTeam team)) { if (t.removedSharedFolders != null) { uids.Clear(); uids.UnionWith(t.removedSharedFolders); team.sharedFolderKeys = t.sharedFolderKeys.Where(x => uids.Contains(x.sharedFolderUid)).ToArray(); } if (t.sharedFolderKeys != null) { if (team.sharedFolderKeys == null) { team.sharedFolderKeys = t.sharedFolderKeys; } else { team.sharedFolderKeys = team.sharedFolderKeys.Concat(t.sharedFolderKeys).ToArray(); } } team.name = t.name ?? team.name; team.restrictEdit = t.restrictEdit; team.restrictView = t.restrictView; team.restrictShare = t.restrictShare; } else { vault.teams.Add(t.teamUid, t); } } } if (rs.sharedFolders != null) { foreach (var sf in rs.sharedFolders) { if (sf.fullSync == true) { vault.sharedFolders.Remove(sf.sharedFolderUid); } if (vault.sharedFolders.TryGetValue(sf.sharedFolderUid, out SyncDownSharedFolder sharedFolder)) { sharedFolder.revision = sf.revision; sharedFolder.manageRecords = sf.manageRecords ?? sharedFolder.manageRecords; sharedFolder.manageUsers = sf.manageUsers ?? sharedFolder.manageUsers; sharedFolder.name = sf.name ?? sharedFolder.name; if (sf.recordsRemoved != null && sharedFolder.records != null) { uids.Clear(); uids.UnionWith(sf.recordsRemoved); sharedFolder.records = sharedFolder.records.Where(x => !uids.Contains(x.recordUid)).ToArray(); } if (sf.usersRemoved != null && sharedFolder.users != null) { uids.Clear(); uids.UnionWith(sf.usersRemoved); sharedFolder.users = sharedFolder.users.Where(x => !uids.Contains(x.username)).ToArray(); } if (sf.teamsRemoved != null && sharedFolder.teams != null) { uids.Clear(); uids.UnionWith(sf.teamsRemoved); sharedFolder.teams = sharedFolder.teams.Where(x => !uids.Contains(x.teamUid)).ToArray(); } if (sf.records != null) { if (sharedFolder.records != null) { sharedFolder.records = sharedFolder.records.Concat(sf.records).ToArray(); } else { sharedFolder.records = sf.records; } } if (sf.users != null) { if (sharedFolder.users != null) { sharedFolder.users = sharedFolder.users.Concat(sf.users).ToArray(); } else { sharedFolder.users = sf.users; } } if (sf.teams != null) { if (sharedFolder.teams != null) { sharedFolder.teams = sharedFolder.teams.Concat(sf.teams).ToArray(); } else { sharedFolder.teams = sf.teams; } } } else { vault.sharedFolders.Add(sf.sharedFolderUid, sf); } vault.keeperSharedFolders.Remove(sf.sharedFolderUid); } } if (rs.recordMetaData != null) { foreach (var rmd in rs.recordMetaData) { if (vault.metaData.TryGetValue(rmd.recordUid, out SyncDownRecordMetaData metaData)) { metaData.recordKey = rmd.recordKey; metaData.recordKeyType = rmd.recordKeyType; metaData.owner = rmd.owner; metaData.canEdit = rmd.canEdit; metaData.canShare = rmd.canShare; } else { vault.metaData.Add(rmd.recordUid, rmd); } } } if (rs.records != null) { foreach (var r in rs.records) { if (vault.records.TryGetValue(r.recordUid, out SyncDownRecord record)) { record.data = r.data; record.extra = r.extra; record.udata = r.udata; record.clientModifiedTime = r.clientModifiedTime; record.revision = r.revision; record.version = r.version; record.shared = r.shared; } else { vault.records.Add(r.recordUid, r); } vault.keeperRecords.Remove(r.recordUid); } } //Process keys foreach (var team in vault.teams.Values) { if (team.unencryptedTeamKey == null) { byte[] teamKey = null; try { if (team.teamKeyType == 1) { teamKey = CryptoUtils.DecryptAesV1(team.teamKey.Base64UrlDecode(), vault.Auth.DataKey); } else if (team.teamKeyType == 2) { teamKey = CryptoUtils.DecryptRsa(team.teamKey.Base64UrlDecode(), vault.Auth.PrivateKey); } } catch (Exception e) { Trace.TraceError("Decrypt Team Key: UID: {0}, {1}: \"{2}\"", team.teamUid, e.GetType().Name, e.Message); } if (teamKey != null) { team.unencryptedTeamKey = teamKey; } } } foreach (var sharedFolder in vault.sharedFolders.Values) { if (sharedFolder.unencryptedSharedFolderKey == null) { byte[] key = null; if (string.IsNullOrEmpty(sharedFolder.sharedFolderKey)) { if (sharedFolder.teams != null) { foreach (var team in sharedFolder.teams) { if (vault.teams.TryGetValue(team.teamUid, out SyncDownTeam sdt)) { if (sdt.sharedFolderKeys != null) { var sfk = sdt.sharedFolderKeys.FirstOrDefault(x => x.sharedFolderUid == sharedFolder.sharedFolderUid); if (sfk != null) { try { if (sfk.keyType == 1) { key = CryptoUtils.DecryptAesV1(sfk.sharedFolderKey.Base64UrlDecode(), sdt.unencryptedTeamKey); } else if (sfk.keyType == 2) { key = CryptoUtils.DecryptRsa(sfk.sharedFolderKey.Base64UrlDecode(), sdt.PrivateKey); } } catch (Exception e) { Trace.TraceError("Decrypt Shared Folder Key: UID: {0}, Team UID: {1}, {2}: \"{3}\"", sharedFolder.sharedFolderUid, sdt.teamUid, e.GetType().Name, e.Message); } } } } if (key != null) { break; } } } } else { try { if (sharedFolder.keyType == 1) { key = CryptoUtils.DecryptAesV1(sharedFolder.sharedFolderKey.Base64UrlDecode(), vault.Auth.DataKey); } else if (sharedFolder.keyType == 2) { key = CryptoUtils.DecryptRsa(sharedFolder.sharedFolderKey.Base64UrlDecode(), vault.Auth.PrivateKey); } } catch (Exception e) { Trace.TraceError("Decrypt Shared Folder Key: UID: {0}, {1}: \"{2}\"", sharedFolder.sharedFolderUid, e.GetType().Name, e.Message); } } if (key != null) { sharedFolder.unencryptedSharedFolderKey = key; } } } uids.Clear(); uids.UnionWith(vault.sharedFolders.Values.Where(x => x.unencryptedSharedFolderKey == null).Select(x => x.sharedFolderUid)); foreach (var uid in uids) { vault.sharedFolders.Remove(uid); } foreach (var record in vault.records.Values) { if (record.unencryptedRecordKey == null) { byte[] key = null; if (vault.metaData.TryGetValue(record.recordUid, out SyncDownRecordMetaData sdrmd)) { if (string.IsNullOrEmpty(sdrmd.recordKey)) { key = vault.Auth.DataKey; } else { try { if (sdrmd.recordKeyType == 1) { key = CryptoUtils.DecryptAesV1(sdrmd.recordKey.Base64UrlDecode(), vault.Auth.DataKey); } else if (sdrmd.recordKeyType == 2) { key = CryptoUtils.DecryptRsa(sdrmd.recordKey.Base64UrlDecode(), vault.Auth.PrivateKey); } } catch (Exception e) { Trace.TraceError("Decrypt Record Key: UID: {0}, {1}: \"{2}\"", record.recordUid, e.GetType().Name, e.Message); } } } else { foreach (var sharedFolder in vault.sharedFolders.Values) { if (sharedFolder.records != null) { var sfr = sharedFolder.records.FirstOrDefault(x => x.recordUid == record.recordUid); if (sfr != null) { try { key = CryptoUtils.DecryptAesV1(sfr.recordKey.Base64UrlDecode(), sharedFolder.unencryptedSharedFolderKey); } catch (Exception e) { Trace.TraceError("Decrypt Record Key: UID: {0}, Shared Folder UID: {1}, {2}: \"{3}\"", record.recordUid, sharedFolder.sharedFolderUid, e.GetType().Name, e.Message); } } if (key != null) { break; } } } } if (key != null) { record.unencryptedRecordKey = key; } } } uids.Clear(); uids.UnionWith(vault.records.Values.Where(x => x.unencryptedRecordKey == null).Select(x => x.recordUid)); foreach (var uid in uids) { vault.records.Remove(uid); } uids.Clear(); uids.UnionWith(vault.keeperRecords.Keys); uids.ExceptWith(vault.records.Keys); foreach (var uid in uids) { vault.keeperRecords.Remove(uid); } vault.DecryptRecords(); uids.Clear(); uids.UnionWith(vault.sharedFolders.Values.Where(x => x.unencryptedSharedFolderKey == null).Select(x => x.sharedFolderUid)); foreach (var uid in uids) { vault.sharedFolders.Remove(uid); } uids.Clear(); uids.UnionWith(vault.keeperSharedFolders.Keys); uids.ExceptWith(vault.sharedFolders.Keys); foreach (var uid in uids) { vault.keeperSharedFolders.Remove(uid); } vault.DecryptSharedFolders(); vault.ProcessSubFolders(rs); }
public async Task SaveRecord(PasswordRecord record, bool skipData = false, bool skipExtra = true) { SyncDownRecord existingRecord = null; if (!string.IsNullOrEmpty(record.Uid)) { records.TryGetValue(record.Uid, out existingRecord); } var updateRecord = new RecordUpdateRecord(); byte[] recordKey = null; if (existingRecord != null) { updateRecord.recordUid = existingRecord.recordUid; recordKey = existingRecord.unencryptedRecordKey; if (metaData.TryGetValue(existingRecord.recordUid, out SyncDownRecordMetaData sdrmd)) { if (sdrmd.recordKeyType == 2) { updateRecord.recordKey = CryptoUtils.EncryptAesV1(recordKey, Auth.DataKey).Base64UrlEncode(); } } updateRecord.revision = existingRecord.revision; ResolveRecordAccessPath(updateRecord); } else { updateRecord.recordUid = CryptoUtils.GenerateUid(); recordKey = CryptoUtils.GenerateEncryptionKey(); updateRecord.recordKey = CryptoUtils.EncryptAesV1(recordKey, Auth.DataKey).Base64UrlEncode(); updateRecord.revision = 0; } var settings = new DataContractJsonSerializerSettings { UseSimpleDictionaryFormat = true }; if (!skipData) { var dataSerializer = new DataContractJsonSerializer(typeof(RecordData), settings); RecordData existingData = null; if (existingRecord != null) { try { var unencrypted_data = CryptoUtils.DecryptAesV1(existingRecord.data.Base64UrlDecode(), existingRecord.unencryptedRecordKey); using (var ms = new MemoryStream(unencrypted_data)) { existingData = (RecordData)dataSerializer.ReadObject(ms); } } catch (Exception e) { Trace.TraceError("Decrypt Record: UID: {0}, {1}: \"{2}\"", existingRecord.recordUid, e.GetType().Name, e.Message); } } var data = record.ExtractRecordData(existingData); using (var ms = new MemoryStream()) { dataSerializer.WriteObject(ms, data); updateRecord.data = CryptoUtils.EncryptAesV1(ms.ToArray(), recordKey).Base64UrlEncode(); } } if (!skipExtra) { var extraSerializer = new DataContractJsonSerializer(typeof(RecordExtra), settings); RecordExtra existingExtra = null; if (existingRecord != null) { try { var unencrypted_extra = CryptoUtils.DecryptAesV1(existingRecord.extra.Base64UrlDecode(), existingRecord.unencryptedRecordKey); using (var ms = new MemoryStream(unencrypted_extra)) { existingExtra = (RecordExtra)extraSerializer.ReadObject(ms); } } catch (Exception e) { Trace.TraceError("Decrypt Record: UID: {0}, {1}: \"{2}\"", existingRecord.recordUid, e.GetType().Name, e.Message); } } var extra = record.ExtractRecordExtra(existingExtra); using (var ms = new MemoryStream()) { extraSerializer.WriteObject(ms, extra); updateRecord.extra = CryptoUtils.EncryptAesV1(ms.ToArray(), recordKey).Base64UrlEncode(); } var udata = new RecordUpdateUData(); var ids = new HashSet <string>(); if (record.Attachments != null) { foreach (var atta in record.Attachments) { ids.Add(atta.Id); if (atta.Thumbnails != null) { foreach (var thumb in atta.Thumbnails) { ids.Add(thumb.Id); } } } } udata.fileIds = ids.ToArray(); updateRecord.udata = udata; } var command = new RecordUpdateCommand(); if (existingRecord != null) { command.updateRecords = new RecordUpdateRecord[] { updateRecord }; } else { command.addRecords = new RecordUpdateRecord[] { updateRecord }; } var rs = await Auth.ExecuteAuthCommand <RecordUpdateCommand, RecordUpdateResponse>(command); await this.SyncDown(); }
public async Task Login(IUserConfiguration user = null) { var configuration = Api.Storage.Get(); user = await ResolveUserConfiguration(user, configuration); var username = user?.Username; var password = user?.Password; if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password)) { return; } var token = user.TwoFactorToken; var tokenType = "device_token"; string authHash = null; PreLoginResponse preLogin = null; while (true) { if (preLogin == null) { preLogin = await Api.GetPreLogin(username); authHash = null; } var authParams = preLogin.Salt[0]; int iterations = authParams.Iterations; byte[] salt = authParams.Salt_.ToByteArray(); if (authHash == null) { authHash = CryptoUtils.DeriveV1KeyHash(password, salt, iterations).Base64UrlEncode(); } var command = new LoginCommand(); command.username = username; command.authResponse = authHash; command.include = new[] { "keys", "settings", "enforcements", "is_enterprise_admin" }; command.twoFactorToken = token; command.twoFactorType = !string.IsNullOrEmpty(token) ? tokenType : null; command.deviceTokenExpiresInDays = !string.IsNullOrEmpty(token) && tokenType != "device_token" ? 9999 : (int?)null; var loginRs = await Api.ExecuteV2Command <LoginCommand, LoginResponse>(command); if (!loginRs.IsSuccess && loginRs.resultCode == "auth_failed") // invalid password { throw new Exception("Invalid user name or password"); } else { if (!string.IsNullOrEmpty(loginRs.deviceToken)) { token = loginRs.deviceToken; tokenType = "device_token"; } SessionToken = loginRs.sessionToken; Username = username; accountSettings = loginRs.accountSettings; if (loginRs.keys != null) { if (loginRs.keys.encryptedDataKey != null) { var key = CryptoUtils.DeriveKeyV2("data_key", password, salt, iterations); DataKey = CryptoUtils.DecryptAesV2(loginRs.keys.encryptedDataKey.Base64UrlDecode(), key); } else if (loginRs.keys.encryptionParams != null) { DataKey = CryptoUtils.DecryptEncryptionParams(password, loginRs.keys.encryptionParams.Base64UrlDecode()); } else { throw new Exception("Missing data key"); } if (loginRs.keys.encryptedPrivateKey != null) { privateKeyData = CryptoUtils.DecryptAesV1(loginRs.keys.encryptedPrivateKey.Base64UrlDecode(), DataKey); privateKey = null; } } if (loginRs.IsSuccess) { EncryptedPassword = CryptoUtils.EncryptAesV2(Encoding.UTF8.GetBytes(password), DataKey); TwoFactorToken = token; authResponse = authHash; IsEnterpriseAdmin = loginRs.isEnterpriseAdmin ?? false; enforcements = loginRs.enforcements; StoreConfigurationIfChanged(configuration); break; } switch (loginRs.resultCode) { case "need_totp": case "invalid_device_token": case "invalid_totp": token = await Ui.GetTwoFactorCode(); if (!string.IsNullOrEmpty(token)) { tokenType = "one_time"; continue; } break; case "auth_expired": await Ui.DisplayDialog(DialogType.Information, loginRs.message); password = await this.ChangeMasterPassword(iterations); if (!string.IsNullOrEmpty(password)) { preLogin = null; continue; } break; case "auth_expired_transfer": var shareAccountTo = loginRs.accountSettings.shareAccountTo; if (await Ui.DisplayDialog(DialogType.Confirmation, "Do you accept Account Transfer policy?")) { await this.ShareAccount(); continue; } break; } throw new KeeperApiException(loginRs.resultCode, loginRs.message); } } }