mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-11-15 17:02:06 +01:00
Separate out syntax logic
This commit is contained in:
parent
c4e937c0f9
commit
a9f67c508f
6 changed files with 189 additions and 156 deletions
|
@ -0,0 +1,119 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package org.pgpainless.decryption_verification.syntax_check;
|
||||||
|
|
||||||
|
import org.pgpainless.exception.MalformedOpenPgpMessageException;
|
||||||
|
|
||||||
|
public class OpenPgpMessageSyntax implements Syntax {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Transition transition(State from, InputAlphabet input, StackAlphabet stackItem)
|
||||||
|
throws MalformedOpenPgpMessageException {
|
||||||
|
switch (from) {
|
||||||
|
case OpenPgpMessage:
|
||||||
|
return fromOpenPgpMessage(input, stackItem);
|
||||||
|
case LiteralMessage:
|
||||||
|
return fromLiteralMessage(input, stackItem);
|
||||||
|
case CompressedMessage:
|
||||||
|
return fromCompressedMessage(input, stackItem);
|
||||||
|
case EncryptedMessage:
|
||||||
|
return fromEncryptedMessage(input, stackItem);
|
||||||
|
case Valid:
|
||||||
|
return fromValid(input, stackItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new MalformedOpenPgpMessageException(from, input, stackItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
Transition fromOpenPgpMessage(InputAlphabet input, StackAlphabet stackItem)
|
||||||
|
throws MalformedOpenPgpMessageException {
|
||||||
|
if (stackItem != StackAlphabet.msg) {
|
||||||
|
throw new MalformedOpenPgpMessageException(State.OpenPgpMessage, input, stackItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (input) {
|
||||||
|
case LiteralData:
|
||||||
|
return new Transition(State.LiteralMessage);
|
||||||
|
|
||||||
|
case Signature:
|
||||||
|
return new Transition(State.OpenPgpMessage, StackAlphabet.msg);
|
||||||
|
|
||||||
|
case OnePassSignature:
|
||||||
|
return new Transition(State.OpenPgpMessage, StackAlphabet.ops, StackAlphabet.msg);
|
||||||
|
|
||||||
|
case CompressedData:
|
||||||
|
return new Transition(State.CompressedMessage);
|
||||||
|
|
||||||
|
case EncryptedData:
|
||||||
|
return new Transition(State.EncryptedMessage);
|
||||||
|
|
||||||
|
case EndOfSequence:
|
||||||
|
default:
|
||||||
|
throw new MalformedOpenPgpMessageException(State.OpenPgpMessage, input, stackItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Transition fromLiteralMessage(InputAlphabet input, StackAlphabet stackItem)
|
||||||
|
throws MalformedOpenPgpMessageException {
|
||||||
|
switch (input) {
|
||||||
|
case Signature:
|
||||||
|
if (stackItem == StackAlphabet.ops) {
|
||||||
|
return new Transition(State.LiteralMessage);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EndOfSequence:
|
||||||
|
if (stackItem == StackAlphabet.terminus) {
|
||||||
|
return new Transition(State.Valid);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new MalformedOpenPgpMessageException(State.LiteralMessage, input, stackItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
Transition fromCompressedMessage(InputAlphabet input, StackAlphabet stackItem)
|
||||||
|
throws MalformedOpenPgpMessageException {
|
||||||
|
switch (input) {
|
||||||
|
case Signature:
|
||||||
|
if (stackItem == StackAlphabet.ops) {
|
||||||
|
return new Transition(State.CompressedMessage);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EndOfSequence:
|
||||||
|
if (stackItem == StackAlphabet.terminus) {
|
||||||
|
return new Transition(State.Valid);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new MalformedOpenPgpMessageException(State.CompressedMessage, input, stackItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
Transition fromEncryptedMessage(InputAlphabet input, StackAlphabet stackItem)
|
||||||
|
throws MalformedOpenPgpMessageException {
|
||||||
|
switch (input) {
|
||||||
|
case Signature:
|
||||||
|
if (stackItem == StackAlphabet.ops) {
|
||||||
|
return new Transition(State.EncryptedMessage);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EndOfSequence:
|
||||||
|
if (stackItem == StackAlphabet.terminus) {
|
||||||
|
return new Transition(State.Valid);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new MalformedOpenPgpMessageException(State.EncryptedMessage, input, stackItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
Transition fromValid(InputAlphabet input, StackAlphabet stackItem)
|
||||||
|
throws MalformedOpenPgpMessageException {
|
||||||
|
throw new MalformedOpenPgpMessageException(State.Valid, input, stackItem);
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,177 +14,31 @@ import java.util.List;
|
||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
|
|
||||||
import static org.pgpainless.decryption_verification.syntax_check.StackAlphabet.msg;
|
import static org.pgpainless.decryption_verification.syntax_check.StackAlphabet.msg;
|
||||||
import static org.pgpainless.decryption_verification.syntax_check.StackAlphabet.ops;
|
|
||||||
import static org.pgpainless.decryption_verification.syntax_check.StackAlphabet.terminus;
|
import static org.pgpainless.decryption_verification.syntax_check.StackAlphabet.terminus;
|
||||||
|
|
||||||
public class PDA {
|
public class PDA {
|
||||||
|
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(PDA.class);
|
private static final Logger LOGGER = LoggerFactory.getLogger(PDA.class);
|
||||||
|
|
||||||
/**
|
|
||||||
* Set of states of the automaton.
|
|
||||||
* Each state defines its valid transitions in their {@link State#transition(InputAlphabet, PDA)} method.
|
|
||||||
*/
|
|
||||||
public enum State {
|
|
||||||
|
|
||||||
OpenPgpMessage {
|
|
||||||
@Override
|
|
||||||
State transition(InputAlphabet input, PDA automaton) throws MalformedOpenPgpMessageException {
|
|
||||||
StackAlphabet stackItem = automaton.popStack();
|
|
||||||
if (stackItem != msg) {
|
|
||||||
throw new MalformedOpenPgpMessageException(this, input, stackItem);
|
|
||||||
}
|
|
||||||
switch (input) {
|
|
||||||
|
|
||||||
case LiteralData:
|
|
||||||
return LiteralMessage;
|
|
||||||
|
|
||||||
case Signature:
|
|
||||||
automaton.pushStack(msg);
|
|
||||||
return OpenPgpMessage;
|
|
||||||
|
|
||||||
case OnePassSignature:
|
|
||||||
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, PDA automaton) throws MalformedOpenPgpMessageException {
|
|
||||||
StackAlphabet stackItem = automaton.popStack();
|
|
||||||
switch (input) {
|
|
||||||
|
|
||||||
case Signature:
|
|
||||||
if (stackItem == ops) {
|
|
||||||
return LiteralMessage;
|
|
||||||
} 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 OnePassSignature:
|
|
||||||
case CompressedData:
|
|
||||||
case EncryptedData:
|
|
||||||
default:
|
|
||||||
throw new MalformedOpenPgpMessageException(this, input, stackItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
CompressedMessage {
|
|
||||||
@Override
|
|
||||||
State transition(InputAlphabet input, PDA automaton) throws MalformedOpenPgpMessageException {
|
|
||||||
StackAlphabet stackItem = automaton.popStack();
|
|
||||||
switch (input) {
|
|
||||||
case Signature:
|
|
||||||
if (stackItem == ops) {
|
|
||||||
return CompressedMessage;
|
|
||||||
} 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 OnePassSignature:
|
|
||||||
case CompressedData:
|
|
||||||
case EncryptedData:
|
|
||||||
default:
|
|
||||||
throw new MalformedOpenPgpMessageException(this, input, stackItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
EncryptedMessage {
|
|
||||||
@Override
|
|
||||||
State transition(InputAlphabet input, PDA automaton) throws MalformedOpenPgpMessageException {
|
|
||||||
StackAlphabet stackItem = automaton.popStack();
|
|
||||||
switch (input) {
|
|
||||||
case Signature:
|
|
||||||
if (stackItem == ops) {
|
|
||||||
return EncryptedMessage;
|
|
||||||
} 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 OnePassSignature:
|
|
||||||
case CompressedData:
|
|
||||||
case EncryptedData:
|
|
||||||
default:
|
|
||||||
throw new MalformedOpenPgpMessageException(this, input, stackItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
Valid {
|
|
||||||
@Override
|
|
||||||
State transition(InputAlphabet input, PDA 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, PDA automaton) throws MalformedOpenPgpMessageException;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Stack<StackAlphabet> stack = new Stack<>();
|
private final Stack<StackAlphabet> stack = new Stack<>();
|
||||||
private final List<InputAlphabet> inputs = new ArrayList<>(); // keep track of inputs for debugging / error reporting
|
private final List<InputAlphabet> inputs = new ArrayList<>(); // keep track of inputs for debugging / error reporting
|
||||||
private State state;
|
private State state;
|
||||||
|
private Syntax syntax = new OpenPgpMessageSyntax();
|
||||||
|
|
||||||
public PDA() {
|
public PDA() {
|
||||||
state = State.OpenPgpMessage;
|
state = State.OpenPgpMessage;
|
||||||
stack.push(terminus);
|
pushStack(terminus);
|
||||||
stack.push(msg);
|
pushStack(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void next(InputAlphabet input) throws MalformedOpenPgpMessageException {
|
public void next(InputAlphabet input) throws MalformedOpenPgpMessageException {
|
||||||
try {
|
try {
|
||||||
state = state.transition(input, this);
|
Transition transition = syntax.transition(state, input, popStack());
|
||||||
inputs.add(input);
|
inputs.add(input);
|
||||||
|
state = transition.getNewState();
|
||||||
|
for (StackAlphabet item : transition.getPushedItems()) {
|
||||||
|
pushStack(item);
|
||||||
|
}
|
||||||
} catch (MalformedOpenPgpMessageException e) {
|
} catch (MalformedOpenPgpMessageException e) {
|
||||||
MalformedOpenPgpMessageException wrapped = new MalformedOpenPgpMessageException("Malformed message: After reading stream " + Arrays.toString(inputs.toArray()) +
|
MalformedOpenPgpMessageException wrapped = new MalformedOpenPgpMessageException("Malformed message: After reading stream " + Arrays.toString(inputs.toArray()) +
|
||||||
", token '" + input + "' is unexpected and illegal.", e);
|
", token '" + input + "' is unexpected and illegal.", e);
|
||||||
|
@ -230,6 +84,9 @@ public class PDA {
|
||||||
* @return stack item
|
* @return stack item
|
||||||
*/
|
*/
|
||||||
private StackAlphabet popStack() {
|
private StackAlphabet popStack() {
|
||||||
|
if (stack.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return stack.pop();
|
return stack.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package org.pgpainless.decryption_verification.syntax_check;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set of states of the automaton.
|
||||||
|
*/
|
||||||
|
public enum State {
|
||||||
|
OpenPgpMessage,
|
||||||
|
LiteralMessage,
|
||||||
|
CompressedMessage,
|
||||||
|
EncryptedMessage,
|
||||||
|
Valid
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package org.pgpainless.decryption_verification.syntax_check;
|
||||||
|
|
||||||
|
import org.pgpainless.exception.MalformedOpenPgpMessageException;
|
||||||
|
|
||||||
|
public interface Syntax {
|
||||||
|
|
||||||
|
Transition transition(State from, InputAlphabet inputAlphabet, StackAlphabet stackItem)
|
||||||
|
throws MalformedOpenPgpMessageException;
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package org.pgpainless.decryption_verification.syntax_check;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class Transition {
|
||||||
|
|
||||||
|
private final List<StackAlphabet> pushedItems = new ArrayList<>();
|
||||||
|
private final State newState;
|
||||||
|
|
||||||
|
public Transition(State newState, StackAlphabet... pushedItems) {
|
||||||
|
this.newState = newState;
|
||||||
|
this.pushedItems.addAll(Arrays.asList(pushedItems));
|
||||||
|
}
|
||||||
|
|
||||||
|
public State getNewState() {
|
||||||
|
return newState;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<StackAlphabet> getPushedItems() {
|
||||||
|
return new ArrayList<>(pushedItems);
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,8 +5,8 @@
|
||||||
package org.pgpainless.exception;
|
package org.pgpainless.exception;
|
||||||
|
|
||||||
import org.pgpainless.decryption_verification.syntax_check.InputAlphabet;
|
import org.pgpainless.decryption_verification.syntax_check.InputAlphabet;
|
||||||
import org.pgpainless.decryption_verification.syntax_check.PDA;
|
|
||||||
import org.pgpainless.decryption_verification.syntax_check.StackAlphabet;
|
import org.pgpainless.decryption_verification.syntax_check.StackAlphabet;
|
||||||
|
import org.pgpainless.decryption_verification.syntax_check.State;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception that gets thrown if the OpenPGP message is malformed.
|
* Exception that gets thrown if the OpenPGP message is malformed.
|
||||||
|
@ -20,7 +20,7 @@ public class MalformedOpenPgpMessageException extends RuntimeException {
|
||||||
super(message);
|
super(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public MalformedOpenPgpMessageException(PDA.State state, InputAlphabet input, StackAlphabet stackItem) {
|
public MalformedOpenPgpMessageException(State state, InputAlphabet input, StackAlphabet stackItem) {
|
||||||
this("There is no legal transition from state '" + state + "' for input '" + input + "' when '" + stackItem + "' is on top of the stack.");
|
this("There is no legal transition from state '" + state + "' for input '" + input + "' when '" + stackItem + "' is on top of the stack.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue