1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2025-12-17 01:31:08 +01:00

Add ProducerOptions.applyCRLFEncoding()

Enabling it will automatically apply CRLF encoding to input data.
Further, disentangle signing from the encryption stream
This commit is contained in:
Paul Schaub 2022-03-31 15:03:50 +02:00
parent ade07bde85
commit f8e66f4d61
4 changed files with 214 additions and 41 deletions

View file

@ -21,6 +21,7 @@ import org.bouncycastle.openpgp.PGPSignatureGenerator;
import org.bouncycastle.openpgp.operator.PGPDataEncryptorBuilder;
import org.bouncycastle.openpgp.operator.PGPKeyEncryptionMethodGenerator;
import org.pgpainless.algorithm.CompressionAlgorithm;
import org.pgpainless.algorithm.StreamEncoding;
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
import org.pgpainless.implementation.ImplementationFactory;
import org.pgpainless.key.SubkeyIdentifier;
@ -70,6 +71,8 @@ public final class EncryptionStream extends OutputStream {
prepareCompression();
prepareOnePassSignatures();
prepareLiteralDataProcessing();
prepareSigningStream();
prepareInputEncoding();
}
private void prepareArmor() {
@ -174,20 +177,19 @@ public final class EncryptionStream extends OutputStream {
.setFileEncoding(options.getEncoding());
}
public void prepareSigningStream() {
outermostStream = new SignatureGenerationStream(outermostStream, options.getSigningOptions());
}
public void prepareInputEncoding() {
CRLFGeneratorStream crlfGeneratorStream = new CRLFGeneratorStream(outermostStream,
options.isApplyCRLFEncoding() ? StreamEncoding.UTF8 : StreamEncoding.BINARY);
outermostStream = crlfGeneratorStream;
}
@Override
public void write(int data) throws IOException {
outermostStream.write(data);
SigningOptions signingOptions = options.getSigningOptions();
if (signingOptions == null || signingOptions.getSigningMethods().isEmpty()) {
return;
}
for (SubkeyIdentifier signingKey : signingOptions.getSigningMethods().keySet()) {
SigningOptions.SigningMethod signingMethod = signingOptions.getSigningMethods().get(signingKey);
PGPSignatureGenerator signatureGenerator = signingMethod.getSignatureGenerator();
byte asByte = (byte) (data & 0xff);
signatureGenerator.update(asByte);
}
}
@Override
@ -199,15 +201,6 @@ public final class EncryptionStream extends OutputStream {
@Override
public void write(@Nonnull byte[] buffer, int off, int len) throws IOException {
outermostStream.write(buffer, 0, len);
SigningOptions signingOptions = options.getSigningOptions();
if (signingOptions == null || signingOptions.getSigningMethods().isEmpty()) {
return;
}
for (SubkeyIdentifier signingKey : signingOptions.getSigningMethods().keySet()) {
SigningOptions.SigningMethod signingMethod = signingOptions.getSigningMethods().get(signingKey);
PGPSignatureGenerator signatureGenerator = signingMethod.getSignatureGenerator();
signatureGenerator.update(buffer, 0, len);
}
}
@Override
@ -221,6 +214,8 @@ public final class EncryptionStream extends OutputStream {
return;
}
outermostStream.close();
// Literal Data
if (literalDataStream != null) {
literalDataStream.flush();

View file

@ -19,7 +19,8 @@ public final class ProducerOptions {
private final SigningOptions signingOptions;
private String fileName = "";
private Date modificationDate = PGPLiteralData.NOW;
private StreamEncoding streamEncoding = StreamEncoding.BINARY;
private StreamEncoding encodingField = StreamEncoding.BINARY;
private boolean applyCRLFEncoding = false;
private boolean cleartextSigned = false;
private CompressionAlgorithm compressionAlgorithmOverride = PGPainless.getPolicy().getCompressionAlgorithmPolicy()
@ -223,9 +224,12 @@ public final class ProducerOptions {
}
/**
* Set the format of the literal data packet.
* Set format metadata field of the literal data packet.
* Defaults to {@link StreamEncoding#BINARY}.
*
* This does not change the encoding of the wrapped data itself.
* To apply CR/LF encoding to your input data before processing, use {@link #applyCRLFEncoding(boolean)} instead.
*
* @see <a href="https://datatracker.ietf.org/doc/html/rfc4880#section-5.9">RFC4880 §5.9. Literal Data Packet</a>
*
* @param encoding encoding
@ -235,12 +239,37 @@ public final class ProducerOptions {
*/
@Deprecated
public ProducerOptions setEncoding(@Nonnull StreamEncoding encoding) {
this.streamEncoding = encoding;
this.encodingField = encoding;
return this;
}
public StreamEncoding getEncoding() {
return streamEncoding;
return encodingField;
}
/**
* Apply special encoding of line endings to the input data.
* By default, this is set to <pre>false</pre>, which means that the data is not altered.
*
* Setting it to <pre>true</pre> will change the line endings to CR/LF.
* Note: The encoding will not be reversed when decrypting, so applying CR/LF encoding will result in
* the identity "decrypt(encrypt(data)) == data == verify(sign(data))".
*
* @param applyCRLFEncoding apply crlf encoding
* @return this
*/
public ProducerOptions applyCRLFEncoding(boolean applyCRLFEncoding) {
this.applyCRLFEncoding = applyCRLFEncoding;
return this;
}
/**
* Return the input encoding that will be applied before signing / encryption.
*
* @return input encoding
*/
public boolean isApplyCRLFEncoding() {
return applyCRLFEncoding;
}
/**

View file

@ -0,0 +1,61 @@
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.encryption_signing;
import org.bouncycastle.openpgp.PGPSignatureGenerator;
import org.pgpainless.key.SubkeyIdentifier;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.io.OutputStream;
class SignatureGenerationStream extends OutputStream {
private final OutputStream wrapped;
private final SigningOptions options;
SignatureGenerationStream(OutputStream wrapped, SigningOptions signingOptions) {
this.wrapped = wrapped;
this.options = signingOptions;
}
@Override
public void write(int b) throws IOException {
wrapped.write(b);
if (options == null || options.getSigningMethods().isEmpty()) {
return;
}
for (SubkeyIdentifier signingKey : options.getSigningMethods().keySet()) {
SigningOptions.SigningMethod signingMethod = options.getSigningMethods().get(signingKey);
PGPSignatureGenerator signatureGenerator = signingMethod.getSignatureGenerator();
byte asByte = (byte) (b & 0xff);
signatureGenerator.update(asByte);
}
}
@Override
public void write(@Nonnull byte[] buffer) throws IOException {
write(buffer, 0, buffer.length);
}
@Override
public void write(@Nonnull byte[] buffer, int off, int len) throws IOException {
wrapped.write(buffer, 0, len);
if (options == null || options.getSigningMethods().isEmpty()) {
return;
}
for (SubkeyIdentifier signingKey : options.getSigningMethods().keySet()) {
SigningOptions.SigningMethod signingMethod = options.getSigningMethods().get(signingKey);
PGPSignatureGenerator signatureGenerator = signingMethod.getSignatureGenerator();
signatureGenerator.update(buffer, 0, len);
}
}
@Override
public void close() throws IOException {
wrapped.close();
}
}