private bool ProcessMessageRequestEntrySecrets(
            GetMessages.ResponseParamsMessage messageItem, DeviceToDeviceMessages.IMessage message)
        {
            var messageContents = (DeviceToDeviceMessages.RequestEntrySecrets)message;
            
            var accountSettings = _model.ServerAccountSettings.Query().First();

            var entry = _model.Entries.Query().FirstOrDefault(r => r.Identifier == messageContents.EntryIdentifier);
            if (entry == null)
                return false;
            
            var linkedDeviceCryptoKeyId = accountSettings.LinkedDeviceCryptoKeyId;
            var linkedDeviceCryptoKey = _model.CryptoKeys.Query().First(r => r.Id == linkedDeviceCryptoKeyId);

            var entrySecrets = new Dictionary<string, string>();
            foreach (var secretIdentifier in messageContents.SecretIdentifiers)
            {
                var entrySecretsResult = _model.EntriesSharedSecrets.Query()
                    .FirstOrDefault(r => r.EntryId == entry.Id && r.SecretIdentifier == secretIdentifier);

                if (entrySecretsResult == null)
                    continue;

                var entrySecretData = _model.EntriesSharedSecretsData.Query().First(r => r.Id == entrySecretsResult.EntrySecretDataId);
                entrySecrets.Add(entrySecretsResult.SecretIdentifier, entrySecretData.Secret);
            }

            if (entrySecrets.Count != messageContents.SecretIdentifiers.Count)
            {
                // We don't appear to have all the requested secrets.
                return false;
            }

            var requestConfirmed = _controller.PromptSecretShareRequestSafe();

            var apiRequest = new SendLinkedDeviceMessage
            {
                //LinkIdentifier = link.Identifier,
                SecondsValidFor = 30
            };
            if (!requestConfirmed)
            {
                var deniedMessage = new DeviceToDeviceMessages.SendEntrySecrets
                {
                    OriginalMessageIdentifier = messageItem.Identifier,
                    RequestAccepted = false
                };
                apiRequest.SetMessage(deniedMessage, linkedDeviceCryptoKey.PublicKeyPem);
                apiRequest.GetResponse(GetApiClient());
                return true;
            }

            var acceptedMessage = new DeviceToDeviceMessages.SendEntrySecrets
            {
                OriginalMessageIdentifier = messageItem.Identifier,
                RequestAccepted = true,
                Secrets = entrySecrets
            };
            apiRequest.SetMessage(acceptedMessage, linkedDeviceCryptoKey.PublicKeyPem);
            apiRequest.GetResponse(GetApiClient());

            return true;
        }
        private bool ProcessMessageRequestEntrySecrets(
            GetMessages.ResponseParamsMessage messageItem, DeviceToDeviceMessages.IMessage message)
        {
            var messageContents = (DeviceToDeviceMessages.RequestEntrySecrets)message;

            var accountSettings = _model.ServerAccountSettings.Query().First();

            var entry = _model.Entries.Query().FirstOrDefault(r => r.Identifier == messageContents.EntryIdentifier);

            if (entry == null)
            {
                return(false);
            }

            var linkedDeviceCryptoKeyId = accountSettings.LinkedDeviceCryptoKeyId;
            var linkedDeviceCryptoKey   = _model.CryptoKeys.Query().First(r => r.Id == linkedDeviceCryptoKeyId);

            var entrySecrets = new Dictionary <string, string>();

            foreach (var secretIdentifier in messageContents.SecretIdentifiers)
            {
                var entrySecretsResult = _model.EntriesSharedSecrets.Query()
                                         .FirstOrDefault(r => r.EntryId == entry.Id && r.SecretIdentifier == secretIdentifier);

                if (entrySecretsResult == null)
                {
                    continue;
                }

                var entrySecretData = _model.EntriesSharedSecretsData.Query().First(r => r.Id == entrySecretsResult.EntrySecretDataId);
                entrySecrets.Add(entrySecretsResult.SecretIdentifier, entrySecretData.Secret);
            }

            if (entrySecrets.Count != messageContents.SecretIdentifiers.Count)
            {
                // We don't appear to have all the requested secrets.
                return(false);
            }

            var requestConfirmed = _controller.PromptSecretShareRequestSafe();

            var apiRequest = new SendLinkedDeviceMessage
            {
                //LinkIdentifier = link.Identifier,
                SecondsValidFor = 30
            };

            if (!requestConfirmed)
            {
                var deniedMessage = new DeviceToDeviceMessages.SendEntrySecrets
                {
                    OriginalMessageIdentifier = messageItem.Identifier,
                    RequestAccepted           = false
                };
                apiRequest.SetMessage(deniedMessage, linkedDeviceCryptoKey.PublicKeyPem);
                apiRequest.GetResponse(GetApiClient());
                return(true);
            }

            var acceptedMessage = new DeviceToDeviceMessages.SendEntrySecrets
            {
                OriginalMessageIdentifier = messageItem.Identifier,
                RequestAccepted           = true,
                Secrets = entrySecrets
            };

            apiRequest.SetMessage(acceptedMessage, linkedDeviceCryptoKey.PublicKeyPem);
            apiRequest.GetResponse(GetApiClient());

            return(true);
        }
Beispiel #3
0
        private void SyncDatabaseUploadEntries(int databaseId)
        {
            if (_databaseEntriesUploadRunning)
                return;

            if (_requestErrors > 0)
                return;

            var serverAccount = GetServerAccount();
            var linkedClientCryptoKey = Model.CryptoKeys.Get(serverAccount.LinkedDeviceCryptoKeyId);
            var database = Model.Databases.Get(databaseId);

            var entriesToBeDeleted = Model.DatabasesEntries.Find(new DatabaseEntry {ToBeDeleted = true}).ToList();
            foreach (var entryToBeDeleted in entriesToBeDeleted)
            {
                if (_stopSyncLoop || _processMessagesOnly)
                    return;

                var deleteRequest = new DeleteDatabaseEntry
                {
                    LinkIdentifier = database.Identifier,
                    EntryIdentifier = entryToBeDeleted.Identifier
                };

                var deleteOnDeviceMessageContent = new DeviceToDeviceMessages.DeleteEntry
                {
                    LinkIdentifier = database.Identifier,
                    EntryIdentifier = entryToBeDeleted.Identifier
                };
                var deleteOnDeviceMessageRequest = new SendLinkedDeviceMessage();
                deleteOnDeviceMessageRequest.SetMessage(deleteOnDeviceMessageContent, linkedClientCryptoKey.PublicKeyPem);

                try
                {
                    deleteRequest.GetResponse(GetApiClient());
                    deleteOnDeviceMessageRequest.GetResponse(GetApiClient());

                    _requestErrors = 0;
                }
                catch (NetworkErrorException)
                {
                    _requestErrors++;
                    return;
                }
                catch (RequestException)
                {
                    // Try again next time.
                    continue;
                }

                var versions = Model.DatabasesEntriesDataVersions.Find(new DatabaseEntryDataVersion
                {
                    DatabaseEntryId = entryToBeDeleted.Id
                });
                foreach (var version in versions)
                {
                    Model.DatabasesEntriesData.Delete(version.DatabaseEntryDataId);
                    Model.DatabasesEntriesDataVersions.Delete(version.Id);
                }

                Model.DatabasesEntries.Delete(entryToBeDeleted.Id);
            }

            _databaseEntriesUploadRunning = true;
            Task.Run(() =>
            {
                var databaseEntries = Model.DatabasesEntries.Find(new DatabaseEntry { DatabaseId = databaseId });

                var entriesToSend = new List<DatabaseEntry>();
                var entriesDataToSend = new List<DatabaseEntryData>();
                var entriesDataSerialized = new List<string>();
                var entriesSyncRequest = new List<DatabaseEntryDataSync>();
                var entriesUploadItems = new List<SetDatabaseEntries.EntryItem>();

                foreach (var entry in databaseEntries)
                {
                    if (_stopSyncLoop || _processMessagesOnly)
                    {
                        _databaseEntriesUploadRunning = false;
                        return;
                    }

                    var entryData = Model.DatabasesEntriesData.Get(entry.DatabaseEntryDataId);
                    if (!entryData.PasswordShared)
                        continue;

                    // Is there an update request for this entry?
                    var updateRequests = Model.DatabasesEntriesDataSync.Find(new DatabaseEntryDataSync
                    {
                        DatabaseEntryId = entry.Id
                    });
                    var updateRequestsSorted = updateRequests?.OrderByDescending(update => update.CreatedAt);
                    var latestUpdateRequest = updateRequestsSorted?.FirstOrDefault();
                    if (latestUpdateRequest == null)
                        continue;

                    // Remove duplicate update requests
                    foreach (var updateRequest in updateRequestsSorted)
                    {
                        if (updateRequest.Id == latestUpdateRequest.Id)
                            continue;

                        Model.DatabasesEntriesDataSync.Delete(updateRequest.Id);
                    }

                    var newVersion = entry.Version + 1;
                    var serializedEntry = JsonConvert.SerializeObject(entryData);
                    var encryptedData = new EncryptedDataWithPassword(
                        Util.CompressData(serializedEntry), serverAccount.BackupEncryptionPassword).ToString();

                    var group = Model.DatabasesGroups.Get(entry.DatabaseGroupId);
                    entriesUploadItems.Add(new SetDatabaseEntries.EntryItem
                    {
                        Version = newVersion,
                        GroupIdentifier = group.Identifier,
                        EntryIdentifier = entry.Identifier,
                        DataType = "ModelJsonGz",
                        Data = encryptedData
                    });

                    entriesToSend.Add(entry);
                    entriesDataToSend.Add(entryData);
                    entriesDataSerialized.Add(serializedEntry);
                    entriesSyncRequest.Add(latestUpdateRequest);
                }

                if (entriesToSend.Count == 0)
                {
                    _databaseEntriesUploadRunning = false;
                    return;
                }

                _controller.SetDatabaseSyncStatus(databaseId, Statuses.Syncing);

                var request = new SetDatabaseEntries
                {
                    LinkIdentifier = database.Identifier,
                    Entries = entriesUploadItems
                };

                try
                {
                    request.GetResponse(GetApiClient());
                    _requestErrors = 0;
                }
                catch (ConflictException)
                {
                    if (Program.AppEnvDebug)
                        throw new NotImplementedException("TODO: Download update to group");
                    _databaseEntriesUploadRunning = false;
                    return;
                }
                catch (NetworkErrorException)
                {
                    _requestErrors++;
                    _databaseEntriesUploadRunning = false;
                    return;
                }
                catch (RequestException)
                {
                    _databaseEntriesUploadRunning = false;
                    return;
                }

                for (var i = 0; i < entriesToSend.Count; i++)
                {
                    var entry = entriesToSend[i];
                    var entryUploadItem = entriesUploadItems[i];
                    var serializedEntry = entriesDataSerialized[i];
                    var latestUpdateRequest = entriesSyncRequest[i];
                    var newVersion = entryUploadItem.Version;

                    // Create archive version
                    var archiveData = JsonConvert.DeserializeObject<DatabaseEntryData>(serializedEntry);
                    archiveData.RemoveId();
                    var archiveDataId = Model.DatabasesEntriesData.Create(archiveData);
                    Model.DatabasesEntriesDataVersions.Create(new DatabaseEntryDataVersion
                    {
                        DatabaseEntryId = entry.Id,
                        Version = newVersion,
                        DatabaseEntryDataId = archiveDataId
                    });

                    // Update current record
                    Model.DatabasesEntries.Update(entry.Id, new DatabaseEntry
                    {
                        Version = newVersion
                    });

                    // Sync completed
                    Model.DatabasesEntriesDataSync.Delete(latestUpdateRequest.Id);
                }
                
                _controller.SetDatabaseSyncStatus(databaseId, Statuses.UpToDate);
                _databaseEntriesUploadRunning = false;
            });
        }
Beispiel #4
0
        public string GetSecret(int databaseId, int entryId, string secretIdentifier, string secretEncryptedData)
        {
            var database = Model.Databases.Get(databaseId);
            var serverAccount = Model.ServerAccounts.Get(database.ServerAccountId);
            var linkedDeviceCryptoKey = Model.CryptoKeys.Get(serverAccount.LinkedDeviceCryptoKeyId);
            var entry = Model.DatabasesEntries.Get(entryId);
            var deviceMessage = new ServerAPI.DeviceToDeviceMessages.RequestEntrySecrets
            {
                EntryIdentifier = entry.Identifier,
                SecretIdentifiers = new List<string> { secretIdentifier }
            };
            var request = new SendLinkedDeviceMessage
            {
                //LinkIdentifier = database.Identifier,
                SecondsValidFor = 30
            };
            request.SetMessage(deviceMessage, linkedDeviceCryptoKey.PublicKeyPem);

            SendLinkedDeviceMessage.ResponseParams response;
            try
            {
                response = request.GetResponse(GetApiClient(serverAccount.Id));
            }
            catch (RequestException)
            {
                return null;
            }

            var messageHandler = new SendSecretShareMessageHandler(
                _syncServers[serverAccount.Id], response.MessageIdentifier);

            // Give the sync process a kick up the arse
            _syncServers[serverAccount.Id].ProcessMessagesOnly();

            var replyReceived = messageHandler.WaitForReply();
            
            // Give the sync process a kick up the arse
            _syncServers[serverAccount.Id].ProcessMessagesOnlyStop();

            if (!replyReceived)
            {
                MessageBox.Show(@"Secret share request timed out");
                return null;
            }

            var secretShareMessage = messageHandler.Reply;

            if (!secretShareMessage.RequestAccepted)
            {
                MessageBox.Show(@"Secret share request denied");
                return null;
            }

            string secretShare;
            secretShareMessage.Secrets.TryGetValue(secretIdentifier, out secretShare);
            if (string.IsNullOrEmpty(secretShare))
            {
                messageHandler.MarkAsProcessed(false);
                MessageBox.Show(@"Error obtaining secret share");
                return null;
            }
            
            var decryptedData = EncryptedData.DecryptData(secretShare, secretEncryptedData);
            
            messageHandler.MarkAsProcessed(true);

            return decryptedData;
        }
Beispiel #5
0
        private void ProcessShares(int databaseId)
        {
            if (_processingSecretShares)
                return;

            var serverAccount = GetServerAccount();

            if (serverAccount.LinkedDeviceCryptoKeyId == 0)
                return;

            var apiClient = GetApiClient();
            var database = Model.Databases.Get(databaseId);
            var linkedClientCryptoKey = Model.CryptoKeys.Get(serverAccount.LinkedDeviceCryptoKeyId);
            
            _processingSecretShares = true;
            Task.Run(() => 
            {
                bool moreToProcess;
                do
                {
                    moreToProcess = false;
                    var entries = Model.DatabasesEntries.Find(new DatabaseEntry { DatabaseId = databaseId });
                    var entriesToSend = new List<DatabaseEntry>();
                    var entriesDataToSend = new List<DatabaseEntryData>();
                    foreach (var entry in entries)
                    {
                        var entryData = Model.DatabasesEntriesData.Get(entry.DatabaseEntryDataId);
                        if (entryData.PasswordShared)
                            continue;

                        if (entryData.Password == "")
                            continue;

                        if (entriesToSend.Count > 50)
                        {
                            moreToProcess = true;
                            break;
                        }

                        entriesToSend.Add(entry);
                        entriesDataToSend.Add(entryData);
                    }

                    if (entriesToSend.Count == 0)
                    {
                        _processingSecretShares = false;
                        return;
                    }

                    var entrySecretsToSend = new List<DeviceToDeviceMessages.NewSecretItem>();
                    var entriesEncryptedPassword = new List<EncryptedData>();
                    for (var i = 0; i < entriesToSend.Count; i++)
                    {
                        var entry = entriesToSend[i];
                        var entryData = entriesDataToSend[i];

                        var encryptedPassword = new EncryptedData(entryData.Password);
                        var newSecretIdentifier = Guid.NewGuid().ToString();

                        entrySecretsToSend.Add(new DeviceToDeviceMessages.NewSecretItem
                        {
                            EntryIdentifier = entry.Identifier,
                            SecretIdentifier = newSecretIdentifier,
                            Secret = encryptedPassword.Key
                        });

                        entriesEncryptedPassword.Add(encryptedPassword);
                    }

                    var message = new DeviceToDeviceMessages.NewSecret
                    {
                        LinkIdentifier = database.Identifier,
                        Secrets = entrySecretsToSend
                    };
                    var request = new SendLinkedDeviceMessage
                    {
                        SecondsValidFor = 120
                    };
                    request.SetMessage(message, linkedClientCryptoKey.PublicKeyPem);

                    SendLinkedDeviceMessage.ResponseParams response;
                    try
                    {
                        response = request.GetResponse(apiClient);
                    }
                    catch (RequestException)
                    {
                        // Try again later
                        _processingSecretShares = false;
                        return;
                    }

                    var processedSuccess = ProcessSharesCheckReceived(response.MessageIdentifier);
                    if (!processedSuccess)
                    {
                        _processingSecretShares = false;
                        return;
                    }

                    for (var i = 0; i < entriesToSend.Count; i++)
                    {
                        var entry = entriesToSend[i];
                        var entryData = entriesDataToSend[i];
                        var entrySecret = entrySecretsToSend[i];
                        var entryEncryptedPassword = entriesEncryptedPassword[i];

                        var originalSecretIdentifier = entryData.PasswordSharedIdentifier;
                        var newSecretIdentifier = entrySecret.SecretIdentifier;

                        Model.DatabasesEntriesData.Update(entryData.Id, new DatabaseEntryData
                        {
                            Password = "",
                            PasswordShared = true,
                            PasswordSharedIdentifier = newSecretIdentifier,
                            PasswordEncryptedData = entryEncryptedPassword.ToString()
                        });
                        Model.DatabasesEntriesDataSync.Create(new DatabaseEntryDataSync { DatabaseEntryId = entry.Id });

                        if (!string.IsNullOrEmpty(originalSecretIdentifier))
                        {
                            // Tell the device the previous secret can be deleted.

                            var deleteSecretMessage = new DeviceToDeviceMessages.DeleteSecret
                            {
                                SecretIdentifier = originalSecretIdentifier
                            };

                            var deleteSecretRequest = new SendLinkedDeviceMessage();
                            deleteSecretRequest.SetMessage(deleteSecretMessage, linkedClientCryptoKey.PublicKeyPem);

                            try
                            {
                                deleteSecretRequest.GetResponse(apiClient);
                            }
                            catch (RequestException)
                            {
                                // Errr, probably should retry this another time, but lots of effort for little gain.
                                // I'll come back to it later.
                            }
                        }
                    }

                } while (moreToProcess);

                _processingSecretShares = false;

                RestartSyncLoop();
            });
        }