// SPDX-FileCopyrightText: 2023 Paul Schaub // // SPDX-License-Identifier: Apache-2.0 package org.pgpainless.decryption_verification.syntax_check import org.pgpainless.exception.MalformedOpenPgpMessageException /** * This class describes the syntax for OpenPGP messages as specified by rfc4880. * * See [rfc4880 - ยง11.3. OpenPGP Messages](https://www.rfc-editor.org/rfc/rfc4880#section-11.3) See * [Blog post about theoretic background and translation of grammar to PDA syntax](https://blog.jabberhead.tk/2022/09/14/using-pushdown-automata-to-verify-packet-sequences/) * See * [Blog post about practically implementing the PDA for packet syntax validation](https://blog.jabberhead.tk/2022/10/26/implementing-packet-sequence-validation-using-pushdown-automata/) */ class OpenPgpMessageSyntax : Syntax { override fun transition(from: State, input: InputSymbol, stackItem: StackSymbol?): Transition { return when (from) { State.OPENPGP_MESSAGE -> fromOpenPgpMessage(input, stackItem) State.LITERAL_MESSAGE -> fromLiteralMessage(input, stackItem) State.COMPRESSED_MESSAGE -> fromCompressedMessage(input, stackItem) State.ENCRYPTED_MESSAGE -> fromEncryptedMessage(input, stackItem) State.VALID -> fromValid(input, stackItem) else -> throw MalformedOpenPgpMessageException(from, input, stackItem) } } fun fromOpenPgpMessage(input: InputSymbol, stackItem: StackSymbol?): Transition { if (stackItem !== StackSymbol.MSG) { throw MalformedOpenPgpMessageException(State.OPENPGP_MESSAGE, input, stackItem) } return when (input) { InputSymbol.LITERAL_DATA -> Transition(State.LITERAL_MESSAGE) InputSymbol.SIGNATURE -> Transition(State.OPENPGP_MESSAGE, StackSymbol.MSG) InputSymbol.ONE_PASS_SIGNATURE -> Transition(State.OPENPGP_MESSAGE, StackSymbol.OPS, StackSymbol.MSG) InputSymbol.COMPRESSED_DATA -> Transition(State.COMPRESSED_MESSAGE) InputSymbol.ENCRYPTED_DATA -> Transition(State.ENCRYPTED_MESSAGE) InputSymbol.END_OF_SEQUENCE -> throw MalformedOpenPgpMessageException(State.OPENPGP_MESSAGE, input, stackItem) else -> throw MalformedOpenPgpMessageException(State.OPENPGP_MESSAGE, input, stackItem) } } @Throws(MalformedOpenPgpMessageException::class) fun fromLiteralMessage(input: InputSymbol, stackItem: StackSymbol?): Transition { if (input == InputSymbol.SIGNATURE && stackItem == StackSymbol.OPS) { return Transition(State.LITERAL_MESSAGE) } if (input == InputSymbol.END_OF_SEQUENCE && stackItem == StackSymbol.TERMINUS) { return Transition(State.VALID) } throw MalformedOpenPgpMessageException(State.LITERAL_MESSAGE, input, stackItem) } @Throws(MalformedOpenPgpMessageException::class) fun fromCompressedMessage(input: InputSymbol, stackItem: StackSymbol?): Transition { if (input == InputSymbol.SIGNATURE && stackItem == StackSymbol.OPS) { return Transition(State.COMPRESSED_MESSAGE) } if (input == InputSymbol.END_OF_SEQUENCE && stackItem == StackSymbol.TERMINUS) { return Transition(State.VALID) } throw MalformedOpenPgpMessageException(State.COMPRESSED_MESSAGE, input, stackItem) } @Throws(MalformedOpenPgpMessageException::class) fun fromEncryptedMessage(input: InputSymbol, stackItem: StackSymbol?): Transition { if (input == InputSymbol.SIGNATURE && stackItem == StackSymbol.OPS) { return Transition(State.ENCRYPTED_MESSAGE) } if (input == InputSymbol.END_OF_SEQUENCE && stackItem == StackSymbol.TERMINUS) { return Transition(State.VALID) } throw MalformedOpenPgpMessageException(State.ENCRYPTED_MESSAGE, input, stackItem) } @Throws(MalformedOpenPgpMessageException::class) fun fromValid(input: InputSymbol, stackItem: StackSymbol?): Transition { if (input == InputSymbol.END_OF_SEQUENCE) { // allow subsequent read() calls. return Transition(State.VALID) } throw MalformedOpenPgpMessageException(State.VALID, input, stackItem) } }