mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-11-14 16:32:06 +01:00
Implement experimental signature verification (correctness only)
This commit is contained in:
parent
0bf85c32c0
commit
bb31fea265
7 changed files with 497 additions and 1236 deletions
|
@ -1,278 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package org.pgpainless.decryption_verification;
|
|
||||||
|
|
||||||
import org.bouncycastle.bcpg.BCPGInputStream;
|
|
||||||
import org.bouncycastle.bcpg.BCPGOutputStream;
|
|
||||||
import org.bouncycastle.bcpg.ModDetectionCodePacket;
|
|
||||||
import org.bouncycastle.bcpg.OnePassSignaturePacket;
|
|
||||||
import org.bouncycastle.bcpg.Packet;
|
|
||||||
import org.bouncycastle.bcpg.PacketTags;
|
|
||||||
import org.bouncycastle.bcpg.PublicKeyEncSessionPacket;
|
|
||||||
import org.bouncycastle.bcpg.SignaturePacket;
|
|
||||||
import org.bouncycastle.bcpg.SymmetricKeyEncSessionPacket;
|
|
||||||
import org.bouncycastle.openpgp.PGPCompressedData;
|
|
||||||
import org.bouncycastle.openpgp.PGPEncryptedData;
|
|
||||||
import org.bouncycastle.openpgp.PGPEncryptedDataList;
|
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
|
||||||
import org.bouncycastle.openpgp.PGPLiteralData;
|
|
||||||
import org.bouncycastle.openpgp.PGPOnePassSignatureList;
|
|
||||||
import org.bouncycastle.openpgp.PGPPBEEncryptedData;
|
|
||||||
import org.bouncycastle.openpgp.PGPSignatureList;
|
|
||||||
import org.bouncycastle.openpgp.operator.PBEDataDecryptorFactory;
|
|
||||||
import org.pgpainless.algorithm.OpenPgpPacket;
|
|
||||||
import org.pgpainless.decryption_verification.automaton.InputAlphabet;
|
|
||||||
import org.pgpainless.decryption_verification.automaton.NestingPDA;
|
|
||||||
import org.pgpainless.exception.MalformedOpenPgpMessageException;
|
|
||||||
import org.pgpainless.exception.MessageNotIntegrityProtectedException;
|
|
||||||
import org.pgpainless.exception.MissingDecryptionMethodException;
|
|
||||||
import org.pgpainless.implementation.ImplementationFactory;
|
|
||||||
import org.pgpainless.util.Passphrase;
|
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.SequenceInputStream;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.NoSuchElementException;
|
|
||||||
import java.util.Stack;
|
|
||||||
|
|
||||||
public class MessageDecryptionStream extends InputStream {
|
|
||||||
|
|
||||||
private final ConsumerOptions options;
|
|
||||||
|
|
||||||
NestingPDA automaton = new NestingPDA();
|
|
||||||
// nested streams, outermost at the bottom of the stack
|
|
||||||
Stack<Layer> packetLayers = new Stack<>();
|
|
||||||
List<PublicKeyEncSessionPacket> pkeskList = new ArrayList<>();
|
|
||||||
List<SymmetricKeyEncSessionPacket> skeskList = new ArrayList<>();
|
|
||||||
|
|
||||||
public MessageDecryptionStream(InputStream inputStream, ConsumerOptions options)
|
|
||||||
throws IOException, PGPException {
|
|
||||||
this.options = options;
|
|
||||||
packetLayers.push(Layer.initial(inputStream));
|
|
||||||
walkLayer();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void walkLayer() throws PGPException, IOException {
|
|
||||||
if (packetLayers.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We are currently in the deepest layer
|
|
||||||
Layer layer = packetLayers.peek();
|
|
||||||
BCPGInputStream inputStream = (BCPGInputStream) layer.inputStream;
|
|
||||||
|
|
||||||
loop: while (true) {
|
|
||||||
if (inputStream.nextPacketTag() == -1) {
|
|
||||||
popLayer();
|
|
||||||
break loop;
|
|
||||||
}
|
|
||||||
OpenPgpPacket tag = nextTagOrThrow(inputStream);
|
|
||||||
switch (tag) {
|
|
||||||
|
|
||||||
case LIT:
|
|
||||||
automaton.next(InputAlphabet.LiteralData);
|
|
||||||
PGPLiteralData literalData = new PGPLiteralData(inputStream);
|
|
||||||
packetLayers.push(Layer.literalMessage(literalData.getDataStream()));
|
|
||||||
break loop;
|
|
||||||
|
|
||||||
case COMP:
|
|
||||||
automaton.next(InputAlphabet.CompressedData);
|
|
||||||
PGPCompressedData compressedData = new PGPCompressedData(inputStream);
|
|
||||||
inputStream = new BCPGInputStream(compressedData.getDataStream());
|
|
||||||
packetLayers.push(Layer.compressedData(inputStream));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OPS:
|
|
||||||
automaton.next(InputAlphabet.OnePassSignatures);
|
|
||||||
ByteArrayOutputStream buf = new ByteArrayOutputStream();
|
|
||||||
BCPGOutputStream bcpgOut = new BCPGOutputStream(buf);
|
|
||||||
while (inputStream.nextPacketTag() == PacketTags.ONE_PASS_SIGNATURE || inputStream.nextPacketTag() == PacketTags.MARKER) {
|
|
||||||
Packet packet = inputStream.readPacket();
|
|
||||||
if (packet instanceof OnePassSignaturePacket) {
|
|
||||||
OnePassSignaturePacket sig = (OnePassSignaturePacket) packet;
|
|
||||||
sig.encode(bcpgOut);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PGPOnePassSignatureList onePassSignatures = (PGPOnePassSignatureList) ImplementationFactory.getInstance()
|
|
||||||
.getPGPObjectFactory(buf.toByteArray()).nextObject();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SIG:
|
|
||||||
automaton.next(InputAlphabet.Signatures);
|
|
||||||
|
|
||||||
buf = new ByteArrayOutputStream();
|
|
||||||
bcpgOut = new BCPGOutputStream(buf);
|
|
||||||
while (inputStream.nextPacketTag() == PacketTags.SIGNATURE || inputStream.nextPacketTag() == PacketTags.MARKER) {
|
|
||||||
Packet packet = inputStream.readPacket();
|
|
||||||
if (packet instanceof SignaturePacket) {
|
|
||||||
SignaturePacket sig = (SignaturePacket) packet;
|
|
||||||
sig.encode(bcpgOut);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PGPSignatureList signatures = (PGPSignatureList) ImplementationFactory.getInstance()
|
|
||||||
.getPGPObjectFactory(buf.toByteArray()).nextObject();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PKESK:
|
|
||||||
PublicKeyEncSessionPacket pkeskPacket = (PublicKeyEncSessionPacket) inputStream.readPacket();
|
|
||||||
pkeskList.add(pkeskPacket);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SKESK:
|
|
||||||
SymmetricKeyEncSessionPacket skeskPacket = (SymmetricKeyEncSessionPacket) inputStream.readPacket();
|
|
||||||
skeskList.add(skeskPacket);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SED:
|
|
||||||
if (!options.isIgnoreMDCErrors()) {
|
|
||||||
throw new MessageNotIntegrityProtectedException();
|
|
||||||
}
|
|
||||||
// No break; we continue below!
|
|
||||||
case SEIPD:
|
|
||||||
automaton.next(InputAlphabet.EncryptedData);
|
|
||||||
PGPEncryptedDataList encryptedDataList = assembleEncryptedDataList(inputStream);
|
|
||||||
|
|
||||||
for (PGPEncryptedData encData : encryptedDataList) {
|
|
||||||
if (encData instanceof PGPPBEEncryptedData) {
|
|
||||||
PGPPBEEncryptedData skenc = (PGPPBEEncryptedData) encData;
|
|
||||||
for (Passphrase passphrase : options.getDecryptionPassphrases()) {
|
|
||||||
PBEDataDecryptorFactory decryptorFactory = ImplementationFactory.getInstance()
|
|
||||||
.getPBEDataDecryptorFactory(passphrase);
|
|
||||||
InputStream decryptedIn = skenc.getDataStream(decryptorFactory);
|
|
||||||
packetLayers.push(Layer.encryptedData(new BCPGInputStream(decryptedIn)));
|
|
||||||
walkLayer();
|
|
||||||
break loop;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new MissingDecryptionMethodException("Cannot decrypt message.");
|
|
||||||
|
|
||||||
case MARKER:
|
|
||||||
inputStream.readPacket(); // discard
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SK:
|
|
||||||
case PK:
|
|
||||||
case SSK:
|
|
||||||
case PSK:
|
|
||||||
case TRUST:
|
|
||||||
case UID:
|
|
||||||
case UATTR:
|
|
||||||
throw new MalformedOpenPgpMessageException("OpenPGP packet " + tag + " MUST NOT be part of OpenPGP messages.");
|
|
||||||
case MOD:
|
|
||||||
ModDetectionCodePacket modDetectionCodePacket = (ModDetectionCodePacket) inputStream.readPacket();
|
|
||||||
break;
|
|
||||||
case EXP_1:
|
|
||||||
case EXP_2:
|
|
||||||
case EXP_3:
|
|
||||||
case EXP_4:
|
|
||||||
throw new MalformedOpenPgpMessageException("Experimental packet " + tag + " found inside the message.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private PGPEncryptedDataList assembleEncryptedDataList(BCPGInputStream inputStream)
|
|
||||||
throws IOException {
|
|
||||||
ByteArrayOutputStream buf = new ByteArrayOutputStream();
|
|
||||||
BCPGOutputStream bcpgOut = new BCPGOutputStream(buf);
|
|
||||||
|
|
||||||
for (SymmetricKeyEncSessionPacket skesk : skeskList) {
|
|
||||||
bcpgOut.write(skesk.getEncoded());
|
|
||||||
}
|
|
||||||
skeskList.clear();
|
|
||||||
for (PublicKeyEncSessionPacket pkesk : pkeskList) {
|
|
||||||
bcpgOut.write(pkesk.getEncoded());
|
|
||||||
}
|
|
||||||
pkeskList.clear();
|
|
||||||
|
|
||||||
SequenceInputStream sqin = new SequenceInputStream(
|
|
||||||
new ByteArrayInputStream(buf.toByteArray()), inputStream);
|
|
||||||
|
|
||||||
PGPEncryptedDataList encryptedDataList = (PGPEncryptedDataList) ImplementationFactory.getInstance()
|
|
||||||
.getPGPObjectFactory(sqin).nextObject();
|
|
||||||
return encryptedDataList;
|
|
||||||
}
|
|
||||||
|
|
||||||
private OpenPgpPacket nextTagOrThrow(BCPGInputStream inputStream)
|
|
||||||
throws IOException, InvalidOpenPgpPacketException {
|
|
||||||
try {
|
|
||||||
return OpenPgpPacket.requireFromTag(inputStream.nextPacketTag());
|
|
||||||
} catch (NoSuchElementException e) {
|
|
||||||
throw new InvalidOpenPgpPacketException(e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void popLayer() throws MalformedOpenPgpMessageException {
|
|
||||||
if (packetLayers.pop().isNested)
|
|
||||||
automaton.next(InputAlphabet.EndOfSequence);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int read() throws IOException {
|
|
||||||
if (packetLayers.isEmpty()) {
|
|
||||||
automaton.assertValid();
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int r = -1;
|
|
||||||
try {
|
|
||||||
r = packetLayers.peek().inputStream.read();
|
|
||||||
} catch (IOException e) {
|
|
||||||
}
|
|
||||||
if (r == -1) {
|
|
||||||
popLayer();
|
|
||||||
try {
|
|
||||||
walkLayer();
|
|
||||||
} catch (PGPException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
return read();
|
|
||||||
}
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class InvalidOpenPgpPacketException extends PGPException {
|
|
||||||
|
|
||||||
public InvalidOpenPgpPacketException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class Layer {
|
|
||||||
InputStream inputStream;
|
|
||||||
boolean isNested;
|
|
||||||
|
|
||||||
private Layer(InputStream inputStream, boolean isNested) {
|
|
||||||
this.inputStream = inputStream;
|
|
||||||
this.isNested = isNested;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Layer initial(InputStream inputStream) {
|
|
||||||
BCPGInputStream bcpgIn;
|
|
||||||
if (inputStream instanceof BCPGInputStream) {
|
|
||||||
bcpgIn = (BCPGInputStream) inputStream;
|
|
||||||
} else {
|
|
||||||
bcpgIn = new BCPGInputStream(inputStream);
|
|
||||||
}
|
|
||||||
return new Layer(bcpgIn, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Layer literalMessage(InputStream inputStream) {
|
|
||||||
return new Layer(inputStream, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Layer compressedData(InputStream inputStream) {
|
|
||||||
return new Layer(inputStream, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Layer encryptedData(InputStream inputStream) {
|
|
||||||
return new Layer(inputStream, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -18,11 +18,13 @@ import org.bouncycastle.openpgp.PGPPBEEncryptedData;
|
||||||
import org.bouncycastle.openpgp.PGPPrivateKey;
|
import org.bouncycastle.openpgp.PGPPrivateKey;
|
||||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData;
|
import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData;
|
||||||
|
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||||
import org.bouncycastle.openpgp.PGPSecretKey;
|
import org.bouncycastle.openpgp.PGPSecretKey;
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||||
import org.bouncycastle.openpgp.PGPSignature;
|
import org.bouncycastle.openpgp.PGPSignature;
|
||||||
import org.bouncycastle.openpgp.PGPSignatureList;
|
import org.bouncycastle.openpgp.PGPSignatureList;
|
||||||
import org.bouncycastle.openpgp.operator.PBEDataDecryptorFactory;
|
import org.bouncycastle.openpgp.operator.PBEDataDecryptorFactory;
|
||||||
|
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.pgpainless.PGPainless;
|
import org.pgpainless.PGPainless;
|
||||||
|
@ -38,6 +40,7 @@ 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.SignatureUtils;
|
||||||
import org.pgpainless.util.Passphrase;
|
import org.pgpainless.util.Passphrase;
|
||||||
import org.pgpainless.util.Tuple;
|
import org.pgpainless.util.Tuple;
|
||||||
|
|
||||||
|
@ -57,7 +60,7 @@ public class OpenPgpMessageInputStream extends InputStream {
|
||||||
|
|
||||||
private boolean closed = false;
|
private boolean closed = false;
|
||||||
|
|
||||||
private Signatures signatures = new Signatures();
|
private Signatures signatures;
|
||||||
|
|
||||||
public OpenPgpMessageInputStream(InputStream inputStream, ConsumerOptions options)
|
public OpenPgpMessageInputStream(InputStream inputStream, ConsumerOptions options)
|
||||||
throws IOException, PGPException {
|
throws IOException, PGPException {
|
||||||
|
@ -69,6 +72,7 @@ public class OpenPgpMessageInputStream extends InputStream {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.options = options;
|
this.options = options;
|
||||||
|
this.signatures = new Signatures(options);
|
||||||
this.signatures.addDetachedSignatures(options.getDetachedSignatures());
|
this.signatures.addDetachedSignatures(options.getDetachedSignatures());
|
||||||
|
|
||||||
consumePackets();
|
consumePackets();
|
||||||
|
@ -88,8 +92,9 @@ 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 = bcpgIn.nextPacketTag()) != -1) {
|
loop: while ((tag = getTag()) != -1) {
|
||||||
OpenPgpPacket nextPacket = OpenPgpPacket.requireFromTag(tag);
|
OpenPgpPacket nextPacket = OpenPgpPacket.requireFromTag(tag);
|
||||||
|
System.out.println(nextPacket);
|
||||||
switch (nextPacket) {
|
switch (nextPacket) {
|
||||||
|
|
||||||
// Literal Data - the literal data content is the new input stream
|
// Literal Data - the literal data content is the new input stream
|
||||||
|
@ -114,9 +119,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;
|
||||||
automaton.next(InputAlphabet.Signatures);
|
automaton.next(InputAlphabet.Signatures);
|
||||||
PGPSignatureList signatureList = readSignatures();
|
PGPSignatureList signatureList = readSignatures();
|
||||||
if (automaton.peekStack() == StackAlphabet.ops) {
|
if (isCorrespondingToOPS) {
|
||||||
signatures.addOnePassCorrespondingSignatures(signatureList);
|
signatures.addOnePassCorrespondingSignatures(signatureList);
|
||||||
} else {
|
} else {
|
||||||
signatures.addPrependedSignatures(signatureList);
|
signatures.addPrependedSignatures(signatureList);
|
||||||
|
@ -246,6 +252,19 @@ public class OpenPgpMessageInputStream extends InputStream {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int getTag() throws IOException {
|
||||||
|
try {
|
||||||
|
return bcpgIn.nextPacketTag();
|
||||||
|
} catch (IOException e) {
|
||||||
|
if ("Stream closed".equals(e.getMessage())) {
|
||||||
|
// ZipInflater Streams sometimes close under our feet -.-
|
||||||
|
// Therefore we catch resulting IOEs and return -1 instead.
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private List<Tuple<PGPSecretKeyRing, PGPSecretKey>> findPotentialDecryptionKeys(PGPPublicKeyEncryptedData pkesk) {
|
private List<Tuple<PGPSecretKeyRing, PGPSecretKey>> findPotentialDecryptionKeys(PGPPublicKeyEncryptedData pkesk) {
|
||||||
int algorithm = pkesk.getAlgorithm();
|
int algorithm = pkesk.getAlgorithm();
|
||||||
List<Tuple<PGPSecretKeyRing, PGPSecretKey>> decryptionKeyCandidates = new ArrayList<>();
|
List<Tuple<PGPSecretKeyRing, PGPSecretKey>> decryptionKeyCandidates = new ArrayList<>();
|
||||||
|
@ -276,8 +295,8 @@ public class OpenPgpMessageInputStream extends InputStream {
|
||||||
private PGPOnePassSignatureList 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;
|
||||||
while (tag == PacketTags.ONE_PASS_SIGNATURE || tag == PacketTags.MARKER) {
|
while ((tag = getTag()) == 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;
|
||||||
|
@ -294,13 +313,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 = bcpgIn.nextPacketTag();
|
int tag = getTag();
|
||||||
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 = bcpgIn.nextPacketTag();
|
tag = getTag();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bcpgOut.close();
|
bcpgOut.close();
|
||||||
|
@ -328,6 +347,16 @@ public class OpenPgpMessageInputStream extends InputStream {
|
||||||
byte b = (byte) r;
|
byte b = (byte) r;
|
||||||
signatures.update(b);
|
signatures.update(b);
|
||||||
} else {
|
} else {
|
||||||
|
in.close();
|
||||||
|
in = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
consumePackets();
|
||||||
|
} catch (PGPException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
signatures.finish();
|
||||||
|
/*
|
||||||
if (in instanceof OpenPgpMessageInputStream) {
|
if (in instanceof OpenPgpMessageInputStream) {
|
||||||
in.close();
|
in.close();
|
||||||
in = null;
|
in = null;
|
||||||
|
@ -335,10 +364,12 @@ public class OpenPgpMessageInputStream extends InputStream {
|
||||||
try {
|
try {
|
||||||
System.out.println("Read consume");
|
System.out.println("Read consume");
|
||||||
consumePackets();
|
consumePackets();
|
||||||
|
signatures.finish();
|
||||||
} catch (PGPException e) {
|
} catch (PGPException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
@ -354,6 +385,16 @@ public class OpenPgpMessageInputStream extends InputStream {
|
||||||
|
|
||||||
int r = in.read(b, off, len);
|
int r = in.read(b, off, len);
|
||||||
if (r == -1) {
|
if (r == -1) {
|
||||||
|
in.close();
|
||||||
|
in = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
consumePackets();
|
||||||
|
} catch (PGPException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
signatures.finish();
|
||||||
|
/*
|
||||||
if (in instanceof OpenPgpMessageInputStream) {
|
if (in instanceof OpenPgpMessageInputStream) {
|
||||||
in.close();
|
in.close();
|
||||||
in = null;
|
in = null;
|
||||||
|
@ -364,6 +405,7 @@ public class OpenPgpMessageInputStream extends InputStream {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
@ -380,6 +422,12 @@ public class OpenPgpMessageInputStream extends InputStream {
|
||||||
in = null;
|
in = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
consumePackets();
|
||||||
|
} catch (PGPException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
automaton.next(InputAlphabet.EndOfSequence);
|
automaton.next(InputAlphabet.EndOfSequence);
|
||||||
automaton.assertValid();
|
automaton.assertValid();
|
||||||
closed = true;
|
closed = true;
|
||||||
|
@ -418,50 +466,133 @@ public class OpenPgpMessageInputStream extends InputStream {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class Signatures {
|
private static class Signatures {
|
||||||
|
final ConsumerOptions options;
|
||||||
List<PGPSignature> detachedSignatures = new ArrayList<>();
|
List<PGPSignature> detachedSignatures = new ArrayList<>();
|
||||||
List<PGPSignature> prependedSignatures = new ArrayList<>();
|
List<PGPSignature> prependedSignatures = new ArrayList<>();
|
||||||
List<PGPOnePassSignature> onePassSignatures = new ArrayList<>();
|
List<PGPOnePassSignature> onePassSignatures = new ArrayList<>();
|
||||||
List<PGPSignature> correspondingSignatures = new ArrayList<>();
|
List<PGPSignature> correspondingSignatures = new ArrayList<>();
|
||||||
|
|
||||||
|
|
||||||
|
private Signatures(ConsumerOptions options) {
|
||||||
|
this.options = options;
|
||||||
|
}
|
||||||
|
|
||||||
void addDetachedSignatures(Collection<PGPSignature> signatures) {
|
void addDetachedSignatures(Collection<PGPSignature> signatures) {
|
||||||
|
for (PGPSignature signature : signatures) {
|
||||||
|
long keyId = SignatureUtils.determineIssuerKeyId(signature);
|
||||||
|
PGPPublicKeyRing certificate = findCertificate(keyId);
|
||||||
|
initialize(signature, certificate, keyId);
|
||||||
|
}
|
||||||
this.detachedSignatures.addAll(signatures);
|
this.detachedSignatures.addAll(signatures);
|
||||||
}
|
}
|
||||||
|
|
||||||
void addPrependedSignatures(PGPSignatureList signatures) {
|
void addPrependedSignatures(PGPSignatureList signatures) {
|
||||||
|
System.out.println("Adding " + signatures.size() + " prepended Signatures");
|
||||||
for (PGPSignature signature : signatures) {
|
for (PGPSignature signature : signatures) {
|
||||||
|
long keyId = SignatureUtils.determineIssuerKeyId(signature);
|
||||||
|
PGPPublicKeyRing certificate = findCertificate(keyId);
|
||||||
|
initialize(signature, certificate, keyId);
|
||||||
this.prependedSignatures.add(signature);
|
this.prependedSignatures.add(signature);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void addOnePassSignatures(PGPOnePassSignatureList signatures) {
|
void addOnePassSignatures(PGPOnePassSignatureList signatures) {
|
||||||
|
System.out.println("Adding " + signatures.size() + " OPSs");
|
||||||
for (PGPOnePassSignature ops : signatures) {
|
for (PGPOnePassSignature ops : signatures) {
|
||||||
|
PGPPublicKeyRing certificate = findCertificate(ops.getKeyID());
|
||||||
|
initialize(ops, certificate);
|
||||||
this.onePassSignatures.add(ops);
|
this.onePassSignatures.add(ops);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void addOnePassCorrespondingSignatures(PGPSignatureList signatures) {
|
void addOnePassCorrespondingSignatures(PGPSignatureList signatures) {
|
||||||
|
System.out.println("Adding " + signatures.size() + " Corresponding Signatures");
|
||||||
for (PGPSignature signature : signatures) {
|
for (PGPSignature signature : signatures) {
|
||||||
correspondingSignatures.add(signature);
|
correspondingSignatures.add(signature);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void initialize(PGPSignature signature, PGPPublicKeyRing certificate, long keyId) {
|
||||||
|
if (certificate == null) {
|
||||||
|
// SHIT
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PGPContentVerifierBuilderProvider verifierProvider = ImplementationFactory.getInstance()
|
||||||
|
.getPGPContentVerifierBuilderProvider();
|
||||||
|
try {
|
||||||
|
signature.init(verifierProvider, certificate.getPublicKey(keyId));
|
||||||
|
} catch (PGPException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initialize(PGPOnePassSignature ops, PGPPublicKeyRing certificate) {
|
||||||
|
if (certificate == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PGPContentVerifierBuilderProvider verifierProvider = ImplementationFactory.getInstance()
|
||||||
|
.getPGPContentVerifierBuilderProvider();
|
||||||
|
try {
|
||||||
|
ops.init(verifierProvider, certificate.getPublicKey(ops.getKeyID()));
|
||||||
|
} catch (PGPException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private PGPPublicKeyRing findCertificate(long keyId) {
|
||||||
|
for (PGPPublicKeyRing cert : options.getCertificates()) {
|
||||||
|
PGPPublicKey verificationKey = cert.getPublicKey(keyId);
|
||||||
|
if (verificationKey != null) {
|
||||||
|
return cert;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null; // TODO: Missing cert for sig
|
||||||
|
}
|
||||||
|
|
||||||
public void update(byte b) {
|
public void update(byte b) {
|
||||||
/**
|
for (PGPSignature prepended : prependedSignatures) {
|
||||||
for (PGPSignature prepended : prependedSignatures) {
|
prepended.update(b);
|
||||||
prepended.update(b);
|
}
|
||||||
}
|
for (PGPOnePassSignature ops : onePassSignatures) {
|
||||||
for (PGPOnePassSignature ops : onePassSignatures) {
|
ops.update(b);
|
||||||
ops.update(b);
|
}
|
||||||
}
|
for (PGPSignature detached : detachedSignatures) {
|
||||||
for (PGPSignature detached : detachedSignatures) {
|
detached.update(b);
|
||||||
detached.update(b);
|
}
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void finish() {
|
public void finish() {
|
||||||
for (PGPSignature detached : detachedSignatures) {
|
for (PGPSignature detached : detachedSignatures) {
|
||||||
|
boolean verified = false;
|
||||||
|
try {
|
||||||
|
verified = detached.verify();
|
||||||
|
} catch (PGPException e) {
|
||||||
|
System.out.println(e.getMessage());
|
||||||
|
}
|
||||||
|
System.out.println("Detached Signature by " + Long.toHexString(detached.getKeyID()) + " is " + (verified ? "verified" : "unverified"));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (PGPSignature prepended : prependedSignatures) {
|
||||||
|
boolean verified = false;
|
||||||
|
try {
|
||||||
|
verified = prepended.verify();
|
||||||
|
} catch (PGPException e) {
|
||||||
|
System.out.println(e.getMessage());
|
||||||
|
}
|
||||||
|
System.out.println("Prepended Signature by " + Long.toHexString(prepended.getKeyID()) + " is " + (verified ? "verified" : "unverified"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for (int i = 0; i < onePassSignatures.size(); i++) {
|
||||||
|
PGPOnePassSignature ops = onePassSignatures.get(i);
|
||||||
|
PGPSignature signature = correspondingSignatures.get(correspondingSignatures.size() - i - 1);
|
||||||
|
boolean verified = false;
|
||||||
|
try {
|
||||||
|
verified = ops.verify(signature);
|
||||||
|
} catch (PGPException e) {
|
||||||
|
System.out.println(e.getMessage());
|
||||||
|
}
|
||||||
|
System.out.println("One-Pass-Signature by " + Long.toHexString(ops.getKeyID()) + " is " + (verified ? "verified" : "unverified"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,339 +0,0 @@
|
||||||
package org.pgpainless.decryption_verification.automaton;
|
|
||||||
|
|
||||||
import org.pgpainless.exception.MalformedOpenPgpMessageException;
|
|
||||||
|
|
||||||
import java.util.Stack;
|
|
||||||
|
|
||||||
import static org.pgpainless.decryption_verification.automaton.StackAlphabet.msg;
|
|
||||||
import static org.pgpainless.decryption_verification.automaton.StackAlphabet.ops;
|
|
||||||
import static org.pgpainless.decryption_verification.automaton.StackAlphabet.terminus;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pushdown Automaton to verify the correct syntax of OpenPGP messages during decryption.
|
|
||||||
* <p>
|
|
||||||
* OpenPGP messages MUST follow certain rules in order to be well-formed.
|
|
||||||
* Section §11.3. of RFC4880 specifies a formal grammar for OpenPGP messages.
|
|
||||||
* <p>
|
|
||||||
* This grammar was transformed into a pushdown automaton, which is implemented below.
|
|
||||||
* The automaton only ends up in a valid state ({@link #isValid()} iff the OpenPGP message conformed to the
|
|
||||||
* grammar.
|
|
||||||
* <p>
|
|
||||||
* There are some specialties with this implementation though:
|
|
||||||
* Bouncy Castle combines ESKs and Encrypted Data Packets into a single object, so we do not have to
|
|
||||||
* handle those manually.
|
|
||||||
* <p>
|
|
||||||
* Bouncy Castle further combines OnePassSignatures and Signatures into lists, so instead of pushing multiple
|
|
||||||
* 'o's onto the stack repeatedly, a sequence of OnePassSignatures causes a single 'o' to be pushed to the stack.
|
|
||||||
* The same is true for Signatures.
|
|
||||||
* <p>
|
|
||||||
* Therefore, a message is valid, even if the number of OnePassSignatures and Signatures does not match.
|
|
||||||
* If a message contains at least one OnePassSignature, it is sufficient if there is at least one Signature to
|
|
||||||
* not cause a {@link MalformedOpenPgpMessageException}.
|
|
||||||
*
|
|
||||||
* @see <a href="https://www.rfc-editor.org/rfc/rfc4880#section-11.3">RFC4880 §11.3. OpenPGP Messages</a>
|
|
||||||
*/
|
|
||||||
public class NestingPDA {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set of states of the automaton.
|
|
||||||
* Each state defines its valid transitions in their {@link State#transition(InputAlphabet, NestingPDA)}
|
|
||||||
* method.
|
|
||||||
*/
|
|
||||||
public enum State {
|
|
||||||
|
|
||||||
OpenPgpMessage {
|
|
||||||
@Override
|
|
||||||
State transition(InputAlphabet input, NestingPDA automaton) throws MalformedOpenPgpMessageException {
|
|
||||||
StackAlphabet stackItem = automaton.popStack();
|
|
||||||
if (stackItem != msg) {
|
|
||||||
throw new MalformedOpenPgpMessageException(this, input, stackItem);
|
|
||||||
}
|
|
||||||
switch (input) {
|
|
||||||
|
|
||||||
case LiteralData:
|
|
||||||
return LiteralMessage;
|
|
||||||
|
|
||||||
case Signatures:
|
|
||||||
automaton.pushStack(msg);
|
|
||||||
return OpenPgpMessage;
|
|
||||||
|
|
||||||
case OnePassSignatures:
|
|
||||||
automaton.pushStack(ops);
|
|
||||||
automaton.pushStack(msg);
|
|
||||||
return OpenPgpMessage;
|
|
||||||
|
|
||||||
case CompressedData:
|
|
||||||
return CompressedMessage;
|
|
||||||
|
|
||||||
case EncryptedData:
|
|
||||||
return EncryptedMessage;
|
|
||||||
|
|
||||||
case EndOfSequence:
|
|
||||||
default:
|
|
||||||
throw new MalformedOpenPgpMessageException(this, input, stackItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
LiteralMessage {
|
|
||||||
@Override
|
|
||||||
State transition(InputAlphabet input, NestingPDA automaton) throws MalformedOpenPgpMessageException {
|
|
||||||
StackAlphabet stackItem = automaton.popStack();
|
|
||||||
switch (input) {
|
|
||||||
|
|
||||||
case Signatures:
|
|
||||||
if (stackItem == ops) {
|
|
||||||
return CorrespondingSignature;
|
|
||||||
} else {
|
|
||||||
throw new MalformedOpenPgpMessageException(this, input, stackItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
case EndOfSequence:
|
|
||||||
if (stackItem == terminus && automaton.stack.isEmpty()) {
|
|
||||||
return Valid;
|
|
||||||
} else {
|
|
||||||
throw new MalformedOpenPgpMessageException(this, input, stackItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
case LiteralData:
|
|
||||||
case OnePassSignatures:
|
|
||||||
case CompressedData:
|
|
||||||
case EncryptedData:
|
|
||||||
default:
|
|
||||||
throw new MalformedOpenPgpMessageException(this, input, stackItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
CompressedMessage {
|
|
||||||
@Override
|
|
||||||
State transition(InputAlphabet input, NestingPDA automaton) throws MalformedOpenPgpMessageException {
|
|
||||||
StackAlphabet stackItem = automaton.popStack();
|
|
||||||
switch (input) {
|
|
||||||
case Signatures:
|
|
||||||
if (stackItem == ops) {
|
|
||||||
return CorrespondingSignature;
|
|
||||||
} else {
|
|
||||||
throw new MalformedOpenPgpMessageException(this, input, stackItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
case EndOfSequence:
|
|
||||||
if (stackItem == terminus && automaton.stack.isEmpty()) {
|
|
||||||
return Valid;
|
|
||||||
} else {
|
|
||||||
throw new MalformedOpenPgpMessageException(this, input, stackItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
case LiteralData:
|
|
||||||
case OnePassSignatures:
|
|
||||||
case CompressedData:
|
|
||||||
case EncryptedData:
|
|
||||||
default:
|
|
||||||
throw new MalformedOpenPgpMessageException(this, input, stackItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
EncryptedMessage {
|
|
||||||
@Override
|
|
||||||
State transition(InputAlphabet input, NestingPDA automaton) throws MalformedOpenPgpMessageException {
|
|
||||||
StackAlphabet stackItem = automaton.popStack();
|
|
||||||
switch (input) {
|
|
||||||
case Signatures:
|
|
||||||
if (stackItem == ops) {
|
|
||||||
return CorrespondingSignature;
|
|
||||||
} else {
|
|
||||||
throw new MalformedOpenPgpMessageException(this, input, stackItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
case EndOfSequence:
|
|
||||||
if (stackItem == terminus && automaton.stack.isEmpty()) {
|
|
||||||
return Valid;
|
|
||||||
} else {
|
|
||||||
throw new MalformedOpenPgpMessageException(this, input, stackItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
case LiteralData:
|
|
||||||
case OnePassSignatures:
|
|
||||||
case CompressedData:
|
|
||||||
case EncryptedData:
|
|
||||||
default:
|
|
||||||
throw new MalformedOpenPgpMessageException(this, input, stackItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
CorrespondingSignature {
|
|
||||||
@Override
|
|
||||||
State transition(InputAlphabet input, NestingPDA automaton) throws MalformedOpenPgpMessageException {
|
|
||||||
StackAlphabet stackItem = automaton.popStack();
|
|
||||||
if (stackItem == terminus && input == InputAlphabet.EndOfSequence && automaton.stack.isEmpty()) {
|
|
||||||
return Valid;
|
|
||||||
} else {
|
|
||||||
throw new MalformedOpenPgpMessageException(this, input, stackItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
Valid {
|
|
||||||
@Override
|
|
||||||
State transition(InputAlphabet input, NestingPDA automaton) throws MalformedOpenPgpMessageException {
|
|
||||||
throw new MalformedOpenPgpMessageException(this, input, null);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pop the automatons stack and transition to another state.
|
|
||||||
* If no valid transition from the current state is available given the popped stack item and input symbol,
|
|
||||||
* a {@link MalformedOpenPgpMessageException} is thrown.
|
|
||||||
* Otherwise, the stack is manipulated according to the valid transition and the new state is returned.
|
|
||||||
*
|
|
||||||
* @param input input symbol
|
|
||||||
* @param automaton automaton
|
|
||||||
* @return new state of the automaton
|
|
||||||
* @throws MalformedOpenPgpMessageException in case of an illegal input symbol
|
|
||||||
*/
|
|
||||||
abstract State transition(InputAlphabet input, NestingPDA automaton) throws MalformedOpenPgpMessageException;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Stack<StackAlphabet> stack = new Stack<>();
|
|
||||||
private State state;
|
|
||||||
// Some OpenPGP packets have nested contents (e.g. compressed / encrypted data).
|
|
||||||
NestingPDA nestedSequence = null;
|
|
||||||
|
|
||||||
public NestingPDA() {
|
|
||||||
state = State.OpenPgpMessage;
|
|
||||||
stack.push(terminus);
|
|
||||||
stack.push(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Process the next input packet.
|
|
||||||
*
|
|
||||||
* @param input input
|
|
||||||
* @throws MalformedOpenPgpMessageException in case the input packet is illegal here
|
|
||||||
*/
|
|
||||||
public void next(InputAlphabet input) throws MalformedOpenPgpMessageException {
|
|
||||||
_next(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Process the next input packet.
|
|
||||||
* This method returns true, iff the given input triggered a successful closing of this PDAs nested PDA.
|
|
||||||
* <p>
|
|
||||||
* This is for example the case, if the current packet is a Compressed Data packet which contains a
|
|
||||||
* valid nested OpenPGP message and the last input was {@link InputAlphabet#EndOfSequence} indicating the
|
|
||||||
* end of the Compressed Data packet.
|
|
||||||
* <p>
|
|
||||||
* If the input triggered this PDAs nested PDA to close its nested PDA, this method returns false
|
|
||||||
* in order to prevent this PDA from closing its nested PDA prematurely.
|
|
||||||
*
|
|
||||||
* @param input input
|
|
||||||
* @return true if this just closed its nested sequence, false otherwise
|
|
||||||
* @throws MalformedOpenPgpMessageException if the input is illegal
|
|
||||||
*/
|
|
||||||
private boolean _next(InputAlphabet input) throws MalformedOpenPgpMessageException {
|
|
||||||
if (nestedSequence != null) {
|
|
||||||
boolean sequenceInNestedSequenceWasClosed = nestedSequence._next(input);
|
|
||||||
if (sequenceInNestedSequenceWasClosed) return false; // No need to close out nested sequence too.
|
|
||||||
} else {
|
|
||||||
// make a state transition in this automaton
|
|
||||||
state = state.transition(input, this);
|
|
||||||
|
|
||||||
// If the processed packet contains nested sequence, open nested automaton for it
|
|
||||||
if (input == InputAlphabet.CompressedData || input == InputAlphabet.EncryptedData) {
|
|
||||||
nestedSequence = new NestingPDA();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input != InputAlphabet.EndOfSequence) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close nested sequence if needed
|
|
||||||
boolean nestedIsInnerMost = nestedSequence != null && nestedSequence.isInnerMost();
|
|
||||||
if (nestedIsInnerMost) {
|
|
||||||
if (nestedSequence.isValid()) {
|
|
||||||
// Close nested sequence
|
|
||||||
nestedSequence = null;
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
throw new MalformedOpenPgpMessageException("Climbing up nested message validation failed." +
|
|
||||||
" Automaton for current nesting level is not in valid state: " + nestedSequence.getState() + " " + nestedSequence.stack.peek() + " (Input was " + input + ")");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the current state of the PDA.
|
|
||||||
*
|
|
||||||
* @return state
|
|
||||||
*/
|
|
||||||
private State getState() {
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true, if the PDA is in a valid state (the OpenPGP message is valid).
|
|
||||||
*
|
|
||||||
* @return true if valid, false otherwise
|
|
||||||
*/
|
|
||||||
public boolean isValid() {
|
|
||||||
return getState() == State.Valid && stack.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void assertValid() throws MalformedOpenPgpMessageException {
|
|
||||||
if (!isValid()) {
|
|
||||||
throw new MalformedOpenPgpMessageException("Pushdown Automaton is not in an acceptable state: " + toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pop an item from the stack.
|
|
||||||
*
|
|
||||||
* @return stack item
|
|
||||||
*/
|
|
||||||
private StackAlphabet popStack() {
|
|
||||||
return stack.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Push an item onto the stack.
|
|
||||||
*
|
|
||||||
* @param item item
|
|
||||||
*/
|
|
||||||
private void pushStack(StackAlphabet item) {
|
|
||||||
stack.push(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true, if this packet sequence has no nested sequence.
|
|
||||||
* A nested sequence is for example the content of a Compressed Data packet.
|
|
||||||
*
|
|
||||||
* @return true if PDA is innermost, false if it has a nested sequence
|
|
||||||
*/
|
|
||||||
private boolean isInnerMost() {
|
|
||||||
return nestedSequence == null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
StringBuilder out = new StringBuilder("State: ").append(state)
|
|
||||||
.append(", Stack (asc.): ").append(stack)
|
|
||||||
.append('\n');
|
|
||||||
if (nestedSequence != null) {
|
|
||||||
// recursively call toString() on nested PDAs and indent their representation
|
|
||||||
String nestedToString = nestedSequence.toString();
|
|
||||||
String[] lines = nestedToString.split("\n");
|
|
||||||
for (int i = 0; i < lines.length; i++) {
|
|
||||||
String nestedLine = lines[i];
|
|
||||||
out.append(i == 0 ? "⤷ " : " ") // indent nested PDA
|
|
||||||
.append(nestedLine)
|
|
||||||
.append('\n');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return out.toString();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,7 +5,6 @@
|
||||||
package org.pgpainless.exception;
|
package org.pgpainless.exception;
|
||||||
|
|
||||||
import org.pgpainless.decryption_verification.automaton.InputAlphabet;
|
import org.pgpainless.decryption_verification.automaton.InputAlphabet;
|
||||||
import org.pgpainless.decryption_verification.automaton.NestingPDA;
|
|
||||||
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;
|
||||||
|
|
||||||
|
@ -21,21 +20,7 @@ public class MalformedOpenPgpMessageException extends RuntimeException {
|
||||||
super(message);
|
super(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public MalformedOpenPgpMessageException(String message, MalformedOpenPgpMessageException cause) {
|
|
||||||
super(message, cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MalformedOpenPgpMessageException(NestingPDA.State state,
|
|
||||||
InputAlphabet input,
|
|
||||||
StackAlphabet stackItem) {
|
|
||||||
this("Invalid input: There is no legal transition from state '" + state + "' for input '" + input + "' when '" + stackItem + "' is on top of the stack.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public MalformedOpenPgpMessageException(PDA.State state, InputAlphabet input, StackAlphabet stackItem) {
|
public MalformedOpenPgpMessageException(PDA.State state, InputAlphabet input, StackAlphabet stackItem) {
|
||||||
this("Invalid input: There is no legal transition from state '" + state + "' for input '" + input + "' when '" + stackItem + "' is on top of the stack.");
|
this("Invalid input: There is no legal transition from state '" + state + "' for input '" + input + "' when '" + stackItem + "' is on top of the stack.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public MalformedOpenPgpMessageException(String message, PDA automaton) {
|
|
||||||
super(message + automaton.toString());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,87 +1,402 @@
|
||||||
package org.pgpainless.decryption_verification;
|
package org.pgpainless.decryption_verification;
|
||||||
|
|
||||||
import org.bouncycastle.bcpg.ArmoredInputStream;
|
import org.bouncycastle.bcpg.ArmoredInputStream;
|
||||||
|
import org.bouncycastle.bcpg.ArmoredOutputStream;
|
||||||
|
import org.bouncycastle.bcpg.CompressionAlgorithmTags;
|
||||||
|
import org.bouncycastle.openpgp.PGPCompressedDataGenerator;
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
|
import org.bouncycastle.openpgp.PGPLiteralData;
|
||||||
|
import org.bouncycastle.openpgp.PGPLiteralDataGenerator;
|
||||||
|
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||||
|
import org.bouncycastle.openpgp.PGPSignature;
|
||||||
import org.bouncycastle.util.io.Streams;
|
import org.bouncycastle.util.io.Streams;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.pgpainless.PGPainless;
|
import org.pgpainless.PGPainless;
|
||||||
|
import org.pgpainless.algorithm.CompressionAlgorithm;
|
||||||
|
import org.pgpainless.encryption_signing.EncryptionOptions;
|
||||||
|
import org.pgpainless.encryption_signing.EncryptionResult;
|
||||||
|
import org.pgpainless.encryption_signing.EncryptionStream;
|
||||||
|
import org.pgpainless.encryption_signing.ProducerOptions;
|
||||||
|
import org.pgpainless.encryption_signing.SigningOptions;
|
||||||
import org.pgpainless.exception.MalformedOpenPgpMessageException;
|
import org.pgpainless.exception.MalformedOpenPgpMessageException;
|
||||||
|
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
||||||
import org.pgpainless.util.ArmoredInputStreamFactory;
|
import org.pgpainless.util.ArmoredInputStreamFactory;
|
||||||
import org.pgpainless.util.Passphrase;
|
import org.pgpainless.util.Passphrase;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
import static org.pgpainless.decryption_verification.PGPDecryptionStreamTest.COMP;
|
|
||||||
import static org.pgpainless.decryption_verification.PGPDecryptionStreamTest.COMP_COMP_LIT;
|
|
||||||
import static org.pgpainless.decryption_verification.PGPDecryptionStreamTest.COMP_LIT;
|
|
||||||
import static org.pgpainless.decryption_verification.PGPDecryptionStreamTest.KEY;
|
|
||||||
import static org.pgpainless.decryption_verification.PGPDecryptionStreamTest.LIT;
|
|
||||||
import static org.pgpainless.decryption_verification.PGPDecryptionStreamTest.LIT_LIT;
|
|
||||||
import static org.pgpainless.decryption_verification.PGPDecryptionStreamTest.PASSPHRASE;
|
|
||||||
import static org.pgpainless.decryption_verification.PGPDecryptionStreamTest.PENC_COMP_LIT;
|
|
||||||
import static org.pgpainless.decryption_verification.PGPDecryptionStreamTest.PLAINTEXT;
|
|
||||||
import static org.pgpainless.decryption_verification.PGPDecryptionStreamTest.SENC_LIT;
|
|
||||||
import static org.pgpainless.decryption_verification.PGPDecryptionStreamTest.SIG_LIT;
|
|
||||||
|
|
||||||
public class OpenPgpMessageInputStreamTest {
|
public class OpenPgpMessageInputStreamTest {
|
||||||
|
|
||||||
|
|
||||||
|
public static final String KEY = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" +
|
||||||
|
"Version: PGPainless\n" +
|
||||||
|
"Comment: DA05 848F 37D4 68E6 F982 C889 7A70 1FC6 904D 3F4C\n" +
|
||||||
|
"Comment: Alice <alice@pgpainless.org>\n" +
|
||||||
|
"\n" +
|
||||||
|
"lFgEYxzSCBYJKwYBBAHaRw8BAQdAeJU8m4GOJb1eQgv/ryilFHRfNLTYFMNqL6zj\n" +
|
||||||
|
"r0vF7dsAAP42rAtngpJ6dZxoZlJX0Je65zk1VMPeTrXaWfPS2HSKBRGptBxBbGlj\n" +
|
||||||
|
"ZSA8YWxpY2VAcGdwYWlubGVzcy5vcmc+iI8EExYKAEEFAmMc0ggJEHpwH8aQTT9M\n" +
|
||||||
|
"FiEE2gWEjzfUaOb5gsiJenAfxpBNP0wCngECmwEFFgIDAQAECwkIBwUVCgkICwKZ\n" +
|
||||||
|
"AQAApZEBALUXHtvswPZG28YO+16Men6/fpk+scvqpNMnD4ty3IkAAPwK6TuXjNnZ\n" +
|
||||||
|
"0XuWdnilvLMV23Ai1d5g6em+lwLK5M2SApxdBGMc0ggSCisGAQQBl1UBBQEBB0D8\n" +
|
||||||
|
"mNUVX8y2MXFaSeFYqOTPFnGT7dgNVdn6yc0UtkkHOgMBCAcAAP9y9OtP4SX9voPb\n" +
|
||||||
|
"ID2u9PkJKgo4hTB8NK5LouGppdRtEBGriHUEGBYKAB0FAmMc0ggCngECmwwFFgID\n" +
|
||||||
|
"AQAECwkIBwUVCgkICwAKCRB6cB/GkE0/TAywAQDpZRJS/joFH4+xcwheqWfI7ay/\n" +
|
||||||
|
"WfojUoGQMYGnUjsgYwEAkceRUsgkqI0SVgYvuglfaQpZ9k2ns1mZGVLkXvu/yQyc\n" +
|
||||||
|
"WARjHNIIFgkrBgEEAdpHDwEBB0BGN9BybSOrj8B6gim1SjbB/IiqAshlzMDunVkQ\n" +
|
||||||
|
"X23npQABAJqvjOOY7qhBuTusC5/Q5+25iLrhMn4TI+LXlJHMVNOaE0OI1QQYFgoA\n" +
|
||||||
|
"fQUCYxzSCAKeAQKbAgUWAgMBAAQLCQgHBRUKCQgLXyAEGRYKAAYFAmMc0ggACgkQ\n" +
|
||||||
|
"KALh4BJQXl6yTQD/dh0N5228Uwtu7XHy6dmpMRX62cac5tXQ9WaDzpy8STgBAMdn\n" +
|
||||||
|
"Mq948UOYEhdk/ZY2/hwux/4t+FHvqrXW8ziBe4cLAAoJEHpwH8aQTT9M1hQA/3Ms\n" +
|
||||||
|
"P3kzoed3VsWu1ZMr7dKEngbc6SoJ2XPayzN0QYJaAQCIY5NcT9mZF97HWV3Vgeum\n" +
|
||||||
|
"00sWMHXfkW3+nl5OpUZaDA==\n" +
|
||||||
|
"=THgv\n" +
|
||||||
|
"-----END PGP PRIVATE KEY BLOCK-----";
|
||||||
|
|
||||||
|
public static final String PLAINTEXT = "Hello, World!\n";
|
||||||
|
public static final String PASSPHRASE = "sw0rdf1sh";
|
||||||
|
|
||||||
|
public static final String LIT = "" +
|
||||||
|
"-----BEGIN PGP MESSAGE-----\n" +
|
||||||
|
"Version: PGPainless\n" +
|
||||||
|
"\n" +
|
||||||
|
"yxRiAAAAAABIZWxsbywgV29ybGQhCg==\n" +
|
||||||
|
"=WGju\n" +
|
||||||
|
"-----END PGP MESSAGE-----";
|
||||||
|
|
||||||
|
public static final String LIT_LIT = "" +
|
||||||
|
"-----BEGIN PGP MESSAGE-----\n" +
|
||||||
|
"Version: BCPG v1.71\n" +
|
||||||
|
"\n" +
|
||||||
|
"yxRiAAAAAABIZWxsbywgV29ybGQhCssUYgAAAAAASGVsbG8sIFdvcmxkIQo=\n" +
|
||||||
|
"=A91Q\n" +
|
||||||
|
"-----END PGP MESSAGE-----";
|
||||||
|
|
||||||
|
public static final String COMP_LIT = "" +
|
||||||
|
"-----BEGIN PGP MESSAGE-----\n" +
|
||||||
|
"Version: BCPG v1.71\n" +
|
||||||
|
"\n" +
|
||||||
|
"owE7LZLEAAIeqTk5+ToK4flFOSmKXAA=\n" +
|
||||||
|
"=ZYDg\n" +
|
||||||
|
"-----END PGP MESSAGE-----";
|
||||||
|
|
||||||
|
public static final String COMP = "" +
|
||||||
|
"-----BEGIN PGP MESSAGE-----\n" +
|
||||||
|
"Version: BCPG v1.71\n" +
|
||||||
|
"\n" +
|
||||||
|
"owEDAA==\n" +
|
||||||
|
"=MDzg\n" +
|
||||||
|
"-----END PGP MESSAGE-----";
|
||||||
|
|
||||||
|
public static final String COMP_COMP_LIT = "" +
|
||||||
|
"-----BEGIN PGP MESSAGE-----\n" +
|
||||||
|
"Version: BCPG v1.71\n" +
|
||||||
|
"\n" +
|
||||||
|
"owEBRwC4/6MDQlpoOTFBWSZTWVuW2KAAAAr3hGAQBABgBABAAIAWBJAAAAggADFM\n" +
|
||||||
|
"ABNBqBo00N6puqWR+TqInoXQ58XckU4UJBbltigA\n" +
|
||||||
|
"=K9Zl\n" +
|
||||||
|
"-----END PGP MESSAGE-----";
|
||||||
|
|
||||||
|
public static final String SIG_LIT = "" +
|
||||||
|
"-----BEGIN PGP MESSAGE-----\n" +
|
||||||
|
"Version: BCPG v1.71\n" +
|
||||||
|
"\n" +
|
||||||
|
"iHUEABYKACcFAmMc1i0JECgC4eASUF5eFiEEjN3RiJxCf/TyYOQjKALh4BJQXl4A\n" +
|
||||||
|
"AHkrAP98uPpqrgIix7epgL7MM1cjXXGSxqbDfXHwgptk1YGQlgD/fw89VGcXwFaI\n" +
|
||||||
|
"2k7kpXQYy/1BqnovM/jZ3X3mXhhTaAOjATstksQAAh6pOTn5Ogrh+UU5KYpcAA==\n" +
|
||||||
|
"=WKPn\n" +
|
||||||
|
"-----END PGP MESSAGE-----";
|
||||||
|
|
||||||
|
public static final String SENC_LIT = "" +
|
||||||
|
"-----BEGIN PGP MESSAGE-----\n" +
|
||||||
|
"Version: PGPainless\n" +
|
||||||
|
"\n" +
|
||||||
|
"jA0ECQMCuZ0qHNXWnGhg0j8Bdm1cxV65sYb7jDgb4rRMtdNpQ1dC4UpSYuk9YWS2\n" +
|
||||||
|
"DpNEijbX8b/P1UOK2kJczNDADMRegZuLEI+dNsBnJjk=\n" +
|
||||||
|
"=i4Y0\n" +
|
||||||
|
"-----END PGP MESSAGE-----";
|
||||||
|
|
||||||
|
public static final String PENC_COMP_LIT = "" +
|
||||||
|
"-----BEGIN PGP MESSAGE-----\n" +
|
||||||
|
"Version: PGPainless\n" +
|
||||||
|
"\n" +
|
||||||
|
"hF4Dyqa/GWUy6WsSAQdAQ62BwmUt8Iby0+jvrLhMgST79KR/as+dyl0nf1uki2sw\n" +
|
||||||
|
"Thg1Ojtf0hOyJgcpQ4nP2Q0wYFR0F1sCydaIlTGreYZHlGtybP7/Ml6KNZILTRWP\n" +
|
||||||
|
"0kYBkGBgK7oQWRIVyoF2POvEP6EX1X8nvQk7O3NysVdRVbnia7gE3AzRYuha4kxs\n" +
|
||||||
|
"pI6xJkntLMS3K6him1Y9FHINIASFSB+C\n" +
|
||||||
|
"=5p00\n" +
|
||||||
|
"-----END PGP MESSAGE-----";
|
||||||
|
|
||||||
|
public static final String OPS_LIT_SIG = "" +
|
||||||
|
"-----BEGIN PGP MESSAGE-----\n" +
|
||||||
|
"Version: PGPainless\n" +
|
||||||
|
"\n" +
|
||||||
|
"kA0DAAoWKALh4BJQXl4ByxRiAAAAAABIZWxsbywgV29ybGQhCoh1BAAWCgAnBQJj\n" +
|
||||||
|
"I3fSCRAoAuHgElBeXhYhBIzd0YicQn/08mDkIygC4eASUF5eAADLOgEA766VyMMv\n" +
|
||||||
|
"sxfQwQHly3T6ySHSNhYEpoyvdxVqhjBBR+EA/3i6C8lKFPPTh/PvTGbVFOl+eUSV\n" +
|
||||||
|
"I0w3c+BRY/pO0m4H\n" +
|
||||||
|
"=tkTV\n" +
|
||||||
|
"-----END PGP MESSAGE-----";
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
// genLIT();
|
||||||
|
// genLIT_LIT();
|
||||||
|
// genCOMP_LIT();
|
||||||
|
// genCOMP();
|
||||||
|
// genCOMP_COMP_LIT();
|
||||||
|
// genKey();
|
||||||
|
// genSIG_LIT();
|
||||||
|
// genSENC_LIT();
|
||||||
|
// genPENC_COMP_LIT();
|
||||||
|
genOPS_LIT_SIG();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void genLIT() throws IOException {
|
||||||
|
ArmoredOutputStream armorOut = new ArmoredOutputStream(System.out);
|
||||||
|
PGPLiteralDataGenerator litGen = new PGPLiteralDataGenerator();
|
||||||
|
OutputStream litOut = litGen.open(armorOut, PGPLiteralDataGenerator.BINARY, "", PGPLiteralData.NOW, new byte[1 << 9]);
|
||||||
|
litOut.write(PLAINTEXT.getBytes(StandardCharsets.UTF_8));
|
||||||
|
litOut.close();
|
||||||
|
armorOut.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void genLIT_LIT() throws IOException {
|
||||||
|
ArmoredOutputStream armorOut = new ArmoredOutputStream(System.out);
|
||||||
|
PGPLiteralDataGenerator litGen = new PGPLiteralDataGenerator();
|
||||||
|
OutputStream litOut = litGen.open(armorOut, PGPLiteralDataGenerator.BINARY, "", PGPLiteralData.NOW, new byte[1 << 9]);
|
||||||
|
litOut.write(PLAINTEXT.getBytes(StandardCharsets.UTF_8));
|
||||||
|
litOut.close();
|
||||||
|
|
||||||
|
litOut = litGen.open(armorOut, PGPLiteralDataGenerator.BINARY, "", PGPLiteralData.NOW, new byte[1 << 9]);
|
||||||
|
litOut.write(PLAINTEXT.getBytes(StandardCharsets.UTF_8));
|
||||||
|
litOut.close();
|
||||||
|
|
||||||
|
armorOut.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void genCOMP_LIT() throws IOException {
|
||||||
|
ArmoredOutputStream armorOut = new ArmoredOutputStream(System.out);
|
||||||
|
PGPCompressedDataGenerator compGen = new PGPCompressedDataGenerator(CompressionAlgorithmTags.ZIP);
|
||||||
|
OutputStream compOut = compGen.open(armorOut);
|
||||||
|
PGPLiteralDataGenerator litGen = new PGPLiteralDataGenerator();
|
||||||
|
OutputStream litOut = litGen.open(compOut, PGPLiteralDataGenerator.BINARY, "", PGPLiteralData.NOW, new byte[1 << 9]);
|
||||||
|
litOut.write(PLAINTEXT.getBytes(StandardCharsets.UTF_8));
|
||||||
|
litOut.close();
|
||||||
|
compOut.close();
|
||||||
|
armorOut.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void genCOMP() throws IOException {
|
||||||
|
ArmoredOutputStream armorOut = new ArmoredOutputStream(System.out);
|
||||||
|
PGPCompressedDataGenerator compGen = new PGPCompressedDataGenerator(CompressionAlgorithmTags.ZIP);
|
||||||
|
OutputStream compOut = compGen.open(armorOut);
|
||||||
|
compOut.close();
|
||||||
|
armorOut.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void genCOMP_COMP_LIT() throws IOException {
|
||||||
|
ArmoredOutputStream armorOut = new ArmoredOutputStream(System.out);
|
||||||
|
|
||||||
|
PGPCompressedDataGenerator compGen1 = new PGPCompressedDataGenerator(CompressionAlgorithmTags.ZIP);
|
||||||
|
OutputStream compOut1 = compGen1.open(armorOut);
|
||||||
|
|
||||||
|
PGPCompressedDataGenerator compGen2 = new PGPCompressedDataGenerator(CompressionAlgorithmTags.BZIP2);
|
||||||
|
OutputStream compOut2 = compGen2.open(compOut1);
|
||||||
|
|
||||||
|
PGPLiteralDataGenerator litGen = new PGPLiteralDataGenerator();
|
||||||
|
OutputStream litOut = litGen.open(compOut2, PGPLiteralDataGenerator.BINARY, "", PGPLiteralDataGenerator.NOW, new byte[1 << 9]);
|
||||||
|
|
||||||
|
litOut.write(PLAINTEXT.getBytes(StandardCharsets.UTF_8));
|
||||||
|
litOut.close();
|
||||||
|
compOut2.close();
|
||||||
|
compOut1.close();
|
||||||
|
armorOut.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void genKey() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException {
|
||||||
|
System.out.println(PGPainless.asciiArmor(
|
||||||
|
PGPainless.generateKeyRing().modernKeyRing("Alice <alice@pgpainless.org>")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void genSIG_LIT() throws PGPException, IOException {
|
||||||
|
PGPSecretKeyRing secretKeys = PGPainless.readKeyRing().secretKeyRing(KEY);
|
||||||
|
ByteArrayOutputStream msgOut = new ByteArrayOutputStream();
|
||||||
|
EncryptionStream signer = PGPainless.encryptAndOrSign()
|
||||||
|
.onOutputStream(msgOut)
|
||||||
|
.withOptions(
|
||||||
|
ProducerOptions.sign(
|
||||||
|
SigningOptions.get()
|
||||||
|
.addDetachedSignature(SecretKeyRingProtector.unprotectedKeys(), secretKeys)
|
||||||
|
).setAsciiArmor(false)
|
||||||
|
);
|
||||||
|
|
||||||
|
Streams.pipeAll(new ByteArrayInputStream(PLAINTEXT.getBytes(StandardCharsets.UTF_8)), signer);
|
||||||
|
signer.close();
|
||||||
|
EncryptionResult result = signer.getResult();
|
||||||
|
PGPSignature detachedSignature = result.getDetachedSignatures().get(result.getDetachedSignatures().keySet().iterator().next()).iterator().next();
|
||||||
|
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
ArmoredOutputStream armorOut = new ArmoredOutputStream(out);
|
||||||
|
armorOut.flush();
|
||||||
|
detachedSignature.encode(armorOut);
|
||||||
|
armorOut.write(msgOut.toByteArray());
|
||||||
|
armorOut.close();
|
||||||
|
|
||||||
|
String armored = out.toString();
|
||||||
|
System.out.println(armored
|
||||||
|
.replace("-----BEGIN PGP SIGNATURE-----\n", "-----BEGIN PGP MESSAGE-----\n")
|
||||||
|
.replace("-----END PGP SIGNATURE-----", "-----END PGP MESSAGE-----"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void genSENC_LIT() throws PGPException, IOException {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
EncryptionStream enc = PGPainless.encryptAndOrSign()
|
||||||
|
.onOutputStream(out)
|
||||||
|
.withOptions(ProducerOptions.encrypt(EncryptionOptions.get()
|
||||||
|
.addPassphrase(Passphrase.fromPassword(PASSPHRASE)))
|
||||||
|
.overrideCompressionAlgorithm(CompressionAlgorithm.UNCOMPRESSED));
|
||||||
|
enc.write(PLAINTEXT.getBytes(StandardCharsets.UTF_8));
|
||||||
|
enc.close();
|
||||||
|
|
||||||
|
System.out.println(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void genPENC_COMP_LIT() throws IOException, PGPException {
|
||||||
|
PGPSecretKeyRing secretKeys = PGPainless.readKeyRing().secretKeyRing(KEY);
|
||||||
|
PGPPublicKeyRing cert = PGPainless.extractCertificate(secretKeys);
|
||||||
|
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
EncryptionStream enc = PGPainless.encryptAndOrSign()
|
||||||
|
.onOutputStream(out)
|
||||||
|
.withOptions(ProducerOptions.encrypt(EncryptionOptions.get()
|
||||||
|
.addRecipient(cert))
|
||||||
|
.overrideCompressionAlgorithm(CompressionAlgorithm.ZLIB));
|
||||||
|
|
||||||
|
Streams.pipeAll(new ByteArrayInputStream(PLAINTEXT.getBytes(StandardCharsets.UTF_8)), enc);
|
||||||
|
enc.close();
|
||||||
|
|
||||||
|
System.out.println(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void genOPS_LIT_SIG() throws PGPException, IOException {
|
||||||
|
PGPSecretKeyRing secretKeys = PGPainless.readKeyRing().secretKeyRing(KEY);
|
||||||
|
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
EncryptionStream enc = PGPainless.encryptAndOrSign()
|
||||||
|
.onOutputStream(out)
|
||||||
|
.withOptions(ProducerOptions.sign(SigningOptions.get()
|
||||||
|
.addSignature(SecretKeyRingProtector.unprotectedKeys(), secretKeys))
|
||||||
|
.overrideCompressionAlgorithm(CompressionAlgorithm.UNCOMPRESSED));
|
||||||
|
Streams.pipeAll(new ByteArrayInputStream(PLAINTEXT.getBytes(StandardCharsets.UTF_8)), enc);
|
||||||
|
enc.close();
|
||||||
|
|
||||||
|
System.out.println(out);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testProcessLIT() throws IOException, PGPException {
|
public void testProcessLIT() throws IOException, PGPException {
|
||||||
String plain = process(LIT, ConsumerOptions.get());
|
String plain = processReadBuffered(LIT, ConsumerOptions.get());
|
||||||
|
assertEquals(PLAINTEXT, plain);
|
||||||
|
|
||||||
|
plain = processReadSequential(LIT, ConsumerOptions.get());
|
||||||
assertEquals(PLAINTEXT, plain);
|
assertEquals(PLAINTEXT, plain);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testProcessLIT_LIT_fails() {
|
public void testProcessLIT_LIT_fails() {
|
||||||
assertThrows(MalformedOpenPgpMessageException.class,
|
assertThrows(MalformedOpenPgpMessageException.class,
|
||||||
() -> process(LIT_LIT, ConsumerOptions.get()));
|
() -> processReadBuffered(LIT_LIT, ConsumerOptions.get()));
|
||||||
|
|
||||||
|
assertThrows(MalformedOpenPgpMessageException.class,
|
||||||
|
() -> processReadSequential(LIT_LIT, ConsumerOptions.get()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testProcessCOMP_LIT() throws PGPException, IOException {
|
public void testProcessCOMP_LIT() throws PGPException, IOException {
|
||||||
String plain = process(COMP_LIT, ConsumerOptions.get());
|
String plain = processReadBuffered(COMP_LIT, ConsumerOptions.get());
|
||||||
|
assertEquals(PLAINTEXT, plain);
|
||||||
|
|
||||||
|
plain = processReadSequential(COMP_LIT, ConsumerOptions.get());
|
||||||
assertEquals(PLAINTEXT, plain);
|
assertEquals(PLAINTEXT, plain);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testProcessCOMP_fails() {
|
public void testProcessCOMP_fails() {
|
||||||
assertThrows(MalformedOpenPgpMessageException.class,
|
assertThrows(MalformedOpenPgpMessageException.class,
|
||||||
() -> process(COMP, ConsumerOptions.get()));
|
() -> processReadBuffered(COMP, ConsumerOptions.get()));
|
||||||
|
|
||||||
|
assertThrows(MalformedOpenPgpMessageException.class,
|
||||||
|
() -> processReadSequential(COMP, ConsumerOptions.get()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testProcessCOMP_COMP_LIT() throws PGPException, IOException {
|
public void testProcessCOMP_COMP_LIT() throws PGPException, IOException {
|
||||||
String plain = process(COMP_COMP_LIT, ConsumerOptions.get());
|
String plain = processReadBuffered(COMP_COMP_LIT, ConsumerOptions.get());
|
||||||
|
assertEquals(PLAINTEXT, plain);
|
||||||
|
|
||||||
|
plain = processReadSequential(COMP_COMP_LIT, ConsumerOptions.get());
|
||||||
assertEquals(PLAINTEXT, plain);
|
assertEquals(PLAINTEXT, plain);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testProcessSIG_LIT() throws PGPException, IOException {
|
public void testProcessSIG_LIT() throws PGPException, IOException {
|
||||||
String plain = process(SIG_LIT, ConsumerOptions.get());
|
PGPPublicKeyRing cert = PGPainless.extractCertificate(
|
||||||
|
PGPainless.readKeyRing().secretKeyRing(KEY));
|
||||||
|
|
||||||
|
String plain = processReadBuffered(SIG_LIT, ConsumerOptions.get()
|
||||||
|
.addVerificationCert(cert));
|
||||||
|
assertEquals(PLAINTEXT, plain);
|
||||||
|
|
||||||
|
plain = processReadSequential(SIG_LIT, ConsumerOptions.get()
|
||||||
|
.addVerificationCert(cert));
|
||||||
assertEquals(PLAINTEXT, plain);
|
assertEquals(PLAINTEXT, plain);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testProcessSENC_LIT() throws PGPException, IOException {
|
public void testProcessSENC_LIT() throws PGPException, IOException {
|
||||||
String plain = process(SENC_LIT, ConsumerOptions.get().addDecryptionPassphrase(Passphrase.fromPassword(PASSPHRASE)));
|
String plain = processReadBuffered(SENC_LIT, ConsumerOptions.get().addDecryptionPassphrase(Passphrase.fromPassword(PASSPHRASE)));
|
||||||
|
assertEquals(PLAINTEXT, plain);
|
||||||
|
|
||||||
|
plain = processReadSequential(SENC_LIT, ConsumerOptions.get().addDecryptionPassphrase(Passphrase.fromPassword(PASSPHRASE)));
|
||||||
assertEquals(PLAINTEXT, plain);
|
assertEquals(PLAINTEXT, plain);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testProcessPENC_COMP_LIT() throws IOException, PGPException {
|
public void testProcessPENC_COMP_LIT() throws IOException, PGPException {
|
||||||
PGPSecretKeyRing secretKeys = PGPainless.readKeyRing().secretKeyRing(KEY);
|
PGPSecretKeyRing secretKeys = PGPainless.readKeyRing().secretKeyRing(KEY);
|
||||||
String plain = process(PENC_COMP_LIT, ConsumerOptions.get()
|
String plain = processReadBuffered(PENC_COMP_LIT, ConsumerOptions.get()
|
||||||
|
.addDecryptionKey(secretKeys));
|
||||||
|
assertEquals(PLAINTEXT, plain);
|
||||||
|
|
||||||
|
plain = processReadSequential(PENC_COMP_LIT, ConsumerOptions.get()
|
||||||
.addDecryptionKey(secretKeys));
|
.addDecryptionKey(secretKeys));
|
||||||
assertEquals(PLAINTEXT, plain);
|
assertEquals(PLAINTEXT, plain);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String process(String armoredMessage, ConsumerOptions options) throws PGPException, IOException {
|
@Test
|
||||||
|
public void testProcessOPS_LIT_SIG() throws IOException, PGPException {
|
||||||
|
PGPPublicKeyRing cert = PGPainless.extractCertificate(PGPainless.readKeyRing().secretKeyRing(KEY));
|
||||||
|
String plain = processReadBuffered(OPS_LIT_SIG, ConsumerOptions.get()
|
||||||
|
.addVerificationCert(cert));
|
||||||
|
assertEquals(PLAINTEXT, plain);
|
||||||
|
|
||||||
|
plain = processReadSequential(OPS_LIT_SIG, ConsumerOptions.get()
|
||||||
|
.addVerificationCert(cert));
|
||||||
|
assertEquals(PLAINTEXT, plain);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String processReadBuffered(String armoredMessage, ConsumerOptions options) throws PGPException, IOException {
|
||||||
OpenPgpMessageInputStream in = get(armoredMessage, options);
|
OpenPgpMessageInputStream in = get(armoredMessage, options);
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
Streams.pipeAll(in, out);
|
Streams.pipeAll(in, out);
|
||||||
|
@ -89,6 +404,19 @@ public class OpenPgpMessageInputStreamTest {
|
||||||
return out.toString();
|
return out.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String processReadSequential(String armoredMessage, ConsumerOptions options) throws PGPException, IOException {
|
||||||
|
OpenPgpMessageInputStream in = get(armoredMessage, options);
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
int r;
|
||||||
|
while ((r = in.read()) != -1) {
|
||||||
|
out.write(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
in.close();
|
||||||
|
return out.toString();
|
||||||
|
}
|
||||||
|
|
||||||
private OpenPgpMessageInputStream get(String armored, ConsumerOptions options) throws IOException, PGPException {
|
private OpenPgpMessageInputStream get(String armored, ConsumerOptions options) 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);
|
||||||
|
|
|
@ -1,361 +0,0 @@
|
||||||
package org.pgpainless.decryption_verification;
|
|
||||||
|
|
||||||
import org.bouncycastle.bcpg.ArmoredInputStream;
|
|
||||||
import org.bouncycastle.bcpg.ArmoredOutputStream;
|
|
||||||
import org.bouncycastle.bcpg.CompressionAlgorithmTags;
|
|
||||||
import org.bouncycastle.openpgp.PGPCompressedDataGenerator;
|
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
|
||||||
import org.bouncycastle.openpgp.PGPLiteralData;
|
|
||||||
import org.bouncycastle.openpgp.PGPLiteralDataGenerator;
|
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
|
||||||
import org.bouncycastle.openpgp.PGPSignature;
|
|
||||||
import org.bouncycastle.util.io.Streams;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.pgpainless.PGPainless;
|
|
||||||
import org.pgpainless.algorithm.CompressionAlgorithm;
|
|
||||||
import org.pgpainless.encryption_signing.EncryptionOptions;
|
|
||||||
import org.pgpainless.encryption_signing.EncryptionResult;
|
|
||||||
import org.pgpainless.encryption_signing.EncryptionStream;
|
|
||||||
import org.pgpainless.encryption_signing.ProducerOptions;
|
|
||||||
import org.pgpainless.encryption_signing.SigningOptions;
|
|
||||||
import org.pgpainless.exception.MalformedOpenPgpMessageException;
|
|
||||||
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
|
||||||
import org.pgpainless.util.ArmoredInputStreamFactory;
|
|
||||||
import org.pgpainless.util.Passphrase;
|
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
|
||||||
|
|
||||||
public class PGPDecryptionStreamTest {
|
|
||||||
|
|
||||||
public static final String KEY = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" +
|
|
||||||
"Version: PGPainless\n" +
|
|
||||||
"Comment: DA05 848F 37D4 68E6 F982 C889 7A70 1FC6 904D 3F4C\n" +
|
|
||||||
"Comment: Alice <alice@pgpainless.org>\n" +
|
|
||||||
"\n" +
|
|
||||||
"lFgEYxzSCBYJKwYBBAHaRw8BAQdAeJU8m4GOJb1eQgv/ryilFHRfNLTYFMNqL6zj\n" +
|
|
||||||
"r0vF7dsAAP42rAtngpJ6dZxoZlJX0Je65zk1VMPeTrXaWfPS2HSKBRGptBxBbGlj\n" +
|
|
||||||
"ZSA8YWxpY2VAcGdwYWlubGVzcy5vcmc+iI8EExYKAEEFAmMc0ggJEHpwH8aQTT9M\n" +
|
|
||||||
"FiEE2gWEjzfUaOb5gsiJenAfxpBNP0wCngECmwEFFgIDAQAECwkIBwUVCgkICwKZ\n" +
|
|
||||||
"AQAApZEBALUXHtvswPZG28YO+16Men6/fpk+scvqpNMnD4ty3IkAAPwK6TuXjNnZ\n" +
|
|
||||||
"0XuWdnilvLMV23Ai1d5g6em+lwLK5M2SApxdBGMc0ggSCisGAQQBl1UBBQEBB0D8\n" +
|
|
||||||
"mNUVX8y2MXFaSeFYqOTPFnGT7dgNVdn6yc0UtkkHOgMBCAcAAP9y9OtP4SX9voPb\n" +
|
|
||||||
"ID2u9PkJKgo4hTB8NK5LouGppdRtEBGriHUEGBYKAB0FAmMc0ggCngECmwwFFgID\n" +
|
|
||||||
"AQAECwkIBwUVCgkICwAKCRB6cB/GkE0/TAywAQDpZRJS/joFH4+xcwheqWfI7ay/\n" +
|
|
||||||
"WfojUoGQMYGnUjsgYwEAkceRUsgkqI0SVgYvuglfaQpZ9k2ns1mZGVLkXvu/yQyc\n" +
|
|
||||||
"WARjHNIIFgkrBgEEAdpHDwEBB0BGN9BybSOrj8B6gim1SjbB/IiqAshlzMDunVkQ\n" +
|
|
||||||
"X23npQABAJqvjOOY7qhBuTusC5/Q5+25iLrhMn4TI+LXlJHMVNOaE0OI1QQYFgoA\n" +
|
|
||||||
"fQUCYxzSCAKeAQKbAgUWAgMBAAQLCQgHBRUKCQgLXyAEGRYKAAYFAmMc0ggACgkQ\n" +
|
|
||||||
"KALh4BJQXl6yTQD/dh0N5228Uwtu7XHy6dmpMRX62cac5tXQ9WaDzpy8STgBAMdn\n" +
|
|
||||||
"Mq948UOYEhdk/ZY2/hwux/4t+FHvqrXW8ziBe4cLAAoJEHpwH8aQTT9M1hQA/3Ms\n" +
|
|
||||||
"P3kzoed3VsWu1ZMr7dKEngbc6SoJ2XPayzN0QYJaAQCIY5NcT9mZF97HWV3Vgeum\n" +
|
|
||||||
"00sWMHXfkW3+nl5OpUZaDA==\n" +
|
|
||||||
"=THgv\n" +
|
|
||||||
"-----END PGP PRIVATE KEY BLOCK-----";
|
|
||||||
|
|
||||||
public static final String PLAINTEXT = "Hello, World!\n";
|
|
||||||
public static final String PASSPHRASE = "sw0rdf1sh";
|
|
||||||
|
|
||||||
public static final String LIT = "" +
|
|
||||||
"-----BEGIN PGP MESSAGE-----\n" +
|
|
||||||
"Version: PGPainless\n" +
|
|
||||||
"\n" +
|
|
||||||
"yxRiAAAAAABIZWxsbywgV29ybGQhCg==\n" +
|
|
||||||
"=WGju\n" +
|
|
||||||
"-----END PGP MESSAGE-----";
|
|
||||||
|
|
||||||
public static final String LIT_LIT = "" +
|
|
||||||
"-----BEGIN PGP MESSAGE-----\n" +
|
|
||||||
"Version: BCPG v1.71\n" +
|
|
||||||
"\n" +
|
|
||||||
"yxRiAAAAAABIZWxsbywgV29ybGQhCssUYgAAAAAASGVsbG8sIFdvcmxkIQo=\n" +
|
|
||||||
"=A91Q\n" +
|
|
||||||
"-----END PGP MESSAGE-----";
|
|
||||||
|
|
||||||
public static final String COMP_LIT = "" +
|
|
||||||
"-----BEGIN PGP MESSAGE-----\n" +
|
|
||||||
"Version: BCPG v1.71\n" +
|
|
||||||
"\n" +
|
|
||||||
"owE7LZLEAAIeqTk5+ToK4flFOSmKXAA=\n" +
|
|
||||||
"=ZYDg\n" +
|
|
||||||
"-----END PGP MESSAGE-----";
|
|
||||||
|
|
||||||
public static final String COMP = "" +
|
|
||||||
"-----BEGIN PGP MESSAGE-----\n" +
|
|
||||||
"Version: BCPG v1.71\n" +
|
|
||||||
"\n" +
|
|
||||||
"owEDAA==\n" +
|
|
||||||
"=MDzg\n" +
|
|
||||||
"-----END PGP MESSAGE-----";
|
|
||||||
|
|
||||||
public static final String COMP_COMP_LIT = "" +
|
|
||||||
"-----BEGIN PGP MESSAGE-----\n" +
|
|
||||||
"Version: BCPG v1.71\n" +
|
|
||||||
"\n" +
|
|
||||||
"owEBRwC4/6MDQlpoOTFBWSZTWVuW2KAAAAr3hGAQBABgBABAAIAWBJAAAAggADFM\n" +
|
|
||||||
"ABNBqBo00N6puqWR+TqInoXQ58XckU4UJBbltigA\n" +
|
|
||||||
"=K9Zl\n" +
|
|
||||||
"-----END PGP MESSAGE-----";
|
|
||||||
|
|
||||||
public static final String SIG_LIT = "" +
|
|
||||||
"-----BEGIN PGP MESSAGE-----\n" +
|
|
||||||
"Version: BCPG v1.71\n" +
|
|
||||||
"\n" +
|
|
||||||
"iHUEABYKACcFAmMc1i0JECgC4eASUF5eFiEEjN3RiJxCf/TyYOQjKALh4BJQXl4A\n" +
|
|
||||||
"AHkrAP98uPpqrgIix7epgL7MM1cjXXGSxqbDfXHwgptk1YGQlgD/fw89VGcXwFaI\n" +
|
|
||||||
"2k7kpXQYy/1BqnovM/jZ3X3mXhhTaAOjATstksQAAh6pOTn5Ogrh+UU5KYpcAA==\n" +
|
|
||||||
"=WKPn\n" +
|
|
||||||
"-----END PGP MESSAGE-----";
|
|
||||||
|
|
||||||
public static final String SENC_LIT = "" +
|
|
||||||
"-----BEGIN PGP MESSAGE-----\n" +
|
|
||||||
"Version: PGPainless\n" +
|
|
||||||
"\n" +
|
|
||||||
"jA0ECQMCuZ0qHNXWnGhg0j8Bdm1cxV65sYb7jDgb4rRMtdNpQ1dC4UpSYuk9YWS2\n" +
|
|
||||||
"DpNEijbX8b/P1UOK2kJczNDADMRegZuLEI+dNsBnJjk=\n" +
|
|
||||||
"=i4Y0\n" +
|
|
||||||
"-----END PGP MESSAGE-----";
|
|
||||||
|
|
||||||
public static final String PENC_COMP_LIT = "" +
|
|
||||||
"-----BEGIN PGP MESSAGE-----\n" +
|
|
||||||
"Version: PGPainless\n" +
|
|
||||||
"\n" +
|
|
||||||
"hF4Dyqa/GWUy6WsSAQdAQ62BwmUt8Iby0+jvrLhMgST79KR/as+dyl0nf1uki2sw\n" +
|
|
||||||
"Thg1Ojtf0hOyJgcpQ4nP2Q0wYFR0F1sCydaIlTGreYZHlGtybP7/Ml6KNZILTRWP\n" +
|
|
||||||
"0kYBkGBgK7oQWRIVyoF2POvEP6EX1X8nvQk7O3NysVdRVbnia7gE3AzRYuha4kxs\n" +
|
|
||||||
"pI6xJkntLMS3K6him1Y9FHINIASFSB+C\n" +
|
|
||||||
"=5p00\n" +
|
|
||||||
"-----END PGP MESSAGE-----";
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void genLIT() throws IOException {
|
|
||||||
ArmoredOutputStream armorOut = new ArmoredOutputStream(System.out);
|
|
||||||
PGPLiteralDataGenerator litGen = new PGPLiteralDataGenerator();
|
|
||||||
OutputStream litOut = litGen.open(armorOut, PGPLiteralDataGenerator.BINARY, "", PGPLiteralData.NOW, new byte[1 << 9]);
|
|
||||||
litOut.write(PLAINTEXT.getBytes(StandardCharsets.UTF_8));
|
|
||||||
litOut.close();
|
|
||||||
armorOut.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void processLIT() throws IOException, PGPException {
|
|
||||||
ByteArrayInputStream bytesIn = new ByteArrayInputStream(LIT.getBytes(StandardCharsets.UTF_8));
|
|
||||||
ArmoredInputStream armorIn = ArmoredInputStreamFactory.get(bytesIn);
|
|
||||||
MessageDecryptionStream decIn = new MessageDecryptionStream(armorIn, ConsumerOptions.get());
|
|
||||||
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
Streams.pipeAll(decIn, out);
|
|
||||||
assertEquals(PLAINTEXT, out.toString());
|
|
||||||
armorIn.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getLIT_LIT() throws IOException {
|
|
||||||
ArmoredOutputStream armorOut = new ArmoredOutputStream(System.out);
|
|
||||||
PGPLiteralDataGenerator litGen = new PGPLiteralDataGenerator();
|
|
||||||
OutputStream litOut = litGen.open(armorOut, PGPLiteralDataGenerator.BINARY, "", PGPLiteralData.NOW, new byte[1 << 9]);
|
|
||||||
litOut.write(PLAINTEXT.getBytes(StandardCharsets.UTF_8));
|
|
||||||
litOut.close();
|
|
||||||
|
|
||||||
litOut = litGen.open(armorOut, PGPLiteralDataGenerator.BINARY, "", PGPLiteralData.NOW, new byte[1 << 9]);
|
|
||||||
litOut.write(PLAINTEXT.getBytes(StandardCharsets.UTF_8));
|
|
||||||
litOut.close();
|
|
||||||
|
|
||||||
armorOut.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void processLIT_LIT() throws IOException, PGPException {
|
|
||||||
ByteArrayInputStream bytesIn = new ByteArrayInputStream(LIT_LIT.getBytes(StandardCharsets.UTF_8));
|
|
||||||
ArmoredInputStream armorIn = ArmoredInputStreamFactory.get(bytesIn);
|
|
||||||
MessageDecryptionStream decIn = new MessageDecryptionStream(armorIn, ConsumerOptions.get());
|
|
||||||
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
assertThrows(MalformedOpenPgpMessageException.class, () -> Streams.pipeAll(decIn, out));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void genCOMP_LIT() throws IOException {
|
|
||||||
ArmoredOutputStream armorOut = new ArmoredOutputStream(System.out);
|
|
||||||
PGPCompressedDataGenerator compGen = new PGPCompressedDataGenerator(CompressionAlgorithmTags.ZIP);
|
|
||||||
OutputStream compOut = compGen.open(armorOut);
|
|
||||||
PGPLiteralDataGenerator litGen = new PGPLiteralDataGenerator();
|
|
||||||
OutputStream litOut = litGen.open(compOut, PGPLiteralDataGenerator.BINARY, "", PGPLiteralData.NOW, new byte[1 << 9]);
|
|
||||||
litOut.write(PLAINTEXT.getBytes(StandardCharsets.UTF_8));
|
|
||||||
litOut.close();
|
|
||||||
compOut.close();
|
|
||||||
armorOut.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void processCOMP_LIT() throws IOException, PGPException {
|
|
||||||
ByteArrayInputStream bytesIn = new ByteArrayInputStream(COMP_LIT.getBytes(StandardCharsets.UTF_8));
|
|
||||||
ArmoredInputStream armorIn = ArmoredInputStreamFactory.get(bytesIn);
|
|
||||||
MessageDecryptionStream decIn = new MessageDecryptionStream(armorIn, ConsumerOptions.get());
|
|
||||||
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
Streams.pipeAll(decIn, out);
|
|
||||||
decIn.close();
|
|
||||||
armorIn.close();
|
|
||||||
|
|
||||||
assertEquals(PLAINTEXT, out.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void genCOMP() throws IOException {
|
|
||||||
ArmoredOutputStream armorOut = new ArmoredOutputStream(System.out);
|
|
||||||
PGPCompressedDataGenerator compGen = new PGPCompressedDataGenerator(CompressionAlgorithmTags.ZIP);
|
|
||||||
OutputStream compOut = compGen.open(armorOut);
|
|
||||||
compOut.close();
|
|
||||||
armorOut.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void processCOMP() throws IOException {
|
|
||||||
ByteArrayInputStream bytesIn = new ByteArrayInputStream(COMP.getBytes(StandardCharsets.UTF_8));
|
|
||||||
ArmoredInputStream armorIn = ArmoredInputStreamFactory.get(bytesIn);
|
|
||||||
assertThrows(MalformedOpenPgpMessageException.class, () -> {
|
|
||||||
MessageDecryptionStream decIn = new MessageDecryptionStream(armorIn, ConsumerOptions.get());
|
|
||||||
Streams.drain(decIn);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void genCOMP_COMP_LIT() throws IOException {
|
|
||||||
ArmoredOutputStream armorOut = new ArmoredOutputStream(System.out);
|
|
||||||
|
|
||||||
PGPCompressedDataGenerator compGen1 = new PGPCompressedDataGenerator(CompressionAlgorithmTags.ZIP);
|
|
||||||
OutputStream compOut1 = compGen1.open(armorOut);
|
|
||||||
|
|
||||||
PGPCompressedDataGenerator compGen2 = new PGPCompressedDataGenerator(CompressionAlgorithmTags.BZIP2);
|
|
||||||
OutputStream compOut2 = compGen2.open(compOut1);
|
|
||||||
|
|
||||||
PGPLiteralDataGenerator litGen = new PGPLiteralDataGenerator();
|
|
||||||
OutputStream litOut = litGen.open(compOut2, PGPLiteralDataGenerator.BINARY, "", PGPLiteralDataGenerator.NOW, new byte[1 << 9]);
|
|
||||||
|
|
||||||
litOut.write(PLAINTEXT.getBytes(StandardCharsets.UTF_8));
|
|
||||||
litOut.close();
|
|
||||||
compOut2.close();
|
|
||||||
compOut1.close();
|
|
||||||
armorOut.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void processCOMP_COMP_LIT() throws PGPException, IOException {
|
|
||||||
ByteArrayInputStream bytesIn = new ByteArrayInputStream(COMP_COMP_LIT.getBytes(StandardCharsets.UTF_8));
|
|
||||||
ArmoredInputStream armorIn = ArmoredInputStreamFactory.get(bytesIn);
|
|
||||||
MessageDecryptionStream decIn = new MessageDecryptionStream(armorIn, ConsumerOptions.get());
|
|
||||||
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
Streams.pipeAll(decIn, out);
|
|
||||||
decIn.close();
|
|
||||||
|
|
||||||
assertEquals(PLAINTEXT, out.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void genKey() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException {
|
|
||||||
System.out.println(PGPainless.asciiArmor(
|
|
||||||
PGPainless.generateKeyRing().modernKeyRing("Alice <alice@pgpainless.org>")
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void genSIG_LIT() throws PGPException, IOException {
|
|
||||||
PGPSecretKeyRing secretKeys = PGPainless.readKeyRing().secretKeyRing(KEY);
|
|
||||||
ByteArrayOutputStream msgOut = new ByteArrayOutputStream();
|
|
||||||
EncryptionStream signer = PGPainless.encryptAndOrSign()
|
|
||||||
.onOutputStream(msgOut)
|
|
||||||
.withOptions(
|
|
||||||
ProducerOptions.sign(
|
|
||||||
SigningOptions.get()
|
|
||||||
.addDetachedSignature(SecretKeyRingProtector.unprotectedKeys(), secretKeys)
|
|
||||||
).setAsciiArmor(false)
|
|
||||||
);
|
|
||||||
|
|
||||||
Streams.pipeAll(new ByteArrayInputStream(PLAINTEXT.getBytes(StandardCharsets.UTF_8)), signer);
|
|
||||||
signer.close();
|
|
||||||
EncryptionResult result = signer.getResult();
|
|
||||||
PGPSignature detachedSignature = result.getDetachedSignatures().get(result.getDetachedSignatures().keySet().iterator().next()).iterator().next();
|
|
||||||
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
ArmoredOutputStream armorOut = new ArmoredOutputStream(out);
|
|
||||||
armorOut.flush();
|
|
||||||
detachedSignature.encode(armorOut);
|
|
||||||
armorOut.write(msgOut.toByteArray());
|
|
||||||
armorOut.close();
|
|
||||||
|
|
||||||
String armored = out.toString();
|
|
||||||
System.out.println(armored
|
|
||||||
.replace("-----BEGIN PGP SIGNATURE-----\n", "-----BEGIN PGP MESSAGE-----\n")
|
|
||||||
.replace("-----END PGP SIGNATURE-----", "-----END PGP MESSAGE-----"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void processSIG_LIT() throws IOException, PGPException {
|
|
||||||
ByteArrayInputStream bytesIn = new ByteArrayInputStream(SIG_LIT.getBytes(StandardCharsets.UTF_8));
|
|
||||||
ArmoredInputStream armorIn = ArmoredInputStreamFactory.get(bytesIn);
|
|
||||||
MessageDecryptionStream decIn = new MessageDecryptionStream(armorIn, ConsumerOptions.get());
|
|
||||||
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
Streams.pipeAll(decIn, out);
|
|
||||||
decIn.close();
|
|
||||||
|
|
||||||
System.out.println(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void genSENC_LIT() throws PGPException, IOException {
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
EncryptionStream enc = PGPainless.encryptAndOrSign()
|
|
||||||
.onOutputStream(out)
|
|
||||||
.withOptions(ProducerOptions.encrypt(EncryptionOptions.get()
|
|
||||||
.addPassphrase(Passphrase.fromPassword(PASSPHRASE)))
|
|
||||||
.overrideCompressionAlgorithm(CompressionAlgorithm.UNCOMPRESSED));
|
|
||||||
enc.write(PLAINTEXT.getBytes(StandardCharsets.UTF_8));
|
|
||||||
enc.close();
|
|
||||||
|
|
||||||
System.out.println(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void processSENC_LIT() throws IOException, PGPException {
|
|
||||||
ByteArrayInputStream bytesIn = new ByteArrayInputStream(SENC_LIT.getBytes(StandardCharsets.UTF_8));
|
|
||||||
ArmoredInputStream armorIn = ArmoredInputStreamFactory.get(bytesIn);
|
|
||||||
MessageDecryptionStream decIn = new MessageDecryptionStream(armorIn, ConsumerOptions.get()
|
|
||||||
.addDecryptionPassphrase(Passphrase.fromPassword(PASSPHRASE)));
|
|
||||||
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
Streams.pipeAll(decIn, out);
|
|
||||||
decIn.close();
|
|
||||||
|
|
||||||
System.out.println(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void genPENC_COMP_LIT() throws IOException, PGPException {
|
|
||||||
PGPSecretKeyRing secretKeys = PGPainless.readKeyRing().secretKeyRing(KEY);
|
|
||||||
PGPPublicKeyRing cert = PGPainless.extractCertificate(secretKeys);
|
|
||||||
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
EncryptionStream enc = PGPainless.encryptAndOrSign()
|
|
||||||
.onOutputStream(out)
|
|
||||||
.withOptions(ProducerOptions.encrypt(EncryptionOptions.get()
|
|
||||||
.addRecipient(cert))
|
|
||||||
.overrideCompressionAlgorithm(CompressionAlgorithm.ZLIB));
|
|
||||||
|
|
||||||
Streams.pipeAll(new ByteArrayInputStream(PLAINTEXT.getBytes(StandardCharsets.UTF_8)), enc);
|
|
||||||
enc.close();
|
|
||||||
|
|
||||||
System.out.println(out);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,205 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package org.pgpainless.decryption_verification.automaton;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.pgpainless.exception.MalformedOpenPgpMessageException;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
||||||
|
|
||||||
public class NestingPDATest {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* MSG is valid.
|
|
||||||
*
|
|
||||||
* @throws MalformedOpenPgpMessageException fail
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testSimpleLiteralMessageIsValid() throws MalformedOpenPgpMessageException {
|
|
||||||
NestingPDA automaton = new NestingPDA();
|
|
||||||
automaton.next(InputAlphabet.LiteralData);
|
|
||||||
automaton.next(InputAlphabet.EndOfSequence);
|
|
||||||
|
|
||||||
assertTrue(automaton.isValid());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* OPS MSG SIG is valid.
|
|
||||||
*
|
|
||||||
* @throws MalformedOpenPgpMessageException fail
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testSimpleOpsSignedMesssageIsValid() throws MalformedOpenPgpMessageException {
|
|
||||||
NestingPDA automaton = new NestingPDA();
|
|
||||||
automaton.next(InputAlphabet.OnePassSignatures);
|
|
||||||
automaton.next(InputAlphabet.LiteralData);
|
|
||||||
automaton.next(InputAlphabet.Signatures);
|
|
||||||
automaton.next(InputAlphabet.EndOfSequence);
|
|
||||||
|
|
||||||
assertTrue(automaton.isValid());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SIG MSG is valid.
|
|
||||||
*
|
|
||||||
* @throws MalformedOpenPgpMessageException fail
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testSimplePrependSignedMessageIsValid() throws MalformedOpenPgpMessageException {
|
|
||||||
NestingPDA automaton = new NestingPDA();
|
|
||||||
automaton.next(InputAlphabet.Signatures);
|
|
||||||
automaton.next(InputAlphabet.LiteralData);
|
|
||||||
automaton.next(InputAlphabet.EndOfSequence);
|
|
||||||
|
|
||||||
assertTrue(automaton.isValid());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* OPS COMP(MSG) SIG is valid.
|
|
||||||
*
|
|
||||||
* @throws MalformedOpenPgpMessageException fail
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testOPSSignedCompressedMessageIsValid() throws MalformedOpenPgpMessageException {
|
|
||||||
NestingPDA automaton = new NestingPDA();
|
|
||||||
automaton.next(InputAlphabet.OnePassSignatures);
|
|
||||||
automaton.next(InputAlphabet.CompressedData);
|
|
||||||
automaton.next(InputAlphabet.LiteralData);
|
|
||||||
automaton.next(InputAlphabet.EndOfSequence);
|
|
||||||
automaton.next(InputAlphabet.Signatures);
|
|
||||||
automaton.next(InputAlphabet.EndOfSequence);
|
|
||||||
|
|
||||||
assertTrue(automaton.isValid());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* OPS ENC(COMP(COMP(MSG))) SIG is valid.
|
|
||||||
*
|
|
||||||
* @throws MalformedOpenPgpMessageException fail
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testOpsSignedEncryptedCompressedCompressedMessageIsValid() throws MalformedOpenPgpMessageException {
|
|
||||||
NestingPDA automaton = new NestingPDA();
|
|
||||||
automaton.next(InputAlphabet.OnePassSignatures);
|
|
||||||
automaton.next(InputAlphabet.EncryptedData);
|
|
||||||
automaton.next(InputAlphabet.CompressedData);
|
|
||||||
automaton.next(InputAlphabet.CompressedData);
|
|
||||||
|
|
||||||
automaton.next(InputAlphabet.LiteralData);
|
|
||||||
|
|
||||||
automaton.next(InputAlphabet.EndOfSequence);
|
|
||||||
automaton.next(InputAlphabet.EndOfSequence);
|
|
||||||
automaton.next(InputAlphabet.EndOfSequence);
|
|
||||||
automaton.next(InputAlphabet.Signatures);
|
|
||||||
automaton.next(InputAlphabet.EndOfSequence);
|
|
||||||
|
|
||||||
assertTrue(automaton.isValid());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* MSG SIG is invalid.
|
|
||||||
*
|
|
||||||
* @throws MalformedOpenPgpMessageException fail
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testLiteralPlusSigsFails() throws MalformedOpenPgpMessageException {
|
|
||||||
NestingPDA automaton = new NestingPDA();
|
|
||||||
automaton.next(InputAlphabet.LiteralData);
|
|
||||||
assertThrows(MalformedOpenPgpMessageException.class,
|
|
||||||
() -> automaton.next(InputAlphabet.Signatures));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* MSG MSG is invalid.
|
|
||||||
*
|
|
||||||
* @throws MalformedOpenPgpMessageException fail
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testTwoLiteralDataPacketsFails() throws MalformedOpenPgpMessageException {
|
|
||||||
NestingPDA automaton = new NestingPDA();
|
|
||||||
automaton.next(InputAlphabet.LiteralData);
|
|
||||||
assertThrows(MalformedOpenPgpMessageException.class,
|
|
||||||
() -> automaton.next(InputAlphabet.LiteralData));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* OPS COMP(MSG MSG) SIG is invalid (two literal packets are illegal).
|
|
||||||
*
|
|
||||||
* @throws MalformedOpenPgpMessageException fail
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testOPSSignedMessageWithTwoLiteralDataPacketsFails() throws MalformedOpenPgpMessageException {
|
|
||||||
NestingPDA automaton = new NestingPDA();
|
|
||||||
automaton.next(InputAlphabet.OnePassSignatures);
|
|
||||||
automaton.next(InputAlphabet.CompressedData);
|
|
||||||
automaton.next(InputAlphabet.LiteralData);
|
|
||||||
assertThrows(MalformedOpenPgpMessageException.class,
|
|
||||||
() -> automaton.next(InputAlphabet.LiteralData));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* OPS COMP(MSG) MSG SIG is invalid.
|
|
||||||
*
|
|
||||||
* @throws MalformedOpenPgpMessageException fail
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testOPSSignedMessageWithTwoLiteralDataPacketsFails2() throws MalformedOpenPgpMessageException {
|
|
||||||
NestingPDA automaton = new NestingPDA();
|
|
||||||
automaton.next(InputAlphabet.OnePassSignatures);
|
|
||||||
automaton.next(InputAlphabet.CompressedData);
|
|
||||||
automaton.next(InputAlphabet.LiteralData);
|
|
||||||
automaton.next(InputAlphabet.EndOfSequence);
|
|
||||||
assertThrows(MalformedOpenPgpMessageException.class,
|
|
||||||
() -> automaton.next(InputAlphabet.LiteralData));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* OPS COMP(MSG SIG) is invalid (MSG SIG does not form valid nested message).
|
|
||||||
*
|
|
||||||
* @throws MalformedOpenPgpMessageException fail
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testCorrespondingSignaturesOfOpsSignedMessageAreLayerFurtherDownFails() throws MalformedOpenPgpMessageException {
|
|
||||||
NestingPDA automaton = new NestingPDA();
|
|
||||||
automaton.next(InputAlphabet.OnePassSignatures);
|
|
||||||
automaton.next(InputAlphabet.CompressedData);
|
|
||||||
automaton.next(InputAlphabet.LiteralData);
|
|
||||||
assertThrows(MalformedOpenPgpMessageException.class,
|
|
||||||
() -> automaton.next(InputAlphabet.Signatures));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Empty COMP is invalid.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testEmptyCompressedDataIsInvalid() throws MalformedOpenPgpMessageException {
|
|
||||||
NestingPDA automaton = new NestingPDA();
|
|
||||||
automaton.next(InputAlphabet.CompressedData);
|
|
||||||
assertThrows(MalformedOpenPgpMessageException.class,
|
|
||||||
() -> automaton.next(InputAlphabet.EndOfSequence));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testOPSSignedEncryptedCompressedOPSSignedMessageIsValid() throws MalformedOpenPgpMessageException {
|
|
||||||
NestingPDA automaton = new NestingPDA();
|
|
||||||
automaton.next(InputAlphabet.OnePassSignatures);
|
|
||||||
|
|
||||||
automaton.next(InputAlphabet.EncryptedData);
|
|
||||||
automaton.next(InputAlphabet.OnePassSignatures);
|
|
||||||
|
|
||||||
automaton.next(InputAlphabet.CompressedData);
|
|
||||||
automaton.next(InputAlphabet.LiteralData);
|
|
||||||
automaton.next(InputAlphabet.EndOfSequence);
|
|
||||||
|
|
||||||
automaton.next(InputAlphabet.Signatures);
|
|
||||||
automaton.next(InputAlphabet.EndOfSequence);
|
|
||||||
|
|
||||||
automaton.next(InputAlphabet.Signatures);
|
|
||||||
automaton.next(InputAlphabet.EndOfSequence);
|
|
||||||
|
|
||||||
assertTrue(automaton.isValid());
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue