Skip to content

Latest commit

 

History

History
142 lines (115 loc) · 4.96 KB

File metadata and controls

142 lines (115 loc) · 4.96 KB

qlcrypt — Public API Reference

This is a hand-written reference. The headers in include/qlcrypt/ are the canonical source. Doxygen comments on every declaration provide authoritative per-function documentation.

Header files

Header Purpose
qlcrypt/qlcrypt.hpp Umbrella include for all of the below
qlcrypt/version.hpp VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, VERSION_STRING, VERSION_NUMBER
qlcrypt/status.hpp enum class Status, ok(), toString()
qlcrypt/constants.hpp MAGIC_QLEN, FORMAT_VERSION_V2, CIPHER_AES_256_GCM, HEADER_SIZE_V2, NONCE_LEN, TAG_LEN, KEY_LEN_V2, KEY_LEN_V1, FINGERPRINT_LEN, MAX_PLAINTEXT_LEN
qlcrypt/header_info.hpp struct HeaderInfo (parsed header)
qlcrypt/key_db.hpp class KeyDB (opaque key holder)
qlcrypt/file_crypt.hpp class FileCrypt (stateless encrypt/decrypt/inspect)
qlcrypt/processor.hpp class Processor (decrypt-once cache)

Status

enum class qlcrypt::Status { Ok, FileNotFound, ..., InternalError };
constexpr bool ok(Status s) noexcept;
std::string_view toString(Status s) noexcept;

Every public API returns Status; check with qlcrypt::ok() or compare to Status::Ok. toString() returns a static string suitable for logging.

KeyDB

class KeyDB {
public:
    KeyDB();                                          // empty
    static Status create(KeyDB& out,
                         std::optional<std::string> customerId = {});
    static Status load(std::string_view path, KeyDB& out);
    Status save(std::string_view path) const;         // always v2

    bool hasKey() const noexcept;
    bool isLegacyV1() const noexcept;
    std::array<std::uint8_t, FINGERPRINT_LEN> fingerprint() const noexcept;
    std::string fingerprintHex() const;               // 16-char lowercase
    const std::string& customerId() const noexcept;
};
  • Move-only. Destructor securely wipes key bytes.
  • create() generates a random 256-bit key.
  • load() accepts either v2 or legacy v1 key databases; v1 databases set isLegacyV1() == true and are decrypt-only.
  • save() always writes v2 format.

FileCrypt

class FileCrypt {
public:
    explicit FileCrypt(const KeyDB& keys);             // non-owning ref

    Status encryptFile(std::string_view in,
                       std::string_view out) const;    // always v2
    Status decryptFile(std::string_view in,
                       std::string& out) const;        // v2, falls back to v1
    Status verifyFile(std::string_view in) const;      // decrypt + wipe
    static Status inspect(std::string_view in,
                          HeaderInfo& out);            // no key required
};
  • FileCrypt is move-only and re-entrant for const methods.
  • The bound KeyDB& must outlive the FileCrypt.
  • encryptFile() uses a fresh random 12-byte nonce per call and binds the entire 27-byte header as AAD.
  • decryptFile() first attempts v2; if the magic is absent and the loaded key database is v1, it falls back. Any v2 header with bad fields short-circuits without calling Crypto++.

Processor

class Processor {
public:
    explicit Processor(KeyDB&& keys);                  // takes ownership

    Status decryptFile(std::string_view path,
                       std::string_view& outView);

    void clearCache() noexcept;
    std::size_t cacheSize() const noexcept;
    std::size_t decryptCallCount() const noexcept;
    std::size_t decryptMissCount() const noexcept;
};
  • The decryption cache is keyed on weakly_canonical(path).
  • Subsequent calls with the same path return a string_view into the same buffer — never a copy. The view is valid until clearCache() or destruction.
  • Thread-safe: concurrent calls on disjoint paths run in parallel; on the same path, one decryption wins and the other returns the cached view.
  • The destructor securely wipes every cached buffer and unlocks any pinned pages.

Sample integration

#include <qlcrypt/qlcrypt.hpp>

int loadEncryptedAsset(const std::filesystem::path& keyDbPath,
                       const std::filesystem::path& enPath,
                       std::string_view& outView,
                       qlcrypt::Processor& processor) {
    if (!processor.cacheSize()) {
        // first call — KeyDB is already owned by the Processor
    }
    return qlcrypt::ok(processor.decryptFile(enPath.string(), outView)) ? 0 : -1;
}

ABI notes

  • All public types are final from a layout perspective: PIMPL'd classes (KeyDB, FileCrypt, Processor) own a single unique_ptr<Impl>. Adding state to the impl never breaks ABI.
  • No virtual functions in any public class.
  • No exceptions cross the API boundary.

Version checks

#if QLCRYPT_VERSION_NUMBER < 10000  // less than 1.0.0
#  error "qlcrypt 1.0.0 or later required"
#endif

Note: pre-1.0 minor bumps (0.x → 0.y) may break ABI. Tag a known-good commit/tag in your submodule pin until v1.0.0 ships.