mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-12-25 04:17:59 +01:00
Work on getting signature verification to function again
This commit is contained in:
parent
39fa8ad291
commit
48e83420a3
2 changed files with 116 additions and 36 deletions
|
@ -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());
|
||||||
|
|
||||||
|
consumePackets();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void walk() throws IOException, PGPException {
|
/**
|
||||||
loop: while (true) {
|
* 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>
|
||||||
int tag = bcpgIn.nextPacketTag();
|
* to the nested stream and breaks the loop.
|
||||||
if (tag == -1) {
|
* The nested stream is either a simple {@link InputStream} (in case of Literal Data), or another
|
||||||
break loop;
|
* {@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) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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).
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in a new issue