mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-12-22 19:08:00 +01:00
Separate out syntax logic
This commit is contained in:
parent
798e68e87f
commit
b3d61b0494
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 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;
|
||||
|
||||
public class PDA {
|
||||
|
||||
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 List<InputAlphabet> inputs = new ArrayList<>(); // keep track of inputs for debugging / error reporting
|
||||
private State state;
|
||||
private Syntax syntax = new OpenPgpMessageSyntax();
|
||||
|
||||
public PDA() {
|
||||
state = State.OpenPgpMessage;
|
||||
stack.push(terminus);
|
||||
stack.push(msg);
|
||||
pushStack(terminus);
|
||||
pushStack(msg);
|
||||
}
|
||||
|
||||
public void next(InputAlphabet input) throws MalformedOpenPgpMessageException {
|
||||
try {
|
||||
state = state.transition(input, this);
|
||||
Transition transition = syntax.transition(state, input, popStack());
|
||||
inputs.add(input);
|
||||
state = transition.getNewState();
|
||||
for (StackAlphabet item : transition.getPushedItems()) {
|
||||
pushStack(item);
|
||||
}
|
||||
} catch (MalformedOpenPgpMessageException e) {
|
||||
MalformedOpenPgpMessageException wrapped = new MalformedOpenPgpMessageException("Malformed message: After reading stream " + Arrays.toString(inputs.toArray()) +
|
||||
", token '" + input + "' is unexpected and illegal.", e);
|
||||
|
@ -230,6 +84,9 @@ public class PDA {
|
|||
* @return stack item
|
||||
*/
|
||||
private StackAlphabet popStack() {
|
||||
if (stack.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
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;
|
||||
|
||||
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.State;
|
||||
|
||||
/**
|
||||
* Exception that gets thrown if the OpenPGP message is malformed.
|
||||
|
@ -20,7 +20,7 @@ public class MalformedOpenPgpMessageException extends RuntimeException {
|
|||
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.");
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue