Code View

plugin / plugin-2.2.0.0 / src / sign / Manifest.hh
// SPDX-License-Identifier: MIT
// sign/Manifest.hh
#pragma once
//
// fedem::sign::Manifest — Read and write signing metadata embedded in a DSO.
//
// A signed DSO carries two extra ELF sections:
//
//   .dso_manifest   UTF-8 JSON (see ManifestData below)
//   .dso_sig        64 raw bytes — Ed25519 signature over .dso_manifest bytes
//
// Both sections are added by the dso-sign tool via objcopy:
//
//   objcopy --add-section .dso_manifest=addon.manifest.json
//           --set-section-flags .dso_manifest=noload,readonly
//           --add-section .dso_sig=addon.manifest.sig
//           --set-section-flags .dso_sig=noload,readonly
//           addon.so
//
// Reading at runtime
// ──────────────────
// Manifest::read(soPath) memory-maps the DSO, walks the ELF section table,
// and returns a ManifestResult.  No external library (libelf) is required.
//
// JSON schema (.dso_manifest)
// ───────────────────────────
// {
//   "name":        "addon-name",
//   "version":     "1.0.0",
//   "author":      "Name <email>",
//   "sha256":      "<hex — SHA-256 of the DSO file BEFORE sections were added>",
//   "publicKeyId": "<hex — SHA-256 of the signer's DER-encoded public key>",
//   "abiMajor":    1,
//   "abiMinor":    0,
//   "timestamp":   "2026-05-23T00:00:00Z"
// }
//
// sha256 is computed over the DSO binary *before* the manifest sections are
// embedded.  Verifier::verify() strips the sections to a temp file, hashes
// that, and compares with the manifest value — making the hash stable across
// re-signing.

#include <array>
#include <cstdint>
#include <string>
#include <vector>

namespace fedem {
namespace sign {

// ─────────────────────────────────────────────────────────────────────────────
// Section name constants
// ─────────────────────────────────────────────────────────────────────────────
inline constexpr std::size_t ED25519_SIG_BYTES = 64;
inline constexpr char const* kManifestSection  = ".dso_manifest";
inline constexpr char const* kSigSection       = ".dso_sig";

// ─────────────────────────────────────────────────────────────────────────────
// ManifestData — parsed fields from the .dso_manifest JSON section
// ─────────────────────────────────────────────────────────────────────────────
struct ManifestData
{
    std::string name;           ///< addon/component identifier
    std::string version;        ///< semver: "1.0.0"
    std::string author;         ///< "Name <email>"
    std::string sha256;         ///< hex SHA-256 of DSO before signing
    std::string publicKeyId;    ///< hex SHA-256 of signer's DER public key
    int         abiMajor = 0;   ///< host-defined ABI major version
    int         abiMinor = 0;   ///< host-defined ABI minor version
    std::string timestamp;      ///< ISO-8601
};

// ─────────────────────────────────────────────────────────────────────────────
// ManifestResult — returned by Manifest::read()
// ─────────────────────────────────────────────────────────────────────────────
struct ManifestResult
{
    bool hasManifest = false;  ///< .dso_manifest section found
    bool hasSig      = false;  ///< .dso_sig section found

    std::string              manifestJson;  ///< raw UTF-8 JSON
    std::array<uint8_t, 64>  sig{};         ///< raw 64-byte Ed25519 signature
    ManifestData             data;          ///< parsed fields

    std::string error;  ///< non-empty on parse / I/O failure
};

// ─────────────────────────────────────────────────────────────────────────────
// Manifest
// ─────────────────────────────────────────────────────────────────────────────
class Manifest
{
public:
    /**
     * Read and parse manifest + signature from a DSO (ELF .so / .ffs / …).
     *
     * @param soPath  absolute or relative path to the DSO file
     */
    static ManifestResult read( std::string const& soPath );

    /**
     * Serialise ManifestData to canonical JSON (fixed key insertion order).
     * dso-sign uses this to produce the JSON before signing.
     */
    static std::string toJson( ManifestData const& d );

    /**
     * Parse JSON into ManifestData.
     * Returns false and sets @p error on failure.
     */
    static bool fromJson( std::string const& json,
                          ManifestData&      out,
                          std::string&       error );

private:
    /// ELF section extraction — mmap + manual header walk, no libelf.
    static bool readElfSection( std::string const&    path,
                                std::string const&    sectionName,
                                std::vector<uint8_t>& out,
                                std::string&          error );
};

} // namespace sign
} // namespace fedem