mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-12-25 12:27:58 +01:00
Fix CRCing test and fully depend on new stream for decryption
This commit is contained in:
parent
ba947c81d3
commit
ce85b01057
5 changed files with 137 additions and 55 deletions
|
@ -31,7 +31,7 @@ public class DecryptionBuilder implements DecryptionBuilderInterface {
|
||||||
throw new IllegalArgumentException("Consumer options cannot be null.");
|
throw new IllegalArgumentException("Consumer options cannot be null.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return DecryptionStreamFactory.create(inputStream, consumerOptions);
|
return OpenPgpMessageInputStream.create(inputStream, consumerOptions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -164,19 +164,35 @@ public class MessageMetadata {
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getFilename() {
|
public String getFilename() {
|
||||||
return findLiteralData().getFileName();
|
LiteralData literalData = findLiteralData();
|
||||||
|
if (literalData == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return literalData.getFileName();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Date getModificationDate() {
|
public Date getModificationDate() {
|
||||||
return findLiteralData().getModificationDate();
|
LiteralData literalData = findLiteralData();
|
||||||
|
if (literalData == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return literalData.getModificationDate();
|
||||||
}
|
}
|
||||||
|
|
||||||
public StreamEncoding getFormat() {
|
public StreamEncoding getFormat() {
|
||||||
return findLiteralData().getFormat();
|
LiteralData literalData = findLiteralData();
|
||||||
|
if (literalData == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return literalData.getFormat();
|
||||||
}
|
}
|
||||||
|
|
||||||
private LiteralData findLiteralData() {
|
private LiteralData findLiteralData() {
|
||||||
Nested nested = message.child;
|
Nested nested = message.child;
|
||||||
|
if (nested == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
while (nested.hasNestedChild()) {
|
while (nested.hasNestedChild()) {
|
||||||
Layer layer = (Layer) nested;
|
Layer layer = (Layer) nested;
|
||||||
nested = layer.child;
|
nested = layer.child;
|
||||||
|
|
|
@ -37,6 +37,8 @@ import org.bouncycastle.openpgp.operator.PBEDataDecryptorFactory;
|
||||||
import org.bouncycastle.openpgp.operator.PGPContentVerifierBuilderProvider;
|
import org.bouncycastle.openpgp.operator.PGPContentVerifierBuilderProvider;
|
||||||
import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
|
import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
|
||||||
import org.bouncycastle.openpgp.operator.SessionKeyDataDecryptorFactory;
|
import org.bouncycastle.openpgp.operator.SessionKeyDataDecryptorFactory;
|
||||||
|
import org.bouncycastle.util.io.Streams;
|
||||||
|
import org.bouncycastle.util.io.TeeInputStream;
|
||||||
import org.pgpainless.PGPainless;
|
import org.pgpainless.PGPainless;
|
||||||
import org.pgpainless.algorithm.CompressionAlgorithm;
|
import org.pgpainless.algorithm.CompressionAlgorithm;
|
||||||
import org.pgpainless.algorithm.EncryptionPurpose;
|
import org.pgpainless.algorithm.EncryptionPurpose;
|
||||||
|
@ -79,32 +81,87 @@ public class OpenPgpMessageInputStream extends DecryptionStream {
|
||||||
|
|
||||||
// Options to consume the data
|
// Options to consume the data
|
||||||
protected final ConsumerOptions options;
|
protected final ConsumerOptions options;
|
||||||
|
|
||||||
|
private final Policy policy;
|
||||||
// Pushdown Automaton to verify validity of OpenPGP packet sequence in an OpenPGP message
|
// Pushdown Automaton to verify validity of OpenPGP packet sequence in an OpenPGP message
|
||||||
protected final PDA syntaxVerifier = new PDA();
|
protected final PDA syntaxVerifier = new PDA();
|
||||||
// InputStream of OpenPGP packets
|
// InputStream of OpenPGP packets
|
||||||
protected TeeBCPGInputStream packetInputStream;
|
protected TeeBCPGInputStream packetInputStream;
|
||||||
// InputStream of a nested data packet
|
// InputStream of a data packet containing nested data
|
||||||
protected InputStream nestedInputStream;
|
protected InputStream nestedInputStream;
|
||||||
|
|
||||||
private boolean closed = false;
|
private boolean closed = false;
|
||||||
|
|
||||||
private final Signatures signatures;
|
private final Signatures signatures;
|
||||||
private final MessageMetadata.Layer metadata;
|
private final MessageMetadata.Layer metadata;
|
||||||
private final Policy policy;
|
|
||||||
|
|
||||||
public OpenPgpMessageInputStream(@Nonnull InputStream inputStream,
|
/**
|
||||||
|
* Create an {@link OpenPgpMessageInputStream} suitable for decryption and verification of
|
||||||
|
* OpenPGP messages and signatures.
|
||||||
|
* This constructor will use the global PGPainless {@link Policy}.
|
||||||
|
*
|
||||||
|
* @param inputStream underlying input stream
|
||||||
|
* @param options options for consuming the stream
|
||||||
|
*
|
||||||
|
* @throws IOException in case of an IO error
|
||||||
|
* @throws PGPException in case of an OpenPGP error
|
||||||
|
*/
|
||||||
|
public static OpenPgpMessageInputStream create(@Nonnull InputStream inputStream,
|
||||||
@Nonnull ConsumerOptions options)
|
@Nonnull ConsumerOptions options)
|
||||||
throws IOException, PGPException {
|
throws IOException, PGPException {
|
||||||
this(inputStream, options, PGPainless.getPolicy());
|
return create(inputStream, options, PGPainless.getPolicy());
|
||||||
}
|
}
|
||||||
|
|
||||||
public OpenPgpMessageInputStream(@Nonnull InputStream inputStream,
|
/**
|
||||||
|
* Create an {@link OpenPgpMessageInputStream} suitable for decryption and verification of
|
||||||
|
* OpenPGP messages and signatures.
|
||||||
|
*
|
||||||
|
* @param inputStream underlying input stream containing the OpenPGP message
|
||||||
|
* @param options options for consuming the message
|
||||||
|
* @param policy policy for acceptable algorithms etc.
|
||||||
|
*
|
||||||
|
* @throws PGPException in case of an OpenPGP error
|
||||||
|
* @throws IOException in case of an IO error
|
||||||
|
*/
|
||||||
|
public static OpenPgpMessageInputStream create(@Nonnull InputStream inputStream,
|
||||||
@Nonnull ConsumerOptions options,
|
@Nonnull ConsumerOptions options,
|
||||||
@Nonnull Policy policy)
|
@Nonnull Policy policy)
|
||||||
throws PGPException, IOException {
|
throws PGPException, IOException {
|
||||||
this(
|
return create(inputStream, options, new MessageMetadata.Message(), policy);
|
||||||
prepareInputStream(inputStream, options, policy),
|
}
|
||||||
options, new MessageMetadata.Message(), policy);
|
|
||||||
|
protected static OpenPgpMessageInputStream create(@Nonnull InputStream inputStream,
|
||||||
|
@Nonnull ConsumerOptions options,
|
||||||
|
@Nonnull MessageMetadata.Layer metadata,
|
||||||
|
@Nonnull Policy policy)
|
||||||
|
throws IOException, PGPException {
|
||||||
|
OpenPgpInputStream openPgpIn = new OpenPgpInputStream(inputStream);
|
||||||
|
openPgpIn.reset();
|
||||||
|
|
||||||
|
if (openPgpIn.isNonOpenPgp() || options.isForceNonOpenPgpData()) {
|
||||||
|
return new OpenPgpMessageInputStream(Type.non_openpgp,
|
||||||
|
openPgpIn, options, metadata, policy);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (openPgpIn.isBinaryOpenPgp()) {
|
||||||
|
// Simply consume OpenPGP message
|
||||||
|
return new OpenPgpMessageInputStream(Type.standard,
|
||||||
|
openPgpIn, options, metadata, policy);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (openPgpIn.isAsciiArmored()) {
|
||||||
|
ArmoredInputStream armorIn = ArmoredInputStreamFactory.get(openPgpIn);
|
||||||
|
if (armorIn.isClearText()) {
|
||||||
|
return new OpenPgpMessageInputStream(Type.cleartext_signed,
|
||||||
|
armorIn, options, metadata, policy);
|
||||||
|
} else {
|
||||||
|
// Simply consume dearmored OpenPGP message
|
||||||
|
return new OpenPgpMessageInputStream(Type.standard,
|
||||||
|
armorIn, options, metadata, policy);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new AssertionError("Huh?");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected OpenPgpMessageInputStream(@Nonnull InputStream inputStream,
|
protected OpenPgpMessageInputStream(@Nonnull InputStream inputStream,
|
||||||
|
@ -131,50 +188,54 @@ public class OpenPgpMessageInputStream extends DecryptionStream {
|
||||||
consumePackets();
|
consumePackets();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected OpenPgpMessageInputStream(@Nonnull InputStream inputStream,
|
enum Type {
|
||||||
@Nonnull Policy policy,
|
standard,
|
||||||
@Nonnull ConsumerOptions options) {
|
cleartext_signed,
|
||||||
|
non_openpgp
|
||||||
|
}
|
||||||
|
|
||||||
|
protected OpenPgpMessageInputStream(@Nonnull Type type,
|
||||||
|
@Nonnull InputStream inputStream,
|
||||||
|
@Nonnull ConsumerOptions options,
|
||||||
|
@Nonnull MessageMetadata.Layer metadata,
|
||||||
|
@Nonnull Policy policy) throws PGPException, IOException {
|
||||||
super(OpenPgpMetadata.getBuilder());
|
super(OpenPgpMetadata.getBuilder());
|
||||||
this.policy = policy;
|
this.policy = policy;
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.metadata = new MessageMetadata.Message();
|
this.metadata = metadata;
|
||||||
this.signatures = new Signatures(options);
|
this.signatures = new Signatures(options);
|
||||||
|
|
||||||
|
if (metadata instanceof MessageMetadata.Message) {
|
||||||
this.signatures.addDetachedSignatures(options.getDetachedSignatures());
|
this.signatures.addDetachedSignatures(options.getDetachedSignatures());
|
||||||
this.packetInputStream = new TeeBCPGInputStream(BCPGInputStream.wrap(inputStream), signatures);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static InputStream prepareInputStream(InputStream inputStream, ConsumerOptions options, Policy policy)
|
switch (type) {
|
||||||
throws IOException, PGPException {
|
case standard:
|
||||||
OpenPgpInputStream openPgpIn = new OpenPgpInputStream(inputStream);
|
// tee out packet bytes for signature verification
|
||||||
openPgpIn.reset();
|
packetInputStream = new TeeBCPGInputStream(BCPGInputStream.wrap(inputStream), this.signatures);
|
||||||
|
|
||||||
if (openPgpIn.isBinaryOpenPgp()) {
|
// *omnomnom*
|
||||||
return openPgpIn;
|
consumePackets();
|
||||||
}
|
break;
|
||||||
|
case cleartext_signed:
|
||||||
if (openPgpIn.isAsciiArmored()) {
|
|
||||||
ArmoredInputStream armorIn = ArmoredInputStreamFactory.get(openPgpIn);
|
|
||||||
if (armorIn.isClearText()) {
|
|
||||||
return parseCleartextSignedMessage(armorIn, options, policy);
|
|
||||||
} else {
|
|
||||||
return armorIn;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return openPgpIn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static DecryptionStream parseCleartextSignedMessage(ArmoredInputStream armorIn, ConsumerOptions options, Policy policy)
|
|
||||||
throws IOException, PGPException {
|
|
||||||
MultiPassStrategy multiPassStrategy = options.getMultiPassStrategy();
|
MultiPassStrategy multiPassStrategy = options.getMultiPassStrategy();
|
||||||
PGPSignatureList signatures = ClearsignedMessageUtil.detachSignaturesFromInbandClearsignedMessage(armorIn, multiPassStrategy.getMessageOutputStream());
|
PGPSignatureList detachedSignatures = ClearsignedMessageUtil
|
||||||
|
.detachSignaturesFromInbandClearsignedMessage(
|
||||||
|
inputStream, multiPassStrategy.getMessageOutputStream());
|
||||||
|
|
||||||
for (PGPSignature signature : signatures) {
|
for (PGPSignature signature : detachedSignatures) {
|
||||||
options.addVerificationOfDetachedSignature(signature);
|
options.addVerificationOfDetachedSignature(signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
options.forceNonOpenPgpData();
|
options.forceNonOpenPgpData();
|
||||||
return new OpenPgpMessageInputStream(multiPassStrategy.getMessageInputStream(), policy, options);
|
packetInputStream = null;
|
||||||
|
nestedInputStream = new TeeInputStream(multiPassStrategy.getMessageInputStream(), this.signatures);
|
||||||
|
break;
|
||||||
|
case non_openpgp:
|
||||||
|
packetInputStream = null;
|
||||||
|
nestedInputStream = new TeeInputStream(inputStream, this.signatures);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -196,6 +257,9 @@ public class OpenPgpMessageInputStream extends DecryptionStream {
|
||||||
private void consumePackets()
|
private void consumePackets()
|
||||||
throws IOException, PGPException {
|
throws IOException, PGPException {
|
||||||
OpenPgpPacket nextPacket;
|
OpenPgpPacket nextPacket;
|
||||||
|
if (packetInputStream == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
loop: // we break this when we go deeper.
|
loop: // we break this when we go deeper.
|
||||||
while ((nextPacket = packetInputStream.nextPacketTag()) != null) {
|
while ((nextPacket = packetInputStream.nextPacketTag()) != null) {
|
||||||
|
|
|
@ -4,6 +4,11 @@
|
||||||
|
|
||||||
package org.pgpainless.decryption_verification;
|
package org.pgpainless.decryption_verification;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
import org.bouncycastle.bcpg.BCPGInputStream;
|
import org.bouncycastle.bcpg.BCPGInputStream;
|
||||||
import org.bouncycastle.bcpg.MarkerPacket;
|
import org.bouncycastle.bcpg.MarkerPacket;
|
||||||
import org.bouncycastle.bcpg.Packet;
|
import org.bouncycastle.bcpg.Packet;
|
||||||
|
@ -15,11 +20,6 @@ import org.bouncycastle.openpgp.PGPOnePassSignature;
|
||||||
import org.bouncycastle.openpgp.PGPSignature;
|
import org.bouncycastle.openpgp.PGPSignature;
|
||||||
import org.pgpainless.algorithm.OpenPgpPacket;
|
import org.pgpainless.algorithm.OpenPgpPacket;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.util.NoSuchElementException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Since we need to update signatures with data from the underlying stream, this class is used to tee out the data.
|
* 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
|
* Unfortunately we cannot simply override {@link BCPGInputStream#read()} to tee the data out though, since
|
||||||
|
@ -96,7 +96,6 @@ public class TeeBCPGInputStream {
|
||||||
return markerPacket;
|
return markerPacket;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
this.packetInputStream.close();
|
this.packetInputStream.close();
|
||||||
this.delayedTee.close();
|
this.delayedTee.close();
|
||||||
|
@ -122,6 +121,9 @@ public class TeeBCPGInputStream {
|
||||||
last = inputStream.read();
|
last = inputStream.read();
|
||||||
return last;
|
return last;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
if ("crc check failed in armored message.".equals(e.getMessage())) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -677,7 +677,7 @@ public class OpenPgpMessageInputStreamTest {
|
||||||
throws IOException, PGPException {
|
throws IOException, PGPException {
|
||||||
ByteArrayInputStream bytesIn = new ByteArrayInputStream(armored.getBytes(StandardCharsets.UTF_8));
|
ByteArrayInputStream bytesIn = new ByteArrayInputStream(armored.getBytes(StandardCharsets.UTF_8));
|
||||||
ArmoredInputStream armorIn = ArmoredInputStreamFactory.get(bytesIn);
|
ArmoredInputStream armorIn = ArmoredInputStreamFactory.get(bytesIn);
|
||||||
OpenPgpMessageInputStream pgpIn = new OpenPgpMessageInputStream(armorIn, options);
|
OpenPgpMessageInputStream pgpIn = OpenPgpMessageInputStream.create(armorIn, options);
|
||||||
return pgpIn;
|
return pgpIn;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue