// Copyright (c) 2026 Juhapekka Piiroinen // All Rights Reserved. // AmoosedWeb project: https://amoosed.com #pragma once #include "AmoosedWeb/Transport.hpp" #include #include #include #include #include #include #include #include namespace amoosed { struct FetchResponse { int status = 0; bool same_origin = false; std::string content_range, etag; std::vector body; }; class HttpHost { public: virtual ~HttpHost() = default; // A transport may be cancelled from another thread. Reset occurs only on // the serialized worker before a new single-use operation begins. virtual void cancel() noexcept {} virtual void reset_cancel() noexcept {} // The adapter must request a closed byte range, omit credentials, disable // HTTP cache, reject redirects, and cap received bytes at max_body. virtual bool fetch_range(std::string_view relative_path, std::size_t first, std::size_t last, std::size_t max_body, FetchResponse& response, std::string& error) = 0; virtual bool fetch_suffix(std::string_view relative_path, std::size_t length, std::size_t max_body, FetchResponse& response, std::string& error) = 0; // Media carriers need the complete original PNG/WAV body before decoding. // The adapter must apply the same origin, redirect, credential, cache, and // byte-bound rules as range requests. Existing adapters may reject this. virtual bool fetch_complete(std::string_view relative_path, std::size_t max_body, FetchResponse& response, std::string& error) { (void)relative_path; (void)max_body; (void)response; error = "Whole-carrier fetch is not supported by this host"; return false; } virtual bool cancelled() const = 0; }; class Hasher { public: virtual ~Hasher() = default; virtual bool update(const std::uint8_t* bytes, std::size_t length) = 0; virtual bool finish(std::array& digest) = 0; }; enum class GcmOpenResult : std::uint32_t { Success, AuthenticationFailed, ProviderFailed }; class CryptoHost { public: virtual ~CryptoHost() = default; virtual std::unique_ptr new_sha256() = 0; // Compiler-only randomness. Built-in hosts use the platform CSPRNG; // custom Reader hosts may leave this unsupported. A failed call must leave // no usable bytes in the caller's output buffer. virtual bool random_bytes(std::uint8_t* output, std::size_t length) { if (output) for (std::size_t i = 0; i < length; ++i) output[i] = 0; return false; } virtual bool aes256_ctr(const std::array& key, const std::array& counter, const std::vector& input, std::vector& output) = 0; virtual bool aes256_gcm_open(const std::array& key, const std::array& nonce, const std::vector& aad, const std::vector& ciphertext_and_tag, std::vector& output) = 0; // Built-in hosts distinguish an invalid tag from a provider failure. // Legacy custom hosts retain the conservative authentication result. virtual GcmOpenResult aes256_gcm_open_result( const std::array& key, const std::array& nonce, const std::vector& aad, const std::vector& ciphertext_and_tag, std::vector& output) { return aes256_gcm_open(key, nonce, aad, ciphertext_and_tag, output) ? GcmOpenResult::Success : GcmOpenResult::AuthenticationFailed; } // Compiler-only operation. Reader-only custom hosts may leave the default // unsupported result; compiler callers must require a working seal host. virtual bool aes256_gcm_seal(const std::array& key, const std::array& nonce, const std::vector& aad, const std::vector& plaintext, std::vector& ciphertext_and_tag) { (void)key; (void)nonce; (void)aad; (void)plaintext; ciphertext_and_tag.clear(); return false; } }; class MediaDecoder { public: virtual ~MediaDecoder() = default; // Decode a complete, bounded original carrier to its payload bytes. // The Reader still authenticates each indexed shard after slicing. virtual bool decode(std::string_view codec, const std::vector& carrier, const AudioCarrier* audio, CryptoHost& crypto, std::vector& payload, std::string& error) = 0; }; class OutputHost { public: virtual ~OutputHost() = default; // Writes go to a private temporary sibling. Commit must publish only after // the resource digest passes; abort removes the temporary file. virtual bool write(const std::uint8_t* bytes, std::size_t length) = 0; virtual bool commit() = 0; virtual void abort() = 0; }; struct DownloadResult { std::size_t bytes = 0; std::string sha256; }; // Preview failure category. Diagnostic text is for logs; callers should use // this value for control flow. InvalidData retains its old enum slot but new // call sites must use a more precise category. enum class ReaderFailure : std::uint32_t { None, InvalidData, BadFormat, Authentication, Verification, Network, Output, Unsupported, Cancelled, Internal, NotFound, InvalidState, StaleRevision }; struct IndexResult { AppIndex app; std::string payload_sha256; }; // Discovers /favicon.ico, verifies footer, AMW header, manifest, sections, and // complete payload, then validates the app resource map. The unrelated media // prefix is not fetched, matching the browser's progressive range policy. bool open_hosted_index(HttpHost& http, CryptoHost& crypto, IndexResult& result, std::string& error, ReaderFailure* failure = nullptr); // Accepts a separately delivered AMWKEY1 text or packed ICO credential. // Its locator is relative to the selected site; the adapter retains origin control. bool open_external_index(HttpHost& http, CryptoHost& crypto, std::string_view key_file, IndexResult& result, std::string& error, ReaderFailure* failure = nullptr); // Supports byte-addressable append, PNG ancillary, and MP4 UUID-box parts. // A supplied MediaDecoder can also recover supported whole-file carriers; // WindowsMediaDecoder and PosixMediaDecoder provide png-lsb1, // frame-luma1/2, and wav-4fsk1. // Other codecs fail closed. bool download_resource(const AppIndex& index, std::string_view path, const std::array* member_key, HttpHost& http, CryptoHost& crypto, OutputHost& output, DownloadResult& result, std::string& error, MediaDecoder* media = nullptr, ReaderFailure* failure = nullptr); // Checks each stored part digest of a protected resource without its member // key. This does not authenticate the member plaintext or its complete digest; // use download_resource with the member key when that proof is required. bool verify_protected_stored_parts(const AppIndex& index, const Resource& resource, HttpHost& http, CryptoHost& crypto, std::string& error, MediaDecoder* media = nullptr, ReaderFailure* failure = nullptr); // Recover an indexed standalone multi-clip WAV exchange. The caller validates // the index document and supplies its global packet map and per-clip locations. // Output is assigned only after all packet hashes and AES-GCM authenticate. bool decode_audio_set(const std::vector& clips, const AudioCarrier& manifest, const std::array& nonce, std::size_t plain_length, const std::array& media_key, CryptoHost& crypto, std::vector& output, std::string& error); }