Convert AttachmentCipherOutputStream to kotlin.

This commit is contained in:
Greyson Parrelli
2025-06-11 14:00:25 -04:00
committed by Michelle Tang
parent 381c0e186f
commit 2e79e257a3
2 changed files with 89 additions and 108 deletions

View File

@@ -1,108 +0,0 @@
/*
* Copyright (C) 2014-2017 Open Whisper Systems
*
* Licensed according to the LICENSE file in this repository.
*/
package org.whispersystems.signalservice.api.crypto;
import org.whispersystems.signalservice.internal.util.Util;
import java.io.IOException;
import java.io.OutputStream;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.Mac;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class AttachmentCipherOutputStream extends DigestingOutputStream {
private final Cipher cipher;
private final Mac mac;
public AttachmentCipherOutputStream(byte[] combinedKeyMaterial,
byte[] iv,
OutputStream outputStream)
throws IOException
{
super(outputStream);
try {
this.cipher = initializeCipher();
this.mac = initializeMac();
byte[][] keyParts = Util.split(combinedKeyMaterial, 32, 32);
if (iv == null) {
this.cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyParts[0], "AES"));
} else {
this.cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyParts[0], "AES"), new IvParameterSpec(iv));
}
this.mac.init(new SecretKeySpec(keyParts[1], "HmacSHA256"));
mac.update(cipher.getIV());
super.write(cipher.getIV());
} catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
throw new AssertionError(e);
}
}
@Override
public void write(byte[] buffer) throws IOException {
write(buffer, 0, buffer.length);
}
@Override
public void write(byte[] buffer, int offset, int length) throws IOException {
byte[] ciphertext = cipher.update(buffer, offset, length);
if (ciphertext != null) {
mac.update(ciphertext);
super.write(ciphertext);
}
}
@Override
public void write(int b) throws IOException {
byte[] input = new byte[1];
input[0] = (byte) b;
write(input, 0, 1);
}
@Override
public void close() throws IOException {
try {
byte[] ciphertext = cipher.doFinal();
byte[] auth = mac.doFinal(ciphertext);
super.write(ciphertext);
super.write(auth);
super.close();
} catch (IllegalBlockSizeException | BadPaddingException e) {
throw new AssertionError(e);
}
}
private Mac initializeMac() {
try {
return Mac.getInstance("HmacSHA256");
} catch (NoSuchAlgorithmException e) {
throw new AssertionError(e);
}
}
private Cipher initializeCipher() {
try {
return Cipher.getInstance("AES/CBC/PKCS5Padding");
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
throw new AssertionError(e);
}
}
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright (C) 2014-2017 Open Whisper Systems
*
* Licensed according to the LICENSE file in this repository.
*/
package org.whispersystems.signalservice.api.crypto
import org.whispersystems.signalservice.internal.util.Util
import java.io.IOException
import java.io.OutputStream
import javax.crypto.BadPaddingException
import javax.crypto.Cipher
import javax.crypto.IllegalBlockSizeException
import javax.crypto.Mac
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec
/**
* An OutputStream for encrypting attachment data.
* The output stream writes the IV, ciphertext, and HMAC in sequence.
*
* @param combinedKeyMaterial The key material used for encryption and authentication. It is expected to be a byte array
* containing two parts: the first half being the AES key and the second half being the HMAC key.
* @param iv The initialization vector (IV) for the cipher, or null to generate a random one.
* @param outputStream The underlying output stream to write the encrypted data to.
*/
class AttachmentCipherOutputStream(
combinedKeyMaterial: ByteArray,
iv: ByteArray?,
outputStream: OutputStream
) : DigestingOutputStream(outputStream) {
private val cipher: Cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
private val mac: Mac = Mac.getInstance("HmacSHA256")
init {
val keyParts = Util.split(combinedKeyMaterial, 32, 32)
if (iv == null) {
cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(keyParts[0], "AES"))
} else {
cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(keyParts[0], "AES"), IvParameterSpec(iv))
}
mac.init(SecretKeySpec(keyParts[1], "HmacSHA256"))
mac.update(cipher.iv)
super.write(cipher.iv)
}
@Throws(IOException::class)
override fun write(buffer: ByteArray) {
write(buffer, 0, buffer.size)
}
@Throws(IOException::class)
override fun write(buffer: ByteArray, offset: Int, length: Int) {
val ciphertext = cipher.update(buffer, offset, length)
if (ciphertext != null) {
mac.update(ciphertext)
super.write(ciphertext)
}
}
@Throws(IOException::class)
override fun write(b: Int) {
val input = ByteArray(1)
input[0] = b.toByte()
write(input, 0, 1)
}
@Throws(IOException::class)
override fun close() {
try {
val ciphertext = cipher.doFinal()
val auth = mac.doFinal(ciphertext)
super.write(ciphertext)
super.write(auth)
super.close()
} catch (e: IllegalBlockSizeException) {
throw AssertionError(e)
} catch (e: BadPaddingException) {
throw AssertionError(e)
}
}
}