1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2024-11-16 17:32:06 +01:00

Work on getting signature verification to function again

This commit is contained in:
Paul Schaub 2022-09-14 19:29:47 +02:00
parent 39fa8ad291
commit 48e83420a3
2 changed files with 116 additions and 36 deletions

View file

@ -30,19 +30,24 @@ import org.pgpainless.algorithm.EncryptionPurpose;
import org.pgpainless.algorithm.OpenPgpPacket; import org.pgpainless.algorithm.OpenPgpPacket;
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.exception.MalformedOpenPgpMessageException;
import org.pgpainless.exception.MessageNotIntegrityProtectedException; import org.pgpainless.exception.MessageNotIntegrityProtectedException;
import org.pgpainless.exception.MissingDecryptionMethodException; import org.pgpainless.exception.MissingDecryptionMethodException;
import org.pgpainless.implementation.ImplementationFactory; import org.pgpainless.implementation.ImplementationFactory;
import org.pgpainless.key.info.KeyRingInfo; import org.pgpainless.key.info.KeyRingInfo;
import org.pgpainless.key.protection.SecretKeyRingProtector; import org.pgpainless.key.protection.SecretKeyRingProtector;
import org.pgpainless.key.protection.UnlockSecretKey; import org.pgpainless.key.protection.UnlockSecretKey;
import org.pgpainless.signature.consumer.DetachedSignatureCheck;
import org.pgpainless.util.Passphrase; import org.pgpainless.util.Passphrase;
import org.pgpainless.util.Tuple; import org.pgpainless.util.Tuple;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.List; import java.util.List;
public class OpenPgpMessageInputStream extends InputStream { public class OpenPgpMessageInputStream extends InputStream {
@ -52,12 +57,12 @@ public class OpenPgpMessageInputStream extends InputStream {
protected final BCPGInputStream bcpgIn; protected final BCPGInputStream bcpgIn;
protected InputStream in; protected InputStream in;
private List<PGPSignature> signatures = new ArrayList<>(); private boolean closed = false;
private List<PGPOnePassSignature> onePassSignatures = new ArrayList<>();
private Signatures signatures = new Signatures();
public OpenPgpMessageInputStream(InputStream inputStream, ConsumerOptions options) public OpenPgpMessageInputStream(InputStream inputStream, ConsumerOptions options)
throws IOException, PGPException { throws IOException, PGPException {
this.options = options;
// 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;
@ -65,41 +70,61 @@ public class OpenPgpMessageInputStream extends InputStream {
this.bcpgIn = new BCPGInputStream(inputStream); this.bcpgIn = new BCPGInputStream(inputStream);
} }
walk(); this.options = options;
} this.signatures.addDetachedSignatures(options.getDetachedSignatures());
private void walk() throws IOException, PGPException { consumePackets();
loop: while (true) {
int tag = bcpgIn.nextPacketTag();
if (tag == -1) {
break loop;
} }
/**
* 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>
* to the nested stream and breaks the loop.
* The nested stream is either a simple {@link InputStream} (in case of Literal Data), or another
* {@link OpenPgpMessageInputStream} in case of Compressed and Encrypted Data.
*
* @throws IOException
* @throws PGPException
*/
private void consumePackets()
throws IOException, PGPException {
int tag;
loop: while ((tag = bcpgIn.nextPacketTag()) != -1) {
OpenPgpPacket nextPacket = OpenPgpPacket.requireFromTag(tag); OpenPgpPacket nextPacket = OpenPgpPacket.requireFromTag(tag);
switch (nextPacket) { switch (nextPacket) {
// Literal Data - the literal data content is the new input stream
case LIT: case LIT:
automaton.next(InputAlphabet.LiteralData); automaton.next(InputAlphabet.LiteralData);
PGPLiteralData literalData = new PGPLiteralData(bcpgIn); PGPLiteralData literalData = new PGPLiteralData(bcpgIn);
in = literalData.getDataStream(); in = literalData.getDataStream();
break loop; break loop;
// Compressed Data - the content contains another OpenPGP message
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); in = new OpenPgpMessageInputStream(compressedData.getDataStream(), options);
break loop; break loop;
// One Pass Signatures
case OPS: case OPS:
automaton.next(InputAlphabet.OnePassSignatures); automaton.next(InputAlphabet.OnePassSignatures);
readOnePassSignatures(); signatures.addOnePassSignatures(readOnePassSignatures());
break; break;
// Signatures - either prepended to the message, or corresponding to the One Pass Signatures
case SIG: case SIG:
automaton.next(InputAlphabet.Signatures); automaton.next(InputAlphabet.Signatures);
readSignatures(); PGPSignatureList signatureList = readSignatures();
if (automaton.peekStack() == StackAlphabet.ops) {
signatures.addOnePassCorrespondingSignatures(signatureList);
} else {
signatures.addPrependedSignatures(signatureList);
}
break; break;
// Encrypted Data (ESKs and SED/SEIPD are parsed the same by BC)
case PKESK: case PKESK:
case SKESK: case SKESK:
case SED: case SED:
@ -200,6 +225,7 @@ public class OpenPgpMessageInputStream extends InputStream {
bcpgIn.readPacket(); // skip marker packet bcpgIn.readPacket(); // skip marker packet
break; break;
// Key Packets are illegal in this context
case SK: case SK:
case PK: case PK:
case SSK: case SSK:
@ -209,13 +235,14 @@ public class OpenPgpMessageInputStream extends InputStream {
case UATTR: case UATTR:
case MOD: case MOD:
break; throw new MalformedOpenPgpMessageException("Unexpected Packet in Stream: " + nextPacket);
// Experimental Packets are not supported
case EXP_1: case EXP_1:
case EXP_2: case EXP_2:
case EXP_3: case EXP_3:
case EXP_4: case EXP_4:
break; throw new MalformedOpenPgpMessageException("Unsupported Packet in Stream: " + nextPacket);
} }
} }
} }
@ -247,7 +274,7 @@ public class OpenPgpMessageInputStream extends InputStream {
return null; return null;
} }
private void readOnePassSignatures() throws IOException { private PGPOnePassSignatureList readOnePassSignatures() throws IOException {
ByteArrayOutputStream buf = new ByteArrayOutputStream(); ByteArrayOutputStream buf = new ByteArrayOutputStream();
BCPGOutputStream bcpgOut = new BCPGOutputStream(buf); BCPGOutputStream bcpgOut = new BCPGOutputStream(buf);
int tag = bcpgIn.nextPacketTag(); int tag = bcpgIn.nextPacketTag();
@ -262,12 +289,10 @@ public class OpenPgpMessageInputStream extends InputStream {
PGPObjectFactory objectFactory = ImplementationFactory.getInstance().getPGPObjectFactory(buf.toByteArray()); PGPObjectFactory objectFactory = ImplementationFactory.getInstance().getPGPObjectFactory(buf.toByteArray());
PGPOnePassSignatureList signatureList = (PGPOnePassSignatureList) objectFactory.nextObject(); PGPOnePassSignatureList signatureList = (PGPOnePassSignatureList) objectFactory.nextObject();
for (PGPOnePassSignature ops : signatureList) { return signatureList;
onePassSignatures.add(ops);
}
} }
private void 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 = bcpgIn.nextPacketTag(); int tag = bcpgIn.nextPacketTag();
@ -283,9 +308,7 @@ public class OpenPgpMessageInputStream extends InputStream {
PGPObjectFactory objectFactory = ImplementationFactory.getInstance().getPGPObjectFactory(buf.toByteArray()); PGPObjectFactory objectFactory = ImplementationFactory.getInstance().getPGPObjectFactory(buf.toByteArray());
PGPSignatureList signatureList = (PGPSignatureList) objectFactory.nextObject(); PGPSignatureList signatureList = (PGPSignatureList) objectFactory.nextObject();
for (PGPSignature signature : signatureList) { return signatureList;
signatures.add(signature);
}
} }
@Override @Override
@ -298,15 +321,17 @@ public class OpenPgpMessageInputStream extends InputStream {
// //
} }
} }
if (r == -1) { if (r != -1) {
byte b = (byte) r;
signatures.update(b);
} else {
if (in instanceof OpenPgpMessageInputStream) { if (in instanceof OpenPgpMessageInputStream) {
System.out.println("Read -1: close " + automaton);
in.close(); in.close();
in = null; in = null;
} else { } else {
try { try {
System.out.println("Walk " + automaton); System.out.println("Walk " + automaton);
walk(); consumePackets();
} catch (PGPException e) { } catch (PGPException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@ -317,25 +342,24 @@ public class OpenPgpMessageInputStream extends InputStream {
@Override @Override
public void close() throws IOException { public void close() throws IOException {
if (in == null) { if (closed) {
System.out.println("Close " + automaton);
automaton.next(InputAlphabet.EndOfSequence);
automaton.assertValid();
return; return;
} }
try {
if (in != null) {
in.close(); in.close();
in = null; in = null; // TODO: Collect result of in before nulling
// Nested streams (except LiteralData) need to be closed.
if (automaton.getState() != PDA.State.LiteralMessage) { if (automaton.getState() != PDA.State.LiteralMessage) {
automaton.next(InputAlphabet.EndOfSequence); automaton.next(InputAlphabet.EndOfSequence);
automaton.assertValid(); automaton.assertValid();
} }
} catch (IOException e) { } else {
// automaton.next(InputAlphabet.EndOfSequence);
automaton.assertValid();
} }
super.close(); super.close();
closed = true;
} }
private static class SortedESKs { private static class SortedESKs {
@ -369,4 +393,53 @@ public class OpenPgpMessageInputStream extends InputStream {
return esks; return esks;
} }
} }
private static class Signatures {
List<PGPSignature> detachedSignatures = new ArrayList<>();
List<PGPSignature> prependedSignatures = new ArrayList<>();
List<PGPOnePassSignature> onePassSignatures = new ArrayList<>();
List<PGPSignature> correspondingSignatures = new ArrayList<>();
void addDetachedSignatures(Collection<PGPSignature> signatures) {
this.detachedSignatures.addAll(signatures);
}
void addPrependedSignatures(PGPSignatureList signatures) {
for (PGPSignature signature : signatures) {
this.prependedSignatures.add(signature);
}
}
void addOnePassSignatures(PGPOnePassSignatureList signatures) {
for (PGPOnePassSignature ops : signatures) {
this.onePassSignatures.add(ops);
}
}
void addOnePassCorrespondingSignatures(PGPSignatureList signatures) {
for (PGPSignature signature : signatures) {
correspondingSignatures.add(signature);
}
}
public void update(byte b) {
/**
for (PGPSignature prepended : prependedSignatures) {
prepended.update(b);
}
for (PGPOnePassSignature ops : onePassSignatures) {
ops.update(b);
}
for (PGPSignature detached : detachedSignatures) {
detached.update(b);
}
*/
}
public void finish() {
for (PGPSignature detached : detachedSignatures) {
}
}
}
} }

View file

@ -202,6 +202,13 @@ public class PDA {
return state; return state;
} }
public StackAlphabet peekStack() {
if (stack.isEmpty()) {
return null;
}
return stack.peek();
}
/** /**
* Return true, if the PDA is in a valid state (the OpenPGP message is valid). * Return true, if the PDA is in a valid state (the OpenPGP message is valid).
* *