public static RecordExtra ExtractRecordExtra(this PasswordRecord record, RecordExtra existingExtra = null) { IDictionary <string, RecordExtraFile> extraFiles = null; if (existingExtra != null && existingExtra.files != null && existingExtra.files.Length > 0) { extraFiles = new Dictionary <string, RecordExtraFile>(); foreach (var f in existingExtra.files) { extraFiles.Add(f.id, f); } } return(new RecordExtra { files = record.Attachments?.Select(x => { RecordExtraFile extraFile; if (extraFiles != null) { if (extraFiles.TryGetValue(x.Id, out extraFile)) { return extraFile; } } extraFile = new RecordExtraFile { id = x.Id, key = x.Key, name = x.Name, title = x.Title ?? x.Name, size = x.Size, type = x.Type }; if (x.Thumbnails != null && x.Thumbnails.Length > 0) { extraFile.thumbs = x.Thumbnails.Select(y => new RecordExtraFileThumb { id = y.Id, size = y.Size, type = y.Type }) .ToArray(); } return extraFile; }).ToArray(), ExtensionData = existingExtra?.ExtensionData }); }
public static RecordData ExtractRecordData(this PasswordRecord record, RecordData existingData = null) { return(new RecordData { title = record.Title, folder = existingData?.folder, secret1 = record.Login, secret2 = record.Password, link = record.Link, notes = record.Notes, custom = record.Custom?.Select(x => new RecordDataCustom { name = x.Name, value = x.Value, type = x.Type }).ToArray() }); }
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 UploadAttachment(this Vault vault, PasswordRecord record, IAttachmentUploadTask uploadTask) { var fileStream = uploadTask.Stream; if (fileStream == null) { throw new KeeperInvalidParameter("Vault::UploadAttachment", "uploadTask", "GetStream()", "null"); } var thumbStream = uploadTask.Thumbnail?.Stream; var command = new RequestUploadCommand(); command.fileCount = 1; command.thumbnailCount = thumbStream != null ? 1 : 0; var rs = await vault.Auth.ExecuteAuthCommand <RequestUploadCommand, RequestUpoadResponse>(command); if (rs.fileUploads == null || rs.fileUploads.Length < 1) { throw new KeeperInvalidParameter("Vault::UploadAttachment", "request_upload", "file_uploads", "empty"); } UploadParameters fileUpload = rs.fileUploads[0]; UploadParameters thumbUpload = null; if (rs.thumbnailUploads != null && rs.thumbnailUploads.Length > 0) { thumbUpload = rs.thumbnailUploads[0]; } var key = CryptoUtils.GenerateEncryptionKey(); var atta = new AttachmentFile { Id = fileUpload.fileID, Name = uploadTask.Name, Title = uploadTask.Title, Key = key.Base64UrlEncode(), Type = uploadTask.MimeType, LastModified = DateTimeOffset.Now, }; var transform = new EncryptAesV1Transform(key); using (var cryptoStream = new CryptoStream(fileStream, transform, CryptoStreamMode.Read)) { await UploadSingleFile(fileUpload, cryptoStream); atta.Size = transform.EncryptedBytes; } if (thumbUpload != null) { try { transform = new EncryptAesV1Transform(key); using (var cryptoStream = new CryptoStream(thumbStream, transform, CryptoStreamMode.Read)) { await UploadSingleFile(thumbUpload, cryptoStream); } var thumbnail = new AttachmentFileThumb { Id = thumbUpload.fileID, Type = uploadTask.Thumbnail.MimeType, Size = uploadTask.Thumbnail.Size }; var ts = new AttachmentFileThumb[] { thumbnail }; if (atta.Thumbnails == null) { atta.Thumbnails = ts; } else { atta.Thumbnails = atta.Thumbnails.Concat(ts).ToArray(); } } catch (Exception e) { Trace.TraceError("Upload Thumbname: {0}: \"{1}\"", e.GetType().Name, e.Message); } } record.Attachments.Add(atta); }
public static async Task DownloadAttachment(this Vault vault, PasswordRecord record, string attachment, Stream destination) { if (record.Attachments == null) { throw new KeeperInvalidParameter("Vault::DownloadAttachment", "record", record.Uid, "has no attachments"); } AttachmentFile attachmentFile = null; if (string.IsNullOrEmpty(attachment)) { if (record.Attachments.Count == 1) { attachmentFile = record.Attachments[0]; } else { throw new KeeperInvalidParameter("Vault::DownloadAttachment", "attachment", "", "is empty"); } } else { attachmentFile = record.Attachments .FirstOrDefault(x => { if (attachment == x.Id || attachment == x.Name || attachment == x.Title) { return(true); } if (x.Thumbnails != null) { var thumbId = x.Thumbnails.Select(y => y.Id).FirstOrDefault(y => y == attachment); if (!string.IsNullOrEmpty(thumbId)) { return(true); } } return(false); }); } if (attachmentFile == null) { throw new KeeperInvalidParameter("Vault::DownloadAttachment", "attachment", attachment, "not found"); } var attachmentId = attachmentFile.Id; if (attachmentFile.Thumbnails != null) { foreach (var th in attachmentFile.Thumbnails) { if (th.Id == attachment) { attachmentId = th.Id; break; } } } var request = await CreateAttachmentDownloadRequest(vault, record.Uid, attachmentId); using (var response = await request.GetResponseAsync() as HttpWebResponse) { using (var stream = response.GetResponseStream()) { var transform = new DecryptAesV1Transform(attachmentFile.Key.Base64UrlDecode()); using (var decodeStream = new CryptoStream(stream, transform, CryptoStreamMode.Read)) { if (destination != null) { await decodeStream.CopyToAsync(destination); } } } } }
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 AddRecord(PasswordRecord record, string folderUid) { IFolderNode node = null; if (!string.IsNullOrEmpty(folderUid)) { userFolders.TryGetValue(folderUid, out node); } var recordKey = CryptoUtils.GenerateEncryptionKey(); var recordAdd = new RecordAddCommand { recordUid = CryptoUtils.GenerateUid(), recordKey = CryptoUtils.EncryptAesV1(recordKey, Auth.DataKey).Base64UrlEncode(), recordType = "password" }; if (node == null) { recordAdd.folderType = "user_folder"; } else { switch (node.Type) { case FolderType.UserFolder: recordAdd.folderType = "user_folder"; recordAdd.folderUid = node.FolderUid; break; case FolderType.SharedFolder: case FolderType.SharedFolderForder: recordAdd.folderUid = node.FolderUid; recordAdd.folderType = node.Type == FolderType.SharedFolder ? "shared_folder" : "shared_folder_folder"; if (node is ISharedFolderNode sfn) { if (sharedFolders.TryGetValue(sfn.SharedFolderUid, out SyncDownSharedFolder sf)) { recordAdd.folderKey = CryptoUtils.EncryptAesV1(recordKey, sf.unencryptedSharedFolderKey).Base64UrlEncode(); } } if (string.IsNullOrEmpty(recordAdd.folderKey)) { throw new Exception(string.Format("Cannot resolve shared folder for folder UID", folderUid)); } break; } } var settings = new DataContractJsonSerializerSettings { UseSimpleDictionaryFormat = true }; var dataSerializer = new DataContractJsonSerializer(typeof(RecordData), settings); var data = record.ExtractRecordData(); using (var ms = new MemoryStream()) { dataSerializer.WriteObject(ms, data); recordAdd.data = CryptoUtils.EncryptAesV1(ms.ToArray(), recordKey).Base64UrlEncode(); } await Auth.ExecuteAuthCommand <RecordAddCommand>(recordAdd); await this.SyncDown(); }
public bool TryGetRecord(string recordUid, out PasswordRecord node) { return(keeperRecords.TryGetValue(recordUid, out node)); }