/*
 * libotppad.h — format-critical helpers for one-time-pad encryption.
 *
 * Bit-compatible with the `otp` project (https://git.laantungir.net/laantungir/otp):
 *   - XOR transform
 *   - ASCII armored message format ("-----BEGIN OTP MESSAGE-----")
 *   - Binary .otp file format (magic "OTP\0")
 *   - ISO/IEC 9797-1 Method 2 (Padmé) padding with exponential bucketing
 *   - Per-pad .state file ("offset=<n>\n")
 *   - 256-bit XOR pad checksum (position-dependent, XORed with first 32 pad bytes)
 *
 * This library is self-contained: it does not depend on the `otp` project's
 * main.h, global state, or UI code. Both `otp` and `n_signer` link against it.
 *
 * License: same as the otp project.
 */
#ifndef LIBOTPPAD_H
#define LIBOTPPAD_H

#include <stddef.h>
#include <stdint.h>
#include <stdio.h>

#ifdef __cplusplus
extern "C" {
#endif

/* ------------------------------------------------------------------ */
/* Constants                                                          */
/* ------------------------------------------------------------------ */

#define OTPPAD_CHKSUM_HEX_LEN   64          /* 32 bytes -> 64 hex chars */
#define OTPPAD_CHKSUM_BIN_LEN   32
#define OTPPAD_HEADER_RESERVED  32          /* bytes reserved at start of pad */
#define OTPPAD_MAGIC            "OTP\0"     /* 4-byte binary file magic   */
#define OTPPAD_MAGIC_LEN        4
#define OTPPAD_FORMAT_VERSION   1           /* binary .otp format version */

#define OTPPAD_ARMOR_BEGIN      "-----BEGIN OTP MESSAGE-----"
#define OTPPAD_ARMOR_END        "-----END OTP MESSAGE-----"

/* ------------------------------------------------------------------ */
/* XOR transform                                                      */
/* ------------------------------------------------------------------ */

/*
 * XOR `data_len` bytes of `data` with `pad_data` into `result`.
 * `data`, `pad_data`, `result` must each be at least `data_len` bytes.
 * `result` may alias `data` or `pad_data`.
 * Returns 0 on success, non-zero on null pointer.
 */
int otppad_xor(const unsigned char *data, size_t data_len,
               const unsigned char *pad_data, unsigned char *result);

/* ------------------------------------------------------------------ */
/* Base64                                                             */
/* ------------------------------------------------------------------ */

/* Encode `length` bytes of `input` as a NUL-terminated base64 string.
 * Caller frees the returned string. Returns NULL on allocation failure. */
char *otppad_base64_encode(const unsigned char *input, int length);

/* Decode NUL-terminated base64 `input` into bytes.
 * Caller frees the returned buffer. *output_length receives the byte count.
 * Returns NULL on invalid input or allocation failure. */
unsigned char *otppad_base64_decode(const char *input, int *output_length);

/* ------------------------------------------------------------------ */
/* Padmé padding (ISO/IEC 9797-1 Method 2) + exponential bucketing    */
/* ------------------------------------------------------------------ */

/* Calculate the bucket size for a message of `msg_len` bytes.
 * Starts at 256 bytes and doubles until `chunk >= msg_len + 1`. */
size_t otppad_chunk_size(size_t msg_len);

/* Apply Padmé padding to `buffer` (must hold `chunk_size` bytes).
 * Writes 0x80 at `buffer[msg_len]` then zeroes to `chunk_size`.
 * Returns 0 on success, non-zero on error. */
int otppad_pad_apply(unsigned char *buffer, size_t msg_len, size_t chunk_size);

/* Remove Padmé padding: scan backwards for 0x80, set *msg_len to its index.
 * Returns 0 on success, non-zero on invalid padding. */
int otppad_pad_remove(const unsigned char *buffer, size_t chunk_size,
                      size_t *msg_len);

/* Human-readable chunk size string (e.g. "256 bytes", "1.0 KB"). */
void otppad_chunk_format(size_t chunk_size, char *buffer, size_t buffer_size);

/* ------------------------------------------------------------------ */
/* ASCII armored message format                                       */
/* ------------------------------------------------------------------ */

/*
 * Parse an ASCII-armored OTP message.
 *
 * `chksum` must be at least OTPPAD_CHKSUM_HEX_LEN+1 bytes.
 * `base64_data` must be at least `base64_buf_size` bytes; the decoded
 * ciphertext is NOT returned here — only the raw base64 text. Use
 * otppad_base64_decode() to get the bytes.
 *
 * On success returns 0 and sets `chksum`, `*offset`, and `base64_data`.
 * Returns non-zero on malformed input.
 */
int otppad_armor_parse(const char *message, char *chksum, uint64_t *offset,
                       char *base64_data, size_t base64_buf_size);

/*
 * Build an ASCII-armored OTP message.
 *
 * `version` is the version string for the "Version:" header (e.g. "v0.3.53").
 * `chksum` is the 64-char hex pad checksum.
 * `offset` is the pad offset where the slice begins.
 * `encrypted_data` / `data_length` is the ciphertext to base64-encode.
 *
 * On success returns 0 and sets `*ascii_output` to a malloc'd NUL-terminated
 * string. Caller frees `*ascii_output`.
 */
int otppad_armor_generate(const char *version, const char *chksum,
                          uint64_t offset,
                          const unsigned char *encrypted_data, size_t data_length,
                          char **ascii_output);

/* ------------------------------------------------------------------ */
/* Binary .otp file format                                            */
/* ------------------------------------------------------------------ */

/*
 * Binary .otp header (58 bytes). All fields are little-endian on disk
 * (written via fwrite of host-endian integers — matches the otp project,
 * which is x86/ARM little-endian in practice).
 *
 * Offset  Size  Field
 * 0       4     Magic "OTP\0"
 * 4       2     Version (uint16, currently 1)
 * 6       32    Pad checksum (binary, 32 bytes)
 * 38      8     Pad offset (uint64)
 * 46      4     Original file mode (uint32)
 * 50      8     Original file size (uint64, NOT padded size)
 * 58      var   Encrypted (padded) data
 */
typedef struct {
    char          magic[OTPPAD_MAGIC_LEN];
    uint16_t      version;
    unsigned char pad_chksum[OTPPAD_CHKSUM_BIN_LEN];
    uint64_t      pad_offset;
    uint32_t      file_mode;
    uint64_t      file_size;        /* original (unpadded) size */
} otppad_bin_header_t;

/* Write a binary .otp header to `fp`. Returns 0 on success. */
int otppad_bin_header_write(FILE *fp, const otppad_bin_header_t *hdr);

/* Read a binary .otp header from `fp`. Returns 0 on success, non-zero on
 * malformed input. Does not validate the magic — use otppad_bin_is_magic()
 * first if needed. */
int otppad_bin_header_read(FILE *fp, otppad_bin_header_t *hdr);

/* Return 1 if the first 4 bytes of `buf` match the OTP magic. */
int otppad_bin_is_magic(const unsigned char *buf, size_t len);

/* ------------------------------------------------------------------ */
/* Per-pad .state file                                                */
/* ------------------------------------------------------------------ */

/*
 * Read the offset from `<pads_dir>/<chksum>.state`.
 * Returns 0 on success and sets *offset. Non-zero on error.
 */
int otppad_state_read(const char *pads_dir, const char *chksum, uint64_t *offset);

/*
 * Atomically write the offset to `<pads_dir>/<chksum>.state`.
 * Writes to a temp file then renames, so a crash cannot corrupt the state.
 * Returns 0 on success, non-zero on error.
 */
int otppad_state_write(const char *pads_dir, const char *chksum, uint64_t offset);

/* ------------------------------------------------------------------ */
/* Pad checksum                                                       */
/* ------------------------------------------------------------------ */

/*
 * Compute the 256-bit XOR checksum of the pad file at `pad_path`.
 * `checksum_hex` must be at least OTPPAD_CHKSUM_HEX_LEN+1 bytes.
 *
 * Algorithm (matches otp/src/crypto.c:calculate_checksum):
 *   - XOR every byte into one of 32 buckets, selected by (position % 32),
 *     also XORing in bytes (pos>>8),(pos>>16),(pos>>24) of the position.
 *   - XOR the resulting 32-byte checksum with the first 32 bytes of the pad
 *     (the "pad key").
 *   - Hex-encode the 32-byte result.
 *
 * Returns 0 on success, non-zero on error.
 */
int otppad_checksum(const char *pad_path, char *checksum_hex);

#ifdef __cplusplus
}
#endif

#endif /* LIBOTPPAD_H */
