Match incremental MAC calculation.

This commit is contained in:
Nicholas Tinsley
2023-09-08 15:38:36 -04:00
parent 1565c32162
commit 5caf3409db
8 changed files with 37 additions and 21 deletions

View File

@@ -19,8 +19,8 @@ import org.thoughtcrime.securesms.database.AttachmentTable;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.events.PartProgressEvent;
import org.thoughtcrime.securesms.jobmanager.JsonJobData;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.JsonJobData;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.mms.PartAuthority;
import org.thoughtcrime.securesms.net.NotPushRegisteredException;
@@ -29,7 +29,7 @@ import org.thoughtcrime.securesms.service.NotificationController;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.AttachmentCipherOutputStream;
import org.whispersystems.signalservice.api.crypto.AttachmentCipherStreamUtil;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream;
@@ -69,7 +69,7 @@ public final class AttachmentUploadJob extends BaseJob {
public static long getMaxPlaintextSize() {
long maxCipherTextSize = FeatureFlags.maxAttachmentSizeBytes();
long maxPaddedSize = AttachmentCipherOutputStream.getPlaintextLength(maxCipherTextSize);
long maxPaddedSize = AttachmentCipherStreamUtil.getPlaintextLength(maxCipherTextSize);
return PaddingInputStream.getMaxUnpaddedSize(maxPaddedSize);
}

View File

@@ -23,6 +23,7 @@ import org.signal.libsignal.protocol.state.PreKeyBundle;
import org.signal.libsignal.protocol.util.Pair;
import org.signal.libsignal.zkgroup.profiles.ClientZkProfileOperations;
import org.whispersystems.signalservice.api.crypto.AttachmentCipherOutputStream;
import org.whispersystems.signalservice.api.crypto.AttachmentCipherStreamUtil;
import org.whispersystems.signalservice.api.crypto.ContentHint;
import org.whispersystems.signalservice.api.crypto.EnvelopeContent;
import org.whispersystems.signalservice.api.crypto.SignalGroupSessionBuilder;
@@ -786,7 +787,7 @@ public class SignalServiceMessageSender {
byte[] attachmentIV = attachment.getResumableUploadSpec().map(ResumableUploadSpec::getIV).orElseGet(() -> Util.getSecretBytes(16));
long paddedLength = PaddingInputStream.getPaddedSize(attachment.getLength());
InputStream dataStream = new PaddingInputStream(attachment.getInputStream(), attachment.getLength());
long ciphertextLength = AttachmentCipherOutputStream.getCiphertextLength(paddedLength);
long ciphertextLength = AttachmentCipherStreamUtil.getCiphertextLength(paddedLength);
PushAttachmentData attachmentData = new PushAttachmentData(attachment.getContentType(),
dataStream,
ciphertextLength,

View File

@@ -11,6 +11,8 @@ import org.signal.libsignal.protocol.InvalidMessageException;
import org.signal.libsignal.protocol.incrementalmac.ChunkSizeChoice;
import org.signal.libsignal.protocol.incrementalmac.IncrementalMacInputStream;
import org.signal.libsignal.protocol.kdf.HKDFv3;
import org.signal.libsignal.protocol.logging.Log;
import org.whispersystems.signalservice.internal.crypto.PaddingInputStream;
import org.whispersystems.signalservice.internal.util.ContentLengthInputStream;
import org.whispersystems.signalservice.internal.util.Util;
@@ -78,10 +80,12 @@ public class AttachmentCipherInputStream extends FilterInputStream {
}
wrappedStream = new FileInputStream(file);
} else {
final int dataSize = Math.toIntExact(AttachmentCipherStreamUtil.getCiphertextLength(PaddingInputStream.getPaddedSize(plaintextLength)));
final ChunkSizeChoice sizeChoice = ChunkSizeChoice.inferChunkSize(dataSize);
wrappedStream = new IncrementalMacInputStream(
new FileInputStream(file),
parts[1],
ChunkSizeChoice.inferChunkSize(Math.toIntExact(plaintextLength)),
sizeChoice,
incrementalDigest);
}
InputStream inputStream = new AttachmentCipherInputStream(wrappedStream, parts[0], file.length() - BLOCK_SIZE - mac.getMacLength());

View File

@@ -88,14 +88,6 @@ public class AttachmentCipherOutputStream extends DigestingOutputStream {
}
}
public static long getCiphertextLength(long plaintextLength) {
return 16 + (((plaintextLength / 16) + 1) * 16) + 32;
}
public static long getPlaintextLength(long ciphertextLength) {
return (((ciphertextLength - 16 - 32) / 16) - 1) * 16;
}
private Mac initializeMac() {
try {
return Mac.getInstance("HmacSHA256");

View File

@@ -0,0 +1,20 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.signalservice.api.crypto
class AttachmentCipherStreamUtil {
companion object {
@JvmStatic
fun getCiphertextLength(plaintextLength: Long): Long {
return 16 + (plaintextLength / 16 + 1) * 16 + 32
}
@JvmStatic
fun getPlaintextLength(ciphertextLength: Long): Long {
return ((ciphertextLength - 16 - 32) / 16 - 1) * 16
}
}
}

View File

@@ -33,8 +33,8 @@ class AttachmentCipherOutputStreamFactory(private val key: ByteArray, private va
}
val privateKey = key.sliceArray(AES_KEY_LENGTH until key.size)
val chunkSizeChoice = ChunkSizeChoice.inferChunkSize(length.toInt())
val incrementalStream = IncrementalMacOutputStream(wrap, privateKey, chunkSizeChoice, incrementalDigestOut)
val sizeChoice = ChunkSizeChoice.inferChunkSize(length.toInt())
val incrementalStream = IncrementalMacOutputStream(wrap, privateKey, sizeChoice, incrementalDigestOut)
return createFor(incrementalStream)
}
}

View File

@@ -35,7 +35,7 @@ public final class AttachmentCipherTest {
public void attachment_encryptDecrypt() throws IOException, InvalidMessageException {
byte[] key = Util.getSecretBytes(64);
byte[] plaintextInput = "Peter Parker".getBytes();
EncryptResult encryptResult = encryptData(plaintextInput, key, true);
EncryptResult encryptResult = encryptData(plaintextInput, key, false);
File cipherFile = writeToFile(encryptResult.ciphertext);
InputStream inputStream = AttachmentCipherInputStream.createForAttachment(cipherFile, plaintextInput.length, key, encryptResult.digest, encryptResult.incrementalDigest);
byte[] plaintextOutput = readInputStreamFully(inputStream);
@@ -158,8 +158,7 @@ public final class AttachmentCipherTest {
ByteArrayInputStream inputStream = new ByteArrayInputStream(plaintextInput);
InputStream paddedInputStream = new PaddingInputStream(inputStream, length);
ByteArrayOutputStream destinationOutputStream = new ByteArrayOutputStream();
ByteArrayOutputStream incrementalDigestOutputStream = new ByteArrayOutputStream();
DigestingOutputStream encryptingOutputStream = new AttachmentCipherOutputStreamFactory(key, iv).createIncrementalFor(destinationOutputStream, length, incrementalDigestOutputStream);
DigestingOutputStream encryptingOutputStream = new AttachmentCipherOutputStreamFactory(key, iv).createFor(destinationOutputStream);
Util.copy(paddedInputStream, encryptingOutputStream);
@@ -168,11 +167,10 @@ public final class AttachmentCipherTest {
byte[] encryptedData = destinationOutputStream.toByteArray();
byte[] digest = encryptingOutputStream.getTransmittedDigest();
byte[] incrementalDigest = incrementalDigestOutputStream.toByteArray();
File cipherFile = writeToFile(encryptedData);
InputStream decryptedStream = AttachmentCipherInputStream.createForAttachment(cipherFile, length, key, digest, incrementalDigest);
InputStream decryptedStream = AttachmentCipherInputStream.createForAttachment(cipherFile, length, key, digest, null);
byte[] plaintextOutput = readInputStreamFully(decryptedStream);
assertArrayEquals(plaintextInput, plaintextOutput);

View File

@@ -2,6 +2,7 @@ package org.whispersystems.signalservice.internal.push.http;
import org.junit.Test;
import org.whispersystems.signalservice.api.crypto.AttachmentCipherOutputStream;
import org.whispersystems.signalservice.api.crypto.AttachmentCipherStreamUtil;
import org.whispersystems.signalservice.internal.util.Util;
import java.io.ByteArrayInputStream;
@@ -14,7 +15,7 @@ import static org.junit.Assert.assertEquals;
public class DigestingRequestBodyTest {
private static int CONTENT_LENGTH = 70000;
private static int TOTAL_LENGTH = (int) AttachmentCipherOutputStream.getCiphertextLength(CONTENT_LENGTH);
private static int TOTAL_LENGTH = (int) AttachmentCipherStreamUtil.getCiphertextLength(CONTENT_LENGTH);
private final byte[] attachmentKey = Util.getSecretBytes(64);
private final byte[] attachmentIV = Util.getSecretBytes(16);