// 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