202 lines
8.3 KiB
C
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 */
|