Skip to content

Commit 66516bd

Browse files
committed
Support for padded attachments
1 parent 16efd03 commit 66516bd

11 files changed

+187
-72
lines changed

libsignal-service-dotnet/SignalServiceMessageReceiver.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ public Stream RetrieveProfileAvatar(string path, FileStream destination, byte[]
8383
public Stream RetrieveAttachment(SignalServiceAttachmentPointer pointer, Stream tmpCipherDestination, int maxSizeBytes, ProgressListener listener)
8484
{
8585
Socket.RetrieveAttachment(pointer.Relay, pointer.Id, tmpCipherDestination, maxSizeBytes);
86-
return new AttachmentCipherInputStream(tmpCipherDestination, pointer.Key, pointer.Digest);
86+
return AttachmentCipherInputStream.CreateFor(tmpCipherDestination, pointer.Size != null ? pointer.Size.Value : 0, pointer.Key, pointer.Digest);
8787
}
8888

8989
/// <summary>

libsignal-service-dotnet/SignalServiceMessageSender.cs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -617,9 +617,11 @@ private IList<AttachmentPointer> CreateAttachmentPointers(List<SignalServiceAtta
617617
private AttachmentPointer CreateAttachmentPointer(SignalServiceAttachmentStream attachment)
618618
{
619619
byte[] attachmentKey = Util.getSecretBytes(64);
620+
long paddedLength = PaddingInputStream.GetPaddedSize(attachment.getLength());
621+
long ciphertextLength = AttachmentCipherInputStream.GetCiphertextLength(paddedLength);
620622
PushAttachmentData attachmentData = new PushAttachmentData(attachment.getContentType(),
621-
attachment.getInputStream(),
622-
(ulong)GetCiphertextLength(attachment.getLength()),
623+
new PaddingInputStream(attachment.getInputStream(), attachment.getLength()),
624+
ciphertextLength,
623625
new AttachmentCipherOutputStreamFactory(attachmentKey),
624626
attachment.getListener());
625627

@@ -685,11 +687,6 @@ private AttachmentPointer CreateAttachmentPointerFromPointer(SignalServiceAttach
685687
return socket.RetrieveAttachmentUploadUrl();
686688
}
687689

688-
private long GetCiphertextLength(long plaintextLength)
689-
{
690-
return 16 + (((plaintextLength / 16) + 1) * 16) + 32;
691-
}
692-
693690
/// <summary>
694691
/// Encrypts an attachment to be uploaded
695692
/// </summary>

libsignal-service-dotnet/crypto/AttachmentCipherInputStream.cs

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,33 +30,42 @@ public class AttachmentCipherInputStream : Stream
3030
public override long Length => throw new NotImplementedException();
3131
public override long Position { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
3232

33-
public AttachmentCipherInputStream(Stream inputStream, byte[] combinedKeyMaterial, byte[] digest)
33+
public static Stream CreateFor(Stream inputStream, long plaintextLength, byte[] combinedKeyMaterial, byte[] digest)
3434
{
35-
InputStream = inputStream;
3635
long fileSize = inputStream.Length;
3736
byte[][] keyParts = Util.Split(combinedKeyMaterial, 32, 32);
3837
IncrementalHash mac = IncrementalHash.CreateHMAC(HashAlgorithmName.SHA256, keyParts[1]);
38+
VerifyMac(inputStream, mac, digest);
39+
inputStream.Seek(0, SeekOrigin.Begin);
40+
41+
Stream stream = new AttachmentCipherInputStream(inputStream, keyParts[0], inputStream.Length);
42+
43+
if (plaintextLength > 0)
44+
{
45+
stream = new ContentLengthInputStream(stream, plaintextLength);
46+
}
3947

48+
return stream;
49+
}
4050

51+
private AttachmentCipherInputStream(Stream inputStream, byte[] key, long totalDataSize)
52+
{
53+
InputStream = inputStream;
4154
if (InputStream.Length <= BLOCK_SIZE + 32)
4255
{
4356
throw new InvalidMessageException("Message shorter than crypto overhead!");
4457
}
4558

46-
VerifyMac(InputStream, mac, digest);
47-
InputStream.Seek(0, SeekOrigin.Begin);
48-
4959
byte[] iv = new byte[BLOCK_SIZE];
5060
ReadFully(iv);
5161
Aes = Aes.Create();
52-
Aes.Key = keyParts[0];
62+
Aes.Key = key;
5363
Aes.IV = iv;
5464
Aes.Mode = CipherMode.CBC;
5565
Aes.Padding = PaddingMode.PKCS7;
5666
Decryptor = Aes.CreateDecryptor();
5767
Cipher = new CryptoStream(InputStream, Decryptor, CryptoStreamMode.Read);
58-
59-
TotalDataSize = fileSize - Aes.BlockSize - 32;
68+
TotalDataSize = totalDataSize;
6069
}
6170

6271
public override void Flush()
@@ -90,7 +99,7 @@ public override void Write(byte[] buffer, int offset, int count)
9099
throw new NotImplementedException();
91100
}
92101

93-
private void VerifyMac(Stream fin, IncrementalHash mac, byte[] theirDigest)
102+
private static void VerifyMac(Stream fin, IncrementalHash mac, byte[] theirDigest)
94103
{
95104
IncrementalHash digest = IncrementalHash.CreateHash(HashAlgorithmName.SHA256);
96105
int remainingData = Util.ToIntExact(fin.Length) - 32;
@@ -136,6 +145,11 @@ private void ReadFully(byte[] buffer)
136145
return;
137146
}
138147
}
148+
149+
public static long GetCiphertextLength(long plaintextLength)
150+
{
151+
return 16 + (((plaintextLength / 16) + 1) * 16) + 32;
152+
}
139153
}
140154
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
141155
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
using libsignalservice.util;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.IO;
5+
using System.Text;
6+
7+
namespace libsignalservice.crypto
8+
{
9+
internal class PaddingInputStream : Stream
10+
{
11+
private readonly Stream InputStream;
12+
private long PaddingRemaining;
13+
14+
public override bool CanRead => throw new NotImplementedException();
15+
public override bool CanSeek => throw new NotImplementedException();
16+
public override bool CanWrite => throw new NotImplementedException();
17+
public override long Length { get => InputStream.Length + Util.ToIntExact(PaddingRemaining); }
18+
public override long Position { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
19+
20+
public PaddingInputStream(Stream inputStream, long plainTextLength)
21+
{
22+
InputStream = inputStream;
23+
PaddingRemaining = GetPaddedSize(plainTextLength) - plainTextLength;
24+
}
25+
26+
public override void Flush()
27+
{
28+
throw new NotImplementedException();
29+
}
30+
31+
public override int Read(byte[] buffer, int offset, int count)
32+
{
33+
int result = InputStream.Read(buffer, offset, count);
34+
if (result >= 0)
35+
return result;
36+
37+
if (PaddingRemaining > 0)
38+
{
39+
count = Math.Min(count, Util.ToIntExact(PaddingRemaining));
40+
PaddingRemaining -= count;
41+
return count;
42+
}
43+
return 0;
44+
}
45+
46+
public override long Seek(long offset, SeekOrigin origin)
47+
{
48+
throw new NotImplementedException();
49+
}
50+
51+
public override void SetLength(long value)
52+
{
53+
throw new NotImplementedException();
54+
}
55+
56+
public override void Write(byte[] buffer, int offset, int count)
57+
{
58+
throw new NotImplementedException();
59+
}
60+
61+
public static long GetPaddedSize(long size)
62+
{
63+
return size;
64+
}
65+
66+
private static long GetRoundedUp(long size, long interval)
67+
{
68+
long multiplier = (long)Math.Ceiling(((double)size) / interval);
69+
return interval * multiplier;
70+
}
71+
}
72+
}

libsignal-service-dotnet/crypto/SignalServiceCipher.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,9 @@ private SignalServiceGroup CreateGroupInfo(SignalServiceEnvelope envelope, DataM
375375
pointer.ContentType,
376376
pointer.Key.ToByteArray(),
377377
envelope.getRelay(),
378-
pointer.Digest.ToByteArray(),
378+
pointer.SizeOneofCase == AttachmentPointer.SizeOneofOneofCase.Size ? pointer.Size : 0,
379+
null,
380+
pointer.DigestOneofCase == AttachmentPointer.DigestOneofOneofCase.Digest ? pointer.Digest.ToByteArray() : null,
379381
null,
380382
false);
381383
}
Lines changed: 10 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,17 @@
1-
/**
2-
* Copyright (C) 2017 smndtrl, golf1052
3-
*
4-
* This program is free software: you can redistribute it and/or modify
5-
* it under the terms of the GNU General Public License as published by
6-
* the Free Software Foundation, either version 3 of the License, or
7-
* (at your option) any later version.
8-
*
9-
* This program is distributed in the hope that it will be useful,
10-
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11-
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12-
* GNU General Public License for more details.
13-
*
14-
* You should have received a copy of the GNU General Public License
15-
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16-
*/
17-
1+
using System.IO;
2+
183
namespace libsignalservice.messages
194
{
205
/// <summary>
216
/// Represents a received SignalServiceAttachment "handle." This
227
/// is a pointer to the actual attachment content, which needs to be
23-
/// retrieved using <see cref="SignalServiceMessageReceiver.retrieveAttachment(SignalServiceAttachmentPointer, Windows.Storage.StorageFile)"/>
24-
/// </summary>
8+
/// retrieved using <see cref="SignalServiceMessageReceiver.RetrieveAttachment(SignalServiceAttachmentPointer, Stream, int, ProgressListener)"/>
9+
/// </summary>
10+
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
2511
public class SignalServiceAttachmentPointer : SignalServiceAttachment
26-
{
27-
public ulong Id { get; }
12+
{
13+
14+
public ulong Id { get; }
2815
public byte[] Key { get; }
2916
public string Relay { get; }
3017
public uint? Size { get; }
@@ -33,10 +20,6 @@ public class SignalServiceAttachmentPointer : SignalServiceAttachment
3320
public string FileName { get; }
3421
public bool VoiceNote { get; }
3522

36-
public SignalServiceAttachmentPointer(ulong id, string contentType, byte[] key, string relay, byte[] digest, string fileName, bool voiceNote)
37-
: this(id, contentType, key, relay, null, null, digest, fileName, voiceNote)
38-
{ }
39-
4023
public SignalServiceAttachmentPointer(ulong id, string contentType, byte[] key, string relay, uint? size, byte[] preview, byte[] digest, string fileName, bool voiceNote)
4124
: base(contentType)
4225
{
@@ -59,5 +42,6 @@ public override bool isPointer()
5942
{
6043
return true;
6144
}
62-
}
45+
}
46+
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
6347
}

libsignal-service-dotnet/push/PushAttachmentData.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,16 @@
2222

2323
namespace libsignalservice.push
2424
{
25+
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
2526
public class PushAttachmentData
2627
{
2728
public string ContentType { get; }
2829
public Stream Data { get; }
29-
public ulong DataSize { get; }
30+
public long DataSize { get; }
3031
public OutputStreamFactory OutputFactory { get; }
3132
public ProgressListener Listener { get; }
3233

33-
public PushAttachmentData(String contentType, Stream data, ulong dataSize, OutputStreamFactory outputStreamFactory, ProgressListener listener)
34+
public PushAttachmentData(String contentType, Stream data, long dataSize, OutputStreamFactory outputStreamFactory, ProgressListener listener)
3435
{
3536
ContentType = contentType;
3637
Data = data;
@@ -39,4 +40,5 @@ public PushAttachmentData(String contentType, Stream data, ulong dataSize, Outpu
3940
Listener = listener;
4041
}
4142
}
43+
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
4244
}

libsignal-service-dotnet/push/PushServiceSocket.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -525,7 +525,7 @@ private void DownloadAttachment(string url, Stream localDestination)
525525
}
526526
}
527527

528-
private byte[] UploadAttachment(string method, string url, Stream data, ulong dataSize,
528+
private byte[] UploadAttachment(string method, string url, Stream data, long dataSize,
529529
OutputStreamFactory outputStreamFactory, ProgressListener listener)
530530
{
531531
MemoryStream tmpStream = new MemoryStream();

libsignal-service-dotnet/push/http/AttachmentCipherOutputStreamFactory.cs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,5 @@ public DigestingOutputStream CreateFor(Stream wrap)
2020
{
2121
return new AttachmentCipherOutputStream(Key, wrap);
2222
}
23-
24-
public long getCiphertextLength(long plaintextLength)
25-
{
26-
return AttachmentCipherOutputStream.GetCiphertextLength(plaintextLength);
27-
}
2823
}
2924
}
Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,24 @@
11
using libsignaldotnet.push.http;
2-
using libsignalservice.crypto;
2+
using libsignalservice.crypto;
33
using System;
44
using System.Collections.Generic;
5-
using System.IO;
5+
using System.IO;
66
using System.Text;
77

88
namespace libsignalservice.push.http
99
{
10-
internal class ProfileCipherOutputStreamFactory : OutputStreamFactory
11-
{
12-
private readonly byte[] Key;
13-
14-
internal ProfileCipherOutputStreamFactory(byte[] key)
15-
{
16-
Key = key;
17-
}
18-
19-
public DigestingOutputStream CreateFor(Stream wrap)
20-
{
21-
return new ProfileCipherOutputStream(wrap, Key);
22-
}
23-
24-
public long GetCiphertextLength(long plaintextLength)
25-
{
26-
return ProfileCipherOutputStream.GetCiphertextLength(plaintextLength);
27-
}
10+
internal class ProfileCipherOutputStreamFactory : OutputStreamFactory
11+
{
12+
private readonly byte[] Key;
13+
14+
internal ProfileCipherOutputStreamFactory(byte[] key)
15+
{
16+
Key = key;
17+
}
18+
19+
public DigestingOutputStream CreateFor(Stream wrap)
20+
{
21+
return new ProfileCipherOutputStream(wrap, Key);
22+
}
2823
}
2924
}

0 commit comments

Comments
 (0)