1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2024-11-16 09:22:05 +01:00

Add StreamGeneratorWrapper which uses new PGPCanonicalizedDataGenerator if required

This commit is contained in:
Paul Schaub 2021-10-15 13:32:48 +02:00
parent 888073b604
commit 03f13ee4a7
3 changed files with 103 additions and 11 deletions

View file

@ -15,7 +15,6 @@ import org.bouncycastle.bcpg.BCPGOutputStream;
import org.bouncycastle.openpgp.PGPCompressedDataGenerator; import org.bouncycastle.openpgp.PGPCompressedDataGenerator;
import org.bouncycastle.openpgp.PGPEncryptedDataGenerator; import org.bouncycastle.openpgp.PGPEncryptedDataGenerator;
import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPLiteralDataGenerator;
import org.bouncycastle.openpgp.PGPSignature; import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPSignatureGenerator; import org.bouncycastle.openpgp.PGPSignatureGenerator;
import org.bouncycastle.openpgp.operator.PGPDataEncryptorBuilder; import org.bouncycastle.openpgp.operator.PGPDataEncryptorBuilder;
@ -25,6 +24,7 @@ import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
import org.pgpainless.implementation.ImplementationFactory; import org.pgpainless.implementation.ImplementationFactory;
import org.pgpainless.key.SubkeyIdentifier; import org.pgpainless.key.SubkeyIdentifier;
import org.pgpainless.util.ArmoredOutputStreamFactory; import org.pgpainless.util.ArmoredOutputStreamFactory;
import org.pgpainless.util.StreamGeneratorWrapper;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -50,7 +50,7 @@ public final class EncryptionStream extends OutputStream {
private OutputStream publicKeyEncryptedStream = null; private OutputStream publicKeyEncryptedStream = null;
private PGPCompressedDataGenerator compressedDataGenerator; private PGPCompressedDataGenerator compressedDataGenerator;
private BCPGOutputStream basicCompressionStream; private BCPGOutputStream basicCompressionStream;
private PGPLiteralDataGenerator literalDataGenerator; private StreamGeneratorWrapper streamGeneratorWrapper;
private OutputStream literalDataStream; private OutputStream literalDataStream;
EncryptionStream(@Nonnull OutputStream targetOutputStream, EncryptionStream(@Nonnull OutputStream targetOutputStream,
@ -147,12 +147,10 @@ public final class EncryptionStream extends OutputStream {
armorOutputStream.beginClearText(firstMethod.getHashAlgorithm().getAlgorithmId()); armorOutputStream.beginClearText(firstMethod.getHashAlgorithm().getAlgorithmId());
return; return;
} }
literalDataGenerator = new PGPLiteralDataGenerator();
literalDataStream = literalDataGenerator.open(outermostStream, streamGeneratorWrapper = StreamGeneratorWrapper.forStreamEncoding(options.getEncoding());
options.getEncoding().getCode(), literalDataStream = streamGeneratorWrapper.open(outermostStream,
options.getFileName(), options.getFileName(), options.getModificationDate(), new byte[BUFFER_SIZE]);
options.getModificationDate(),
new byte[BUFFER_SIZE]);
outermostStream = literalDataStream; outermostStream = literalDataStream;
resultBuilder.setFileName(options.getFileName()) resultBuilder.setFileName(options.getFileName())
@ -212,8 +210,8 @@ public final class EncryptionStream extends OutputStream {
literalDataStream.flush(); literalDataStream.flush();
literalDataStream.close(); literalDataStream.close();
} }
if (literalDataGenerator != null) { if (streamGeneratorWrapper != null) {
literalDataGenerator.close(); streamGeneratorWrapper.close();
} }
if (options.isCleartextSigned()) { if (options.isCleartextSigned()) {

View file

@ -0,0 +1,94 @@
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.util;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Date;
import javax.annotation.Nonnull;
import org.bouncycastle.openpgp.PGPCanonicalizedDataGenerator;
import org.bouncycastle.openpgp.PGPLiteralDataGenerator;
import org.pgpainless.algorithm.StreamEncoding;
/**
* Literal Data can be encoded in different ways.
* BINARY encoding leaves the data as is and is generated through the {@link PGPLiteralDataGenerator}.
* However, if the data is encoded in TEXT or UTF8 encoding, we need to use the {@link PGPCanonicalizedDataGenerator}
* instead.
*
* This wrapper class acts as a handle for both options and provides a unified interface for them.
*/
public final class StreamGeneratorWrapper {
private final StreamEncoding encoding;
private final PGPLiteralDataGenerator literalDataGenerator;
private final PGPCanonicalizedDataGenerator canonicalizedDataGenerator;
/**
* Create a new instance for the given encoding.
*
* @param encoding stream encoding
* @return wrapper
*/
public static StreamGeneratorWrapper forStreamEncoding(@Nonnull StreamEncoding encoding) {
if (encoding == StreamEncoding.BINARY) {
return new StreamGeneratorWrapper(encoding, new PGPLiteralDataGenerator());
} else {
return new StreamGeneratorWrapper(encoding, new PGPCanonicalizedDataGenerator());
}
}
private StreamGeneratorWrapper(@Nonnull StreamEncoding encoding, @Nonnull PGPLiteralDataGenerator literalDataGenerator) {
if (encoding != StreamEncoding.BINARY) {
throw new IllegalArgumentException("PGPLiteralDataGenerator can only be used with BINARY encoding.");
}
this.encoding = encoding;
this.literalDataGenerator = literalDataGenerator;
this.canonicalizedDataGenerator = null;
}
private StreamGeneratorWrapper(@Nonnull StreamEncoding encoding, @Nonnull PGPCanonicalizedDataGenerator canonicalizedDataGenerator) {
if (encoding != StreamEncoding.TEXT && encoding != StreamEncoding.UTF8) {
throw new IllegalArgumentException("PGPCanonicalizedDataGenerator can only be used with TEXT or UTF8 encoding.");
}
this.encoding = encoding;
this.canonicalizedDataGenerator = canonicalizedDataGenerator;
this.literalDataGenerator = null;
}
/**
* Open a new encoding stream.
*
* @param outputStream wrapped output stream
* @param filename file name
* @param modificationDate modification date
* @param buffer buffer
* @return encoding stream
* @throws IOException
*/
public OutputStream open(OutputStream outputStream, String filename, Date modificationDate, byte[] buffer) throws IOException {
if (literalDataGenerator != null) {
return literalDataGenerator.open(outputStream, encoding.getCode(), filename, modificationDate, buffer);
} else {
return canonicalizedDataGenerator.open(outputStream, encoding.getCode(), filename, modificationDate, buffer);
}
}
/**
* Close all encoding streams opened by this generator wrapper.
*
* @throws IOException
*/
public void close() throws IOException {
if (literalDataGenerator != null) {
literalDataGenerator.close();
}
if (canonicalizedDataGenerator != null) {
canonicalizedDataGenerator.close();
}
}
}

View file

@ -23,7 +23,7 @@ public class LiteralDataCRLFEncodingTest {
public void testCanonicalization() throws IOException { public void testCanonicalization() throws IOException {
PGPCanonicalizedDataGenerator generator = new PGPCanonicalizedDataGenerator(); PGPCanonicalizedDataGenerator generator = new PGPCanonicalizedDataGenerator();
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
OutputStream canonicalizer = generator.open(out, PGPCanonicalizedDataGenerator.UTF8, "", new Date(), new byte[1<<9]); OutputStream canonicalizer = generator.open(out, PGPCanonicalizedDataGenerator.UTF8, "", new Date(), new byte[1 << 9]);
ByteArrayInputStream in = new ByteArrayInputStream("Foo\nBar\n".getBytes(StandardCharsets.UTF_8)); ByteArrayInputStream in = new ByteArrayInputStream("Foo\nBar\n".getBytes(StandardCharsets.UTF_8));
Streams.pipeAll(in, canonicalizer); Streams.pipeAll(in, canonicalizer);