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

Separate out syntax logic

This commit is contained in:
Paul Schaub 2022-10-27 11:56:10 +02:00
parent c4e937c0f9
commit a9f67c508f
6 changed files with 189 additions and 156 deletions

View file

@ -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);
}
}

View file

@ -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();
} }

View file

@ -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
}

View file

@ -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;
}

View file

@ -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);
}
}

View file

@ -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.");
} }