1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2024-12-25 12:27:58 +01:00

WIP: Add LayerMetadata class

This commit is contained in:
Paul Schaub 2022-09-19 13:07:33 +02:00
parent bb31fea265
commit 9b647742da
2 changed files with 149 additions and 124 deletions

View file

@ -29,7 +29,7 @@ public enum OpenPgpPacket {
PSK(PacketTags.PUBLIC_SUBKEY), PSK(PacketTags.PUBLIC_SUBKEY),
UATTR(PacketTags.USER_ATTRIBUTE), UATTR(PacketTags.USER_ATTRIBUTE),
SEIPD(PacketTags.SYM_ENC_INTEGRITY_PRO), SEIPD(PacketTags.SYM_ENC_INTEGRITY_PRO),
MOD(PacketTags.MOD_DETECTION_CODE), MDC(PacketTags.MOD_DETECTION_CODE),
EXP_1(PacketTags.EXPERIMENTAL_1), EXP_1(PacketTags.EXPERIMENTAL_1),
EXP_2(PacketTags.EXPERIMENTAL_2), EXP_2(PacketTags.EXPERIMENTAL_2),

View file

@ -1,3 +1,7 @@
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.decryption_verification; package org.pgpainless.decryption_verification;
import org.bouncycastle.bcpg.BCPGInputStream; import org.bouncycastle.bcpg.BCPGInputStream;
@ -27,9 +31,12 @@ 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.pqc.crypto.rainbow.Layer;
import org.pgpainless.PGPainless; import org.pgpainless.PGPainless;
import org.pgpainless.algorithm.CompressionAlgorithm;
import org.pgpainless.algorithm.EncryptionPurpose; import org.pgpainless.algorithm.EncryptionPurpose;
import org.pgpainless.algorithm.OpenPgpPacket; import org.pgpainless.algorithm.OpenPgpPacket;
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
import org.pgpainless.decryption_verification.automaton.InputAlphabet; import org.pgpainless.decryption_verification.automaton.InputAlphabet;
import org.pgpainless.decryption_verification.automaton.PDA; import org.pgpainless.decryption_verification.automaton.PDA;
import org.pgpainless.decryption_verification.automaton.StackAlphabet; import org.pgpainless.decryption_verification.automaton.StackAlphabet;
@ -55,15 +62,22 @@ public class OpenPgpMessageInputStream extends InputStream {
protected final PDA automaton = new PDA(); protected final PDA automaton = new PDA();
protected final ConsumerOptions options; protected final ConsumerOptions options;
protected final OpenPgpMetadata.Builder resultBuilder;
protected final BCPGInputStream bcpgIn; protected final BCPGInputStream bcpgIn;
protected InputStream in; protected InputStream in;
private boolean closed = false; private boolean closed = false;
private Signatures signatures; private Signatures signatures;
private LayerMetadata layerMetadata;
public OpenPgpMessageInputStream(InputStream inputStream, ConsumerOptions options) public OpenPgpMessageInputStream(InputStream inputStream, ConsumerOptions options)
throws IOException, PGPException { throws IOException, PGPException {
this(inputStream, options, null);
}
OpenPgpMessageInputStream(InputStream inputStream, ConsumerOptions options, LayerMetadata layerMetadata)
throws PGPException, IOException {
// TODO: Use BCPGInputStream.wrap(inputStream); // TODO: Use BCPGInputStream.wrap(inputStream);
if (inputStream instanceof BCPGInputStream) { if (inputStream instanceof BCPGInputStream) {
this.bcpgIn = (BCPGInputStream) inputStream; this.bcpgIn = (BCPGInputStream) inputStream;
@ -72,12 +86,35 @@ public class OpenPgpMessageInputStream extends InputStream {
} }
this.options = options; this.options = options;
this.resultBuilder = OpenPgpMetadata.getBuilder();
this.signatures = new Signatures(options); this.signatures = new Signatures(options);
this.signatures.addDetachedSignatures(options.getDetachedSignatures()); this.signatures.addDetachedSignatures(options.getDetachedSignatures());
consumePackets(); consumePackets();
} }
static class LayerMetadata {
private CompressionAlgorithm compressionAlgorithm;
private SymmetricKeyAlgorithm symmetricKeyAlgorithm;
private LayerMetadata child;
public LayerMetadata setCompressionAlgorithm(CompressionAlgorithm algorithm) {
this.compressionAlgorithm = algorithm;
return this;
}
public LayerMetadata setSymmetricEncryptionAlgorithm(SymmetricKeyAlgorithm algorithm) {
this.symmetricKeyAlgorithm = algorithm;
return this;
}
public LayerMetadata setChild(LayerMetadata child) {
this.child = child;
return this;
}
}
/** /**
* This method consumes OpenPGP packets from the current {@link BCPGInputStream}. * This method consumes OpenPGP packets from the current {@link BCPGInputStream}.
* Once it reaches a "nested" OpenPGP packet (Literal Data, Compressed Data, Encrypted Data), it sets <pre>in</pre> * Once it reaches a "nested" OpenPGP packet (Literal Data, Compressed Data, Encrypted Data), it sets <pre>in</pre>
@ -92,7 +129,7 @@ public class OpenPgpMessageInputStream extends InputStream {
throws IOException, PGPException { throws IOException, PGPException {
System.out.println("Walk " + automaton); System.out.println("Walk " + automaton);
int tag; int tag;
loop: while ((tag = getTag()) != -1) { loop: while ((tag = nextTag()) != -1) {
OpenPgpPacket nextPacket = OpenPgpPacket.requireFromTag(tag); OpenPgpPacket nextPacket = OpenPgpPacket.requireFromTag(tag);
System.out.println(nextPacket); System.out.println(nextPacket);
switch (nextPacket) { switch (nextPacket) {
@ -108,7 +145,9 @@ public class OpenPgpMessageInputStream extends InputStream {
case COMP: case COMP:
automaton.next(InputAlphabet.CompressedData); automaton.next(InputAlphabet.CompressedData);
PGPCompressedData compressedData = new PGPCompressedData(bcpgIn); PGPCompressedData compressedData = new PGPCompressedData(bcpgIn);
in = new OpenPgpMessageInputStream(compressedData.getDataStream(), options); LayerMetadata compressionLayer = new LayerMetadata();
compressionLayer.setCompressionAlgorithm(CompressionAlgorithm.fromId(compressedData.getAlgorithm()));
in = new OpenPgpMessageInputStream(compressedData.getDataStream(), options, compressionLayer);
break loop; break loop;
// One Pass Signatures // One Pass Signatures
@ -119,10 +158,10 @@ public class OpenPgpMessageInputStream extends InputStream {
// Signatures - either prepended to the message, or corresponding to the One Pass Signatures // Signatures - either prepended to the message, or corresponding to the One Pass Signatures
case SIG: case SIG:
boolean isCorrespondingToOPS = automaton.peekStack() == StackAlphabet.ops; boolean isSigForOPS = automaton.peekStack() == StackAlphabet.ops;
automaton.next(InputAlphabet.Signatures); automaton.next(InputAlphabet.Signatures);
PGPSignatureList signatureList = readSignatures(); PGPSignatureList signatureList = readSignatures();
if (isCorrespondingToOPS) { if (isSigForOPS) {
signatures.addOnePassCorrespondingSignatures(signatureList); signatures.addOnePassCorrespondingSignatures(signatureList);
} else { } else {
signatures.addPrependedSignatures(signatureList); signatures.addPrependedSignatures(signatureList);
@ -135,6 +174,43 @@ public class OpenPgpMessageInputStream extends InputStream {
case SED: case SED:
case SEIPD: case SEIPD:
automaton.next(InputAlphabet.EncryptedData); automaton.next(InputAlphabet.EncryptedData);
if (processEncryptedData()) {
break loop;
}
throw new MissingDecryptionMethodException("No working decryption method found.");
// Marker Packets need to be skipped and ignored
case MARKER:
bcpgIn.readPacket(); // skip
break;
// Key Packets are illegal in this context
case SK:
case PK:
case SSK:
case PSK:
case TRUST:
case UID:
case UATTR:
throw new MalformedOpenPgpMessageException("Illegal Packet in Stream: " + nextPacket);
// MDC packet is usually processed by PGPEncryptedDataList, so it is very likely we encounter this
// packet out of order
case MDC:
throw new MalformedOpenPgpMessageException("Unexpected Packet in Stream: " + nextPacket);
// Experimental Packets are not supported
case EXP_1:
case EXP_2:
case EXP_3:
case EXP_4:
throw new MalformedOpenPgpMessageException("Unsupported Packet in Stream: " + nextPacket);
}
}
}
private boolean processEncryptedData() throws IOException, PGPException {
PGPEncryptedDataList encDataList = new PGPEncryptedDataList(bcpgIn); PGPEncryptedDataList encDataList = new PGPEncryptedDataList(bcpgIn);
// TODO: Replace with !encDataList.isIntegrityProtected() // TODO: Replace with !encDataList.isIntegrityProtected()
@ -144,6 +220,7 @@ public class OpenPgpMessageInputStream extends InputStream {
SortedESKs esks = new SortedESKs(encDataList); SortedESKs esks = new SortedESKs(encDataList);
// Try session key
if (options.getSessionKey() != null) { if (options.getSessionKey() != null) {
SessionKeyDataDecryptorFactory decryptorFactory = ImplementationFactory.getInstance() SessionKeyDataDecryptorFactory decryptorFactory = ImplementationFactory.getInstance()
.getSessionKeyDataDecryptorFactory(options.getSessionKey()); .getSessionKeyDataDecryptorFactory(options.getSessionKey());
@ -153,11 +230,11 @@ public class OpenPgpMessageInputStream extends InputStream {
if (esk instanceof PGPPBEEncryptedData) { if (esk instanceof PGPPBEEncryptedData) {
PGPPBEEncryptedData skesk = (PGPPBEEncryptedData) esk; PGPPBEEncryptedData skesk = (PGPPBEEncryptedData) esk;
in = skesk.getDataStream(decryptorFactory); in = skesk.getDataStream(decryptorFactory);
break loop; return true;
} else if (esk instanceof PGPPublicKeyEncryptedData) { } else if (esk instanceof PGPPublicKeyEncryptedData) {
PGPPublicKeyEncryptedData pkesk = (PGPPublicKeyEncryptedData) esk; PGPPublicKeyEncryptedData pkesk = (PGPPublicKeyEncryptedData) esk;
in = pkesk.getDataStream(decryptorFactory); in = pkesk.getDataStream(decryptorFactory);
break loop; return true;
} else { } else {
throw new RuntimeException("Unknown ESK class type: " + esk.getClass().getName()); throw new RuntimeException("Unknown ESK class type: " + esk.getClass().getName());
} }
@ -174,7 +251,7 @@ public class OpenPgpMessageInputStream extends InputStream {
try { try {
InputStream decrypted = skesk.getDataStream(decryptorFactory); InputStream decrypted = skesk.getDataStream(decryptorFactory);
in = new OpenPgpMessageInputStream(decrypted, options); in = new OpenPgpMessageInputStream(decrypted, options);
break loop; return true;
} catch (PGPException e) { } catch (PGPException e) {
// password mismatch? Try next password // password mismatch? Try next password
} }
@ -198,7 +275,7 @@ public class OpenPgpMessageInputStream extends InputStream {
try { try {
InputStream decrypted = pkesk.getDataStream(decryptorFactory); InputStream decrypted = pkesk.getDataStream(decryptorFactory);
in = new OpenPgpMessageInputStream(decrypted, options); in = new OpenPgpMessageInputStream(decrypted, options);
break loop; return true;
} catch (PGPException e) { } catch (PGPException e) {
// hm :/ // hm :/
} }
@ -215,44 +292,18 @@ public class OpenPgpMessageInputStream extends InputStream {
try { try {
InputStream decrypted = pkesk.getDataStream(decryptorFactory); InputStream decrypted = pkesk.getDataStream(decryptorFactory);
in = new OpenPgpMessageInputStream(decrypted, options); in = new OpenPgpMessageInputStream(decrypted, options);
break loop; return true;
} catch (PGPException e) { } catch (PGPException e) {
// hm :/ // hm :/
} }
} }
} }
// TODO: try interactive password callbacks // we did not yet succeed in decrypting any session key :/
return false;
throw new MissingDecryptionMethodException("No working decryption method found.");
case MARKER:
bcpgIn.readPacket(); // skip marker packet
break;
// Key Packets are illegal in this context
case SK:
case PK:
case SSK:
case PSK:
case TRUST:
case UID:
case UATTR:
case MOD:
throw new MalformedOpenPgpMessageException("Unexpected Packet in Stream: " + nextPacket);
// Experimental Packets are not supported
case EXP_1:
case EXP_2:
case EXP_3:
case EXP_4:
throw new MalformedOpenPgpMessageException("Unsupported Packet in Stream: " + nextPacket);
}
}
} }
private int getTag() throws IOException { private int nextTag() throws IOException {
try { try {
return bcpgIn.nextPacketTag(); return bcpgIn.nextPacketTag();
} catch (IOException e) { } catch (IOException e) {
@ -296,7 +347,7 @@ public class OpenPgpMessageInputStream extends InputStream {
ByteArrayOutputStream buf = new ByteArrayOutputStream(); ByteArrayOutputStream buf = new ByteArrayOutputStream();
BCPGOutputStream bcpgOut = new BCPGOutputStream(buf); BCPGOutputStream bcpgOut = new BCPGOutputStream(buf);
int tag; int tag;
while ((tag = getTag()) == PacketTags.ONE_PASS_SIGNATURE || tag == PacketTags.MARKER) { while ((tag = nextTag()) == PacketTags.ONE_PASS_SIGNATURE || tag == PacketTags.MARKER) {
Packet packet = bcpgIn.readPacket(); Packet packet = bcpgIn.readPacket();
if (tag == PacketTags.ONE_PASS_SIGNATURE) { if (tag == PacketTags.ONE_PASS_SIGNATURE) {
OnePassSignaturePacket sigPacket = (OnePassSignaturePacket) packet; OnePassSignaturePacket sigPacket = (OnePassSignaturePacket) packet;
@ -313,13 +364,13 @@ public class OpenPgpMessageInputStream extends InputStream {
private PGPSignatureList readSignatures() throws IOException { private PGPSignatureList readSignatures() throws IOException {
ByteArrayOutputStream buf = new ByteArrayOutputStream(); ByteArrayOutputStream buf = new ByteArrayOutputStream();
BCPGOutputStream bcpgOut = new BCPGOutputStream(buf); BCPGOutputStream bcpgOut = new BCPGOutputStream(buf);
int tag = getTag(); int tag = nextTag();
while (tag == PacketTags.SIGNATURE || tag == PacketTags.MARKER) { while (tag == PacketTags.SIGNATURE || tag == PacketTags.MARKER) {
Packet packet = bcpgIn.readPacket(); Packet packet = bcpgIn.readPacket();
if (tag == PacketTags.SIGNATURE) { if (tag == PacketTags.SIGNATURE) {
SignaturePacket sigPacket = (SignaturePacket) packet; SignaturePacket sigPacket = (SignaturePacket) packet;
sigPacket.encode(bcpgOut); sigPacket.encode(bcpgOut);
tag = getTag(); tag = nextTag();
} }
} }
bcpgOut.close(); bcpgOut.close();
@ -356,20 +407,6 @@ public class OpenPgpMessageInputStream extends InputStream {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
signatures.finish(); signatures.finish();
/*
if (in instanceof OpenPgpMessageInputStream) {
in.close();
in = null;
} else {
try {
System.out.println("Read consume");
consumePackets();
signatures.finish();
} catch (PGPException e) {
throw new RuntimeException(e);
}
}
*/
} }
return r; return r;
} }
@ -394,18 +431,6 @@ public class OpenPgpMessageInputStream extends InputStream {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
signatures.finish(); signatures.finish();
/*
if (in instanceof OpenPgpMessageInputStream) {
in.close();
in = null;
} else {
try {
consumePackets();
} catch (PGPException e) {
throw new RuntimeException(e);
}
}
*/
} }
return r; return r;
} }