File uyuni-coco-attestation-git-0.1d2f3e1.obscpio of Package uyuni-coco-attestation

07070100000000000041FD00000000000000000000000668FF7B0A00000000000000000000000000000000000000000000001700000000uyuni-coco-attestation07070100000001000081B400000000000000000000000168FF7B0A000046AC000000000000000000000000000000000000001F00000000uyuni-coco-attestation/LICENSE                    GNU GENERAL PUBLIC LICENSE
                       Version 2, June 1991

 Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users.  This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it.  (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.)  You can apply it to
your programs, too.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.

  To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.

  For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have.  You must make sure that they, too, receive or can get the
source code.  And you must show them these terms so they know their
rights.

  We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.

  Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software.  If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.

  Finally, any free program is threatened constantly by software
patents.  We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary.  To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.

  The precise terms and conditions for copying, distribution and
modification follow.

                    GNU GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License.  The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language.  (Hereinafter, translation is included without limitation in
the term "modification".)  Each licensee is addressed as "you".

Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.

  1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.

You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.

  2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:

    a) You must cause the modified files to carry prominent notices
    stating that you changed the files and the date of any change.

    b) You must cause any work that you distribute or publish, that in
    whole or in part contains or is derived from the Program or any
    part thereof, to be licensed as a whole at no charge to all third
    parties under the terms of this License.

    c) If the modified program normally reads commands interactively
    when run, you must cause it, when started running for such
    interactive use in the most ordinary way, to print or display an
    announcement including an appropriate copyright notice and a
    notice that there is no warranty (or else, saying that you provide
    a warranty) and that users may redistribute the program under
    these conditions, and telling the user how to view a copy of this
    License.  (Exception: if the Program itself is interactive but
    does not normally print such an announcement, your work based on
    the Program is not required to print an announcement.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works.  But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.

Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.

In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:

    a) Accompany it with the complete corresponding machine-readable
    source code, which must be distributed under the terms of Sections
    1 and 2 above on a medium customarily used for software interchange; or,

    b) Accompany it with a written offer, valid for at least three
    years, to give any third party, for a charge no more than your
    cost of physically performing source distribution, a complete
    machine-readable copy of the corresponding source code, to be
    distributed under the terms of Sections 1 and 2 above on a medium
    customarily used for software interchange; or,

    c) Accompany it with the information you received as to the offer
    to distribute corresponding source code.  (This alternative is
    allowed only for noncommercial distribution and only if you
    received the program in object code or executable form with such
    an offer, in accord with Subsection b above.)

The source code for a work means the preferred form of the work for
making modifications to it.  For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable.  However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.

If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.

  4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License.  Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.

  5. You are not required to accept this License, since you have not
signed it.  However, nothing else grants you permission to modify or
distribute the Program or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.

  6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions.  You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.

  7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all.  For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.

If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.

It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices.  Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.

This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.

  8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded.  In such case, this License incorporates
the limitation as if written in the body of this License.

  9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time.  Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

Each version is given a distinguishing version number.  If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation.  If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.

  10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission.  For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this.  Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.

                            NO WARRANTY

  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.

  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.

                     END OF TERMS AND CONDITIONS

            How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

Also add information on how to contact you by electronic and paper mail.

If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:

    Gnomovision version 69, Copyright (C) year name of author
    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License.  Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
  `Gnomovision' (which makes passes at compilers) written by James Hacker.

  <signature of Ty Coon>, 1 April 1989
  Ty Coon, President of Vice

This General Public License does not permit incorporating your program into
proprietary programs.  If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library.  If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
07070100000002000041FD00000000000000000000000368FF7B0A00000000000000000000000000000000000000000000002800000000uyuni-coco-attestation/attestation-core07070100000003000081B400000000000000000000000168FF7B0A00001250000000000000000000000000000000000000003000000000uyuni-coco-attestation/attestation-core/pom.xml<?xml version="1.0" encoding="UTF-8"?>
<!--
  ~ Copyright (c) 2024 SUSE LLC
  ~
  ~ This software is licensed to you under the GNU General Public License,
  ~ version 2 (GPLv2). There is NO WARRANTY for this software, express or
  ~ implied, including the implied warranties of MERCHANTABILITY or FITNESS
  ~ FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
  ~ along with this software; if not, see
  ~ http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
  -->

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.uyuni-project</groupId>
        <artifactId>coco-attestation</artifactId>
        <version>5.2.1</version>
    </parent>

    <groupId>org.uyuni-project.coco-attestation</groupId>
    <artifactId>attestation-core</artifactId>

    <build>
        <plugins>
            <!-- Shaded jar for developer convenience in order to have a single jar to run -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>2.3</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <shadedArtifactAttached>true</shadedArtifactAttached>
                            <shadedClassifierName>with-dependencies</shadedClassifierName>
                            <createDependencyReducedPom>false</createDependencyReducedPom>
                            <transformers>
                                <transformer
                                        implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <mainClass>com.suse.coco.CoCoAttestation</mainClass>
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>org.uyuni-project</groupId>
            <artifactId>uyuni-java-common</artifactId>
        </dependency>
        <dependency>
            <groupId>org.uyuni-project.coco-attestation</groupId>
            <artifactId>attestation-module</artifactId>
            <version>${project.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
        </dependency>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
        </dependency>

        <!-- Runtime -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!-- This runtime dependency is just for development purpose -->
        <dependency>
            <groupId>org.uyuni-project.coco-attestation.module</groupId>
            <artifactId>attestation-module-snpguest</artifactId>
            <version>${project.version}</version>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.uyuni-project.coco-attestation.module</groupId>
            <artifactId>attestation-module-secureboot</artifactId>
            <version>${project.version}</version>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <!-- Test -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-junit-jupiter</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.awaitility</groupId>
            <artifactId>awaitility</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>
07070100000004000041FD00000000000000000000000568FF7B0A00000000000000000000000000000000000000000000002C00000000uyuni-coco-attestation/attestation-core/src07070100000005000041FD00000000000000000000000468FF7B0A00000000000000000000000000000000000000000000003100000000uyuni-coco-attestation/attestation-core/src/main07070100000006000041FD00000000000000000000000368FF7B0A00000000000000000000000000000000000000000000003600000000uyuni-coco-attestation/attestation-core/src/main/java07070100000007000041FD00000000000000000000000368FF7B0A00000000000000000000000000000000000000000000003A00000000uyuni-coco-attestation/attestation-core/src/main/java/com07070100000008000041FD00000000000000000000000368FF7B0A00000000000000000000000000000000000000000000003F00000000uyuni-coco-attestation/attestation-core/src/main/java/com/suse07070100000009000041FD00000000000000000000000568FF7B0A00000000000000000000000000000000000000000000004400000000uyuni-coco-attestation/attestation-core/src/main/java/com/suse/coco0707010000000A000081B400000000000000000000000168FF7B0A00000E61000000000000000000000000000000000000005900000000uyuni-coco-attestation/attestation-core/src/main/java/com/suse/coco/CoCoAttestation.java/*
 * Copyright (c) 2024 SUSE LLC
 *
 * This software is licensed to you under the GNU General Public License,
 * version 2 (GPLv2). There is NO WARRANTY for this software, express or
 * implied, including the implied warranties of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
 * along with this software; if not, see
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
 */

package com.suse.coco;

import com.suse.coco.attestation.AttestationQueueProcessor;
import com.suse.coco.configuration.Configuration;
import com.suse.coco.configuration.DefaultConfiguration;
import com.suse.coco.module.AttestationModuleLoader;
import com.suse.common.database.DatabaseSessionFactory;

import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.time.LocalDateTime;

/**
 * Application entry class.
 */
public class CoCoAttestation {

    private static final Logger LOGGER = LogManager.getLogger(CoCoAttestation.class);

    private CoCoAttestation() {
        // Prevent instantiation
    }

    /**
     * Application entry point
     * @param args command line args
     */
    public static void main(String[] args) {
        try {
            LOGGER.info("Starting process at {}", LocalDateTime.now());

            LOGGER.debug("Loading application configuration configuration");
            Configuration configuration = new DefaultConfiguration();

            LOGGER.debug("Initialize Mybatis session factory");
            DatabaseSessionFactory.initialize("mybatis-config.xml", configuration.toProperties());
            SqlSessionFactory sessionFactory = DatabaseSessionFactory.getSessionFactory();

            LOGGER.debug("Loading attestation modules");
            AttestationModuleLoader moduleLoader = new AttestationModuleLoader();
            long count = moduleLoader.loadModules();
            if (count == 0) {
                LOGGER.error("No attestation module found on the classpath. " +
                    "Please install at least one attestation module package.");
                System.exit(0);
                return;
            }
            else {
                LOGGER.info("Supported result type are {}", moduleLoader.getSupportedResultTypes());
            }

            LOGGER.debug("Initializing attestation queue processor");
            var attestationQueueProcessor = new AttestationQueueProcessor(sessionFactory, configuration, moduleLoader);

            // Add shutdown hook to stop the processor and shutdown logging
            Runtime.getRuntime().addShutdownHook(new Thread(() -> {
                try {
                    LOGGER.debug("Received termination signal");
                    if (attestationQueueProcessor.isRunning()) {
                        attestationQueueProcessor.stop();
                        attestationQueueProcessor.awaitTermination();
                    }
                }
                catch (InterruptedException ex) {
                    Thread.currentThread().interrupt();
                }
                finally {
                    LogManager.shutdown();
                }
            }, "shutdown-hook"));

            attestationQueueProcessor.start();
            attestationQueueProcessor.awaitTermination();

            LOGGER.info("Execution completed");
            System.exit(0);
        }
        catch (Exception ex) {
            if (ex instanceof InterruptedException) {
                Thread.currentThread().interrupt();
            }

            LOGGER.error("Unexpected exception", ex);
            System.exit(1);
        }
    }
}
0707010000000B000041FD00000000000000000000000268FF7B0A00000000000000000000000000000000000000000000005000000000uyuni-coco-attestation/attestation-core/src/main/java/com/suse/coco/attestation0707010000000C000081B400000000000000000000000168FF7B0A00000762000000000000000000000000000000000000006D00000000uyuni-coco-attestation/attestation-core/src/main/java/com/suse/coco/attestation/AbstractProcessorThread.java/*
 * Copyright (c) 2024 SUSE LLC
 *
 * This software is licensed to you under the GNU General Public License,
 * version 2 (GPLv2). There is NO WARRANTY for this software, express or
 * implied, including the implied warranties of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
 * along with this software; if not, see
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
 */

package com.suse.coco.attestation;

/**
 * Base class for the threads used by the {@link AttestationQueueProcessor}.
 */
abstract class AbstractProcessorThread implements Runnable {

    private final Thread thread;

    private boolean running;

    protected AbstractProcessorThread(String name) {
        thread = new Thread(this, name);
        running = false;
    }

    /**
     * Starts the processing of the thread.
     */
    public void start() {
        setRunning(true);
        thread.start();
    }

    /**
     * Stops the processing of the thread, if it is running.
     */
    public void stop() {
        if (!isRunning()) {
            return;
        }

        thread.interrupt();
    }

    /**
     * Wait for this thread to complete its processing.
     *
     * @throws InterruptedException when the wait is interrupted
     */
    public void await() throws InterruptedException {
        if (!isRunning()) {
            return;
        }

        thread.join();
    }

    /**
     * Check if this thread is currently running.
     * @return true if the thread is running.
     */
    public synchronized boolean isRunning() {
        return running;
    }

    /**
     * Specify that this thread has already stopped it's processing and should not be considered as running.
     * @param runningIn the new status
     */
    protected synchronized void setRunning(boolean runningIn) {
        this.running = runningIn;
    }
}
0707010000000D000081B400000000000000000000000168FF7B0A00000FFC000000000000000000000000000000000000006F00000000uyuni-coco-attestation/attestation-core/src/main/java/com/suse/coco/attestation/AttestationQueueProcessor.java/*
 * Copyright (c) 2024 SUSE LLC
 *
 * This software is licensed to you under the GNU General Public License,
 * version 2 (GPLv2). There is NO WARRANTY for this software, express or
 * implied, including the implied warranties of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
 * along with this software; if not, see
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
 */

package com.suse.coco.attestation;

import com.suse.coco.configuration.Configuration;
import com.suse.coco.module.AttestationModuleLoader;
import com.suse.common.concurrent.UnboundedGrowingThreadPoolExecutor;

import org.apache.ibatis.session.SqlSessionFactory;

import java.time.Duration;
import java.util.concurrent.ExecutorService;

import javax.sql.DataSource;

/**
 * Process entry in the table suseAttestationResult and executes the proper based on the result type.
 */
public class AttestationQueueProcessor {

    private final ListeningThread listeningThread;

    private final ProcessingThread processingThread;

    /**
     * Create an attestation queue processor.
     * @param sessionFactory the session factory to access the database
     * @param configuration the current application configuration
     * @param moduleLoader the attestation module loader
     */
    public AttestationQueueProcessor(
        SqlSessionFactory sessionFactory,
        Configuration configuration,
        AttestationModuleLoader moduleLoader
    ) {
        // Call the other constructor with the correct values
        this(
            sessionFactory.getConfiguration().getEnvironment().getDataSource(),
            new AttestationResultService(sessionFactory),
            new UnboundedGrowingThreadPoolExecutor(
                configuration.getCorePoolSize(),
                configuration.getMaximumPoolSize(),
                Duration.ofSeconds(configuration.getThreadKeepAliveInSeconds()),
                "attestation-processor-worker"
            ),
            moduleLoader,
            configuration.getBatchSize()
        );
    }

    /**
     * Create an attestation queue processor.
     * @param dataSource the datasource to be used to listen to the database notification
     * @param service the attestation result service
     * @param executorService the executor to perform the process
     * @param moduleLoader the attestation module loader
     * @param batchSize the batch size
     */
    protected AttestationQueueProcessor(
        DataSource dataSource,
        AttestationResultService service,
        ExecutorService executorService,
        AttestationModuleLoader moduleLoader,
        int batchSize
    ) {
        listeningThread = new ListeningThread(dataSource);
        processingThread = new ProcessingThread(service, executorService, moduleLoader, batchSize);

        // Link the two threads
        listeningThread.setProcessingThread(processingThread);
        processingThread.setListeningThread(listeningThread);
    }

    /**
     * Start the execution of the queue processor. This method will start the processing asynchronously and exit
     * immediately. Use {@link #awaitTermination()} to wait for the processor to complete.
     */
    public void start() {
        listeningThread.start();
        processingThread.start();
    }

    /**
     * Wait indefinitely for the processor to complete. This is the same as awaitTermination(0L)
     * @throws InterruptedException if the wait for all the threads to stop is interrupted
     */
    public void awaitTermination() throws InterruptedException {
        listeningThread.await();
        processingThread.await();
    }

    /**
     * Interrupts the execution of this processor.
     */
    public void stop() {
        listeningThread.stop();
        processingThread.stop();
    }

    /**
     * Check if this attestation processor is running
     * @return true if at least one of the processor threads is still running
     */
    public boolean isRunning() {
        return listeningThread.isRunning() || processingThread.isRunning();
    }
}
0707010000000E000081B400000000000000000000000168FF7B0A000011DC000000000000000000000000000000000000006E00000000uyuni-coco-attestation/attestation-core/src/main/java/com/suse/coco/attestation/AttestationResultService.java/*
 * Copyright (c) 2024 SUSE LLC
 *
 * This software is licensed to you under the GNU General Public License,
 * version 2 (GPLv2). There is NO WARRANTY for this software, express or
 * implied, including the implied warranties of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
 * along with this software; if not, see
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
 */

package com.suse.coco.attestation;

import com.suse.coco.model.AttestationResult;
import com.suse.coco.model.AttestationStatus;
import com.suse.coco.module.AttestationWorker;
import com.suse.common.database.DatabaseSessionFactory;

import org.apache.ibatis.exceptions.PersistenceException;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.time.OffsetDateTime;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * Service class to handle {@link AttestationResult} in the database
 */
public class AttestationResultService {

    private static final Logger LOGGER = LogManager.getLogger(AttestationResultService.class);

    private final SqlSessionFactory sessionFactory;

    /**
     * Default constructor.
     */
    public AttestationResultService() {
        this(DatabaseSessionFactory.getSessionFactory());
    }

    /**
     * Build a services with the given session factory.
     * @param sessionFactoryIn the sql session factory
     */
    public AttestationResultService(SqlSessionFactory sessionFactoryIn) {
        this.sessionFactory = Objects.requireNonNull(sessionFactoryIn);
    }

    /**
     * Retrieve the ids of the available attestation results with the given state and result type.
     * @param resultTypeList a list of possible result types to match
     * @param batchSize the number of results to fetch at max
     * @return the ids of the attestation results matching the criteria
     */
    public List<Long> getPendingResultByType(Collection<Integer> resultTypeList, int batchSize) {
        try (SqlSession session = sessionFactory.openSession()) {
            return session.selectList(
                "AttestationResult.listPendingForResultType",
                Map.of("supportedTypes", resultTypeList, "batchSize", batchSize)
            );
        }
    }

    /**
     * Process an attestation result. The result is extracted from the database and locked for update.
     * @param id the id of the attestation result
     * @param worker the worker processing the attestation result
     */
    public void processAttestationResult(long id, AttestationWorker worker) {
        SqlSession session = sessionFactory.openSession();

        try {
            AttestationResult result = lockAttestationResult(session, id);
            if (result == null) {
                LOGGER.info("AttestationResult with id {} already taken - skipping", id);
                session.rollback();
                return;
            }

            LOGGER.info("AttestationResult with id {} selected for processing", id);
            boolean success = worker.process(session, result);
            if (success) {
                result.setStatus(AttestationStatus.SUCCEEDED);
                result.setAttested(OffsetDateTime.now());
            }
            else {
                result.setStatus(AttestationStatus.FAILED);
                result.setAttested(null);
            }

            session.update("AttestationResult.update", result);
            session.commit();
            LOGGER.info("AttestationResult with id {} completed - result is {}", id, result.getStatus());
        }
        catch (Exception ex) {
            LOGGER.debug("Unexpected error while processing of result {}", id, ex);
            session.rollback();
        }
        finally {
            session.close();
        }
    }

    private static AttestationResult lockAttestationResult(SqlSession session, long id) {
        try {
            return session.selectOne("AttestationResult.selectForUpdate", id);
        }
        catch (PersistenceException ex) {
            // If the error was due to the row being lock, just return null no need
            if (ex.getMessage() != null && ex.getMessage().contains("could not obtain lock on row")) {
                return null;
            }

            // Something different happened, better propagate the exception for logging purposes
            throw ex;
        }
    }
}
0707010000000F000081B400000000000000000000000168FF7B0A000013D0000000000000000000000000000000000000006500000000uyuni-coco-attestation/attestation-core/src/main/java/com/suse/coco/attestation/ListeningThread.java/*
 * Copyright (c) 2024 SUSE LLC
 *
 * This software is licensed to you under the GNU General Public License,
 * version 2 (GPLv2). There is NO WARRANTY for this software, express or
 * implied, including the implied warranties of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
 * along with this software; if not, see
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
 */

package com.suse.coco.attestation;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.postgresql.PGNotification;
import org.postgresql.jdbc.PgConnection;

import java.net.SocketException;
import java.sql.Connection;
import java.sql.Statement;
import java.util.stream.IntStream;

import javax.sql.DataSource;

/**
 * Listener thread of the {@link AttestationQueueProcessor}. Waits for the PostgreSQL notifications and notifies
 * the {@link ProcessingThread} when there is data to process.
 */
class ListeningThread extends AbstractProcessorThread {

    private static final Logger LOGGER = LogManager.getLogger(ListeningThread.class);

    private final Object connectionLock = new Object();

    private final DataSource dataSource;

    private boolean forceShutdown;

    private PgConnection pgConnection;

    private ProcessingThread processingThread;

    /**
     * Builds a listener thread
     * @param dataSourceIn the datasource to be used to connect to the database
     */
    ListeningThread(DataSource dataSourceIn) {
        super("attestation-processor-listener");

        dataSource = dataSourceIn;

        forceShutdown = false;

        processingThread = null;
        pgConnection = null;
    }

    public void setProcessingThread(ProcessingThread processingThreadIn) {
        this.processingThread = processingThreadIn;
    }

    @Override
    public void start() {
        if (processingThread == null) {
            throw new IllegalStateException("Set the processing thread before starting the listener");
        }

        super.start();
    }

    @Override
    public void stop() {
        if (!isRunning()) {
            return;
        }

        // Force the closure of the database connection
        forceShutdown = true;
        abortConnection();

        super.stop();
    }

    @Override
    public void run() {
        // Obtain a connection from the datasource
        try (Connection connection = dataSource.getConnection()) {
            // Ensure the connection is of the correct type
            if (!connection.isWrapperFor(PgConnection.class)) {
                throw new IllegalStateException("Unexpected wrapped connection " +
                    connection.unwrap(Object.class).getClass().getName());
            }

            updateConnection(connection.unwrap(PgConnection.class));

            try (Statement statement = pgConnection.createStatement()) {
                statement.execute("LISTEN pendingAttestationResult");
            }

            while (!Thread.currentThread().isInterrupted() && processingThread.isRunning()) {
                PGNotification[] notifications = pgConnection.getNotifications(60_000);
                if (notifications != null && notifications.length > 0) {
                    LOGGER.info("Got {} notification(s) from pendingAttestationResult", notifications.length);

                    if (LOGGER.isDebugEnabled()) {
                        IntStream.range(0, notifications.length)
                            .forEach(idx -> LOGGER.debug(
                                "\tNotification #{} -> {} {} {}",
                                idx,
                                notifications[idx].getPID(),
                                notifications[idx].getName(),
                                notifications[idx].getParameter()
                            ));
                    }

                    // Notify the thread waiting
                    processingThread.notifyDataAvailable();
                }
            }
        }
        catch (Exception ex) {
            // A socket closed exception is expected when we are shutting down the listener
            if (!(forceShutdown && ex.getCause() instanceof SocketException)) {
                LOGGER.error("Unexpected exception while listening to notifications", ex);
            }
        }
        finally {
            // Cleanup the state
            updateConnection(null);
            setRunning(false);

            // We are shutting down the listener. Notify the main thread to unblock it, if it is waiting.
            processingThread.notifyDataAvailable();
        }

        LOGGER.debug("Notification listener thread is stopped");
    }

    private void abortConnection() {
        synchronized (connectionLock) {
            if (pgConnection != null) {
                pgConnection.getQueryExecutor().abort();
            }
        }
    }

    private void updateConnection(PgConnection pgConnectionIn) {
        synchronized (connectionLock) {
            this.pgConnection = pgConnectionIn;
        }
    }
}
07070100000010000081B400000000000000000000000168FF7B0A0000140F000000000000000000000000000000000000006600000000uyuni-coco-attestation/attestation-core/src/main/java/com/suse/coco/attestation/ProcessingThread.java/*
 * Copyright (c) 2024 SUSE LLC
 *
 * This software is licensed to you under the GNU General Public License,
 * version 2 (GPLv2). There is NO WARRANTY for this software, express or
 * implied, including the implied warranties of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
 * along with this software; if not, see
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
 */

package com.suse.coco.attestation;

import com.suse.coco.module.AttestationModuleLoader;
import com.suse.coco.module.AttestationWorker;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * Main processing thread of the {@link AttestationQueueProcessor}. Extracts the attestation result of the supported
 * types and uses the correct {@link com.suse.coco.module.AttestationModule} to process them.
 */
class ProcessingThread extends AbstractProcessorThread {

    private static final Logger LOGGER = LogManager.getLogger(ProcessingThread.class);

    private final Object dataAvailableLock = new Object();

    private final AttestationModuleLoader moduleLoader;

    private final AttestationResultService service;

    private final ExecutorService executorService;

    private final int batchSize;

    private ListeningThread listeningThread;

    ProcessingThread(AttestationResultService serviceIn, ExecutorService executorServiceIn,
                     AttestationModuleLoader moduleLoaderIn, int batchSizeIn) {
        super("attestation-processor-main");

        moduleLoader = moduleLoaderIn;
        service = serviceIn;
        executorService = executorServiceIn;
        batchSize = batchSizeIn;
    }

    public void setListeningThread(ListeningThread listeningThreadIn) {
        this.listeningThread = listeningThreadIn;
    }

    /**
     * Notifies the processor that some data is ready to be processed. Used by the {@link ListeningThread} to trigger
     * the processing after receiving a notification.
     */
    public void notifyDataAvailable() {
        if (isRunning()) {
            synchronized (dataAvailableLock) {
                dataAvailableLock.notifyAll();
            }
        }
    }

    @Override
    public void start() {
        if (listeningThread == null) {
            throw new IllegalArgumentException("Set the listening thread before starting the processor");
        }

        super.start();
    }

    @Override
    public void run() {
        try {
            while (!Thread.currentThread().isInterrupted() && listeningThread.isRunning()) {
                // Load the pending attestation results of the supported types
                List<Long> results = service.getPendingResultByType(moduleLoader.getSupportedResultTypes(), batchSize);
                if (results.isEmpty()) {
                    LOGGER.info("No attestation result to process - Waiting");
                    synchronized (dataAvailableLock) {
                        dataAvailableLock.wait();
                    }
                    continue;
                }

                LOGGER.info("Processing attestation results {}", results);
                CountDownLatch workersDone = new CountDownLatch(results.size());

                // Process each one of them in a separate worker thread
                results.forEach(resultId -> executorService.execute(() -> {
                    try {
                        service.processAttestationResult(resultId, (session, result) -> {
                            AttestationWorker worker = moduleLoader.createWorker(result.getResultType());
                            return worker.process(session, result);
                        });
                    }
                    catch (Exception ex) {
                        LOGGER.error("Unable to correctly process attestation result with id {}", resultId, ex);
                    }
                    finally {
                        workersDone.countDown();
                    }
                }));

                // Wait for all workers to complete
                workersDone.await();
            }
        }
        catch (InterruptedException ex) {
            Thread.currentThread().interrupt();
        }
        finally {
            setRunning(false);

            // Shut down the executor service
            if (!executorService.isShutdown()) {
                LOGGER.debug("Shutting down the worker executor service");
                executorService.shutdown();

                boolean isTerminated = executorService.isTerminated();
                while (!isTerminated) {
                    try {
                        isTerminated = executorService.awaitTermination(1, TimeUnit.SECONDS);
                    }
                    catch (InterruptedException ex) {
                        Thread.currentThread().interrupt();
                    }
                }
            }
        }

        LOGGER.debug("Processor thread is stopped");
    }
}
07070100000011000041FD00000000000000000000000268FF7B0A00000000000000000000000000000000000000000000005200000000uyuni-coco-attestation/attestation-core/src/main/java/com/suse/coco/configuration07070100000012000081B400000000000000000000000168FF7B0A00000757000000000000000000000000000000000000006500000000uyuni-coco-attestation/attestation-core/src/main/java/com/suse/coco/configuration/Configuration.java/*
 * Copyright (c) 2024 SUSE LLC
 *
 * This software is licensed to you under the GNU General Public License,
 * version 2 (GPLv2). There is NO WARRANTY for this software, express or
 * implied, including the implied warranties of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
 * along with this software; if not, see
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
 */

package com.suse.coco.configuration;

import java.util.Properties;

/**
 * Application configuration
 */
public interface Configuration {

    /**
     * Retrieve the database user
     * @return the user to access the database
     */
    String getDatabaseUser();

    /**
     * Retrieves the database password
     * @return the unencrypted password to access the database
     */
    String getDatabasePassword();

    /**
     * Retrieves the connection string
     * @return the jdbc string to connect to the database
     */
    String getDatabaseConnectionString();

    /**
     * Retrieves the number of attestation results to process at a time.
     * @return the maximum number of attestation results to process.
     */
    int getBatchSize();

    /**
     * Retrieves the number of core threads for the queue processor
     * @return the number of core threads
     */
    int getCorePoolSize();

    /**
     * Retrieves the maximum number of threads for the queue processor
     * @return the maximum number of threads
     */
    int getMaximumPoolSize();

    /**
     * The number of seconds a thread will remain alive even without work to do.
     * @return the number of seconds a thread will be kept idle.
     */
    long getThreadKeepAliveInSeconds();

    /**
     * Convert this configuration to a Properties
     * @return a {@link Properties} representing the configuration.
     */
    Properties toProperties();

}
07070100000013000081B400000000000000000000000168FF7B0A00000C61000000000000000000000000000000000000006C00000000uyuni-coco-attestation/attestation-core/src/main/java/com/suse/coco/configuration/DefaultConfiguration.java/*
 * Copyright (c) 2024--2025 SUSE LLC
 *
 * This software is licensed to you under the GNU General Public License,
 * version 2 (GPLv2). There is NO WARRANTY for this software, express or
 * implied, including the implied warranties of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
 * along with this software; if not, see
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
 */

package com.suse.coco.configuration;

import com.suse.common.configuration.ConfigurationSource;
import com.suse.common.configuration.EnvironmentConfigurationSource;
import com.suse.common.configuration.MultipleConfigurationSource;
import com.suse.common.configuration.ResourceConfigurationSource;

import java.util.List;
import java.util.Properties;
import java.util.stream.Stream;

/**
 * Default application configuration. Values are first read from the environment then from a property file
 * which defines the default values. A part for the mandatory properties, all the settings should have a value
 * inside the property file.
 */
public class DefaultConfiguration implements Configuration {

    // These are the only properties that do not have a default value in configuration-defaults.properties
    private static final Stream<String> MANDATORY_PROPERTIES = Stream.of(
        "database_user",
        "database_password",
        "database_connection"
    );

    private final ConfigurationSource configurationSource;

    /**
     * Default constructor
     */
    public DefaultConfiguration() {
        configurationSource = new MultipleConfigurationSource(List.of(
            new EnvironmentConfigurationSource(),
            new ResourceConfigurationSource("configuration-defaults.properties")
        ));

        List<String> missingProperty = MANDATORY_PROPERTIES
            .filter(property -> configurationSource.getString(property).isEmpty())
            .toList();

        if (!missingProperty.isEmpty()) {
            throw new IllegalArgumentException("Mandatory configuration properties are missing: " + missingProperty);
        }
    }

    @Override
    public String getDatabaseUser() {
        return configurationSource.requireString("database_user");
    }

    @Override
    public String getDatabasePassword() {
        return configurationSource.requireString("database_password");
    }

    @Override
    public String getDatabaseConnectionString() {
        return configurationSource.requireString("database_connection");
    }

    @Override
    public int getCorePoolSize() {
        return configurationSource.requireInteger("processor_corePoolSize");
    }

    @Override
    public int getMaximumPoolSize() {
        return configurationSource.requireInteger("processor_maxPoolSize");
    }

    @Override
    public long getThreadKeepAliveInSeconds() {
        return configurationSource.requireInteger("processor_threadKeepAlive");
    }

    @Override
    public int getBatchSize() {
        return configurationSource.requireInteger("processor_batchSize");
    }

    @Override
    public Properties toProperties() {
        return configurationSource.toProperties();
    }
}
07070100000014000041FD00000000000000000000000268FF7B0A00000000000000000000000000000000000000000000004B00000000uyuni-coco-attestation/attestation-core/src/main/java/com/suse/coco/module07070100000015000081B400000000000000000000000168FF7B0A00000F19000000000000000000000000000000000000006800000000uyuni-coco-attestation/attestation-core/src/main/java/com/suse/coco/module/AttestationModuleLoader.java/*
 * Copyright (c) 2024 SUSE LLC
 *
 * This software is licensed to you under the GNU General Public License,
 * version 2 (GPLv2). There is NO WARRANTY for this software, express or
 * implied, including the implied warranties of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
 * along with this software; if not, see
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
 */

package com.suse.coco.module;

import com.suse.common.database.DatabaseSessionFactory;

import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.session.Configuration;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.InputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;

/**
 * Load {@link AttestationModule} from the classpath using Java {@link ServiceLoader} mechanism.
 */
public class AttestationModuleLoader {

    private static final Logger LOGGER = LogManager.getLogger(AttestationModuleLoader.class);

    private final Map<Integer, AttestationModule> workerFactoriesMap;

    /**
     * Default constructor.
     */
    public AttestationModuleLoader() {
        this.workerFactoriesMap = new HashMap<>();
    }

    /**
     * Load all the available implementations of {@link AttestationModule} from the classpath.
     *
     * @return the number of loaded attestation modules.
     */
    public long loadModules() {
        workerFactoriesMap.clear();

        Configuration mybatisConfig = DatabaseSessionFactory.getSessionFactory().getConfiguration();

        ServiceLoader.load(AttestationModule.class).forEach(module -> {
            // Loads additional Mybatis mapper from the attestation module, if any
            loadModuleMappers(mybatisConfig, module);

            workerFactoriesMap.put(module.getSupportedType(), module);
        });

        return workerFactoriesMap.size();
    }

    /**
     * Load Mybatis mappers into the current configuration
     *
     * @param config the current mybatis configuration
     * @param module the attestation module
     */
    private static void loadModuleMappers(Configuration config, AttestationModule module) {
        for (String resourceName : module.getAdditionalMappers()) {
            try (InputStream inputStream = ClassLoader.getSystemResourceAsStream(resourceName)) {
                if (inputStream == null) {
                    throw new NullPointerException("Unable to find resource");
                }

                String qualifiedName = module.getName() + ":" + resourceName;
                var parser = new XMLMapperBuilder(inputStream, config, qualifiedName, config.getSqlFragments());
                parser.parse();
            }
            catch (Exception ex) {
                LOGGER.warn("Unable to load mappers from {}", resourceName, ex);
            }
        }
    }

    /**
     * Get the supported result types, aggregated from every {@link AttestationModule} loaded.
     * @return the set of supported result types
     */
    public Set<Integer> getSupportedResultTypes() {
        return Collections.unmodifiableSet(workerFactoriesMap.keySet());
    }

    /**
     * Create a worker to process an attestation result of the given type
     * @param resultType the result type of the attestation result under processing
     * @return a {@link AttestationWorker} implementation suitable for the result type
     */
    public AttestationWorker createWorker(int resultType) {
        AttestationModule attestationModule = workerFactoriesMap.get(resultType);
        if (attestationModule == null) {
            throw new IllegalArgumentException("Unsupported result type " + resultType);
        }

        return attestationModule.getWorker();
    }

}
07070100000016000041FD00000000000000000000000368FF7B0A00000000000000000000000000000000000000000000003B00000000uyuni-coco-attestation/attestation-core/src/main/resources07070100000017000081B400000000000000000000000168FF7B0A00000122000000000000000000000000000000000000005D00000000uyuni-coco-attestation/attestation-core/src/main/resources/configuration-defaults.properties# Data source
dataSource_acquireIncrement = 1

dataSource_initialPoolSize = 0
dataSource_minPoolSize = 0
dataSource_maxPoolSize = 15

dataSource_maxIdleTime = 8

# Queue processor
processor_batchSize = 500
processor_corePoolSize = 0
processor_maxPoolSize = 5
processor_threadKeepAlive = 60
07070100000018000081B400000000000000000000000168FF7B0A00000282000000000000000000000000000000000000004600000000uyuni-coco-attestation/attestation-core/src/main/resources/log4j2.xml<?xml version="1.0" encoding="UTF-8"?>
<!-- This file just provide a basic fallback configuration useful for development -->
<!-- The actual configuration used at runtime by the packaged application is in scripts/log42.xml -->
<Configuration name="Basic" status="warn">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d [%t] %-5p %c%notEmpty{:%marker} - %m%n" />
        </Console>
    </Appenders>
    <Loggers>
        <Logger name="com.suse.coco" level="debug" />
        <Root level="info">
            <AppenderRef ref="Console"/>
        </Root>
    </Loggers>
</Configuration>
07070100000019000041FD00000000000000000000000268FF7B0A00000000000000000000000000000000000000000000004300000000uyuni-coco-attestation/attestation-core/src/main/resources/mappers0707010000001A000081B400000000000000000000000168FF7B0A0000079D000000000000000000000000000000000000005A00000000uyuni-coco-attestation/attestation-core/src/main/resources/mappers/attestation-result.xml<?xml version="1.0" encoding="UTF-8"?>
<!--
  ~ Copyright (c) 2024 SUSE LLC
  ~
  ~ This software is licensed to you under the GNU General Public License,
  ~ version 2 (GPLv2). There is NO WARRANTY for this software, express or
  ~ implied, including the implied warranties of MERCHANTABILITY or FITNESS
  ~ FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
  ~ along with this software; if not, see
  ~ http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
  -->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="AttestationResult">

    <select id="listPendingForResultType" parameterType="map" resultType="long">
          SELECT id
            FROM suseCoCoAttestationResult
           WHERE status = 'PENDING'
                        AND result_type IN <foreach item="resultType" collection="supportedTypes" open="(" close=")" separator=",">#{resultType}</foreach>
        ORDER BY id
           LIMIT #{batchSize}
    </select>

    <select id="selectForUpdate" parameterType="long" resultType="com.suse.coco.model.AttestationResult">
          SELECT id
                    , report_id
                    , result_type
                    , status
                    , description
                    , details
                    , attested
            FROM suseCoCoAttestationResult
           WHERE id = #{id}
                    AND status = 'PENDING'
        FOR NO KEY UPDATE NOWAIT
    </select>

    <update id="update" parameterType="com.suse.coco.model.AttestationResult">
          UPDATE suseCoCoAttestationResult
             SET status = #{status}
                    , details = #{details}
                    , process_output = #{processOutput}
                <if test="status.name() == 'SUCCEEDED'">
                    , attested = #{attested}
                </if>
           WHERE id = #{id}
    </update>
</mapper>
0707010000001B000081B400000000000000000000000168FF7B0A00000744000000000000000000000000000000000000004E00000000uyuni-coco-attestation/attestation-core/src/main/resources/mybatis-config.xml<?xml version="1.0" encoding="UTF-8"?>
<!--
  ~ Copyright (c) 2024 SUSE LLC
  ~
  ~ This software is licensed to you under the GNU General Public License,
  ~ version 2 (GPLv2). There is NO WARRANTY for this software, express or
  ~ implied, including the implied warranties of MERCHANTABILITY or FITNESS
  ~ FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
  ~ along with this software; if not, see
  ~ http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
  -->
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD SQL Map Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <setting name="logImpl" value="LOG4J2"/>
    </settings>

    <environments default="default">
        <environment id="default">
            <transactionManager type="JDBC"/>
            <dataSource type="com.suse.common.database.C3P0DataSourceFactory">
                <property name="driverClass" value="org.postgresql.Driver"/>
                <property name="jdbcUrl" value="${database_connection}"/>
                <property name="user" value="${database_user}"/>
                <property name="password" value="${database_password}"/>
                <property name="initialPoolSize" value="${dataSource_initialPoolSize}"/>
                <property name="acquireIncrement" value="${dataSource_acquireIncrement}"/>
                <property name="minPoolSize" value="${dataSource_minPoolSize}"/>
                <property name="maxPoolSize" value="${dataSource_maxPoolSize}"/>
                <property name="maxIdleTime" value="${dataSource_maxIdleTime}"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="mappers/attestation-result.xml" />
    </mappers>
</configuration>
0707010000001C000041FD00000000000000000000000268FF7B0A00000000000000000000000000000000000000000000003400000000uyuni-coco-attestation/attestation-core/src/package0707010000001D000081B400000000000000000000000168FF7B0A0000051C000000000000000000000000000000000000004800000000uyuni-coco-attestation/attestation-core/src/package/coco-attestation.sh#!/bin/sh
#
# Copyright (c) 2024 SUSE LLC
#
# This software is licensed to you under the GNU General Public License,
# version 2 (GPLv2). There is NO WARRANTY for this software, express or
# implied, including the implied warranties of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
# along with this software; if not, see
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
#
# Red Hat trademarks are not licensed under GPLv2. No permission is
# granted to use or replicate Red Hat trademarks that are incorporated
# in this software or its documentation.
#

# CoCo Attestation Daemon start script
. /usr/share/coco-attestation/conf/daemon.conf

# Add additional options to /etc/rhn/coco-attestation.conf, if present
if [ -f /etc/coco-attestation.conf ]; then
    . /etc/coco-attestation.conf
fi

ATTESTATION_PARAMS="-Dfile.encoding=UTF-8 -Dlog4j.shutdownHookEnabled=false -Xms${ATTESTATION_INIT_MEMORY}m -Xmx${ATTESTATION_MAX_MEMORY}m ${ATTESTATION_CRASH_PARAMS} ${ATTESTATION_JAVA_OPTS}"
ATTESTATION_CLASSPATH="${ATTESTATION_CLASSES}:${ATTESTATION_JARS}"

exec /usr/bin/java -Djava.library.path="${ATTESTATION_LIBRARY_PATH}" \
    -classpath ${ATTESTATION_CLASSPATH} ${ATTESTATION_PARAMS} ${JAVA_OPTS} ${JAVA_AGENT} \
    com.suse.coco.CoCoAttestation
0707010000001E000081B400000000000000000000000168FF7B0A00000256000000000000000000000000000000000000004000000000uyuni-coco-attestation/attestation-core/src/package/daemon.conf#********************************************************************
# Confidential Computing Attestation Daemon Properties
#********************************************************************

# Basic paths
ATTESTATION_LIBRARY_PATH="/usr/lib:/usr/lib64"
ATTESTATION_CLASSES="/usr/share/coco-attestation/classes"
ATTESTATION_JARS="/usr/share/coco-attestation/lib/*"

# Check if these parameters are already defined in the environment
ATTESTATION_JAVA_OPTS="${ATTESTATION_JAVA_OPTS}"
ATTESTATION_INIT_MEMORY=${ATTESTATION_INIT_MEMORY:-256}
ATTESTATION_MAX_MEMORY="${ATTESTATION_MAX_MEMORY:-4096}"
0707010000001F000081B400000000000000000000000168FF7B0A00000559000000000000000000000000000000000000003F00000000uyuni-coco-attestation/attestation-core/src/package/log4j2.xml<?xml version="1.0" encoding="UTF-8"?>
<!--
  ~ Copyright (c) 2024 SUSE LLC
  ~
  ~ This software is licensed to you under the GNU General Public License,
  ~ version 2 (GPLv2). There is NO WARRANTY for this software, express or
  ~ implied, including the implied warranties of MERCHANTABILITY or FITNESS
  ~ FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
  ~ along with this software; if not, see
  ~ http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
  -->

<!-- Confidential Computing Attestation Log4j2 configuration -->
<Configuration name="CoCoAttestation" status="warn" shutdownHook="disable">

    <Appenders>
        <RollingFile name="LogFile" fileName="/var/log/coco-attestation.log" filePattern="/var/log/coco-attestation-%i.log">
            <PatternLayout pattern="%d [%t] %-5p %c%notEmpty{:%marker} - %m%n" />
            <SizeBasedTriggeringPolicy size="10MB" />
            <DefaultRolloverStrategy max="5" />
        </RollingFile>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d [%t] %-5p %c%notEmpty{:%marker} - %m%n" />
        </Console>
    </Appenders>
    <Loggers>
        <Logger name="com.suse.coco" level="info" />
        <Root level="info">
            <AppenderRef ref="Console"/>
            <AppenderRef ref="LogFile"/>
        </Root>
    </Loggers>
</Configuration>
07070100000020000041FD00000000000000000000000468FF7B0A00000000000000000000000000000000000000000000003100000000uyuni-coco-attestation/attestation-core/src/test07070100000021000041FD00000000000000000000000368FF7B0A00000000000000000000000000000000000000000000003600000000uyuni-coco-attestation/attestation-core/src/test/java07070100000022000041FD00000000000000000000000368FF7B0A00000000000000000000000000000000000000000000003A00000000uyuni-coco-attestation/attestation-core/src/test/java/com07070100000023000041FD00000000000000000000000368FF7B0A00000000000000000000000000000000000000000000003F00000000uyuni-coco-attestation/attestation-core/src/test/java/com/suse07070100000024000041FD00000000000000000000000368FF7B0A00000000000000000000000000000000000000000000004400000000uyuni-coco-attestation/attestation-core/src/test/java/com/suse/coco07070100000025000041FD00000000000000000000000268FF7B0A00000000000000000000000000000000000000000000005000000000uyuni-coco-attestation/attestation-core/src/test/java/com/suse/coco/attestation07070100000026000081B400000000000000000000000168FF7B0A00002059000000000000000000000000000000000000007300000000uyuni-coco-attestation/attestation-core/src/test/java/com/suse/coco/attestation/AttestationQueueProcessorTest.java/*
 * Copyright (c) 2024 SUSE LLC
 *
 * This software is licensed to you under the GNU General Public License,
 * version 2 (GPLv2). There is NO WARRANTY for this software, express or
 * implied, including the implied warranties of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
 * along with this software; if not, see
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
 */

package com.suse.coco.attestation;

import static org.awaitility.Awaitility.await;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.AdditionalAnswers.answersWithDelay;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import com.suse.coco.module.AttestationModuleLoader;

import org.awaitility.Awaitility;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.postgresql.PGNotification;
import org.postgresql.core.QueryExecutor;
import org.postgresql.jdbc.PgConnection;

import java.sql.SQLException;
import java.sql.Statement;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;

import javax.sql.DataSource;

@ExtendWith(MockitoExtension.class)
@Timeout(30)
class AttestationQueueProcessorTest {

    @Mock
    private DataSource dataSource;

    @Mock
    private AttestationResultService resultService;

    @Mock
    private ExecutorService executorService;

    @Mock
    private AttestationModuleLoader moduleLoader;

    @Mock
    private PgConnection pgConnection;

    @Mock
    private QueryExecutor queryExecutor;

    @Mock
    private Statement statement;

    private AttestationQueueProcessor processor;

    @BeforeEach
    public void setUp() throws SQLException, InterruptedException {
        Awaitility.setDefaultTimeout(5, TimeUnit.SECONDS);

        processor = new AttestationQueueProcessor(dataSource, resultService, executorService, moduleLoader, 10);

        // Basic mocking
        when(dataSource.getConnection())
            .thenReturn(pgConnection);

        when(pgConnection.isWrapperFor(PgConnection.class))
            .thenReturn(true);

        when(pgConnection.unwrap(argThat(clazz -> clazz.getName().equals(PgConnection.class.getName()))))
            .thenReturn(pgConnection);

        when(pgConnection.createStatement())
            .thenReturn(statement);

        when(pgConnection.getNotifications(anyInt()))
            // Return a new notification each quarter of a second
            .thenAnswer(answersWithDelay(250, ivn -> new PGNotification[0]));

        // Simulate the executor shutdown
        when(executorService.isShutdown()).thenReturn(false);
        when(executorService.isTerminated()).thenReturn(false);
        when(executorService.awaitTermination(anyLong(), any())).thenReturn(true);
    }

    @AfterEach
    public void tearDown() {
        Awaitility.reset();
    }

    @Test
    @DisplayName("An interruption in the listener process terminates the processor")
    void testInterruptingListenerShouldTerminateProcessor() {
        processor.start();

        await("The listener thread to start")
            .untilAsserted(() -> {
                // Verify the listener thread has been created
                assertNotNull(getThreadByName("attestation-processor-listener"));
                // Verify the main thread has been created
                assertNotNull(getThreadByName("attestation-processor-main"));
                // And it's actually waiting for notifications
                verify(pgConnection, atLeastOnce()).getNotifications(anyInt());
            });

        // Interrupt the listener
        Thread processorListener = getThreadByName("attestation-processor-listener");
        assertNotNull(processorListener);
        processorListener.interrupt();

        // Verify the processor and the listener are correctly stopped
        await("The processor and the listener threads to properly stopped")
            .untilAsserted(() -> {
                assertFalse(processor.isRunning());

                assertNull(getThreadByName("attestation-processor-listener"));
                assertNull(getThreadByName("attestation-processor-main"));
                assertNull(getThreadByName("test-thread"));

                verify(executorService).shutdown();
            });

        // Verify shutdown was called
        verify(executorService).shutdown();
    }

    @Test
    @DisplayName("An interruption in the main process terminates the listener")
    void testInterruptingProcessorShouldTerminateListener() {
        processor.start();

        await("The listener thread to start")
            .untilAsserted(() -> {
                assertTrue(processor.isRunning());

                // Verify the listener thread has been created
                assertNotNull(getThreadByName("attestation-processor-listener"));
                // Verify the main thread has been created
                assertNotNull(getThreadByName("attestation-processor-main"));
                // And it's actually waiting for notifications
                verify(pgConnection, atLeastOnce()).getNotifications(anyInt());
            });

        // Interrupt the listener
        Thread processorMain = getThreadByName("attestation-processor-main");
        assertNotNull(processorMain);
        processorMain.interrupt();

        // Verify the processor and the listener are correctly stopped
        await("The processor and the listener threads to properly stopped")
            .untilAsserted(() -> {
                assertFalse(processor.isRunning());

                assertNull(getThreadByName("attestation-processor-listener"));
                assertNull(getThreadByName("attestation-processor-main"));
                assertNull(getThreadByName("test-thread"));

                verify(executorService).shutdown();
            });
    }

    @Test
    @DisplayName("The processor can be manually shut down")
    void canBeShutDown() {
        when(pgConnection.getQueryExecutor())
            .thenReturn(queryExecutor);

        processor.start();

        await("The processor and the listener threads to properly stopped")
            .untilAsserted(() -> {
                assertTrue(processor.isRunning());

                // Verify the listener thread has been created
                assertNotNull(getThreadByName("attestation-processor-listener"));
                // Verify the main thread has been created
                assertNotNull(getThreadByName("attestation-processor-main"));
                // And it's actually waiting for notifications
                verify(pgConnection, atLeastOnce()).getNotifications(anyInt());
            });

        // Interrupt the listener
        processor.stop();

        // Verify the processor and the listener are correctly stopped
        await("The processor and the listener threads to properly stopped")
            .untilAsserted(() -> {
                assertFalse(processor.isRunning());

                assertNull(getThreadByName("attestation-processor-listener"));
                assertNull(getThreadByName("attestation-processor-main"));
                assertNull(getThreadByName("test-thread"));

                verify(executorService).shutdown();
                verify(queryExecutor).abort();
            });
    }

    private static Thread getThreadByName(String name) {
        return Thread.getAllStackTraces().keySet().stream()
            .filter(t -> Objects.equals(t.getName(), name))
            .findFirst()
            .orElse(null);
    }
}
07070100000027000081B400000000000000000000000168FF7B0A00001BF7000000000000000000000000000000000000007200000000uyuni-coco-attestation/attestation-core/src/test/java/com/suse/coco/attestation/AttestationResultServiceTest.java/*
 * Copyright (c) 2024 SUSE LLC
 *
 * This software is licensed to you under the GNU General Public License,
 * version 2 (GPLv2). There is NO WARRANTY for this software, express or
 * implied, including the implied warranties of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
 * along with this software; if not, see
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
 */

package com.suse.coco.attestation;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;

import com.suse.coco.model.AttestationResult;
import com.suse.coco.model.AttestationStatus;
import com.suse.coco.module.AttestationWorker;

import org.apache.ibatis.exceptions.PersistenceException;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.time.OffsetDateTime;
import java.util.List;
import java.util.Map;

@ExtendWith(MockitoExtension.class)
class AttestationResultServiceTest {

    private static final Logger LOGGER = LogManager.getLogger(AttestationResultServiceTest.class);

    @Mock
    private SqlSession session;

    @Mock
    private AttestationWorker worker;

    private AttestationResultService service;

    @BeforeAll
    public static void beforeAll() {
        LOGGER.debug("Running test from AttestationResultManagerTest");
    }

    @BeforeEach
    public void setup(@Mock SqlSessionFactory sessionFactory) {
        when(sessionFactory.openSession()).thenReturn(session);

        service = new AttestationResultService(sessionFactory);
    }

    @Test
    @DisplayName("The attestation results are listed and filtered by the result types")
    void canListPendingResultsByType() {
        when(session.selectList(
            "AttestationResult.listPendingForResultType",
            Map.of("supportedTypes", List.of(1, 2, 3), "batchSize", 10)
        )).thenReturn(List.of(5L, 7L, 13L));

        List<Long> resultIds = service.getPendingResultByType(List.of(1, 2, 3), 10);
        assertEquals(List.of(5L, 7L, 13L), resultIds);

        verify(session).selectList(
            "AttestationResult.listPendingForResultType",
            Map.of("supportedTypes", List.of(1, 2, 3), "batchSize", 10)
        );
        verify(session).close();

        verifyNoMoreInteractions(session);
    }

    @Test
    @DisplayName("An attestation result is locked, successfully processed by the worker and the result is updated")
    void canProcessAttestationResultAndMarkAsSucceeded() {
        AttestationResult attestationResult = new AttestationResult();
        attestationResult.setId(5L);
        attestationResult.setStatus(AttestationStatus.PENDING);
        attestationResult.setAttested(null);

        when(session.selectOne("AttestationResult.selectForUpdate", 5L)).thenReturn(attestationResult);
        when(worker.process(session, attestationResult)).thenReturn(true);

        OffsetDateTime callStart = OffsetDateTime.now();
        service.processAttestationResult(5L, worker);
        OffsetDateTime callEnd = OffsetDateTime.now();

        verify(session).selectOne("AttestationResult.selectForUpdate", 5L);
        verify(worker).process(session, attestationResult);
        verify(session).update("AttestationResult.update", attestationResult);
        verify(session).commit();
        verify(session).close();

        verifyNoMoreInteractions(session);
        verifyNoMoreInteractions(worker);

        // Confirm the status has been updated
        assertEquals(AttestationStatus.SUCCEEDED, attestationResult.getStatus());
        // Verify the attestation date is correctly marked
        assertNotNull(attestationResult.getAttested());
        assertTrue(attestationResult.getAttested().isAfter(callStart));
        assertTrue(attestationResult.getAttested().isBefore(callEnd));
    }

    @Test
    @DisplayName("An attestation result is locked, worker fails to process and the result is updated")
    void canProcessAttestationResultAndMarkAsFailed() {
        AttestationResult attestationResult = new AttestationResult();
        attestationResult.setId(5L);
        attestationResult.setStatus(AttestationStatus.PENDING);
        attestationResult.setAttested(null);

        when(session.selectOne("AttestationResult.selectForUpdate", 5L)).thenReturn(attestationResult);
        when(worker.process(session, attestationResult)).thenReturn(false);

        service.processAttestationResult(5L, worker);

        verify(session).selectOne("AttestationResult.selectForUpdate", 5L);
        verify(worker).process(session, attestationResult);
        verify(session).update("AttestationResult.update", attestationResult);
        verify(session).commit();
        verify(session).close();

        verifyNoMoreInteractions(session);
        verifyNoMoreInteractions(worker);

        // Confirm the status has been updated
        assertEquals(AttestationStatus.FAILED, attestationResult.getStatus());
        // Verify the attestation date is correctly marked
        assertNull(attestationResult.getAttested());
    }

    @Test
    @DisplayName("Nothing is processed if the attestation result has already been updated")
    void doesNotProcessIfAttestationResultIsAlreadyProcessed() {
        // Select for update returns null when another process already updated the same row
        when(session.selectOne("AttestationResult.selectForUpdate", 5L)).thenReturn(null);

        service.processAttestationResult(5L, worker);

        verify(session).selectOne("AttestationResult.selectForUpdate", 5L);
        verify(session).rollback();
        verify(session).close();

        verifyNoMoreInteractions(session);
        verifyNoInteractions(worker);
    }

    @Test
    @DisplayName("Nothing is processed if the attestation result cannot be locked")
    void doesNotProcessIfAttestationResultCannotBeLocked() {
        // Select for update returns null when another process already updated the same row
        when(session.selectOne("AttestationResult.selectForUpdate", 5L))
            .thenThrow(new PersistenceException("PG/SQL Error: could not obtain lock on row"));

        service.processAttestationResult(5L, worker);

        verify(session).selectOne("AttestationResult.selectForUpdate", 5L);
        verify(session).rollback();
        verify(session).close();

        verifyNoMoreInteractions(session);
        verifyNoInteractions(worker);
    }
}
07070100000028000041FD00000000000000000000000268FF7B0A00000000000000000000000000000000000000000000003B00000000uyuni-coco-attestation/attestation-core/src/test/resources07070100000029000081B400000000000000000000000168FF7B0A00000221000000000000000000000000000000000000004B00000000uyuni-coco-attestation/attestation-core/src/test/resources/log4j2-test.xml<?xml version="1.0" encoding="UTF-8"?>
<!-- This file just provide a basic fallback configuration -->
<!-- The actual configuration is performed at runtime programmatically in the Log4J class -->
<Configuration name="Basic" status="warn">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d %-5p [%-15tn] %c{1} - %m%n"/>
        </Console>
    </Appenders>
    <Loggers>
        <Root level="debug">
            <AppenderRef ref="Console"/>
        </Root>
    </Loggers>
</Configuration>
0707010000002A000041FD00000000000000000000000368FF7B0A00000000000000000000000000000000000000000000003500000000uyuni-coco-attestation/attestation-module-secureboot0707010000002B000081B400000000000000000000000168FF7B0A000006B8000000000000000000000000000000000000003D00000000uyuni-coco-attestation/attestation-module-secureboot/pom.xml<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.uyuni-project</groupId>
        <artifactId>coco-attestation</artifactId>
        <version>5.2.1</version>
    </parent>

    <groupId>org.uyuni-project.coco-attestation.module</groupId>
    <artifactId>attestation-module-secureboot</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.uyuni-project</groupId>
            <artifactId>uyuni-java-common</artifactId>
        </dependency>
        <dependency>
            <groupId>org.uyuni-project.coco-attestation</groupId>
            <artifactId>attestation-module</artifactId>
            <version>${project.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <scope>provided</scope>
        </dependency>

        <!-- Test -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-junit-jupiter</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>
0707010000002C000041FD00000000000000000000000468FF7B0A00000000000000000000000000000000000000000000003900000000uyuni-coco-attestation/attestation-module-secureboot/src0707010000002D000041FD00000000000000000000000468FF7B0A00000000000000000000000000000000000000000000003E00000000uyuni-coco-attestation/attestation-module-secureboot/src/main0707010000002E000041FD00000000000000000000000368FF7B0A00000000000000000000000000000000000000000000004300000000uyuni-coco-attestation/attestation-module-secureboot/src/main/java0707010000002F000041FD00000000000000000000000368FF7B0A00000000000000000000000000000000000000000000004700000000uyuni-coco-attestation/attestation-module-secureboot/src/main/java/com07070100000030000041FD00000000000000000000000368FF7B0A00000000000000000000000000000000000000000000004C00000000uyuni-coco-attestation/attestation-module-secureboot/src/main/java/com/suse07070100000031000041FD00000000000000000000000368FF7B0A00000000000000000000000000000000000000000000005100000000uyuni-coco-attestation/attestation-module-secureboot/src/main/java/com/suse/coco07070100000032000041FD00000000000000000000000368FF7B0A00000000000000000000000000000000000000000000005800000000uyuni-coco-attestation/attestation-module-secureboot/src/main/java/com/suse/coco/module07070100000033000041FD00000000000000000000000268FF7B0A00000000000000000000000000000000000000000000006300000000uyuni-coco-attestation/attestation-module-secureboot/src/main/java/com/suse/coco/module/secureboot07070100000034000081B400000000000000000000000168FF7B0A00000498000000000000000000000000000000000000007900000000uyuni-coco-attestation/attestation-module-secureboot/src/main/java/com/suse/coco/module/secureboot/SecureBootModule.java/*
 * Copyright (c) 2024 SUSE LLC
 *
 * This software is licensed to you under the GNU General Public License,
 * version 2 (GPLv2). There is NO WARRANTY for this software, express or
 * implied, including the implied warranties of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
 * along with this software; if not, see
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
 */
package com.suse.coco.module.secureboot;

import com.suse.coco.module.AttestationModule;
import com.suse.coco.module.AttestationWorker;

import java.util.List;

public class SecureBootModule implements AttestationModule {
    /**
     * Result type for report generated by SNPGuest
     */
    public static final int SECURE_BOOT = 2;

    @Override
    public String getName() {
        return SecureBootModule.class.getName();
    }

    @Override
    public int getSupportedType() {
        return SECURE_BOOT;
    }

    @Override
    public AttestationWorker getWorker() {
        return new SecureBootWorker();
    }

    @Override
    public List<String> getAdditionalMappers() {
        return List.of("mappers/secureboot.xml");
    }
}
07070100000035000081B400000000000000000000000168FF7B0A0000085A000000000000000000000000000000000000007900000000uyuni-coco-attestation/attestation-module-secureboot/src/main/java/com/suse/coco/module/secureboot/SecureBootWorker.java/*
 * Copyright (c) 2024 SUSE LLC
 *
 * This software is licensed to you under the GNU General Public License,
 * version 2 (GPLv2). There is NO WARRANTY for this software, express or
 * implied, including the implied warranties of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
 * along with this software; if not, see
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
 */
package com.suse.coco.module.secureboot;

import com.suse.coco.model.AttestationResult;
import com.suse.coco.module.AttestationWorker;

import org.apache.ibatis.session.SqlSession;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class SecureBootWorker implements AttestationWorker {
    private static final Logger LOGGER = LogManager.getLogger(SecureBootWorker.class);

    @Override
    public boolean process(SqlSession session, AttestationResult result) {
        try {
            LOGGER.debug("Processing attestation result {}", result.getId());

            String secureBootMsg = session.selectOne("SecureBootModule.retrieveReport", result.getReportId());
            if (secureBootMsg == null) {
                LOGGER.error("Unable to retrieve attestation report for result {}", result.getId());
                return false;
            }

            result.setDetails(secureBootMsg);
            String bootMsgLowerCase = secureBootMsg.toLowerCase();
            // got these messages from mokutil source code
            return bootMsgLowerCase.contains("secureboot enabled") &&
                    !bootMsgLowerCase.contains("secureboot disabled") &&
                    !bootMsgLowerCase.contains("is disabled in shim") &&
                    !bootMsgLowerCase.contains("cannot determine secure boot state") &&
                    !bootMsgLowerCase.contains("failed to read") &&
                    !bootMsgLowerCase.contains("efi variables are not supported on this system");
        }
        catch (Exception ex) {
            LOGGER.error("Unable to process attestation result {}", result.getId(), ex);
        }
        return false;
    }
}
07070100000036000041FD00000000000000000000000468FF7B0A00000000000000000000000000000000000000000000004800000000uyuni-coco-attestation/attestation-module-secureboot/src/main/resources07070100000037000041FD00000000000000000000000368FF7B0A00000000000000000000000000000000000000000000005100000000uyuni-coco-attestation/attestation-module-secureboot/src/main/resources/META-INF07070100000038000041FD00000000000000000000000268FF7B0A00000000000000000000000000000000000000000000005A00000000uyuni-coco-attestation/attestation-module-secureboot/src/main/resources/META-INF/services07070100000039000081B400000000000000000000000168FF7B0A00000031000000000000000000000000000000000000008100000000uyuni-coco-attestation/attestation-module-secureboot/src/main/resources/META-INF/services/com.suse.coco.module.AttestationModulecom.suse.coco.module.secureboot.SecureBootModule
0707010000003A000041FD00000000000000000000000268FF7B0A00000000000000000000000000000000000000000000005000000000uyuni-coco-attestation/attestation-module-secureboot/src/main/resources/mappers0707010000003B000081B400000000000000000000000168FF7B0A0000036C000000000000000000000000000000000000005F00000000uyuni-coco-attestation/attestation-module-secureboot/src/main/resources/mappers/secureboot.xml<?xml version="1.0" encoding="UTF-8"?>
<!--
  ~ Copyright (c) 2024 SUSE LLC
  ~
  ~ This software is licensed to you under the GNU General Public License,
  ~ version 2 (GPLv2). There is NO WARRANTY for this software, express or
  ~ implied, including the implied warranties of MERCHANTABILITY or FITNESS
  ~ FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
  ~ along with this software; if not, see
  ~ http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
  -->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="SecureBootModule">

    <select id="retrieveReport" parameterType="Long" resultType="String">
        SELECT out_data->>'mgr_secureboot_enabled' AS secureboot
        FROM suseServerCoCoAttestationReport
        WHERE id = #{reportId}
    </select>
</mapper>
0707010000003C000041FD00000000000000000000000368FF7B0A00000000000000000000000000000000000000000000003E00000000uyuni-coco-attestation/attestation-module-secureboot/src/test0707010000003D000041FD00000000000000000000000368FF7B0A00000000000000000000000000000000000000000000004300000000uyuni-coco-attestation/attestation-module-secureboot/src/test/java0707010000003E000041FD00000000000000000000000368FF7B0A00000000000000000000000000000000000000000000004700000000uyuni-coco-attestation/attestation-module-secureboot/src/test/java/com0707010000003F000041FD00000000000000000000000368FF7B0A00000000000000000000000000000000000000000000004C00000000uyuni-coco-attestation/attestation-module-secureboot/src/test/java/com/suse07070100000040000041FD00000000000000000000000368FF7B0A00000000000000000000000000000000000000000000005100000000uyuni-coco-attestation/attestation-module-secureboot/src/test/java/com/suse/coco07070100000041000041FD00000000000000000000000368FF7B0A00000000000000000000000000000000000000000000005800000000uyuni-coco-attestation/attestation-module-secureboot/src/test/java/com/suse/coco/module07070100000042000041FD00000000000000000000000268FF7B0A00000000000000000000000000000000000000000000006300000000uyuni-coco-attestation/attestation-module-secureboot/src/test/java/com/suse/coco/module/secureboot07070100000043000081B400000000000000000000000168FF7B0A00000C32000000000000000000000000000000000000007D00000000uyuni-coco-attestation/attestation-module-secureboot/src/test/java/com/suse/coco/module/secureboot/SecureBootWorkerTest.java/*
 * Copyright (c) 2024 SUSE LLC
 *
 * This software is licensed to you under the GNU General Public License,
 * version 2 (GPLv2). There is NO WARRANTY for this software, express or
 * implied, including the implied warranties of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
 * along with this software; if not, see
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
 */
package com.suse.coco.module.secureboot;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;

import com.suse.coco.model.AttestationResult;
import com.suse.coco.model.AttestationStatus;

import org.apache.ibatis.session.SqlSession;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.util.stream.Stream;

@ExtendWith(MockitoExtension.class)
class SecureBootWorkerTest {

    private AttestationResult result;

    @Mock
    private SqlSession session;

    private SecureBootWorker worker;

    @BeforeEach
    public void setup() {
        result = new AttestationResult();
        result.setId(1L);
        result.setStatus(AttestationStatus.PENDING);
        result.setReportId(5L);

        worker = new SecureBootWorker();
    }

    @ParameterizedTest(name = "Expecting {1} with message {2}")
    @DisplayName("Test secure boot messages")
    @MethodSource("messagesProvider")
    void testSecureBootMessages(long reportId, boolean expectedOutcome, String message) {
        // Setting the report id we're retrieving
        result.setReportId(reportId);

        // Mock the report retrieval
        when(session.selectOne("SecureBootModule.retrieveReport", reportId))
            .thenReturn(message);

        // Ensure the result is correct
        assertEquals(expectedOutcome, worker.process(session, result));
    }

    static Stream<Arguments> messagesProvider() {
        return Stream.of(
            Arguments.of(1L, true, "SecureBoot enabled\n"),
            Arguments.of(2L, false, "SecureBoot enabled\nSecureBoot validation is disabled in shim\n"),
            Arguments.of(3L, false, "SecureBoot disabled\n"),
            Arguments.of(4L, false, "SecureBoot disabled\nPlatform is in Setup Mode\n"),
            Arguments.of(5L, false, "Cannot determine secure boot state.\n"),
            Arguments.of(6L, false, "Failed to read \"SetupMode\" variable: XYZ\n"),
            Arguments.of(7L, false, "Failed to read \"SecureBoot\" variable: ABC\n"),
            Arguments.of(8L, false, "EFI variables are not supported on this system\n"),
            Arguments.of(9L, false, "This system doesn't support Secure Boot\n"),
            Arguments.of(10L, false, "Could not allocate space: reasons\n"),
            Arguments.of(11L, false, "Totally unexpected error message\n")
        );
    }
}
07070100000044000041FD00000000000000000000000368FF7B0A00000000000000000000000000000000000000000000003300000000uyuni-coco-attestation/attestation-module-snpguest07070100000045000081B400000000000000000000000168FF7B0A00000873000000000000000000000000000000000000003B00000000uyuni-coco-attestation/attestation-module-snpguest/pom.xml<?xml version="1.0" encoding="UTF-8"?>
<!--
  ~ Copyright (c) 2024 SUSE LLC
  ~
  ~ This software is licensed to you under the GNU General Public License,
  ~ version 2 (GPLv2). There is NO WARRANTY for this software, express or
  ~ implied, including the implied warranties of MERCHANTABILITY or FITNESS
  ~ FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
  ~ along with this software; if not, see
  ~ http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
  -->

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.uyuni-project</groupId>
        <artifactId>coco-attestation</artifactId>
        <version>5.2.1</version>
    </parent>

    <groupId>org.uyuni-project.coco-attestation.module</groupId>
    <artifactId>attestation-module-snpguest</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.uyuni-project</groupId>
            <artifactId>uyuni-java-common</artifactId>
        </dependency>
        <dependency>
            <groupId>org.uyuni-project.coco-attestation</groupId>
            <artifactId>attestation-module</artifactId>
            <version>${project.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <scope>provided</scope>
        </dependency>

        <!-- Test -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-junit-jupiter</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>
07070100000046000041FD00000000000000000000000568FF7B0A00000000000000000000000000000000000000000000003700000000uyuni-coco-attestation/attestation-module-snpguest/src07070100000047000041FD00000000000000000000000468FF7B0A00000000000000000000000000000000000000000000003C00000000uyuni-coco-attestation/attestation-module-snpguest/src/main07070100000048000041FD00000000000000000000000368FF7B0A00000000000000000000000000000000000000000000004100000000uyuni-coco-attestation/attestation-module-snpguest/src/main/java07070100000049000041FD00000000000000000000000368FF7B0A00000000000000000000000000000000000000000000004500000000uyuni-coco-attestation/attestation-module-snpguest/src/main/java/com0707010000004A000041FD00000000000000000000000368FF7B0A00000000000000000000000000000000000000000000004A00000000uyuni-coco-attestation/attestation-module-snpguest/src/main/java/com/suse0707010000004B000041FD00000000000000000000000368FF7B0A00000000000000000000000000000000000000000000004F00000000uyuni-coco-attestation/attestation-module-snpguest/src/main/java/com/suse/coco0707010000004C000041FD00000000000000000000000368FF7B0A00000000000000000000000000000000000000000000005600000000uyuni-coco-attestation/attestation-module-snpguest/src/main/java/com/suse/coco/module0707010000004D000041FD00000000000000000000000568FF7B0A00000000000000000000000000000000000000000000005F00000000uyuni-coco-attestation/attestation-module-snpguest/src/main/java/com/suse/coco/module/snpguest0707010000004E000081B400000000000000000000000168FF7B0A000004B2000000000000000000000000000000000000007300000000uyuni-coco-attestation/attestation-module-snpguest/src/main/java/com/suse/coco/module/snpguest/SNPGuestModule.java/*
 * Copyright (c) 2024 SUSE LLC
 *
 * This software is licensed to you under the GNU General Public License,
 * version 2 (GPLv2). There is NO WARRANTY for this software, express or
 * implied, including the implied warranties of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
 * along with this software; if not, see
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
 */

package com.suse.coco.module.snpguest;

import com.suse.coco.module.AttestationModule;
import com.suse.coco.module.AttestationWorker;

import java.util.List;

/**
 * Module descriptio for SNPGuest
 */
public class SNPGuestModule implements AttestationModule {

    /**
     * Result type for report generated by SNPGuest
     */
    public static final int SEV_SNP = 1;

    @Override
    public String getName() {
        return SNPGuestModule.class.getName();
    }

    @Override
    public int getSupportedType() {
        return SEV_SNP;
    }

    @Override
    public AttestationWorker getWorker() {
        return new SNPGuestWorker();
    }

    @Override
    public List<String> getAdditionalMappers() {
        return List.of("mappers/snpguest.xml");
    }
}
0707010000004F000081B400000000000000000000000168FF7B0A00002453000000000000000000000000000000000000007300000000uyuni-coco-attestation/attestation-module-snpguest/src/main/java/com/suse/coco/module/snpguest/SNPGuestWorker.java/*
 * Copyright (c) 2024 SUSE LLC
 *
 * This software is licensed to you under the GNU General Public License,
 * version 2 (GPLv2). There is NO WARRANTY for this software, express or
 * implied, including the implied warranties of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
 * along with this software; if not, see
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
 */

package com.suse.coco.module.snpguest;

import com.suse.coco.model.AttestationResult;
import com.suse.coco.module.AttestationWorker;
import com.suse.coco.module.snpguest.execution.AbstractSNPGuestWrapper;
import com.suse.coco.module.snpguest.execution.ProcessOutput;
import com.suse.coco.module.snpguest.execution.SNPGuestWrapperFactory;
import com.suse.coco.module.snpguest.io.VerificationDirectoryProvider;
import com.suse.coco.module.snpguest.model.AttestationReport;
import com.suse.coco.module.snpguest.model.EpycGeneration;
import com.suse.common.io.ByteSequenceFinder;

import org.apache.ibatis.session.SqlSession;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.nio.file.Path;
import java.util.Optional;

/**
 * Worker class for verifying the reports with SNPGuest
 */
public class SNPGuestWorker implements AttestationWorker {

    private static final Logger LOGGER = LogManager.getLogger(SNPGuestWorker.class);

    private static final int INDENT_SIZE = 4;

    private final VerificationDirectoryProvider directoryProvider;

    private final AbstractSNPGuestWrapper snpGuest;

    private final ByteSequenceFinder sequenceFinder;

    private final StringBuilder outputBuilder;

    /**
     * Default constructor.
     */
    public SNPGuestWorker() {
        this(new VerificationDirectoryProvider(), SNPGuestWrapperFactory.createSNPGuestWrapper(),
                new ByteSequenceFinder());
    }

    /**
     * Constructor with explicit dependencies, for unit test only.
     * @param directoryProviderIn the verification directory provider
     * @param abstractSnpGuestWrapperIn the snpguest executor
     * @param sequenceFinderIn the byte sequence finder
     */
    SNPGuestWorker(VerificationDirectoryProvider directoryProviderIn, AbstractSNPGuestWrapper abstractSnpGuestWrapperIn,
                   ByteSequenceFinder sequenceFinderIn) {
        this.directoryProvider = directoryProviderIn;
        this.snpGuest = abstractSnpGuestWrapperIn;
        this.sequenceFinder = sequenceFinderIn;
        this.outputBuilder = new StringBuilder();
    }

    @Override
    public boolean process(SqlSession session, AttestationResult result) {
        // Reset the output string builder
        outputBuilder.setLength(0);

        try {
            LOGGER.debug("Processing attestation result {}", result.getId());

            AttestationReport report = session.selectOne("SNPGuestModule.retrieveReport", result.getReportId());
            if (report == null) {
                appendError("Unable to retrieve attestation report for result");
                return false;
            }

             LOGGER.debug("Loaded report {}", report);
             if (report.getCpuGeneration() == EpycGeneration.UNKNOWN) {
                appendError("Unable to identify Epyc processor generation for attestation report");
                return false;
            }

            if (report.getRandomNonce() == null || report.getRandomNonce().length == 0) {
                appendError("Unable to verify: randomized nonce not found");
                return false;
            }

             if (report.getReport() == null || report.getReport().length == 0) {
                 appendError("Unable to verify: attestation report not found");
                 return false;
             }

            try (var workingDir = directoryProvider.createDirectoryFor(result.getId(), report)) {
                // Ensure the nonce is present in the report
                sequenceFinder.setSequence(report.getRandomNonce());
                if (sequenceFinder.search(report.getReport()) == -1) {
                    appendError("The report does not contain the expected random nonce");
                    return false;
                }
                else {
                    appendSuccess("The report contains the expected random nonce");
                }

                // Reference to the paths snpguest needs to work with
                Path certsPath = workingDir.getCertsPath();
                Path reportPath = workingDir.getReportPath();
                ProcessOutput processOutput;

                if (report.isUsingVlekAttestation()) {
                    if (!workingDir.isVLEKAvailable()) {
                        appendError("Unable to retrieve VLEK certification file");
                        return false;
                    }
                    else {
                        appendSuccess("VLEK certification retrieved successfully");
                    }
                }
                else {
                    // Download the VCEK for this cpu model
                    processOutput = snpGuest.fetchVCEK(report.getCpuGeneration(), certsPath, reportPath);
                    if (processOutput.getExitCode() != 0 || !workingDir.isVCEKAvailable()) {
                        appendError("Unable to retrieve VCEK file", processOutput);
                        return false;
                    }
                    else {
                        appendSuccess("VCEK fetched successfully", processOutput);
                    }
                }

                // Verify the certificates
                processOutput = snpGuest.verifyCertificates(certsPath);
                if (processOutput.getExitCode() != 0) {
                    appendError("Unable to verify the validity of the certificates", processOutput);
                    return false;
                }
                else {
                    appendSuccess("Certification chain validated successfully", processOutput);
                }

                // Verify the actual attestation report
                processOutput = snpGuest.verifyAttestation(report.getCpuGeneration(), certsPath, reportPath);
                if (processOutput.getExitCode() != 0) {
                    appendError("Unable to verify the attestation report", processOutput);
                    return false;
                }
                else {
                    appendSuccess("Attestation report correctly verified", processOutput);
                }

                processOutput = snpGuest.displayReport(reportPath);
                if (processOutput.getExitCode() != 0) {
                    appendError("Unable to get the attestation report in human readable format", processOutput);
                    return false;
                }

                result.setDetails(processOutput.getStandardOutput());
            }

            return true;
        }
        catch (Exception ex) {
            String exceptionMessage = Optional.ofNullable(ex.getMessage()).orElse(ex.getClass().getName());
            appendError("Unable to process attestation result: " + exceptionMessage, ex);
        }
        finally {
            result.setProcessOutput(outputBuilder.toString());
        }

        return false;
    }

    private void appendError(String message) {
        appendOutput(message, null);
        LOGGER.error(message);
    }

    private void appendError(String message, ProcessOutput processOutput) {
        appendOutput(message, processOutput);
        LOGGER.error(message);
    }

    private void appendError(String message, Exception ex) {
        appendOutput(message, null);
        LOGGER.error(message, ex);
    }

    private void appendSuccess(String message) {
        appendOutput(message, null);
    }
    private void appendSuccess(String message, ProcessOutput output) {
        appendOutput(message, output);
    }

    private void appendOutput(String message, ProcessOutput processOutput) {
        outputBuilder.append("- ").append(message);

        String processDetails = getProcessOutputDetails(processOutput);
        if (!processDetails.isEmpty()) {
            outputBuilder.append(":")
                .append(System.lineSeparator())
                .append(processDetails);
        }
        else {
            outputBuilder.append(System.lineSeparator());
        }
    }

    private static String getProcessOutputDetails(ProcessOutput processOutput) {
        if (processOutput == null) {
            return "";
        }

        StringBuilder processBuilder = new StringBuilder();
        if (processOutput.getExitCode() != 0) {
            processBuilder.append("- Exit code: %d".formatted(processOutput.getExitCode()).indent(INDENT_SIZE));
        }

        if (processOutput.hasStandardOutput()) {
            processBuilder.append("- Standard output: >".indent(INDENT_SIZE));
            processBuilder.append(processOutput.getStandardOutput().indent(INDENT_SIZE * 2));
        }

        if (processOutput.hasStandardError()) {
            processBuilder.append("- Standard error: >".indent(INDENT_SIZE));
            processBuilder.append(processOutput.getStandardError().indent(INDENT_SIZE * 2));
        }

        return processBuilder.toString();
    }
}
07070100000050000041FD00000000000000000000000268FF7B0A00000000000000000000000000000000000000000000006900000000uyuni-coco-attestation/attestation-module-snpguest/src/main/java/com/suse/coco/module/snpguest/execution07070100000051000081B400000000000000000000000168FF7B0A00001827000000000000000000000000000000000000008600000000uyuni-coco-attestation/attestation-module-snpguest/src/main/java/com/suse/coco/module/snpguest/execution/AbstractSNPGuestWrapper.java/*
 * Copyright (c) 2024 SUSE LLC
 *
 * This software is licensed to you under the GNU General Public License,
 * version 2 (GPLv2). There is NO WARRANTY for this software, express or
 * implied, including the implied warranties of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
 * along with this software; if not, see
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
 */

package com.suse.coco.module.snpguest.execution;

import com.suse.coco.module.snpguest.model.EpycGeneration;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.MarkerManager;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Wrapper to execute the command line tool SNPGuest.
 */
public abstract class AbstractSNPGuestWrapper {

    protected static final Marker STDOUT_MARKER = MarkerManager.getMarker("stdout");

    protected static final Marker STDERR_MARKER = MarkerManager.getMarker("stderr");

    protected final Logger logger;

    protected static final Path SNPGUEST = Path.of("/usr/bin/snpguest");

    protected final Runtime runtime;

    /**
     * Default constructor.
     */
    protected AbstractSNPGuestWrapper() {
        this(Runtime.getRuntime());
    }

    /**
     * Constructor to specify a runtime. For unit testing.
     *
     * @param runtimeIn the runtime used to execute processes
     */
    protected AbstractSNPGuestWrapper(Runtime runtimeIn) {
        this.runtime = runtimeIn;
        this.logger = LogManager.getLogger(getClass());
    }

    /**
     * Fetch the VCEK from the KDS.
     * @param generation Specify the processor model for the certificate chain.
     * @param certsDir Directory to store the certificates in
     * @param report Path to attestation report to use to request VCEK
     * @return the exit code of the fetching process
     * @throws ExecutionException when an error happens during the process execution
     */
    public abstract ProcessOutput fetchVCEK(EpycGeneration generation, Path certsDir, Path report)
            throws ExecutionException;

    /**
     * Verify the certificate chain.
     * @param certsDir Path to directory containing certificate chain
     * @return the exit code of the verification process
     * @throws ExecutionException when an error happens during the process execution
     */
    public ProcessOutput verifyCertificates(Path certsDir) throws ExecutionException {
        return executeProcess(
            SNPGUEST.toString(),
            "verify",
            "certs",
            certsDir.toString()
        );
    }

    /**
     * Verify the attestation report.
     * @param generation Specify the processor model for the certificate chain.
     * @param certsDir Path to directory containing VCEK.
     * @param report Path to attestation report to use for validation.
     * @return the exit code of the verification process
     * @throws ExecutionException when an error happens during the process execution
     */
    public abstract ProcessOutput verifyAttestation(EpycGeneration generation, Path certsDir, Path report)
            throws ExecutionException;

    /**
     * Display the attestation report.
     * @param report Path to attestation report to use for validation.
     * @return the exit code of the verification process
     * @throws ExecutionException when an error happens during the process execution
     */
    public ProcessOutput displayReport(Path report) throws ExecutionException {
        return executeProcess(
            SNPGUEST.toString(),
            "display",
            "report",
            report.toString()
        );
    }

    /**
     * Executes a commandline process
     * @param command the command line to execute
     * @return the exit code returned by the process
     * @throws ExecutionException when an error happens during the process execution
     */
    protected ProcessOutput executeProcess(String... command) throws ExecutionException {
        Process snpguestProcess;

        try {
            if (logger.isDebugEnabled()) {
                logger.debug("Executing {}", Arrays.toString(command));
            }

            snpguestProcess = runtime.exec(command);
        }
        catch (IOException ex) {
            throw new ExecutionException("Unable to create snpguest process", ex);
        }

        ExecutorService executor = Executors.newFixedThreadPool(2);

        try {
            int exitCode = snpguestProcess.waitFor();

            String standardOutputIn = getOutput(snpguestProcess.getInputStream(), STDOUT_MARKER);
            String standardErrorIn = getOutput(snpguestProcess.getErrorStream(), STDERR_MARKER);

            return new ProcessOutput(
                exitCode,
                standardOutputIn,
                standardErrorIn
            );
        }
        catch (InterruptedException ex) {
            Thread.currentThread().interrupt();
            throw new ExecutionException("Unable to get snpguest execution result", ex);
        }
        catch (IOException ex) {
            throw new ExecutionException("Unable to get snpguest execution output", ex);
        }
        finally {
            executor.shutdown();
        }
    }

    private String getOutput(InputStream stream, Marker logMarker) throws IOException {
        StringWriter writer = new StringWriter();

        try (BufferedReader inErr = new BufferedReader(new InputStreamReader(stream))) {
            String line;
            while ((line = inErr.readLine()) != null) {
                if (logger.isDebugEnabled()) {
                    logger.debug(logMarker, line);
                }

                writer.write(line);
                writer.write(System.lineSeparator());
            }

            return writer.toString();
        }
    }

}
07070100000052000081B400000000000000000000000168FF7B0A00000B75000000000000000000000000000000000000007C00000000uyuni-coco-attestation/attestation-module-snpguest/src/main/java/com/suse/coco/module/snpguest/execution/ProcessOutput.java/*
 * Copyright (c) 2024 SUSE LLC
 *
 * This software is licensed to you under the GNU General Public License,
 * version 2 (GPLv2). There is NO WARRANTY for this software, express or
 * implied, including the implied warranties of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
 * along with this software; if not, see
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
 */

package com.suse.coco.module.snpguest.execution;

import java.util.Objects;
import java.util.StringJoiner;

/**
 * Represent the output of the execution of a process
 */
public class ProcessOutput {

    private final int exitCode;

    private final String standardOutput;

    private final String standardError;

    /**
     * Default constructor
     * @param exitCodeIn process exit code
     * @param standardOutputIn the standard output
     * @param standardErrorIn the standard error
     */
    public ProcessOutput(int exitCodeIn, String standardOutputIn, String standardErrorIn) {
        this.exitCode = exitCodeIn;
        this.standardOutput = standardOutputIn;
        this.standardError = standardErrorIn;
    }

    /**
     * Builds an instance with only the exit code
     * @param exitCodeIn process exit code
     */
    public ProcessOutput(int exitCodeIn) {
        this(exitCodeIn, null, null);
    }

    public int getExitCode() {
        return exitCode;
    }

    /**
     * Check if this process output has a non-empty standard output
     * @return true if the standard output is not null and not blank
     */
    public boolean hasStandardOutput() {
        return standardOutput != null && !standardOutput.isBlank();
    }

    public String getStandardOutput() {
        return standardOutput;
    }

    /**
     * Check if this process output has a non-empty standard error
     * @return true if the standard error is not null and not blank
     */
    public boolean hasStandardError() {
        return standardError != null && !standardError.isBlank();
    }

    public String getStandardError() {
        return standardError;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof ProcessOutput that)) {
            return false;
        }
        return exitCode == that.exitCode && Objects.equals(standardOutput,
            that.standardOutput) && Objects.equals(standardError, that.standardError);
    }

    @Override
    public int hashCode() {
        return Objects.hash(exitCode, standardOutput, standardError);
    }

    @Override
    public String toString() {
        return new StringJoiner(", ", ProcessOutput.class.getSimpleName() + "[", "]")
            .add("exitCode=" + getExitCode())
            .add("standerOutput='" + getStandardOutput() + "'")
            .add("standardError='" + getStandardError() + "'")
            .toString();
    }
}
07070100000053000081B400000000000000000000000168FF7B0A00000FB2000000000000000000000000000000000000008500000000uyuni-coco-attestation/attestation-module-snpguest/src/main/java/com/suse/coco/module/snpguest/execution/SNPGuestWrapperFactory.java/*
 * Copyright (c) 2025 SUSE LLC
 *
 * This software is licensed to you under the GNU General Public License,
 * version 2 (GPLv2). There is NO WARRANTY for this software, express or
 * implied, including the implied warranties of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
 * along with this software; if not, see
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
 */

package com.suse.coco.module.snpguest.execution;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class SNPGuestWrapperFactory {
    private static final Logger LOGGER = LogManager.getLogger(SNPGuestWrapperFactory.class);

    private static final SNPGuestVersion FALLBACK_VERSION = SNPGuestVersion.VER_07_BELOW;

    public enum SNPGuestVersion {
        VER_07_BELOW,
        VER_09_ABOVE
    }

    private static SNPGuestVersion theSnpguestVersion = null;

    private static Runtime runtime = Runtime.getRuntime();

    /**
     * Method to specify a runtime. For unit testing.
     * @param runtimeIn the runtime used to execute processes
     */
    public static void setRuntime(Runtime runtimeIn) {
        runtime = runtimeIn;
    }

    /**
     * Creates a SNPGuestWrapper instance with the right version
     *
     * @return the SNPGuestWrapper
     */
    public static AbstractSNPGuestWrapper createSNPGuestWrapper() {
        findVersion();

        return switch (theSnpguestVersion) {
            case VER_07_BELOW -> new SNPGuestWrapperVer07Below();
            case VER_09_ABOVE -> new SNPGuestWrapperVer09Above();
        };
    }

    private SNPGuestWrapperFactory() {
        //Utility classes should not have a public or default constructor.
    }

    private static SNPGuestVersion notFoundSetFallback() {
        LOGGER.error("Unable to get snpguest tool version, assuming version 0.7.1 or below");
        theSnpguestVersion = FALLBACK_VERSION;
        return theSnpguestVersion;
    }

    /**
     * tries to find the snpguest tool version
     * @return the snpguest version
     */
    public static synchronized SNPGuestVersion findVersion() {
        if (null != theSnpguestVersion) {
            return theSnpguestVersion;
        }

        int exitCode = -1;
        String versionString = "";
        try {
            // /usr/bin/rpm -q --queryformat '%{VERSION}\n' snpguest

            String[] versionCommand = {"/usr/bin/rpm", "-q", "--queryformat", "%{VERSION}", "snpguest"};
            Process process = runtime.exec(versionCommand);
            exitCode = process.waitFor();

            try (BufferedReader inErr = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
                versionString = inErr.readLine();
            }
        }
        catch (InterruptedException ex) {
            Thread.currentThread().interrupt();
            LOGGER.error("get version throws an exception {}", ex.getMessage());
            return notFoundSetFallback();
        }
        catch (Exception ex) {
            LOGGER.error("get version throws an exception {}", ex.getMessage());
            return notFoundSetFallback();
        }

        if (exitCode != 0) {
            LOGGER.error("get version has exitCode {}", exitCode);
            return notFoundSetFallback();
        }

        if ((null == versionString) || versionString.isEmpty()) {
            LOGGER.error("get version has output {}", versionString);
            return notFoundSetFallback();
        }

        if (versionString.compareToIgnoreCase("0.9") >= 0) {
            LOGGER.info("detected snpguest version 0.9.1 or above {}", versionString);
            theSnpguestVersion = SNPGuestVersion.VER_09_ABOVE;
        }
        else {
            LOGGER.info("detected snpguest version 0.7.1 or below {}", versionString);
            theSnpguestVersion = SNPGuestVersion.VER_07_BELOW;
        }

        return theSnpguestVersion;
    }
}
07070100000054000081B400000000000000000000000168FF7B0A0000075E000000000000000000000000000000000000008800000000uyuni-coco-attestation/attestation-module-snpguest/src/main/java/com/suse/coco/module/snpguest/execution/SNPGuestWrapperVer07Below.java/*
 * Copyright (c) 2025 SUSE LLC
 *
 * This software is licensed to you under the GNU General Public License,
 * version 2 (GPLv2). There is NO WARRANTY for this software, express or
 * implied, including the implied warranties of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
 * along with this software; if not, see
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
 */

package com.suse.coco.module.snpguest.execution;

import com.suse.coco.module.snpguest.model.EpycGeneration;

import java.nio.file.Path;
import java.util.concurrent.ExecutionException;

public class SNPGuestWrapperVer07Below extends AbstractSNPGuestWrapper {
    /**
     * Default constructor.
     */
    public SNPGuestWrapperVer07Below() {
        super();
    }

    /**
     * Constructor to specify a runtime. For unit testing.
     *
     * @param runtimeIn the runtime used to execute processes
     */
    public SNPGuestWrapperVer07Below(Runtime runtimeIn) {
        super(runtimeIn);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public ProcessOutput fetchVCEK(EpycGeneration generation, Path certsDir, Path report) throws ExecutionException {
        return executeProcess(
                SNPGUEST.toString(),
                "fetch",
                "vcek",
                "DER",
                generation.name().toLowerCase(),
                certsDir.toString(),
                report.toString()
        );
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public ProcessOutput verifyAttestation(EpycGeneration generation, Path certsDir, Path report)
            throws ExecutionException {
        return executeProcess(
                SNPGUEST.toString(),
                "verify",
                "attestation",
                certsDir.toString(),
                report.toString()
        );
    }
}
07070100000055000081B400000000000000000000000168FF7B0A000007C9000000000000000000000000000000000000008800000000uyuni-coco-attestation/attestation-module-snpguest/src/main/java/com/suse/coco/module/snpguest/execution/SNPGuestWrapperVer09Above.java/*
 * Copyright (c) 2025 SUSE LLC
 *
 * This software is licensed to you under the GNU General Public License,
 * version 2 (GPLv2). There is NO WARRANTY for this software, express or
 * implied, including the implied warranties of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
 * along with this software; if not, see
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
 */

package com.suse.coco.module.snpguest.execution;

import com.suse.coco.module.snpguest.model.EpycGeneration;

import java.nio.file.Path;
import java.util.concurrent.ExecutionException;

public class SNPGuestWrapperVer09Above extends AbstractSNPGuestWrapper {
    /**
     * Default constructor.
     */
    public SNPGuestWrapperVer09Above() {
        super();
    }

    /**
     * Constructor to specify a runtime. For unit testing.
     *
     * @param runtimeIn the runtime used to execute processes
     */
    public SNPGuestWrapperVer09Above(Runtime runtimeIn) {
        super(runtimeIn);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public ProcessOutput fetchVCEK(EpycGeneration generation, Path certsDir, Path report)
            throws ExecutionException {
        return executeProcess(
                SNPGUEST.toString(),
                "fetch",
                "vcek",
                "-p",
                generation.name().toLowerCase(),
                "DER",
                certsDir.toString(),
                report.toString()
        );
    }


    /**
     * {@inheritDoc}
     */
    @Override
    public ProcessOutput verifyAttestation(EpycGeneration generation, Path certsDir, Path report)
            throws ExecutionException {
        return executeProcess(
                SNPGUEST.toString(),
                "verify",
                "attestation",
                "-p",
                generation.name().toLowerCase(),
                certsDir.toString(),
                report.toString()
        );
    }

}
07070100000056000041FD00000000000000000000000268FF7B0A00000000000000000000000000000000000000000000006200000000uyuni-coco-attestation/attestation-module-snpguest/src/main/java/com/suse/coco/module/snpguest/io07070100000057000081B400000000000000000000000168FF7B0A00000AD8000000000000000000000000000000000000007D00000000uyuni-coco-attestation/attestation-module-snpguest/src/main/java/com/suse/coco/module/snpguest/io/VerificationDirectory.java/*
 * Copyright (c) 2024 SUSE LLC
 *
 * This software is licensed to you under the GNU General Public License,
 * version 2 (GPLv2). There is NO WARRANTY for this software, express or
 * implied, including the implied warranties of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
 * along with this software; if not, see
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
 */

package com.suse.coco.module.snpguest.io;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Comparator;
import java.util.stream.Stream;

/**
 * A class representing a verification directory, used to verify an attestation report. All files and the
 * directory itself will be removed on resource clean up.
 */
public class VerificationDirectory implements AutoCloseable {

    /**
     * The report binary file
     */
    public static final String REPORT_FILE = "report.bin";

    /**
     * The VCEK file name
     */
    public static final String VCEK_FILE = "vcek.der";

    /**
     * The VLEK file name
     */
    public static final String VLEK_FILE = "vlek.pem";

    private final Path path;

    VerificationDirectory(Path pathIn) {
        this.path = pathIn;
    }

    public Path getBasePath() {
        return path;
    }

    public Path getCertsPath() {
        return path.resolve("certs");
    }

    public Path getReportPath() {
        return path.resolve(REPORT_FILE);
    }

    public Path getVCEKPath() {
        return getCertsPath().resolve(VCEK_FILE);
    }

    /**
     * Check if the VCEK file exists
     * @return true if the file {@link #VCEK_FILE} exists.
     */
    public boolean isVCEKAvailable() {
        return Files.exists(getCertsPath().resolve(VCEK_FILE));
    }

    /**
     * Check if the VLEK file exists
     * @return true if the file {@link #VLEK_FILE} exists.
     */
    public boolean isVLEKAvailable() {
        return Files.exists(getCertsPath().resolve(VLEK_FILE));
    }

    /**
     * Removes this verification directory and all the files contained within it
     * @throws IOException when the deletion fails.
     */
    @Override
    public void close() throws IOException {
        try (Stream<Path> fileStream = Files.walk(path)) {
            fileStream
                .sorted(Comparator.reverseOrder())
                .forEach(file -> {
                    try {
                        Files.delete(file);
                    }
                    catch (IOException ioEx) {
                        throw new UncheckedIOException(ioEx);
                    }
                });
        }
        catch (UncheckedIOException ex) {
            throw ex.getCause();
        }
    }
}
07070100000058000081B400000000000000000000000168FF7B0A000014DC000000000000000000000000000000000000008500000000uyuni-coco-attestation/attestation-module-snpguest/src/main/java/com/suse/coco/module/snpguest/io/VerificationDirectoryProvider.java/*
 * Copyright (c) 2024 SUSE LLC
 *
 * This software is licensed to you under the GNU General Public License,
 * version 2 (GPLv2). There is NO WARRANTY for this software, express or
 * implied, including the implied warranties of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
 * along with this software; if not, see
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
 */

package com.suse.coco.module.snpguest.io;

import com.suse.coco.module.snpguest.execution.AbstractSNPGuestWrapper;
import com.suse.coco.module.snpguest.model.AttestationReport;
import com.suse.coco.module.snpguest.model.EpycGeneration;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.Set;
import java.util.stream.Stream;

/**
 * Creates {@link VerificationDirectory} used to store the data needed by the SNPGuest tool
 * to perform validation.
 *
 * @see AbstractSNPGuestWrapper
 */
public class VerificationDirectoryProvider {
    public static final Path DEFAULT_CERTIFICATION_PATH = Path.of("/usr/share/coco-attestation/certs");

    private static final Logger LOGGER = LogManager.getLogger(VerificationDirectoryProvider.class);

    private final Path baseWorkingDir;

    private final Path sourceCertificatesDir;

    /**
     * Default constructor. Creates the working directory in system temporary folder and reads certificates
     * from {@link #DEFAULT_CERTIFICATION_PATH}.
     */
    public VerificationDirectoryProvider() {
        this.baseWorkingDir = null;
        this.sourceCertificatesDir = DEFAULT_CERTIFICATION_PATH;
    }

    VerificationDirectoryProvider(Path baseWorkingDirIn, Path sourceCertificatesDirIn) {
        this.baseWorkingDir = baseWorkingDirIn;
        this.sourceCertificatesDir = sourceCertificatesDirIn;
    }

    /**
     * Creates and prepares a new verification directory. The creation process involves:
     * <ol>
     *     <li>Creating a folder</li>
     *     <li>Copying the certificates into the certs/ subdirectory</li>
     *     <li>Save the report under the file "report.bin"</li>
     * </ol>
     * @param resultId the id of the attestation result linked to this report
     * @param report the attestation report
     * @return the created {@link VerificationDirectory}
     * @throws IOException when something went wrong during the preparation of the directory.
     */
    public VerificationDirectory createDirectoryFor(long resultId, AttestationReport report) throws IOException {
        EpycGeneration cpuGeneration = report.getCpuGeneration();

        // Verify if the source directory exist for this generation
        Path cpuSpecificCerts = sourceCertificatesDir.resolve(cpuGeneration.name().toLowerCase());
        if (!Files.isReadable(cpuSpecificCerts) || !Files.isDirectory(cpuSpecificCerts)) {
            throw new FileNotFoundException("Cannot find certificate for cpu generation " + cpuGeneration);
        }

        // Create the temporary base directory
        Path basePath = createTemporaryPath("snp-guest-worker-" + resultId + "-");

        // Create the certs sub-directory
        Path certs = basePath.resolve("certs");
        Files.createDirectories(certs);

        // Copy the standard certificates
        try (Stream<Path> certsStream = Files.list(cpuSpecificCerts)) {
            certsStream
                .filter(file -> !Files.isDirectory(file) && Files.isReadable(file))
                .forEach(file -> {
                    try {
                        Files.copy(file, certs.resolve(file.getFileName()));
                    }
                    catch (IOException ioEx) {
                        throw new UncheckedIOException(ioEx);
                    }
                });
        }
        catch (UncheckedIOException ex) {
            throw ex.getCause();
        }

        // Retrieve and save VLEK certificate if needed
        if (report.isUsingVlekAttestation()) {
            String vlekCertificate = report.getVlekCertificate();

            try {
                Files.write(certs.resolve(VerificationDirectory.VLEK_FILE), vlekCertificate.getBytes());
            }
            catch (IOException ioEx) {
                throw new UncheckedIOException(ioEx);
            }
        }

        // Store the report in the file report.bin
        Files.write(basePath.resolve("report.bin"), report.getReport());

        LOGGER.debug("Created directory {} for verifying of result {} using {} certs",
            basePath, resultId, cpuGeneration);

        return new VerificationDirectory(basePath);
    }

    private Path createTemporaryPath(String prefix) throws IOException {
        FileAttribute<Set<PosixFilePermission>> attr =
                PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("rwx------"));

        if (baseWorkingDir == null) {
            return Files.createTempDirectory(prefix, attr);
        }

        return Files.createTempDirectory(baseWorkingDir, prefix, attr);
    }
}
07070100000059000041FD00000000000000000000000268FF7B0A00000000000000000000000000000000000000000000006500000000uyuni-coco-attestation/attestation-module-snpguest/src/main/java/com/suse/coco/module/snpguest/model0707010000005A000081B400000000000000000000000168FF7B0A00000AFC000000000000000000000000000000000000007C00000000uyuni-coco-attestation/attestation-module-snpguest/src/main/java/com/suse/coco/module/snpguest/model/AttestationReport.java/*
 * Copyright (c) 2024 SUSE LLC
 *
 * This software is licensed to you under the GNU General Public License,
 * version 2 (GPLv2). There is NO WARRANTY for this software, express or
 * implied, including the implied warranties of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
 * along with this software; if not, see
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
 */

package com.suse.coco.module.snpguest.model;

import java.util.Arrays;
import java.util.Objects;
import java.util.StringJoiner;

/**
 * Represent the data required to verify an attestation report with SNPGuest.
 */
public class AttestationReport {

    private long id;

    private EpycGeneration cpuGeneration;

    private byte[] randomNonce;

    private byte[] report;

    private String vlekCertificate;

    public long getId() {
        return id;
    }

    public void setId(long idIn) {
        this.id = idIn;
    }

    public EpycGeneration getCpuGeneration() {
        return cpuGeneration;
    }

    public void setCpuGeneration(EpycGeneration generationIn) {
        this.cpuGeneration = generationIn;
    }

    public byte[] getRandomNonce() {
        return randomNonce;
    }

    public void setRandomNonce(byte[] randomNonceIn) {
        this.randomNonce = randomNonceIn;
    }

    public byte[] getReport() {
        return report;
    }

    public void setReport(byte[] reportIn) {
        this.report = reportIn;
    }

    public String getVlekCertificate() {
        return vlekCertificate;
    }

    public void setVlekCertificate(String vlekCertificateIn) {
        this.vlekCertificate = vlekCertificateIn;
    }

    public boolean isUsingVlekAttestation() {
        //The Versioned Loaded Endorsement Key (VLEK) is a versioned signing key that is certified by AMD
        //and used by the AMD CPU to sign the AMD SEV-SNP attestation reports

        //if VLEK fails, we assume VCEK.
        return (null != vlekCertificate) && (!vlekCertificate.isEmpty());
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof AttestationReport attestationReport)) {
            return false;
        }
        return id == attestationReport.id &&
            cpuGeneration == attestationReport.cpuGeneration &&
            Arrays.equals(randomNonce, attestationReport.randomNonce);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, cpuGeneration, Arrays.hashCode(randomNonce));
    }

    @Override
    public String toString() {
        return new StringJoiner(", ", AttestationReport.class.getSimpleName() + "[", "]")
            .add("id=" + id)
            .add("cpuGeneration=" + cpuGeneration)
            .toString();
    }
}
0707010000005B000081B400000000000000000000000168FF7B0A000002A9000000000000000000000000000000000000007900000000uyuni-coco-attestation/attestation-module-snpguest/src/main/java/com/suse/coco/module/snpguest/model/EpycGeneration.java/*
 * Copyright (c) 2024 SUSE LLC
 *
 * This software is licensed to you under the GNU General Public License,
 * version 2 (GPLv2). There is NO WARRANTY for this software, express or
 * implied, including the implied warranties of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
 * along with this software; if not, see
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
 */

package com.suse.coco.module.snpguest.model;

/**
 * EPYC CPU Model, used by SNPGuest to identify what certificates to use during verification.
 */
public enum EpycGeneration {
    UNKNOWN,
    MILAN,
    GENOA,
    BERGAMO,
    SIENA,
    TURIN
}

0707010000005C000041FD00000000000000000000000468FF7B0A00000000000000000000000000000000000000000000004600000000uyuni-coco-attestation/attestation-module-snpguest/src/main/resources0707010000005D000041FD00000000000000000000000368FF7B0A00000000000000000000000000000000000000000000004F00000000uyuni-coco-attestation/attestation-module-snpguest/src/main/resources/META-INF0707010000005E000041FD00000000000000000000000268FF7B0A00000000000000000000000000000000000000000000005800000000uyuni-coco-attestation/attestation-module-snpguest/src/main/resources/META-INF/services0707010000005F000081B400000000000000000000000168FF7B0A0000002D000000000000000000000000000000000000007F00000000uyuni-coco-attestation/attestation-module-snpguest/src/main/resources/META-INF/services/com.suse.coco.module.AttestationModulecom.suse.coco.module.snpguest.SNPGuestModule
07070100000060000041FD00000000000000000000000268FF7B0A00000000000000000000000000000000000000000000004E00000000uyuni-coco-attestation/attestation-module-snpguest/src/main/resources/mappers07070100000061000081B400000000000000000000000168FF7B0A000005DF000000000000000000000000000000000000005B00000000uyuni-coco-attestation/attestation-module-snpguest/src/main/resources/mappers/snpguest.xml<?xml version="1.0" encoding="UTF-8"?>
<!--
  ~ Copyright (c) 2024 SUSE LLC
  ~
  ~ This software is licensed to you under the GNU General Public License,
  ~ version 2 (GPLv2). There is NO WARRANTY for this software, express or
  ~ implied, including the implied warranties of MERCHANTABILITY or FITNESS
  ~ FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
  ~ along with this software; if not, see
  ~ http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
  -->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="SNPGuestModule">

    <select id="retrieveReport" parameterType="Long" resultType="com.suse.coco.module.snpguest.model.AttestationReport">
          SELECT id
                    , CASE
                        WHEN env_type = 1 THEN 'MILAN'
                        WHEN env_type = 2 THEN 'GENOA'
                        WHEN env_type = 3 THEN 'BERGAMO'
                        WHEN env_type = 4 THEN 'SIENA'
                        WHEN env_type = 5 THEN 'TURIN'
                        ELSE 'UNKNOWN'
                      END AS cpu_generation
                    , decode(in_data->>'nonce', 'base64') AS random_nonce
                    , decode(out_data->>'mgr_snpguest_report', 'base64') AS report
                    , out_data->>'mgr_vlek_certificate' AS vlek_certificate
            FROM suseServerCoCoAttestationReport
           WHERE id = #{reportId}
    </select>
</mapper>
07070100000062000041FD00000000000000000000000368FF7B0A00000000000000000000000000000000000000000000003F00000000uyuni-coco-attestation/attestation-module-snpguest/src/package07070100000063000041FD00000000000000000000000768FF7B0A00000000000000000000000000000000000000000000004500000000uyuni-coco-attestation/attestation-module-snpguest/src/package/certs07070100000064000041FD00000000000000000000000268FF7B0A00000000000000000000000000000000000000000000004D00000000uyuni-coco-attestation/attestation-module-snpguest/src/package/certs/bergamo07070100000065000081B400000000000000000000000168FF7B0A000008E5000000000000000000000000000000000000005500000000uyuni-coco-attestation/attestation-module-snpguest/src/package/certs/bergamo/ark.pem-----BEGIN CERTIFICATE-----
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
/wMz1R1BHg==
-----END CERTIFICATE-----
07070100000066000081B400000000000000000000000168FF7B0A00000915000000000000000000000000000000000000005500000000uyuni-coco-attestation/attestation-module-snpguest/src/package/certs/bergamo/ask.pem-----BEGIN CERTIFICATE-----
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
-----END CERTIFICATE-----
07070100000067000081B400000000000000000000000168FF7B0A0000091E000000000000000000000000000000000000005600000000uyuni-coco-attestation/attestation-module-snpguest/src/package/certs/bergamo/asvk.pem-----BEGIN CERTIFICATE-----
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
2cko
-----END CERTIFICATE-----
07070100000068000041FD00000000000000000000000268FF7B0A00000000000000000000000000000000000000000000004B00000000uyuni-coco-attestation/attestation-module-snpguest/src/package/certs/genoa07070100000069000081B400000000000000000000000168FF7B0A000008E5000000000000000000000000000000000000005300000000uyuni-coco-attestation/attestation-module-snpguest/src/package/certs/genoa/ark.pem-----BEGIN CERTIFICATE-----
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
/wMz1R1BHg==
-----END CERTIFICATE-----
0707010000006A000081B400000000000000000000000168FF7B0A00000915000000000000000000000000000000000000005300000000uyuni-coco-attestation/attestation-module-snpguest/src/package/certs/genoa/ask.pem-----BEGIN CERTIFICATE-----
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
-----END CERTIFICATE-----
0707010000006B000081B400000000000000000000000168FF7B0A0000091E000000000000000000000000000000000000005400000000uyuni-coco-attestation/attestation-module-snpguest/src/package/certs/genoa/asvk.pem-----BEGIN CERTIFICATE-----
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
2cko
-----END CERTIFICATE-----
0707010000006C000041FD00000000000000000000000268FF7B0A00000000000000000000000000000000000000000000004B00000000uyuni-coco-attestation/attestation-module-snpguest/src/package/certs/milan0707010000006D000081B400000000000000000000000168FF7B0A000008E5000000000000000000000000000000000000005300000000uyuni-coco-attestation/attestation-module-snpguest/src/package/certs/milan/ark.pem-----BEGIN CERTIFICATE-----
MIIGYzCCBBKgAwIBAgIDAQAAMEYGCSqGSIb3DQEBCjA5oA8wDQYJYIZIAWUDBAIC
BQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWUDBAICBQCiAwIBMKMDAgEBMHsxFDAS
BgNVBAsMC0VuZ2luZWVyaW5nMQswCQYDVQQGEwJVUzEUMBIGA1UEBwwLU2FudGEg
Q2xhcmExCzAJBgNVBAgMAkNBMR8wHQYDVQQKDBZBZHZhbmNlZCBNaWNybyBEZXZp
Y2VzMRIwEAYDVQQDDAlBUkstTWlsYW4wHhcNMjAxMDIyMTcyMzA1WhcNNDUxMDIy
MTcyMzA1WjB7MRQwEgYDVQQLDAtFbmdpbmVlcmluZzELMAkGA1UEBhMCVVMxFDAS
BgNVBAcMC1NhbnRhIENsYXJhMQswCQYDVQQIDAJDQTEfMB0GA1UECgwWQWR2YW5j
ZWQgTWljcm8gRGV2aWNlczESMBAGA1UEAwwJQVJLLU1pbGFuMIICIjANBgkqhkiG
9w0BAQEFAAOCAg8AMIICCgKCAgEA0Ld52RJOdeiJlqK2JdsVmD7FktuotWwX1fNg
W41XY9Xz1HEhSUmhLz9Cu9DHRlvgJSNxbeYYsnJfvyjx1MfU0V5tkKiU1EesNFta
1kTA0szNisdYc9isqk7mXT5+KfGRbfc4V/9zRIcE8jlHN61S1ju8X93+6dxDUrG2
SzxqJ4BhqyYmUDruPXJSX4vUc01P7j98MpqOS95rORdGHeI52Naz5m2B+O+vjsC0
60d37jY9LFeuOP4Meri8qgfi2S5kKqg/aF6aPtuAZQVR7u3KFYXP59XmJgtcog05
gmI0T/OitLhuzVvpZcLph0odh/1IPXqx3+MnjD97A7fXpqGd/y8KxX7jksTEzAOg
bKAeam3lm+3yKIcTYMlsRMXPcjNbIvmsBykD//xSniusuHBkgnlENEWx1UcbQQrs
+gVDkuVPhsnzIRNgYvM48Y+7LGiJYnrmE8xcrexekBxrva2V9TJQqnN3Q53kt5vi
Qi3+gCfmkwC0F0tirIZbLkXPrPwzZ0M9eNxhIySb2npJfgnqz55I0u33wh4r0ZNQ
eTGfw03MBUtyuzGesGkcw+loqMaq1qR4tjGbPYxCvpCq7+OgpCCoMNit2uLo9M18
fHz10lOMT8nWAUvRZFzteXCm+7PHdYPlmQwUw3LvenJ/ILXoQPHfbkH0CyPfhl1j
WhJFZasCAwEAAaN+MHwwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSFrBrRQ/fI
rFXUxR1BSKvVeErUUzAPBgNVHRMBAf8EBTADAQH/MDoGA1UdHwQzMDEwL6AtoCuG
KWh0dHBzOi8va2RzaW50Zi5hbWQuY29tL3ZjZWsvdjEvTWlsYW4vY3JsMEYGCSqG
SIb3DQEBCjA5oA8wDQYJYIZIAWUDBAICBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZI
AWUDBAICBQCiAwIBMKMDAgEBA4ICAQC6m0kDp6zv4Ojfgy+zleehsx6ol0ocgVel
ETobpx+EuCsqVFRPK1jZ1sp/lyd9+0fQ0r66n7kagRk4Ca39g66WGTJMeJdqYriw
STjjDCKVPSesWXYPVAyDhmP5n2v+BYipZWhpvqpaiO+EGK5IBP+578QeW/sSokrK
dHaLAxG2LhZxj9aF73fqC7OAJZ5aPonw4RE299FVarh1Tx2eT3wSgkDgutCTB1Yq
zT5DuwvAe+co2CIVIzMDamYuSFjPN0BCgojl7V+bTou7dMsqIu/TW/rPCX9/EUcp
KGKqPQ3P+N9r1hjEFY1plBg93t53OOo49GNI+V1zvXPLI6xIFVsh+mto2RtgEX/e
pmMKTNN6psW88qg7c1hTWtN6MbRuQ0vm+O+/2tKBF2h8THb94OvvHHoFDpbCELlq
HnIYhxy0YKXGyaW1NjfULxrrmxVW4wcn5E8GddmvNa6yYm8scJagEi13mhGu4Jqh
3QU3sf8iUSUr09xQDwHtOQUVIqx4maBZPBtSMf+qUDtjXSSq8lfWcd8bLr9mdsUn
JZJ0+tuPMKmBnSH860llKk+VpVQsgqbzDIvOLvD6W1Umq25boxCYJ+TuBoa4s+HH
CViAvgT9kf/rBq1d+ivj6skkHxuzcxbk1xv6ZGxrteJxVH7KlX7YRdZ6eARKwLe4
AFZEAwoKCQ==
-----END CERTIFICATE-----
0707010000006E000081B400000000000000000000000168FF7B0A00000915000000000000000000000000000000000000005300000000uyuni-coco-attestation/attestation-module-snpguest/src/package/certs/milan/ask.pem-----BEGIN CERTIFICATE-----
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
-----END CERTIFICATE-----
0707010000006F000081B400000000000000000000000168FF7B0A0000091E000000000000000000000000000000000000005400000000uyuni-coco-attestation/attestation-module-snpguest/src/package/certs/milan/asvk.pem-----BEGIN CERTIFICATE-----
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
0YPk
-----END CERTIFICATE-----
07070100000070000041FD00000000000000000000000268FF7B0A00000000000000000000000000000000000000000000004B00000000uyuni-coco-attestation/attestation-module-snpguest/src/package/certs/siena07070100000071000081B400000000000000000000000168FF7B0A000008E5000000000000000000000000000000000000005300000000uyuni-coco-attestation/attestation-module-snpguest/src/package/certs/siena/ark.pem-----BEGIN CERTIFICATE-----
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
/wMz1R1BHg==
-----END CERTIFICATE-----
07070100000072000081B400000000000000000000000168FF7B0A00000915000000000000000000000000000000000000005300000000uyuni-coco-attestation/attestation-module-snpguest/src/package/certs/siena/ask.pem-----BEGIN CERTIFICATE-----
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
-----END CERTIFICATE-----
07070100000073000081B400000000000000000000000168FF7B0A0000091E000000000000000000000000000000000000005400000000uyuni-coco-attestation/attestation-module-snpguest/src/package/certs/siena/asvk.pem-----BEGIN CERTIFICATE-----
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
2cko
-----END CERTIFICATE-----
07070100000074000041FD00000000000000000000000268FF7B0A00000000000000000000000000000000000000000000004B00000000uyuni-coco-attestation/attestation-module-snpguest/src/package/certs/turin07070100000075000081B400000000000000000000000168FF7B0A000008E5000000000000000000000000000000000000005300000000uyuni-coco-attestation/attestation-module-snpguest/src/package/certs/turin/ark.pem-----BEGIN CERTIFICATE-----
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
zpacMwFusA==
-----END CERTIFICATE-----
07070100000076000081B400000000000000000000000168FF7B0A00000915000000000000000000000000000000000000005300000000uyuni-coco-attestation/attestation-module-snpguest/src/package/certs/turin/ask.pem-----BEGIN CERTIFICATE-----
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
-----END CERTIFICATE-----
07070100000077000081B400000000000000000000000168FF7B0A0000091E000000000000000000000000000000000000005400000000uyuni-coco-attestation/attestation-module-snpguest/src/package/certs/turin/asvk.pem-----BEGIN CERTIFICATE-----
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
SUxr
-----END CERTIFICATE-----
07070100000078000041FD00000000000000000000000468FF7B0A00000000000000000000000000000000000000000000003C00000000uyuni-coco-attestation/attestation-module-snpguest/src/test07070100000079000041FD00000000000000000000000368FF7B0A00000000000000000000000000000000000000000000004100000000uyuni-coco-attestation/attestation-module-snpguest/src/test/java0707010000007A000041FD00000000000000000000000368FF7B0A00000000000000000000000000000000000000000000004500000000uyuni-coco-attestation/attestation-module-snpguest/src/test/java/com0707010000007B000041FD00000000000000000000000368FF7B0A00000000000000000000000000000000000000000000004A00000000uyuni-coco-attestation/attestation-module-snpguest/src/test/java/com/suse0707010000007C000041FD00000000000000000000000368FF7B0A00000000000000000000000000000000000000000000004F00000000uyuni-coco-attestation/attestation-module-snpguest/src/test/java/com/suse/coco0707010000007D000041FD00000000000000000000000368FF7B0A00000000000000000000000000000000000000000000005600000000uyuni-coco-attestation/attestation-module-snpguest/src/test/java/com/suse/coco/module0707010000007E000041FD00000000000000000000000468FF7B0A00000000000000000000000000000000000000000000005F00000000uyuni-coco-attestation/attestation-module-snpguest/src/test/java/com/suse/coco/module/snpguest0707010000007F000081B400000000000000000000000168FF7B0A00005D91000000000000000000000000000000000000007700000000uyuni-coco-attestation/attestation-module-snpguest/src/test/java/com/suse/coco/module/snpguest/SNPGuestWorkerTest.java/*
 * Copyright (c) 2024 SUSE LLC
 *
 * This software is licensed to you under the GNU General Public License,
 * version 2 (GPLv2). There is NO WARRANTY for this software, express or
 * implied, including the implied warranties of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
 * along with this software; if not, see
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
 */

package com.suse.coco.module.snpguest;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;

import com.suse.coco.model.AttestationResult;
import com.suse.coco.model.AttestationStatus;
import com.suse.coco.module.snpguest.execution.AbstractSNPGuestWrapper;
import com.suse.coco.module.snpguest.execution.ProcessOutput;
import com.suse.coco.module.snpguest.io.VerificationDirectory;
import com.suse.coco.module.snpguest.io.VerificationDirectoryProvider;
import com.suse.coco.module.snpguest.model.AttestationReport;
import com.suse.coco.module.snpguest.model.EpycGeneration;
import com.suse.common.io.ByteSequenceFinder;

import org.apache.ibatis.exceptions.PersistenceException;
import org.apache.ibatis.session.SqlSession;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.concurrent.ExecutionException;

@ExtendWith(MockitoExtension.class)
class SNPGuestWorkerTest {

    private static final Path MOCK_CERTS_DIR = Path.of("fakedir/certs");
    private static final Path MOCK_REPORT_FILE = Path.of("fakedir/report.bin");

    private AttestationResult result;

    private AttestationReport report;

    @Mock
    private SqlSession session;

    @Mock
    private VerificationDirectoryProvider directoryProvider;

    @Mock
    private VerificationDirectory directory;

    @Mock
    private AbstractSNPGuestWrapper snpWrapper;

    @Mock
    private ByteSequenceFinder sequenceFinder;

    private SNPGuestWorker worker;

    @BeforeEach
    void setup() {
        result = new AttestationResult();
        result.setId(1L);
        result.setStatus(AttestationStatus.PENDING);
        result.setReportId(5L);

        report = new AttestationReport();
        report.setId(5L);
        report.setVlekCertificate(null);

        // Common mocking
        when(session.selectOne("SNPGuestModule.retrieveReport", 5L)).thenReturn(report);

        worker = new SNPGuestWorker(directoryProvider, snpWrapper, sequenceFinder);
    }

    private String getCertificateSuccessfulFetchString(boolean usingVlek) {

        if (usingVlek) {
            return """
                    - VLEK certification retrieved successfully
                    """;
        }

        return """
                - VCEK fetched successfully:
                    - Standard output: >
                        Attempting to fetch VCEK...
                        Fetch ok
                """;
    }

    @Test
    @DisplayName("Rejects report if an exception is thrown")
    void rejectsWhenExceptionHappens() {

        when(session.selectOne("SNPGuestModule.retrieveReport", 5L))
            .thenThrow(PersistenceException.class);

        assertFalse(worker.process(session, result));
        assertEquals("""
                - Unable to process attestation result: org.apache.ibatis.exceptions.PersistenceException
                """,
            result.getProcessOutput()
        );

        // Verify no files have been created
        verifyNoInteractions(directoryProvider);

        // Verify no checks have been performed
        verifyNoInteractions(sequenceFinder);
        verifyNoInteractions(snpWrapper);
    }

    @Test
    @DisplayName("Rejects report if the data is not found")
    void rejectsWhenReportIsNotFound() {

        when(session.selectOne("SNPGuestModule.retrieveReport", 5L))
            .thenReturn(null);

        assertFalse(worker.process(session, result));
        assertEquals("""
                - Unable to retrieve attestation report for result
                """,
            result.getProcessOutput()
        );

        // Verify no files have been created
        verifyNoInteractions(directoryProvider);

        // Verify no checks have been performed
        verifyNoInteractions(sequenceFinder);
        verifyNoInteractions(snpWrapper);
    }

    @Test
    @DisplayName("Rejects report if processor model is unknown")
    void rejectsWithUnknownProcessorModel() {
        // Set the model as UNKNOWN
        report.setCpuGeneration(EpycGeneration.UNKNOWN);

        assertFalse(worker.process(session, result));
        assertEquals("""
                - Unable to identify Epyc processor generation for attestation report
                """,
            result.getProcessOutput()
        );

        // Verify no files have been created
        verifyNoInteractions(directoryProvider);

        // Verify no checks have been performed
        verifyNoInteractions(sequenceFinder);
        verifyNoInteractions(snpWrapper);
    }

    @Test
    @DisplayName("Rejects report if nonce is null")
    void rejectsWithNullNonce() {
        report.setRandomNonce(null);
        report.setReport("REPORT".getBytes(StandardCharsets.UTF_8));

        assertFalse(worker.process(session, result));
        assertEquals("""
                - Unable to verify: randomized nonce not found
                """,
            result.getProcessOutput()
        );

        // Verify no files have been created
        verifyNoInteractions(directoryProvider);

        // Verify no checks have been performed
        verifyNoInteractions(sequenceFinder);
        verifyNoInteractions(snpWrapper);
    }

    @Test
    @DisplayName("Rejects report if nonce is empty")
    void rejectsWithEmptyNonce() {
        report.setRandomNonce(new byte[0]);
        report.setReport("REPORT".getBytes(StandardCharsets.UTF_8));

        assertFalse(worker.process(session, result));
        assertEquals("""
                - Unable to verify: randomized nonce not found
                """,
            result.getProcessOutput()
        );

        // Verify no files have been created
        verifyNoInteractions(directoryProvider);

        // Verify no checks have been performed
        verifyNoInteractions(sequenceFinder);
        verifyNoInteractions(snpWrapper);
    }

    @Test
    @DisplayName("Rejects report if data is null")
    void rejectsWithNullReport() {
        report.setReport(null);
        report.setRandomNonce("NONCE".getBytes(StandardCharsets.UTF_8));

        assertFalse(worker.process(session, result));
        assertEquals("""
                - Unable to verify: attestation report not found
                """,
            result.getProcessOutput()
        );

        // Verify no files have been created
        verifyNoInteractions(directoryProvider);

        // Verify no checks have been performed
        verifyNoInteractions(sequenceFinder);
        verifyNoInteractions(snpWrapper);
    }

    @Test
    @DisplayName("Rejects report if data is empty")
    void rejectsWithEmptyReport() {
        report.setReport(new byte[0]);
        report.setRandomNonce("NONCE".getBytes(StandardCharsets.UTF_8));

        assertFalse(worker.process(session, result));
        assertEquals("""
                - Unable to verify: attestation report not found
                """,
            result.getProcessOutput()
        );

        // Verify no files have been created
        verifyNoInteractions(directoryProvider);

        // Verify no checks have been performed
        verifyNoInteractions(sequenceFinder);
        verifyNoInteractions(snpWrapper);
    }

    @Test
    @DisplayName("Rejects report if nonce is not found in sequence")
    void rejectsWithoutTheNonceInTheReport() throws IOException {
        report.setCpuGeneration(EpycGeneration.MILAN);
        report.setRandomNonce("NONCE".getBytes(StandardCharsets.UTF_8));
        report.setReport("REPORT THAT DOES NOT CONTAIN THE RANDOM SEQUENCE".getBytes(StandardCharsets.UTF_8));

        when(directoryProvider.createDirectoryFor(1L, report)).thenReturn(directory);

        // Nonce verification fails
        when(sequenceFinder.search(report.getReport())).thenReturn(-1);

        assertFalse(worker.process(session, result));
        assertEquals("""
                - The report does not contain the expected random nonce
                """,
            result.getProcessOutput()
        );

        // Verify the required files have been created and deleted
        verify(directoryProvider).createDirectoryFor(1L, report);
        verify(directory).close();

        // Verify the sequence has been searched
        verify(sequenceFinder).setSequence(report.getRandomNonce());
        verify(sequenceFinder).search(report.getReport());

        // Verify no checks have been performed
        verifyNoInteractions(snpWrapper);
    }

    @Test
    @DisplayName("Rejects report if the VCEK could not be retrieved")
    void rejectsWhenVECKIsNotRetrieved() throws IOException, ExecutionException {
        report.setCpuGeneration(EpycGeneration.MILAN);
        report.setRandomNonce("NONCE".getBytes(StandardCharsets.UTF_8));
        report.setReport("REPORT WITH NONCE".getBytes(StandardCharsets.UTF_8));

        when(directoryProvider.createDirectoryFor(1L, report)).thenReturn(directory);

        when(directory.getCertsPath()).thenReturn(MOCK_CERTS_DIR);
        when(directory.getReportPath()).thenReturn(MOCK_REPORT_FILE);

        // Pass nonce verification
        when(sequenceFinder.search(report.getReport())).thenReturn(12);

        // Fetching fails
        when(snpWrapper.fetchVCEK(EpycGeneration.MILAN, MOCK_CERTS_DIR, MOCK_REPORT_FILE))
            .thenReturn(new ProcessOutput(-1, "", "Fetch FAILED"));

        assertFalse(worker.process(session, result));
        assertEquals("""
                - The report contains the expected random nonce
                - Unable to retrieve VCEK file:
                    - Exit code: -1
                    - Standard error: >
                        Fetch FAILED
                """,
            result.getProcessOutput()
        );

        // Verify the required files have been created and deleted
        verify(directoryProvider).createDirectoryFor(1L, report);
        verify(directory).close();

        // Verify the sequence has been searched
        verify(sequenceFinder).setSequence(report.getRandomNonce());
        verify(sequenceFinder).search(report.getReport());

        // Verify the VCEK was fetched but since the result was not ok the VCEK path was not evaluated
        verify(snpWrapper).fetchVCEK(EpycGeneration.MILAN, MOCK_CERTS_DIR, MOCK_REPORT_FILE);
        verify(directory, never()).isVCEKAvailable();

        verifyNoMoreInteractions(snpWrapper);
    }

    @Test
    @DisplayName("Rejects report if the VCEK is not readable")
    void rejectsWhenVECKIsNotReadable() throws IOException, ExecutionException {
        report.setCpuGeneration(EpycGeneration.MILAN);
        report.setRandomNonce("NONCE".getBytes(StandardCharsets.UTF_8));
        report.setReport("REPORT WITH NONCE".getBytes(StandardCharsets.UTF_8));

        when(directoryProvider.createDirectoryFor(1L, report)).thenReturn(directory);

        when(directory.getCertsPath()).thenReturn(MOCK_CERTS_DIR);
        when(directory.getReportPath()).thenReturn(MOCK_REPORT_FILE);

        // Pass nonce verification
        when(sequenceFinder.search(report.getReport())).thenReturn(12);

        // Fetch works but the file does not exist
        when(snpWrapper.fetchVCEK(EpycGeneration.MILAN, MOCK_CERTS_DIR, MOCK_REPORT_FILE))
            .thenReturn(new ProcessOutput(0, "Fetch ok", ""));
        when(directory.isVCEKAvailable()).thenReturn(false);

        assertFalse(worker.process(session, result));
        assertEquals("""
                - The report contains the expected random nonce
                - Unable to retrieve VCEK file:
                    - Standard output: >
                        Fetch ok
                """,
            result.getProcessOutput()
        );

        // Verify the required files have been created and deleted
        verify(directoryProvider).createDirectoryFor(1L, report);
        verify(directory).close();

        // Verify the sequence has been searched
        verify(sequenceFinder).setSequence(report.getRandomNonce());
        verify(sequenceFinder).search(report.getReport());

        // Verify the VCEK was fetched
        verify(snpWrapper).fetchVCEK(EpycGeneration.MILAN, MOCK_CERTS_DIR, MOCK_REPORT_FILE);
        verify(directory).isVCEKAvailable();

        verifyNoMoreInteractions(snpWrapper);
    }

    @ParameterizedTest(name = TestHelper.CPU_USING_VLEK_NAME)
    @MethodSource("com.suse.coco.module.snpguest.TestHelper#listCpuAndUsingVlek")
    @DisplayName("Rejects report if the certificate verification fails")
    void rejectsWhenCertificatesCannotBeVerified(EpycGeneration cpuGeneration, boolean usingVlek)
            throws IOException, ExecutionException {
        report.setCpuGeneration(cpuGeneration);
        report.setRandomNonce("NONCE".getBytes(StandardCharsets.UTF_8));
        report.setReport("REPORT WITH NONCE".getBytes(StandardCharsets.UTF_8));

        when(directoryProvider.createDirectoryFor(1L, report)).thenReturn(directory);

        when(directory.getCertsPath()).thenReturn(MOCK_CERTS_DIR);
        when(directory.getReportPath()).thenReturn(MOCK_REPORT_FILE);

        // Pass nonce verification
        when(sequenceFinder.search(report.getReport())).thenReturn(12);

        if (usingVlek) {
            report.setVlekCertificate("This is a dummy VLEK certificaate");
            when(directory.isVLEKAvailable()).thenReturn(true);
        }
        else {
            // Pass fetching of the VECK
            when(snpWrapper.fetchVCEK(cpuGeneration, MOCK_CERTS_DIR, MOCK_REPORT_FILE))
                    .thenReturn(new ProcessOutput(0, """
                            Attempting to fetch VCEK...
                            Fetch ok
                            """, ""));
            when(directory.isVCEKAvailable()).thenReturn(true);
        }

        // Fail the certificate verification
        when(snpWrapper.verifyCertificates(MOCK_CERTS_DIR))
            .thenReturn(new ProcessOutput(-1, "", "Certificate verify FAILED"));

        assertFalse(worker.process(session, result));
        assertEquals("""
                        - The report contains the expected random nonce
                        """ +
                        getCertificateSuccessfulFetchString(usingVlek) +
                        """
                        - Unable to verify the validity of the certificates:
                            - Exit code: -1
                            - Standard error: >
                                Certificate verify FAILED
                        """,
                result.getProcessOutput()
        );

        // Verify the required files have been created and deleted
        verify(directoryProvider).createDirectoryFor(1L, report);
        verify(directory).close();

        // Verify the sequence has been searched
        verify(sequenceFinder).setSequence(report.getRandomNonce());
        verify(sequenceFinder).search(report.getReport());

        if (usingVlek) {
            verify(directory).isVLEKAvailable();
        }
        else {
            // Verify the VCEK was fetched
            verify(snpWrapper).fetchVCEK(cpuGeneration, MOCK_CERTS_DIR, MOCK_REPORT_FILE);
            verify(directory).isVCEKAvailable();
        }

        // Verify the certificate verification have happened
        verify(snpWrapper).verifyCertificates(MOCK_CERTS_DIR);

        verifyNoMoreInteractions(snpWrapper);
    }

    @ParameterizedTest(name = TestHelper.CPU_USING_VLEK_NAME)
    @MethodSource("com.suse.coco.module.snpguest.TestHelper#listCpuAndUsingVlek")
    @DisplayName("Rejects report if the attestation verification fails")
    void rejectsWhenAttestationCannotBeVerified(EpycGeneration cpuGeneration, boolean usingVlek)
            throws IOException, ExecutionException {
        report.setCpuGeneration(cpuGeneration);
        report.setRandomNonce("NONCE".getBytes(StandardCharsets.UTF_8));
        report.setReport("REPORT WITH NONCE".getBytes(StandardCharsets.UTF_8));

        when(directoryProvider.createDirectoryFor(1L, report)).thenReturn(directory);

        when(directory.getCertsPath()).thenReturn(MOCK_CERTS_DIR);
        when(directory.getReportPath()).thenReturn(MOCK_REPORT_FILE);

        // Pass nonce verification
        when(sequenceFinder.search(report.getReport())).thenReturn(12);

        if (usingVlek) {
            report.setVlekCertificate("This is a dummy VLEK certificaate");
            when(directory.isVLEKAvailable()).thenReturn(true);
        }
        else {
            // Pass fetching of the VECK
            when(snpWrapper.fetchVCEK(cpuGeneration, MOCK_CERTS_DIR, MOCK_REPORT_FILE))
                    .thenReturn(new ProcessOutput(0, """
                            Attempting to fetch VCEK...
                            Fetch ok
                            """, ""));
            when(directory.isVCEKAvailable()).thenReturn(true);
        }

        // Pass the certificate verification
        when(snpWrapper.verifyCertificates(MOCK_CERTS_DIR))
            .thenReturn(new ProcessOutput(0, "Certificate verify ok", ""));

        // Fail the attestation verification
        when(snpWrapper.verifyAttestation(cpuGeneration, MOCK_CERTS_DIR, MOCK_REPORT_FILE))
            .thenReturn(new ProcessOutput(-1, "", "Attestation verify FAILED"));

        assertFalse(worker.process(session, result));
        assertEquals("""
            - The report contains the expected random nonce
            """ +
            getCertificateSuccessfulFetchString(usingVlek) +
            """
            - Certification chain validated successfully:
                - Standard output: >
                    Certificate verify ok
            - Unable to verify the attestation report:
                - Exit code: -1
                - Standard error: >
                    Attestation verify FAILED
            """,
                result.getProcessOutput()
        );

        // Verify the required files have been created and deleted
        verify(directoryProvider).createDirectoryFor(1L, report);
        verify(directory).close();

        // Verify the sequence has been searched
        verify(sequenceFinder).setSequence(report.getRandomNonce());
        verify(sequenceFinder).search(report.getReport());

        if (usingVlek) {
            verify(directory).isVLEKAvailable();
        }
        else {
            // Verify the VCEK was fetched
            verify(snpWrapper).fetchVCEK(cpuGeneration, MOCK_CERTS_DIR, MOCK_REPORT_FILE);
            verify(directory).isVCEKAvailable();
        }

        // Verify the certificate verification have happened
        verify(snpWrapper).verifyCertificates(MOCK_CERTS_DIR);

        // Verify the attestation verification have happened
        verify(snpWrapper).verifyAttestation(cpuGeneration, MOCK_CERTS_DIR, MOCK_REPORT_FILE);
    }

    @ParameterizedTest(name = TestHelper.CPU_USING_VLEK_NAME)
    @MethodSource("com.suse.coco.module.snpguest.TestHelper#listCpuAndUsingVlek")
    @DisplayName("Approves report if all checks pass")
    void approvesReportIfAllChecksPass(EpycGeneration cpuGeneration, boolean usingVlek)
            throws IOException, ExecutionException {
        report.setCpuGeneration(cpuGeneration);
        report.setRandomNonce("NONCE".getBytes(StandardCharsets.UTF_8));
        report.setReport("REPORT WITH NONCE".getBytes(StandardCharsets.UTF_8));

        when(directoryProvider.createDirectoryFor(1L, report)).thenReturn(directory);

        when(directory.getCertsPath()).thenReturn(MOCK_CERTS_DIR);
        when(directory.getReportPath()).thenReturn(MOCK_REPORT_FILE);

        // Pass nonce verification
        when(sequenceFinder.search(report.getReport())).thenReturn(12);

        if (usingVlek) {
            report.setVlekCertificate("This is a dummy VLEK certificate");
            when(directory.isVLEKAvailable()).thenReturn(true);
        }
        else {
            // Pass fetching of the VECK
            when(snpWrapper.fetchVCEK(cpuGeneration, MOCK_CERTS_DIR, MOCK_REPORT_FILE))
                    .thenReturn(new ProcessOutput(0, """
                            Attempting to fetch VCEK...
                            Fetch ok
                            """, ""));
            when(directory.isVCEKAvailable()).thenReturn(true);
        }

        // Pass the certificate verification
        when(snpWrapper.verifyCertificates(MOCK_CERTS_DIR))
            .thenReturn(new ProcessOutput(0, """
                Verifying certificate chain validity...
                Certificates ok
                """, ""));

        // Pass the attestation verification
        when(snpWrapper.verifyAttestation(cpuGeneration, MOCK_CERTS_DIR, MOCK_REPORT_FILE))
            .thenReturn(new ProcessOutput(0, """
                Verifying attestation status...
                Attestation ok
                """, ""));

        // Retrieve the report
        when(snpWrapper.displayReport(MOCK_REPORT_FILE)).thenReturn(new ProcessOutput(0, "dummy-report", ""));

        assertTrue(worker.process(session, result));
        assertEquals("dummy-report", result.getDetails());
        assertEquals("""
                        - The report contains the expected random nonce
                        """ +
                        getCertificateSuccessfulFetchString(usingVlek) +
                        """
                        - Certification chain validated successfully:
                            - Standard output: >
                                Verifying certificate chain validity...
                                Certificates ok
                        - Attestation report correctly verified:
                            - Standard output: >
                                Verifying attestation status...
                                Attestation ok
                        """,
                result.getProcessOutput()
        );

        // Verify the required files have been created and deleted
        verify(directoryProvider).createDirectoryFor(1L, report);
        verify(directory).close();

        // Verify the sequence has been searched
        verify(sequenceFinder).setSequence(report.getRandomNonce());
        verify(sequenceFinder).search(report.getReport());

        if (usingVlek) {
            verify(directory).isVLEKAvailable();
        }
        else {
            // Verify the VCEK was fetched
            verify(snpWrapper).fetchVCEK(cpuGeneration, MOCK_CERTS_DIR, MOCK_REPORT_FILE);
            verify(directory).isVCEKAvailable();
        }

        // Verify the certificate verification have happened
        verify(snpWrapper).verifyCertificates(MOCK_CERTS_DIR);

        // Verify the attestation verification have happened
        verify(snpWrapper).verifyAttestation(cpuGeneration, MOCK_CERTS_DIR, MOCK_REPORT_FILE);

        // Verify the report display have happened
        verify(snpWrapper).displayReport(MOCK_REPORT_FILE);
    }
}
07070100000080000081B400000000000000000000000168FF7B0A000005F8000000000000000000000000000000000000006F00000000uyuni-coco-attestation/attestation-module-snpguest/src/test/java/com/suse/coco/module/snpguest/TestHelper.java/*
 * Copyright (c) 2025 SUSE LLC
 *
 * This software is licensed to you under the GNU General Public License,
 * version 2 (GPLv2). There is NO WARRANTY for this software, express or
 * implied, including the implied warranties of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
 * along with this software; if not, see
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
 */
package com.suse.coco.module.snpguest;

import com.suse.coco.module.snpguest.model.EpycGeneration;

import org.junit.jupiter.params.provider.Arguments;

import java.util.stream.Stream;

public class TestHelper {

    public static final String CPU_USING_VLEK_NAME = "Cpu: {0} using VLEK: {1}";

    public static Stream<Arguments> listCpuAndUsingVlek() {
        return Stream.of(
                Arguments.of(EpycGeneration.MILAN, false),
                Arguments.of(EpycGeneration.MILAN, true),
                Arguments.of(EpycGeneration.GENOA, false),
                Arguments.of(EpycGeneration.GENOA, true),
                Arguments.of(EpycGeneration.BERGAMO, false),
                Arguments.of(EpycGeneration.BERGAMO, true),
                Arguments.of(EpycGeneration.SIENA, false),
                Arguments.of(EpycGeneration.SIENA, true),
                Arguments.of(EpycGeneration.TURIN, false),
                Arguments.of(EpycGeneration.TURIN, true)
        );
    }

    private TestHelper() {
        // utility classes should not have a public or default constructor
    }
}
07070100000081000041FD00000000000000000000000268FF7B0A00000000000000000000000000000000000000000000006900000000uyuni-coco-attestation/attestation-module-snpguest/src/test/java/com/suse/coco/module/snpguest/execution07070100000082000081B400000000000000000000000168FF7B0A00001443000000000000000000000000000000000000008A00000000uyuni-coco-attestation/attestation-module-snpguest/src/test/java/com/suse/coco/module/snpguest/execution/AbstractSNPGuestWrapperTest.java/*
 * Copyright (c) 2024 SUSE LLC
 *
 * This software is licensed to you under the GNU General Public License,
 * version 2 (GPLv2). There is NO WARRANTY for this software, express or
 * implied, including the implied warranties of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
 * along with this software; if not, see
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
 */

package com.suse.coco.module.snpguest.execution;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import com.suse.coco.module.snpguest.model.EpycGeneration;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.io.ByteArrayInputStream;
import java.nio.file.Path;

@ExtendWith(MockitoExtension.class)
class AbstractSNPGuestWrapperTest {

    @Mock
    private Process process;

    @Mock
    private Runtime runtime;

    private AbstractSNPGuestWrapper wrapperVer07Below;
    private AbstractSNPGuestWrapper wrapperVer09Above;

    private AbstractSNPGuestWrapper getWrapperToTest(SNPGuestWrapperFactory.SNPGuestVersion type) {
        return switch (type) {
            case VER_07_BELOW -> wrapperVer07Below;
            case VER_09_ABOVE -> wrapperVer09Above;
        };
    }

    @BeforeEach
    void setup() throws Exception {

        when(runtime.exec(any(String[].class))).thenReturn(process);

        // Basic mocking of process
        when(process.getInputStream()).thenReturn(new ByteArrayInputStream(new byte[0]));
        when(process.getErrorStream()).thenReturn(new ByteArrayInputStream(new byte[0]));
        when(process.waitFor()).thenReturn(0);

        wrapperVer07Below = new SNPGuestWrapperVer07Below(runtime);
        wrapperVer09Above = new SNPGuestWrapperVer09Above(runtime);
    }

    @Test
    @DisplayName("Ver 0.7 and below: Generates the correct command line for downloading VCEK")
    void canDownloadCorrectVCEKVer07Below() throws Exception {
        wrapperVer07Below.fetchVCEK(EpycGeneration.MILAN, Path.of("certs"), Path.of("report.bin"));

        String expectedCommandLine = "/usr/bin/snpguest fetch vcek DER milan certs report.bin";
        verify(runtime).exec(expectedCommandLine.split(" "));
    }

    @Test
    @DisplayName("Ver 0.9 and above: Generates the correct command line for downloading VCEK")
    void canDownloadCorrectVCEKVer09Above() throws Exception {
        wrapperVer09Above.fetchVCEK(EpycGeneration.GENOA, Path.of("certs"), Path.of("report.bin"));

        String expectedCommandLine = "/usr/bin/snpguest fetch vcek -p genoa DER certs report.bin";
        verify(runtime).exec(expectedCommandLine.split(" "));
    }

    @ParameterizedTest
    @EnumSource(SNPGuestWrapperFactory.SNPGuestVersion.class)
    @DisplayName("Generates the correct command line for verifying certificates")
    void canVerifyCertificates(SNPGuestWrapperFactory.SNPGuestVersion type) throws Exception {
        AbstractSNPGuestWrapper wrapper = getWrapperToTest(type);
        wrapper.verifyCertificates(Path.of("/usr/share/certificates"));

        String expectedCommandLine = "/usr/bin/snpguest verify certs /usr/share/certificates";
        verify(runtime).exec(expectedCommandLine.split(" "));
    }

    @Test
    @DisplayName("Ver 0.7 and below: Generates the correct command line for verifying an attestation report")
    void canVerifyAttestationVer07Below() throws Exception {
        wrapperVer07Below.verifyAttestation(EpycGeneration.BERGAMO, Path.of("/srv/attestation/certs"),
                Path.of("/root/report.bin"));

        String expectedCommandLine = "/usr/bin/snpguest verify attestation /srv/attestation/certs /root/report.bin";
        verify(runtime).exec(expectedCommandLine.split(" "));
    }

    @Test
    @DisplayName("Ver 0.9 and above: Generates the correct command line for verifying an attestation report")
    void canVerifyAttestationVer09Above() throws Exception {
        wrapperVer09Above.verifyAttestation(EpycGeneration.TURIN, Path.of("/srv/attestation/certs"),
                Path.of("/root/report.bin"));

        String expectedCommandLine = "/usr/bin/snpguest verify attestation -p turin /srv/attestation/certs " +
                "/root/report.bin";
        verify(runtime).exec(expectedCommandLine.split(" "));
    }

    @ParameterizedTest
    @EnumSource(SNPGuestWrapperFactory.SNPGuestVersion.class)
    @DisplayName("Generates the correct command line for displaying an attestation report")
    void canDisplayReport(SNPGuestWrapperFactory.SNPGuestVersion type) throws Exception {
        AbstractSNPGuestWrapper wrapper = getWrapperToTest(type);
        wrapper.displayReport(Path.of("/root/report.bin"));

        String expectedCommandLine = "/usr/bin/snpguest display report /root/report.bin";
        verify(runtime).exec(expectedCommandLine.split(" "));
    }
}
07070100000083000041FD00000000000000000000000268FF7B0A00000000000000000000000000000000000000000000006200000000uyuni-coco-attestation/attestation-module-snpguest/src/test/java/com/suse/coco/module/snpguest/io07070100000084000081B400000000000000000000000168FF7B0A00001B3E000000000000000000000000000000000000008900000000uyuni-coco-attestation/attestation-module-snpguest/src/test/java/com/suse/coco/module/snpguest/io/VerificationDirectoryProviderTest.java/*
 * Copyright (c) 2024 SUSE LLC
 *
 * This software is licensed to you under the GNU General Public License,
 * version 2 (GPLv2). There is NO WARRANTY for this software, express or
 * implied, including the implied warranties of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
 * along with this software; if not, see
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
 */

package com.suse.coco.module.snpguest.io;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.when;

import com.suse.coco.module.snpguest.TestHelper;
import com.suse.coco.module.snpguest.model.AttestationReport;
import com.suse.coco.module.snpguest.model.EpycGeneration;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Comparator;
import java.util.Objects;
import java.util.stream.Stream;

@ExtendWith(MockitoExtension.class)
class VerificationDirectoryProviderTest {

    private Path destPath;

    @Mock
    private AttestationReport attestationReport;

    private VerificationDirectoryProvider directoryProvider;

    @BeforeEach
    public void setUp() throws Exception {
        destPath = Files.createTempDirectory("verification-directory-provider-test");

        URL resource = VerificationDirectoryProviderTest.class.getResource("/testCerts");
        assertNotNull(resource, "Cannot find resource for test certificates folder");
        Path sourcePath = Path.of(Objects.requireNonNull(resource).toURI());

        directoryProvider = new VerificationDirectoryProvider(destPath, sourcePath);
    }

    @AfterEach
    public void tearDown() throws IOException {
        // Delete test files
        try (Stream<Path> fileStream = Files.walk(destPath)) {
            fileStream
                .sorted(Comparator.reverseOrder())
                .map(Path::toFile)
                .forEach(File::delete);
        }
    }

    @ParameterizedTest(name = TestHelper.CPU_USING_VLEK_NAME)
    @MethodSource("com.suse.coco.module.snpguest.TestHelper#listCpuAndUsingVlek")
    @DisplayName("Verification directory is created by the provider and destroyed on resource closure" +
            " for each cpu generation")
    void canCreateAndDestroyVerificationDirectory(EpycGeneration cpuGeneration, boolean usingVlek) throws IOException {
        Path verificationDirectory;

        when(attestationReport.getCpuGeneration())
            .thenReturn(cpuGeneration);
        when(attestationReport.getReport())
            .thenReturn("This is a dummy report for unit test".getBytes(StandardCharsets.UTF_8));
        when(attestationReport.isUsingVlekAttestation())
                .thenReturn(usingVlek);
        lenient().when(attestationReport.getVlekCertificate())
                .thenReturn(usingVlek ? "This is a dummy VLEK certificaate" : null);

        try (VerificationDirectory directory = directoryProvider.createDirectoryFor(5L, attestationReport)) {
            verificationDirectory = directory.getBasePath();

            // Check the path was created with the expected prefix
            String directoryName = verificationDirectory.getFileName().toString();
            assertTrue(directoryName.startsWith("snp-guest-worker-5-"));

            // Verify the certs directory has been created
            assertTrue(Files.isReadable(directory.getCertsPath()));
            assertTrue(Files.isDirectory(directory.getCertsPath()));
            assertEquals(destPath.resolve(directoryName).resolve("certs"), directory.getCertsPath());

            // Check the certificates are copied
            Path arkCert = directory.getCertsPath().resolve("ark.pem");
            assertTrue(Files.exists(arkCert));

            Path askCert = directory.getCertsPath().resolve("ask.pem");
            assertTrue(Files.exists(askCert));

            Path asvkCert = directory.getCertsPath().resolve("asvk.pem");
            assertTrue(Files.exists(asvkCert));

            Path vlekCert = directory.getCertsPath().resolve("vlek.pem");
            assertEquals(usingVlek, Files.exists(vlekCert));

            // Check they contain the correct value
            String cpuName = cpuGeneration.name().toLowerCase();
            cpuName = cpuName.substring(0, 1).toUpperCase() + cpuName.substring(1);
            assertEquals("%s ROOT fake certificate".formatted(cpuName), Files.readString(arkCert).strip());
            assertEquals("%s INTERMEDIATE fake certificate".formatted(cpuName), Files.readString(askCert).strip());
            assertEquals("%s INTERMEDIATE VLEK fake certificate".formatted(cpuName),
                    Files.readString(asvkCert).strip());

            // Verify the report is present
            assertTrue(Files.isReadable(directory.getReportPath()));
            assertEquals(destPath.resolve(directoryName).resolve("report.bin"), directory.getReportPath());

            // Check the report is correct
            assertEquals(
                "This is a dummy report for unit test",
                Files.readString(directory.getReportPath(), StandardCharsets.UTF_8)
            );
        }

        assertFalse(Files.exists(verificationDirectory));
    }

    @Test
    @DisplayName("An exception is thrown if the certificate files are missing for the given generation")
    void throwsExceptionIfTheGenerationIsNotAvailable() throws IOException {
        long totalFiles = countTotalFileInFolder(destPath);

        when(attestationReport.getCpuGeneration()).thenReturn(EpycGeneration.UNKNOWN);

        IOException ex = assertThrows(IOException.class,
            () -> directoryProvider.createDirectoryFor(3L, attestationReport)
        );

        // Check the exception is the expected one
        assertEquals("Cannot find certificate for cpu generation UNKNOWN", ex.getMessage());
        // Check no files have been created
        assertEquals(totalFiles, countTotalFileInFolder(destPath),
                "Some files have been created and left behind");
    }

    private long countTotalFileInFolder(Path path) throws IOException {
        try (Stream<Path> files = Files.list(path)) {
            return files.count();
        }
    }

}
07070100000085000041FD00000000000000000000000368FF7B0A00000000000000000000000000000000000000000000004600000000uyuni-coco-attestation/attestation-module-snpguest/src/test/resources07070100000086000041FD00000000000000000000000768FF7B0A00000000000000000000000000000000000000000000005000000000uyuni-coco-attestation/attestation-module-snpguest/src/test/resources/testCerts07070100000087000041FD00000000000000000000000268FF7B0A00000000000000000000000000000000000000000000005800000000uyuni-coco-attestation/attestation-module-snpguest/src/test/resources/testCerts/bergamo07070100000088000081B400000000000000000000000168FF7B0A0000001E000000000000000000000000000000000000006000000000uyuni-coco-attestation/attestation-module-snpguest/src/test/resources/testCerts/bergamo/ark.pemBergamo ROOT fake certificate
07070100000089000081B400000000000000000000000168FF7B0A00000026000000000000000000000000000000000000006000000000uyuni-coco-attestation/attestation-module-snpguest/src/test/resources/testCerts/bergamo/ask.pemBergamo INTERMEDIATE fake certificate
0707010000008A000081B400000000000000000000000168FF7B0A0000002B000000000000000000000000000000000000006100000000uyuni-coco-attestation/attestation-module-snpguest/src/test/resources/testCerts/bergamo/asvk.pemBergamo INTERMEDIATE VLEK fake certificate
0707010000008B000041FD00000000000000000000000268FF7B0A00000000000000000000000000000000000000000000005600000000uyuni-coco-attestation/attestation-module-snpguest/src/test/resources/testCerts/genoa0707010000008C000081B400000000000000000000000168FF7B0A0000001C000000000000000000000000000000000000005E00000000uyuni-coco-attestation/attestation-module-snpguest/src/test/resources/testCerts/genoa/ark.pemGenoa ROOT fake certificate
0707010000008D000081B400000000000000000000000168FF7B0A00000024000000000000000000000000000000000000005E00000000uyuni-coco-attestation/attestation-module-snpguest/src/test/resources/testCerts/genoa/ask.pemGenoa INTERMEDIATE fake certificate
0707010000008E000081B400000000000000000000000168FF7B0A00000029000000000000000000000000000000000000005F00000000uyuni-coco-attestation/attestation-module-snpguest/src/test/resources/testCerts/genoa/asvk.pemGenoa INTERMEDIATE VLEK fake certificate
0707010000008F000041FD00000000000000000000000268FF7B0A00000000000000000000000000000000000000000000005600000000uyuni-coco-attestation/attestation-module-snpguest/src/test/resources/testCerts/milan07070100000090000081B400000000000000000000000168FF7B0A0000001C000000000000000000000000000000000000005E00000000uyuni-coco-attestation/attestation-module-snpguest/src/test/resources/testCerts/milan/ark.pemMilan ROOT fake certificate
07070100000091000081B400000000000000000000000168FF7B0A00000024000000000000000000000000000000000000005E00000000uyuni-coco-attestation/attestation-module-snpguest/src/test/resources/testCerts/milan/ask.pemMilan INTERMEDIATE fake certificate
07070100000092000081B400000000000000000000000168FF7B0A00000029000000000000000000000000000000000000005F00000000uyuni-coco-attestation/attestation-module-snpguest/src/test/resources/testCerts/milan/asvk.pemMilan INTERMEDIATE VLEK fake certificate
07070100000093000041FD00000000000000000000000268FF7B0A00000000000000000000000000000000000000000000005600000000uyuni-coco-attestation/attestation-module-snpguest/src/test/resources/testCerts/siena07070100000094000081B400000000000000000000000168FF7B0A0000001C000000000000000000000000000000000000005E00000000uyuni-coco-attestation/attestation-module-snpguest/src/test/resources/testCerts/siena/ark.pemSiena ROOT fake certificate
07070100000095000081B400000000000000000000000168FF7B0A00000024000000000000000000000000000000000000005E00000000uyuni-coco-attestation/attestation-module-snpguest/src/test/resources/testCerts/siena/ask.pemSiena INTERMEDIATE fake certificate
07070100000096000081B400000000000000000000000168FF7B0A00000029000000000000000000000000000000000000005F00000000uyuni-coco-attestation/attestation-module-snpguest/src/test/resources/testCerts/siena/asvk.pemSiena INTERMEDIATE VLEK fake certificate
07070100000097000041FD00000000000000000000000268FF7B0A00000000000000000000000000000000000000000000005600000000uyuni-coco-attestation/attestation-module-snpguest/src/test/resources/testCerts/turin07070100000098000081B400000000000000000000000168FF7B0A0000001C000000000000000000000000000000000000005E00000000uyuni-coco-attestation/attestation-module-snpguest/src/test/resources/testCerts/turin/ark.pemTurin ROOT fake certificate
07070100000099000081B400000000000000000000000168FF7B0A00000024000000000000000000000000000000000000005E00000000uyuni-coco-attestation/attestation-module-snpguest/src/test/resources/testCerts/turin/ask.pemTurin INTERMEDIATE fake certificate
0707010000009A000081B400000000000000000000000168FF7B0A00000029000000000000000000000000000000000000005F00000000uyuni-coco-attestation/attestation-module-snpguest/src/test/resources/testCerts/turin/asvk.pemTurin INTERMEDIATE VLEK fake certificate
0707010000009B000041FD00000000000000000000000368FF7B0A00000000000000000000000000000000000000000000002A00000000uyuni-coco-attestation/attestation-module0707010000009C000081B400000000000000000000000168FF7B0A00000538000000000000000000000000000000000000003200000000uyuni-coco-attestation/attestation-module/pom.xml<?xml version="1.0" encoding="UTF-8"?>
<!--
  ~ Copyright (c) 2024 SUSE LLC
  ~
  ~ This software is licensed to you under the GNU General Public License,
  ~ version 2 (GPLv2). There is NO WARRANTY for this software, express or
  ~ implied, including the implied warranties of MERCHANTABILITY or FITNESS
  ~ FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
  ~ along with this software; if not, see
  ~ http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
  -->

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.uyuni-project</groupId>
        <artifactId>coco-attestation</artifactId>
        <version>5.2.1</version>
    </parent>

    <groupId>org.uyuni-project.coco-attestation</groupId>
    <artifactId>attestation-module</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
        </dependency>
    </dependencies>

</project>
0707010000009D000041FD00000000000000000000000368FF7B0A00000000000000000000000000000000000000000000002E00000000uyuni-coco-attestation/attestation-module/src0707010000009E000041FD00000000000000000000000368FF7B0A00000000000000000000000000000000000000000000003300000000uyuni-coco-attestation/attestation-module/src/main0707010000009F000041FD00000000000000000000000368FF7B0A00000000000000000000000000000000000000000000003800000000uyuni-coco-attestation/attestation-module/src/main/java070701000000A0000041FD00000000000000000000000368FF7B0A00000000000000000000000000000000000000000000003C00000000uyuni-coco-attestation/attestation-module/src/main/java/com070701000000A1000041FD00000000000000000000000368FF7B0A00000000000000000000000000000000000000000000004100000000uyuni-coco-attestation/attestation-module/src/main/java/com/suse070701000000A2000041FD00000000000000000000000468FF7B0A00000000000000000000000000000000000000000000004600000000uyuni-coco-attestation/attestation-module/src/main/java/com/suse/coco070701000000A3000041FD00000000000000000000000268FF7B0A00000000000000000000000000000000000000000000004C00000000uyuni-coco-attestation/attestation-module/src/main/java/com/suse/coco/model070701000000A4000081B400000000000000000000000168FF7B0A00000C01000000000000000000000000000000000000006300000000uyuni-coco-attestation/attestation-module/src/main/java/com/suse/coco/model/AttestationResult.java/*
 * Copyright (c) 2024 SUSE LLC
 *
 * This software is licensed to you under the GNU General Public License,
 * version 2 (GPLv2). There is NO WARRANTY for this software, express or
 * implied, including the implied warranties of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
 * along with this software; if not, see
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
 */

package com.suse.coco.model;

import java.time.OffsetDateTime;
import java.util.Objects;
import java.util.StringJoiner;

/**
 * An attestation result that needs to be verified by an {@link com.suse.coco.module.AttestationModule}.
 */
public class AttestationResult {
    private long id;
    private long reportId;
    private int resultType;
    private AttestationStatus status;
    private String description;
    private String details;
    private String processOutput;
    private OffsetDateTime attested;

    public long getId() {
        return id;
    }

    public void setId(long idIn) {
        this.id = idIn;
    }

    public long getReportId() {
        return reportId;
    }

    public void setReportId(long reportIdIn) {
        this.reportId = reportIdIn;
    }

    public int getResultType() {
        return resultType;
    }

    public void setResultType(int resultTypeIn) {
        this.resultType = resultTypeIn;
    }

    public AttestationStatus getStatus() {
        return status;
    }

    public void setStatus(AttestationStatus statusIn) {
        this.status = statusIn;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String descriptionIn) {
        this.description = descriptionIn;
    }

    public String getDetails() {
        return details;
    }

    public void setDetails(String detailsIn) {
        this.details = detailsIn;
    }

    public String getProcessOutput() {
        return processOutput;
    }

    public void setProcessOutput(String processOutputIn) {
        this.processOutput = processOutputIn;
    }

    public OffsetDateTime getAttested() {
        return attested;
    }

    public void setAttested(OffsetDateTime attestedIn) {
        this.attested = attestedIn;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof AttestationResult that)) {
            return false;
        }
        return Objects.equals(reportId, that.reportId) && Objects.equals(resultType, that.resultType);
    }

    @Override
    public int hashCode() {
        return Objects.hash(reportId, resultType);
    }

    @Override
    public String toString() {
        return new StringJoiner(", ", AttestationResult.class.getSimpleName() + "[", "]")
            .add("id=" + id)
            .add("reportId=" + reportId)
            .add("resultType=" + resultType)
            .add("status='" + status + "'")
            .add("description='" + description + "'")
            .add("attested=" + attested)
            .toString();
    }
}
070701000000A5000081B400000000000000000000000168FF7B0A00000217000000000000000000000000000000000000006300000000uyuni-coco-attestation/attestation-module/src/main/java/com/suse/coco/model/AttestationStatus.java/*
 * Copyright (c) 2024 SUSE LLC
 *
 * This software is licensed to you under the GNU General Public License,
 * version 2 (GPLv2). There is NO WARRANTY for this software, express or
 * implied, including the implied warranties of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
 * along with this software; if not, see
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
 */

package com.suse.coco.model;

public enum AttestationStatus {
    PENDING,
    SUCCEEDED,
    FAILED
}
070701000000A6000041FD00000000000000000000000268FF7B0A00000000000000000000000000000000000000000000004D00000000uyuni-coco-attestation/attestation-module/src/main/java/com/suse/coco/module070701000000A7000081B400000000000000000000000168FF7B0A0000060F000000000000000000000000000000000000006400000000uyuni-coco-attestation/attestation-module/src/main/java/com/suse/coco/module/AttestationModule.java/*
 * Copyright (c) 2024 SUSE LLC
 *
 * This software is licensed to you under the GNU General Public License,
 * version 2 (GPLv2). There is NO WARRANTY for this software, express or
 * implied, including the implied warranties of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
 * along with this software; if not, see
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
 */

package com.suse.coco.module;

import java.util.Collections;
import java.util.List;

/**
 * A component that can verify {@link com.suse.coco.model.AttestationResult} and
 */
public interface AttestationModule {

    /**
     * Retrieves the name of the attestation module
     * @return the unique name of this module
     */
    String getName();

    /**
     * Retrieves attestation result type that this module is able to verify.
     * Please refer to uyuni codebase, class com/suse/manager/model/attestation/CoCoResultType.java for a list
     * of possible values
     * @return the supported result type
     */
    int getSupportedType();

    /**
     * Builds a worker to process {@link com.suse.coco.model.AttestationResult}
     * @return a new instance of {@link AttestationWorker}
     */
    AttestationWorker getWorker();

    /**
     * Additional Mybatis mappers needed by this attestation module.
     * @return a list of resources processable by {@link ClassLoader#getSystemResource(String)}
     */
    default List<String> getAdditionalMappers() {
        return Collections.emptyList();
    }
}
070701000000A8000081B400000000000000000000000168FF7B0A0000040C000000000000000000000000000000000000006400000000uyuni-coco-attestation/attestation-module/src/main/java/com/suse/coco/module/AttestationWorker.java/*
 * Copyright (c) 2024 SUSE LLC
 *
 * This software is licensed to you under the GNU General Public License,
 * version 2 (GPLv2). There is NO WARRANTY for this software, express or
 * implied, including the implied warranties of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
 * along with this software; if not, see
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
 */

package com.suse.coco.module;

import com.suse.coco.model.AttestationResult;

import org.apache.ibatis.session.SqlSession;

/**
 * A worker to process and verify attestation results.
 */
@FunctionalInterface
public interface AttestationWorker {

    /**
     * Process the given attestation result.
     * @param session the active mybatis database session
     * @param attestationResult the attestation result to process.
     * @return <code>true</code> if the processing succeeded, <code>false</code> otherwise.
     */
    boolean process(SqlSession session, AttestationResult attestationResult);
}
070701000000A9000081B400000000000000000000000168FF7B0A00000648000000000000000000000000000000000000001F00000000uyuni-coco-attestation/pom.xml<?xml version="1.0" encoding="UTF-8"?>
<!--
  ~ Copyright (c) 2024 SUSE LLC
  ~
  ~ This software is licensed to you under the GNU General Public License,
  ~ version 2 (GPLv2). There is NO WARRANTY for this software, express or
  ~ implied, including the implied warranties of MERCHANTABILITY or FITNESS
  ~ FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
  ~ along with this software; if not, see
  ~ http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
  -->

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.uyuni-project</groupId>
        <artifactId>uyuni-java-parent</artifactId>
        <version>5.2.1</version>
        <relativePath>../uyuni-java-parent</relativePath>
    </parent>

    <artifactId>coco-attestation</artifactId>
    <version>5.2.1</version>
    <packaging>pom</packaging>

    <modules>
        <module>attestation-core</module>
        <module>attestation-module</module>
        <module>attestation-module-snpguest</module>
        <module>attestation-module-secureboot</module>
    </modules>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.uyuni-project</groupId>
                <artifactId>uyuni-java-common</artifactId>
                <version>5.2.1</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>
070701000000AA000081B400000000000000000000000168FF7B0A000007C7000000000000000000000000000000000000003600000000uyuni-coco-attestation/uyuni-coco-attestation.changes-------------------------------------------------------------------
Mon Oct 27 14:59:17 CET 2025 - rosuna@suse.com

- version 5.2.1-0
  * Support VLEK attestation verification and new AMD cpu models
  * CVE-2025-53192: Do not use apache-commons-ognl but its successor
    ognl (bsc#1248252)
  * Bump version to 5.2.0

-------------------------------------------------------------------
Wed Dec 11 16:43:21 CET 2024 - rosuna@suse.com

- version 5.1.3-0
  * Stop the attestation processing gracefully when receiving a 
    termination signal

-------------------------------------------------------------------
Tue Oct 15 12:21:47 CEST 2024 - marina.latini@suse.com

- version 5.1.2-0
  * Bump to next version

-------------------------------------------------------------------
Mon Oct 14 17:08:57 CEST 2024 - rosuna@suse.com

- version 5.1.1-0
  * Bump version to 5.1.1

-------------------------------------------------------------------
Thu Aug 01 17:49:53 CEST 2024 - marina.latini@suse.com

- version 5.0.5-0
  * Ensure the report and the nonce are not empty before attempting
    to validate
  * Mark Secure Boot as succeeded only if the correct message is
    present

-------------------------------------------------------------------
Fri May 03 16:16:41 CEST 2024 - marina.latini@suse.com

- version 5.0.4-0
  * Improved version management of common dependency
  * Store the snpguest report after a successful attestation
  * Add attestation module for SecureBoot

-------------------------------------------------------------------
Mon Apr 15 19:20:20 CEST 2024 - marina.latini@suse.com

- version 5.0.3-0
  * Fixed license to correctly use GPL-2.0-only

-------------------------------------------------------------------
Fri Apr 05 15:39:55 CEST 2024 - marina.latini@suse.com

- version 5.0.2-0
  * Bump pom version

-------------------------------------------------------------------
Thu Apr 04 23:39:59 CEST 2024 - marina.latini@suse.com

- version 5.0.1-0
  * Initial version
070701000000AB000081B400000000000000000000000168FF7B0A0000168A000000000000000000000000000000000000003300000000uyuni-coco-attestation/uyuni-coco-attestation.spec#
# spec file for package uyuni-coco-attestation
#
# Copyright (c) 2025 SUSE LLC and contributors
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
# upon. The license for this file, and modifications and additions to the
# file, is the same license as for the pristine package itself (unless the
# license for the pristine package is not an Open Source License, in which
# case the license is the MIT License). An "Open Source License" is a
# license that conforms to the Open Source Definition (Version 1.9)
# published by the Open Source Initiative.

# Please submit bugfixes or comments via https://bugs.opensuse.org/
#


# The productprettyname macros is controlled in the prjconf. If not defined, we fallback here
%{!?productprettyname: %global productprettyname Uyuni}

Name:           uyuni-coco-attestation
Version:        5.2.1
Release:        0
Summary:        %{productprettyname} utility for Confidential Computing Attestation
License:        GPL-2.0-only
Group:          System/Daemons
URL:            https://www.uyuni-project.org
#!CreateArchive: %{name}
Source0:        %{name}-%{version}.tar.gz
BuildRequires:  fdupes
BuildRequires:  java-devel >= 17
BuildRequires:  maven-local
BuildRequires:  mvn(com.mchange:c3p0)
BuildRequires:  mvn(com.mchange:mchange-commons-java)
BuildRequires:  mvn(ognl:ognl)
BuildRequires:  mvn(org.apache.logging.log4j:log4j-api)
BuildRequires:  mvn(org.apache.logging.log4j:log4j-core)
BuildRequires:  mvn(org.javassist:javassist)
BuildRequires:  mvn(org.mybatis:mybatis)
BuildRequires:  mvn(org.postgresql:postgresql)
BuildRequires:  mvn(org.uyuni-project:uyuni-java-common)
BuildRequires:  mvn(org.uyuni-project:uyuni-java-parent:pom:)

%description
System daemon used by %{productprettyname} to validate the results of confidential computing attestation.

%package core
Summary:        %{productprettyname} utility for Confidential Computing Attestation
BuildArch:      noarch

%description core
System daemon used by %{productprettyname} to validate the results of confidential computing attestation.

%ifarch x86_64
%package module-snpguest
Summary:        Confidential computing SNPGuest attestation module for %{productprettyname}
Requires:       snpguest

%description module-snpguest
Module for the %{productprettyname} Confidential Computing Attestation that uses SnpGuest.
%endif

%package module-secureboot
Summary:        Confidential computing SecureBoot attestation module for %{productprettyname}

%description module-secureboot
Module for the %{productprettyname} Confidential Computing Attestation for SecureBoot uses the output of mokutil.

%package        javadoc
Summary:        API documentation for %{name}
BuildArch:      noarch

%description    javadoc
Package containing the Javadoc API documentation for %{name}.

%prep
%setup -q

%ifnarch x86_64
# Disable the module snpguest as it requires x86_64
%pom_disable_module 'attestation-module-snpguest'
%endif

# Make sure there are not dependencies on the modules projects
%pom_remove_dep 'org.uyuni-project.coco-attestation.module:' attestation-core/pom.xml

# Shade is used only for developing convenience
%pom_remove_plugin -r :maven-shade-plugin

%{mvn_package} ':attestation-module-snpguest' module-snpguest

%{mvn_package} ':attestation-module-secureboot' module-secureboot

%build
%{mvn_build} -f

%install
%{mvn_install}

# Install required directories
install -d -m 755 %{buildroot}%{_sbindir}
install -d -m 755 %{buildroot}%{_datadir}/coco-attestation/
install -d -m 755 %{buildroot}%{_datadir}/coco-attestation/classes
install -d -m 755 %{buildroot}%{_datadir}/coco-attestation/conf
install -d -m 755 %{buildroot}%{_datadir}/coco-attestation/lib

# Required files
install -p -m 755 attestation-core/src/package/coco-attestation.sh %{buildroot}%{_sbindir}/coco-attestation
install -p -m 644 attestation-core/src/package/daemon.conf %{buildroot}%{_datadir}/coco-attestation/conf
install -p -m 644 attestation-core/src/package/log4j2.xml %{buildroot}%{_datadir}/coco-attestation/classes

# Create links for the jars
build-jar-repository -s -p %{buildroot}%{_datadir}/coco-attestation/lib uyuni-java-common/uyuni-common log4j/log4j-api log4j/log4j-core ongres-scram ongres-stringprep postgresql-jdbc ognl/ognl javassist mybatis mchange-commons c3p0

# Link all the attestation jars built and installed by maven
ln -s -f -r %{buildroot}%{_javadir}/uyuni-coco-attestation/*.jar %{buildroot}%{_datadir}/coco-attestation/lib

%ifarch x86_64
# Install snpguest certificates
cd attestation-module-snpguest/src/package/certs/
for FILE in $(find -name *.pem -type f -printf '%%P\n'); do
    echo $FILE
    install -D -p -m 644 $FILE %{buildroot}%{_datadir}/coco-attestation/certs/$FILE
done
cd -
%endif

%files core -f .mfiles
%dir %{_datadir}/coco-attestation/
%dir %{_datadir}/coco-attestation/conf/
%dir %{_datadir}/coco-attestation/lib/
%dir %{_datadir}/coco-attestation/classes/
%{_datadir}/coco-attestation/lib/*
%attr(755, root, root) %{_sbindir}/coco-attestation
%{_datadir}/coco-attestation/conf/daemon.conf
%{_datadir}/coco-attestation/classes/log4j2.xml
%license LICENSE

# Exclude all modules jars, will be part of their specific packages
%exclude %{_datadir}/coco-attestation/lib/attestation-module-*

%ifarch x86_64
%files module-snpguest -f .mfiles-module-snpguest
%dir %{_datadir}/coco-attestation/certs/
%{_datadir}/coco-attestation/lib/attestation-module-snpguest.jar
%{_datadir}/coco-attestation/certs/*
%license LICENSE
%endif

%files module-secureboot -f .mfiles-module-secureboot
%{_datadir}/coco-attestation/lib/attestation-module-secureboot.jar
%license LICENSE

%files javadoc -f .mfiles-javadoc
%license LICENSE

%changelog
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!
openSUSE Build Service is sponsored by