pgpainless/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/syntax_check/OpenPgpMessageSyntax.kt

92 lines
4.2 KiB
Kotlin

// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
//
// 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)
}
}