Files
n_signer/libotppad/libotppad.h

202 lines
8.3 KiB
C

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