Kotlin conversion: TeeBCPGInputStream

This commit is contained in:
Paul Schaub 2023-09-20 13:37:15 +02:00
parent befb1c8c0f
commit 0fa09065cf
Signed by: vanitasvitae
GPG Key ID: 62BEE9264BF17311
2 changed files with 136 additions and 159 deletions

View File

@ -1,159 +0,0 @@
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.decryption_verification;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.bouncycastle.bcpg.BCPGInputStream;
import org.bouncycastle.bcpg.MarkerPacket;
import org.bouncycastle.bcpg.Packet;
import org.bouncycastle.openpgp.PGPCompressedData;
import org.bouncycastle.openpgp.PGPEncryptedDataList;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPLiteralData;
import org.bouncycastle.openpgp.PGPOnePassSignature;
import org.bouncycastle.openpgp.PGPSignature;
import org.pgpainless.algorithm.OpenPgpPacket;
import javax.annotation.Nonnull;
/**
* Since we need to update signatures with data from the underlying stream, this class is used to tee out the data.
* Unfortunately we cannot simply override {@link BCPGInputStream#read()} to tee the data out though, since
* {@link BCPGInputStream#readPacket()} inconsistently calls a mix of {@link BCPGInputStream#read()} and
* {@link InputStream#read()} of the underlying stream. This would cause the second length byte to get swallowed up.
*
* Therefore, this class delegates the teeing to an {@link DelayedTeeInputStream} which wraps the underlying
* stream. Since calling {@link BCPGInputStream#nextPacketTag()} reads up to and including the next packets tag,
* we need to delay teeing out that byte to signature verifiers.
* Hence, the reading methods of the {@link TeeBCPGInputStream} handle pushing this byte to the output stream using
* {@link DelayedTeeInputStream#squeeze()}.
*/
public class TeeBCPGInputStream {
protected final DelayedTeeInputStream delayedTee;
// InputStream of OpenPGP packets of the current layer
protected final BCPGInputStream packetInputStream;
public TeeBCPGInputStream(BCPGInputStream inputStream, OutputStream outputStream) {
this.delayedTee = new DelayedTeeInputStream(inputStream, outputStream);
this.packetInputStream = BCPGInputStream.wrap(delayedTee);
}
public OpenPgpPacket nextPacketTag() throws IOException {
int tag = packetInputStream.nextPacketTag();
if (tag == -1) {
return null;
}
return OpenPgpPacket.requireFromTag(tag);
}
public Packet readPacket() throws IOException {
return packetInputStream.readPacket();
}
public PGPCompressedData readCompressedData() throws IOException {
delayedTee.squeeze();
PGPCompressedData compressedData = new PGPCompressedData(packetInputStream);
return compressedData;
}
public PGPLiteralData readLiteralData() throws IOException {
delayedTee.squeeze();
return new PGPLiteralData(packetInputStream);
}
public PGPEncryptedDataList readEncryptedDataList() throws IOException {
delayedTee.squeeze();
return new PGPEncryptedDataList(packetInputStream);
}
public PGPOnePassSignature readOnePassSignature() throws PGPException, IOException {
PGPOnePassSignature onePassSignature = new PGPOnePassSignature(packetInputStream);
delayedTee.squeeze();
return onePassSignature;
}
public PGPSignature readSignature() throws PGPException, IOException {
PGPSignature signature = new PGPSignature(packetInputStream);
delayedTee.squeeze();
return signature;
}
public MarkerPacket readMarker() throws IOException {
MarkerPacket markerPacket = (MarkerPacket) readPacket();
delayedTee.squeeze();
return markerPacket;
}
public void close() throws IOException {
this.packetInputStream.close();
}
public static class DelayedTeeInputStream extends InputStream {
private int last = -1;
private final InputStream inputStream;
private final OutputStream outputStream;
public DelayedTeeInputStream(InputStream inputStream, OutputStream outputStream) {
this.inputStream = inputStream;
this.outputStream = outputStream;
}
@Override
public int read() throws IOException {
if (last != -1) {
outputStream.write(last);
}
try {
last = inputStream.read();
return last;
} catch (IOException e) {
if (e.getMessage().contains("crc check failed in armored message")) {
throw e;
}
return -1;
}
}
@Override
public int read(@Nonnull byte[] b, int off, int len) throws IOException {
if (last != -1) {
outputStream.write(last);
}
int r = inputStream.read(b, off, len);
if (r > 0) {
outputStream.write(b, off, r - 1);
last = b[off + r - 1];
} else {
last = -1;
}
return r;
}
/**
* Squeeze the last byte out and update the output stream.
*
* @throws IOException in case of an IO error
*/
public void squeeze() throws IOException {
if (last != -1) {
outputStream.write(last);
}
last = -1;
}
@Override
public void close() throws IOException {
inputStream.close();
outputStream.close();
}
}
}

View File

@ -0,0 +1,136 @@
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.decryption_verification
import org.bouncycastle.bcpg.BCPGInputStream
import org.bouncycastle.bcpg.MarkerPacket
import org.bouncycastle.bcpg.Packet
import org.bouncycastle.openpgp.PGPCompressedData
import org.bouncycastle.openpgp.PGPEncryptedDataList
import org.bouncycastle.openpgp.PGPLiteralData
import org.bouncycastle.openpgp.PGPOnePassSignature
import org.bouncycastle.openpgp.PGPSignature
import org.pgpainless.algorithm.OpenPgpPacket
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
/**
* Since we need to update signatures with data from the underlying stream, this class is used to tee out the data.
* Unfortunately we cannot simply override [BCPGInputStream.read] to tee the data out though, since
* [BCPGInputStream.readPacket] inconsistently calls a mix of [BCPGInputStream.read] and
* [InputStream.read] of the underlying stream. This would cause the second length byte to get swallowed up.
*
* Therefore, this class delegates the teeing to an [DelayedTeeInputStream] which wraps the underlying
* stream. Since calling [BCPGInputStream.nextPacketTag] reads up to and including the next packets tag,
* we need to delay teeing out that byte to signature verifiers.
* Hence, the reading methods of the [TeeBCPGInputStream] handle pushing this byte to the output stream using
* [DelayedTeeInputStream.squeeze].
*/
class TeeBCPGInputStream(
inputStream: BCPGInputStream,
outputStream: OutputStream) {
private val delayedTee: DelayedTeeInputStream
private val packetInputStream: BCPGInputStream
init {
delayedTee = DelayedTeeInputStream(inputStream, outputStream)
packetInputStream = BCPGInputStream(delayedTee)
}
fun nextPacketTag(): OpenPgpPacket? {
return packetInputStream.nextPacketTag().let {
if (it == -1) null
else OpenPgpPacket.requireFromTag(it)
}
}
fun readPacket(): Packet = packetInputStream.readPacket()
fun readCompressedData(): PGPCompressedData {
delayedTee.squeeze()
return PGPCompressedData(packetInputStream)
}
fun readLiteralData(): PGPLiteralData {
delayedTee.squeeze()
return PGPLiteralData(packetInputStream)
}
fun readEncryptedDataList(): PGPEncryptedDataList {
delayedTee.squeeze()
return PGPEncryptedDataList(packetInputStream)
}
fun readOnePassSignature(): PGPOnePassSignature {
return PGPOnePassSignature(packetInputStream).also { delayedTee.squeeze() }
}
fun readSignature(): PGPSignature {
return PGPSignature(packetInputStream).also { delayedTee.squeeze() }
}
fun readMarker(): MarkerPacket {
return (readPacket() as MarkerPacket).also { delayedTee.squeeze() }
}
fun close() {
packetInputStream.close()
}
class DelayedTeeInputStream(
private val inputStream: InputStream,
private val outputStream: OutputStream
) : InputStream() {
private var last: Int = -1
override fun read(): Int {
if (last != -1) {
outputStream.write(last)
}
return try {
last = inputStream.read()
last
} catch (e : IOException) {
if (e.message?.contains("crc check failed in armored message") == true) {
throw e
}
-1
}
}
override fun read(b: ByteArray, off: Int, len: Int): Int {
if (last != -1) {
outputStream.write(last)
}
inputStream.read(b, off, len).let { r ->
last = if (r > 0) {
outputStream.write(b, off, r - 1)
b[off + r - 1].toInt()
} else {
-1
}
return r
}
}
/**
* Squeeze the last byte out and update the output stream.
*/
fun squeeze() {
if (last != -1) {
outputStream.write(last)
}
last = -1
}
override fun close() {
inputStream.close()
outputStream.close()
}
}
}