private async Task TryScheduleAttachmentDownload(SignalAttachment attachment) { if (Downloads.Count < 100) { if (attachment.Status != SignalAttachmentStatus.Finished && !Downloads.ContainsKey(attachment.Id)) { SignalServiceAttachmentPointer attachmentPointer = attachment.ToAttachmentPointer(); IStorageFolder localFolder = ApplicationData.Current.LocalFolder; IStorageFile tmpDownload = await Task.Run(async() => { return(await ApplicationData.Current.LocalCacheFolder.CreateFileAsync(@"Attachments\" + attachment.Id + ".cipher", CreationCollisionOption.ReplaceExisting)); }); BackgroundDownloader downloader = new BackgroundDownloader(); downloader.SetRequestHeader("Content-Type", "application/octet-stream"); // this is the recommended way to call CreateDownload // see https://docs.microsoft.com/en-us/uwp/api/windows.networking.backgroundtransfer.backgrounddownloader#Methods DownloadOperation download = downloader.CreateDownload(new Uri(await MessageReceiver.RetrieveAttachmentDownloadUrl(CancelSource.Token, attachmentPointer)), tmpDownload); attachment.Guid = download.Guid.ToString(); SignalDBContext.UpdateAttachmentGuid(attachment); Downloads.Add(attachment.Id, download); var downloadSuccessfulHandler = Task.Run(async() => { Logger.LogInformation("Waiting for download {0}({1})", attachment.SentFileName, attachment.Id); var t = await download.StartAsync(); await HandleSuccessfullDownload(attachment, tmpDownload, download); }); } } }
/// <summary> /// Retrieves a SignalServiceAttachment /// </summary> /// <param name="token"></param> /// <param name="pointer">The <see cref="SignalServiceAttachmentPointer"/> /// received in a <see cref="SignalServiceDataMessage"/></param> /// <param name="tmpCipherDestination">The temporary destination for this attachment before decryption</param> /// <param name="maxSizeBytes">The maximum size for this attachment (not yet implemented)</param> /// <param name="listener">An optional listener (may be null) to receive callbacks on download progress.</param> public async Task <Stream> RetrieveAttachment(CancellationToken token, SignalServiceAttachmentPointer pointer, Stream tmpCipherDestination, int maxSizeBytes, IProgressListener listener) { await Socket.RetrieveAttachment(token, pointer.Id, tmpCipherDestination, maxSizeBytes); tmpCipherDestination.Position = 0; return(AttachmentCipherInputStream.CreateFor(tmpCipherDestination, pointer.Size != null ? pointer.Size.Value : 0, pointer.Key, pointer.Digest)); }
/// <summary> /// Retrieves a SignalServiceAttachment /// </summary> /// <param name="pointer">The <see cref="SignalServiceAttachmentPointer"/> /// received in a <see cref="SignalServiceDataMessage"/></param> /// <param name="plaintextDestination">The download destination for this attachment.</param> /// <param name="tmpCipherDestination">The temporary destination for this attachment before decryption</param> /// <param name="maxSizeBytes">The maximum size for this attachment (not yet implemented)</param> /// <param name="listener">An optional listener (may be null) to receive callbacks on download progress.</param> public void retrieveAttachment(SignalServiceAttachmentPointer pointer, FileStream plaintextDestination, FileStream tmpCipherDestination, int maxSizeBytes, ProgressListener listener) { socket.retrieveAttachment(pointer.Relay, pointer.Id, tmpCipherDestination, maxSizeBytes); tmpCipherDestination.Seek(0, SeekOrigin.Begin); byte[] combinedKeyMaterial = pointer.Key; byte[][] parts = Util.split(combinedKeyMaterial, CIPHER_KEY_SIZE, MAC_KEY_SIZE); //byte[] digest = pointer.getDigest(); //TODO //verifyMac() byte[] iv = new byte[BLOCK_SIZE]; Util.readFully(tmpCipherDestination, iv); using (var aes = Aes.Create()) { aes.Key = parts[0]; aes.IV = iv; aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.PKCS7; using (var decrypt = aes.CreateDecryptor()) using (var cryptoStream = new CryptoStream(tmpCipherDestination, decrypt, CryptoStreamMode.Read)) { byte[] buffer = new byte[CIPHER_KEY_SIZE]; int read = cryptoStream.Read(buffer, 0, buffer.Length); while (read > 0) { plaintextDestination.Write(buffer, 0, read); read = cryptoStream.Read(buffer, 0, buffer.Length); } } } }
private void DecryptAttachment(SignalServiceAttachmentPointer pointer, Stream ciphertextFileStream, Stream plaintextFileStream) { byte[] buf = new byte[32]; Stream s = AttachmentCipherInputStream.CreateFor(ciphertextFileStream, pointer.Size != null ? pointer.Size.Value : 0, pointer.Key, pointer.Digest); s.CopyTo(plaintextFileStream); }
/// <summary> /// Retrieves a SignalServiceAttachment. /// </summary> /// <param name="pointer">The <see cref="SignalServiceAttachmentPointer"/> received in a <see cref="SignalServiceDataMessage"/>.</param> /// <param name="destination">The download destination for this attachment.</param> /// <param name="maxSizeBytes"></param> /// <param name="token"></param> /// <returns>A Stream that streams the plaintext attachment contents.</returns> /// <exception cref="IOException"></exception> /// <exception cref="InvalidMessageException"></exception> public async Task <Stream> RetrieveAttachmentAsync(SignalServiceAttachmentPointer pointer, Stream destination, int maxSizeBytes, CancellationToken?token = null) { if (token == null) { token = CancellationToken.None; } return(await RetrieveAttachmentAsync(pointer, destination, maxSizeBytes, null, token)); }
private SignalServiceGroup createGroupInfo(SignalServiceEnvelope envelope, DataMessage content) { if (!content.HasGroup) { return(null); } SignalServiceGroup.Type type; switch (content.Group.Type) { case GroupContext.Types.Type.DELIVER: type = SignalServiceGroup.Type.DELIVER; break; case GroupContext.Types.Type.UPDATE: type = SignalServiceGroup.Type.UPDATE; break; case GroupContext.Types.Type.QUIT: type = SignalServiceGroup.Type.QUIT; break; case GroupContext.Types.Type.REQUEST_INFO: type = SignalServiceGroup.Type.REQUEST_INFO; break; default: type = SignalServiceGroup.Type.UNKNOWN; break; } if (content.Group.Type != GroupContext.Types.Type.DELIVER) { String name = null; IList <String> members = null; SignalServiceAttachmentPointer avatar = null; if (content.Group.HasName) { name = content.Group.Name; } if (content.Group.MembersCount > 0) { members = content.Group.MembersList; } if (content.Group.HasAvatar) { avatar = new SignalServiceAttachmentPointer(content.Group.Avatar.Id, content.Group.Avatar.ContentType, content.Group.Avatar.Key.ToByteArray(), envelope.getRelay()); } return(new SignalServiceGroup(type, content.Group.Id.ToByteArray(), name, members, avatar)); } return(new SignalServiceGroup(content.Group.Id.ToByteArray())); }
/// <summary> /// Retrieves a SignalServiceAttachment /// </summary> /// <param name="pointer">The <see cref="SignalServiceAttachmentPointer"/> /// received in a <see cref="SignalServiceDataMessage"/></param> /// <param name="destination">The download destination for this attachment.</param> /// <param name="maxSizeBytes"></param> /// <param name="listener">An optional listener (may be null) to receive callbacks on download progress.</param> /// <param name="token"></param> /// <exception cref="IOException"></exception> /// <exception cref="InvalidMessageException"></exception> public async Task <Stream> RetrieveAttachmentAsync(SignalServiceAttachmentPointer pointer, Stream destination, int maxSizeBytes, IProgressListener?listener, CancellationToken?token = null) { if (token == null) { token = CancellationToken.None; } if (pointer.Digest == null) { throw new InvalidMessageException("No attachment digest!"); } await socket.RetrieveAttachmentAsync(pointer.CdnNumber, pointer.RemoteId, destination, maxSizeBytes, listener, token); destination.Position = 0; return(AttachmentCipherInputStream.CreateForAttachment(destination, pointer.Size != null ? pointer.Size.Value : 0, pointer.Key, pointer.Digest)); }
private AttachmentPointer CreateAttachmentPointerFromPointer(SignalServiceAttachmentPointer attachment) { var attachmentPointer = new AttachmentPointer() { ContentType = attachment.ContentType, Id = attachment.Id, Key = ByteString.CopyFrom(attachment.Key), Digest = ByteString.CopyFrom(attachment.Digest), Size = (uint)attachment.Size }; if (attachment.FileName != null) { attachmentPointer.FileName = attachment.FileName; } if (attachment.VoiceNote) { attachmentPointer.Flags = (uint)AttachmentPointer.Types.Flags.VoiceMessage; } return(attachmentPointer); }
/// <summary> /// Retrieves an attachment URL location /// </summary> /// <param name="token"></param> /// <param name="pointer">The pointer to the attachment</param> /// <returns></returns> public async Task <string> RetrieveAttachmentDownloadUrl(CancellationToken token, SignalServiceAttachmentPointer pointer) { return(await Socket.RetrieveAttachmentDownloadUrl(token, pointer.Id)); }
private SignalServiceGroup createGroupInfo(SignalServiceEnvelope envelope, DataMessage content) { if (content.GroupOneofCase == DataMessage.GroupOneofOneofCase.None) { return(null); } SignalServiceGroup.GroupType type; switch (content.Group.Type) { case GroupContext.Types.Type.Deliver: type = SignalServiceGroup.GroupType.DELIVER; break; case GroupContext.Types.Type.Update: type = SignalServiceGroup.GroupType.UPDATE; break; case GroupContext.Types.Type.Quit: type = SignalServiceGroup.GroupType.QUIT; break; case GroupContext.Types.Type.RequestInfo: type = SignalServiceGroup.GroupType.REQUEST_INFO; break; default: type = SignalServiceGroup.GroupType.UNKNOWN; break; } if (content.Group.Type != GroupContext.Types.Type.Deliver) { String name = null; IList <String> members = null; SignalServiceAttachmentPointer avatar = null; if (content.Group.NameOneofCase == GroupContext.NameOneofOneofCase.Name) { name = content.Group.Name; } if (content.Group.Members.Count > 0) { members = content.Group.Members; } if (content.Group.AvatarOneofCase == GroupContext.AvatarOneofOneofCase.Avatar) { AttachmentPointer pointer = content.Group.Avatar; avatar = new SignalServiceAttachmentPointer(pointer.Id, pointer.ContentType, pointer.Key.ToByteArray(), envelope.getRelay(), pointer.Digest.ToByteArray(), null, false); } return(new SignalServiceGroup() { Type = type, GroupId = content.Group.Id.ToByteArray(), Name = name, Members = members, Avatar = avatar }); } return(new SignalServiceGroup() { GroupId = content.Group.Id.ToByteArray(), Type = type }); }
public string RetrieveAttachmentDownloadUrl(SignalServiceAttachmentPointer pointer) { return(socket.RetrieveAttachmentDownloadUrl(pointer.CdnNumber, pointer.RemoteId)); }
/// <summary> /// Retrieves a SignalServiceAttachment /// </summary> /// <param name="pointer">The <see cref="SignalServiceAttachmentPointer"/> /// received in a <see cref="SignalServiceDataMessage"/></param> /// <param name="destination">The download destination for this attachment.</param> /// <param name="listener">An optional listener (may be null) to receive callbacks on download progress.</param> /// <returns>A Stream that streams the plaintext attachment contents.</returns> public Stream retrieveAttachment(SignalServiceAttachmentPointer pointer, StorageFile destination, ProgressListener listener) { throw new NotImplementedException(); return(new MemoryStream()); }
/// <summary> /// Retrieves a SignalServiceAttachment /// </summary> /// <param name="pointer">The <see cref="SignalServiceAttachmentPointer"/> /// received in a <see cref="SignalServiceDataMessage"/></param> /// <param name="destination">The download destination for this attachment.</param> /// <returns>A Stream that streams the plaintext attachment contents.</returns> public Stream retrieveAttachment(SignalServiceAttachmentPointer pointer, StorageFile destination) { throw new NotImplementedException(); return(retrieveAttachment(pointer, destination, null)); }
private SignalServiceGroup?CreateGroupInfo(DataMessage content) { if (content.GroupOneofCase == DataMessage.GroupOneofOneofCase.None) { return(null); } var type = content.Group.Type switch { GroupContext.Types.Type.Deliver => SignalServiceGroup.GroupType.DELIVER, GroupContext.Types.Type.Update => SignalServiceGroup.GroupType.UPDATE, GroupContext.Types.Type.Quit => SignalServiceGroup.GroupType.QUIT, GroupContext.Types.Type.RequestInfo => SignalServiceGroup.GroupType.REQUEST_INFO, _ => SignalServiceGroup.GroupType.UNKNOWN, }; if (content.Group.Type != GroupContext.Types.Type.Deliver) { string? name = null; IList <string>?members = null; SignalServiceAttachmentPointer?avatar = null; if (content.Group.NameOneofCase == GroupContext.NameOneofOneofCase.Name) { name = content.Group.Name; } if (content.Group.Members.Count > 0) { members = content.Group.Members; } if (content.Group.AvatarOneofCase == GroupContext.AvatarOneofOneofCase.Avatar) { AttachmentPointer pointer = content.Group.Avatar; avatar = new SignalServiceAttachmentPointer(pointer.Id, pointer.ContentType, pointer.Key.ToByteArray(), pointer.SizeOneofCase == AttachmentPointer.SizeOneofOneofCase.Size ? pointer.Size : 0, null, 0, 0, pointer.DigestOneofCase == AttachmentPointer.DigestOneofOneofCase.Digest ? pointer.Digest.ToByteArray() : null, null, false); } return(new SignalServiceGroup() { Type = type, GroupId = content.Group.Id.ToByteArray(), Name = name, Members = members, Avatar = avatar }); } return(new SignalServiceGroup() { GroupId = content.Group.Id.ToByteArray(), Type = type }); }
/// <summary> /// Retrieves a SignalServiceAttachment /// </summary> /// <param name="pointer">The <see cref="SignalServiceAttachmentPointer"/> /// received in a <see cref="SignalServiceDataMessage"/></param> /// <param name="plaintextDestination">The download destination for this attachment.</param> /// <param name="tmpCipherDestination">The temporary destination for this attachment before decryption</param> public void retrieveAttachment(SignalServiceAttachmentPointer pointer, FileStream plaintextDestination, FileStream tmpCipherDestination, int maxSizeBytes) { retrieveAttachment(pointer, plaintextDestination, tmpCipherDestination, maxSizeBytes, null); }