// SPDX-FileCopyrightText: 2022 Paul Schaub // // SPDX-License-Identifier: Apache-2.0 package org.pgpainless.decryption_verification.syntax_check; import org.pgpainless.exception.MalformedOpenPgpMessageException; import javax.annotation.Nonnull; import javax.annotation.Nullable; /** * This class describes the syntax for OpenPGP messages as specified by rfc4880. * * @see * rfc4880 - ยง11.3. OpenPGP Messages * @see * Blog post about theoretic background and translation of grammar to PDA syntax * @see * Blog post about practically implementing the PDA for packet syntax validation */ public class OpenPgpMessageSyntax implements Syntax { @Override public @Nonnull Transition transition(@Nonnull State from, @Nonnull InputSymbol input, @Nullable StackSymbol 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); } @Nonnull Transition fromOpenPgpMessage(@Nonnull InputSymbol input, @Nullable StackSymbol stackItem) throws MalformedOpenPgpMessageException { if (stackItem != StackSymbol.msg) { throw new MalformedOpenPgpMessageException(State.OpenPgpMessage, input, stackItem); } switch (input) { case LiteralData: return new Transition(State.LiteralMessage); case Signature: return new Transition(State.OpenPgpMessage, StackSymbol.msg); case OnePassSignature: return new Transition(State.OpenPgpMessage, StackSymbol.ops, StackSymbol.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); } } @Nonnull Transition fromLiteralMessage(@Nonnull InputSymbol input, @Nullable StackSymbol stackItem) throws MalformedOpenPgpMessageException { switch (input) { case Signature: if (stackItem == StackSymbol.ops) { return new Transition(State.LiteralMessage); } break; case EndOfSequence: if (stackItem == StackSymbol.terminus) { return new Transition(State.Valid); } break; } throw new MalformedOpenPgpMessageException(State.LiteralMessage, input, stackItem); } @Nonnull Transition fromCompressedMessage(@Nonnull InputSymbol input, @Nullable StackSymbol stackItem) throws MalformedOpenPgpMessageException { switch (input) { case Signature: if (stackItem == StackSymbol.ops) { return new Transition(State.CompressedMessage); } break; case EndOfSequence: if (stackItem == StackSymbol.terminus) { return new Transition(State.Valid); } break; } throw new MalformedOpenPgpMessageException(State.CompressedMessage, input, stackItem); } @Nonnull Transition fromEncryptedMessage(@Nonnull InputSymbol input, @Nullable StackSymbol stackItem) throws MalformedOpenPgpMessageException { switch (input) { case Signature: if (stackItem == StackSymbol.ops) { return new Transition(State.EncryptedMessage); } break; case EndOfSequence: if (stackItem == StackSymbol.terminus) { return new Transition(State.Valid); } break; } throw new MalformedOpenPgpMessageException(State.EncryptedMessage, input, stackItem); } @Nonnull Transition fromValid(@Nonnull InputSymbol input, @Nullable StackSymbol stackItem) throws MalformedOpenPgpMessageException { if (input == InputSymbol.EndOfSequence) { // allow subsequent read() calls. return new Transition(State.Valid); } // There is no applicable transition rule out of Valid throw new MalformedOpenPgpMessageException(State.Valid, input, stackItem); } }