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:
parent
bb31fea265
commit
9b647742da
2 changed files with 149 additions and 124 deletions
|
@ -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),
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue