v4.0.0: codec 2.60.4 security release, --pq-box sealed-box mode, F-16 fix
Some checks failed
CI / build-and-test (clang) (push) Has been cancelled
CI / build-and-test (gcc) (push) Has been cancelled
CI / strict-warnings (clang, -Wall -Wextra -Wpedantic -Wshadow -Wcast-align -Wstrict-prototypes -Wmissing-prototypes -Wnull-dereference -O2 -std=c11 -Werror) (push) Has been cancelled
CI / strict-warnings (gcc, -Wall -Wextra -Wpedantic -Wshadow -Wcast-align -Wstrict-prototypes -Wmissing-prototypes -Wnull-dereference -Wformat-security -Wlogical-op -Wjump-misses-init -Wdouble-promotion -O2 -std=c11 -Werror) (push) Has been cancelled
CI / sanitizers (push) Has been cancelled
CI / pie-hardening (push) Has been cancelled
CI / cross-aarch64 (push) Has been cancelled
CI / dist-reproducibility (push) Has been cancelled
CI / packaging-syntax (push) Has been cancelled
CI / release (push) Has been cancelled
Some checks failed
CI / build-and-test (clang) (push) Has been cancelled
CI / build-and-test (gcc) (push) Has been cancelled
CI / strict-warnings (clang, -Wall -Wextra -Wpedantic -Wshadow -Wcast-align -Wstrict-prototypes -Wmissing-prototypes -Wnull-dereference -O2 -std=c11 -Werror) (push) Has been cancelled
CI / strict-warnings (gcc, -Wall -Wextra -Wpedantic -Wshadow -Wcast-align -Wstrict-prototypes -Wmissing-prototypes -Wnull-dereference -Wformat-security -Wlogical-op -Wjump-misses-init -Wdouble-promotion -O2 -std=c11 -Werror) (push) Has been cancelled
CI / sanitizers (push) Has been cancelled
CI / pie-hardening (push) Has been cancelled
CI / cross-aarch64 (push) Has been cancelled
CI / dist-reproducibility (push) Has been cancelled
CI / packaging-syntax (push) Has been cancelled
CI / release (push) Has been cancelled
Major release. Highlights: - Codec: vendored VaptVupt codec moves to canonical 2.60.4 security release. Fixes a high-severity OOB heap write in the AVX2 decode fast path (reachable on a valid stream sized to exactly content_size, both tail variants). Brings CBMC-formally-verified BCJ filters with automatic ELF/PE/Mach-O detection. Compressed output stays byte-identical (ratio gate Δ 0.00%); wire format unchanged at v1.6. - New --pq-box sealed-box recipient mode (vendored libpqvaptvupt 0.6.0): ML-KEM-768 + X25519 combined via HKDF-SHA256 with domain separation, AES-256-CTR + HMAC-SHA256 EtM. Legacy --pq and --pq-sdk stay readable. - F-16: discloses and fixes a pre-existing data-loss defect in the <= 3.8.0 in-tree BCJ encoder. Full back-compat matrix decodes byte-exact under 4.0.0; every readable pre-4.0 archive remains readable. Repository hygiene: - Sync full 4.0.0 source tree (codec, crypto, SDK, GUI, packaging, tests). - Remove internal scratch files (PROMPT.md, FORMAL_AUDIT_PROMPT.md) and superseded version-specific docs (INTEGRATION_PROTOCOL_2.60.4.md, docs/FINDINGS-2.x.md) and a stray test binary. - Refresh README download/install section to real 4.0.0 release assets; bump version badge to 4.0.0. - Add .gitignore for build outputs (keeps vendored prebuilt libraries).
This commit is contained in:
parent
7619c4c577
commit
544a2cd647
98 changed files with 15615 additions and 1397 deletions
|
|
@ -121,7 +121,9 @@ static inline uint32_t vv_bh_pack(vv_block_type_t t, int last, uint32_t sz) {
|
|||
typedef struct {
|
||||
uint32_t magic; /* VV_MAGIC */
|
||||
uint8_t version; /* Format version (1) */
|
||||
uint8_t flags; /* bit0: has_checksum, bit1: has_dict */
|
||||
uint8_t flags; /* bit0: has_checksum, bit1: has_dict,
|
||||
* bit2: x86 BCJ filter applied,
|
||||
* bit3: ARM64 BCJ filter applied */
|
||||
uint8_t mode_hint; /* Compression mode used (informational) */
|
||||
uint8_t window_log; /* Window size = 1 << window_log */
|
||||
uint64_t content_size; /* Uncompressed size (0 = unknown) */
|
||||
|
|
@ -195,6 +197,63 @@ typedef struct {
|
|||
* output must be readable by v2.46.5 or
|
||||
* older decoders. Default 0 (lit_fmt=4
|
||||
* enabled, requires v2.47+ decoder). */
|
||||
int filter_x86; /* 1 = apply the reversible x86 BCJ branch
|
||||
* filter before compression (header flag
|
||||
* bit2). Improves x86/x86-64 machine-code
|
||||
* ratio (~+3–7% measured); the decoder
|
||||
* inverts it automatically. Requires a
|
||||
* v2.53.4+ decoder. Opt-in; default 0. */
|
||||
int filter_arm64; /* 1 = apply the reversible AArch64 (ARM64) BCJ
|
||||
* branch filter (BL + ADRP) before
|
||||
* compression (header flag bit3). Improves
|
||||
* AArch64 machine-code ratio (~+2–5%
|
||||
* measured); the decoder inverts it
|
||||
* automatically. Requires a v2.54.0+
|
||||
* decoder. Opt-in; default 0. Mutually
|
||||
* exclusive with filter_x86 (a file is one
|
||||
* architecture). */
|
||||
int filter_auto; /* 1 = sniff the input for an ELF/PE/Mach-O
|
||||
* header and automatically select the x86
|
||||
* or ARM64 BCJ filter (or none) to match.
|
||||
* Has no effect if filter_x86 or
|
||||
* filter_arm64 is already set, or if no
|
||||
* executable header is recognised — in
|
||||
* which case output is unchanged. Opt-in;
|
||||
* default 0. */
|
||||
uint32_t depth_override;/* 0 = use the mode's default match-finder chain
|
||||
* depth (fast=4, balanced=24, extreme=256).
|
||||
* Non-zero overrides it, clamped to
|
||||
* [1, 4096], trading encode speed for ratio
|
||||
* along a smooth monotonic curve (measured:
|
||||
* on dickens, fast depth 1→8 spans
|
||||
* 1.785@79 MB/s to 2.067@55 MB/s). Affects
|
||||
* only the chosen matches, so output stays a
|
||||
* valid stream any decoder reads; default
|
||||
* output (0) is byte-identical to prior
|
||||
* releases. Opt-in; default 0. */
|
||||
uint32_t accel; /* 0 = off (default; byte-identical). >0 enables
|
||||
* lz4-style position-skip acceleration: after
|
||||
* a run of f consecutive no-match positions
|
||||
* the parser advances by 1 + ((f*accel)>>6)
|
||||
* instead of 1, skipping hash/insert work on
|
||||
* unmatchable input. Massively speeds up
|
||||
* encode on incompressible / already-
|
||||
* compressed data (measured ~8-9x on
|
||||
* random/gzip input) for a small ratio cost
|
||||
* on compressible data (~-0.2% on dickens),
|
||||
* which is why it is opt-in. Clamped to
|
||||
* [0, 64]; higher = more aggressive skipping.
|
||||
* Primarily useful with -m fast. Output stays
|
||||
* decodable by any decoder. */
|
||||
int no_rep; /* 1 = disable rep-match probing in the greedy/
|
||||
* lazy parser. Measured net-positive on ratio
|
||||
* in fast mode (which has no entropy stage, so
|
||||
* rep offsets are not cheaper to code) and
|
||||
* ~10% faster; on binary it can cost a little
|
||||
* ratio, so it is opt-in. Default 0 keeps rep
|
||||
* enabled and output byte-identical. Affects
|
||||
* fast/balanced (the greedy/lazy parser);
|
||||
* designed for -m fast. */
|
||||
} vv_options_t;
|
||||
|
||||
static inline void vv_default_options(vv_options_t *o) {
|
||||
|
|
@ -204,6 +263,12 @@ static inline void vv_default_options(vv_options_t *o) {
|
|||
o->verbose = 0;
|
||||
o->format_v2 = 0;
|
||||
o->compat_v246_5_decoder = 0;
|
||||
o->filter_x86 = 0;
|
||||
o->filter_arm64 = 0;
|
||||
o->filter_auto = 0;
|
||||
o->depth_override = 0;
|
||||
o->accel = 0;
|
||||
o->no_rep = 0;
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════════════════
|
||||
|
|
@ -228,7 +293,7 @@ int64_t vv_decompress(const uint8_t *src, size_t src_len,
|
|||
* Use when the caller has its own
|
||||
* integrity protection (e.g. AES-GCM
|
||||
* wrapping the compressed data, as in
|
||||
* Zupt backups). On RAW/random-data
|
||||
* application backups). On RAW/random-data
|
||||
* inputs where XXH64 dominates decode
|
||||
* time, this flag delivers a ~2× speedup.
|
||||
*
|
||||
|
|
@ -250,8 +315,8 @@ size_t vv_compress_bound(size_t src_len);
|
|||
* MULTI-THREADED COMPRESSION
|
||||
*
|
||||
* Compresses large inputs in parallel by splitting into independent
|
||||
* frames (each a valid .vv frame on its own — concatenated output
|
||||
* is a valid .vv file that vv_decompress handles natively as a
|
||||
* frames (each a valid VaptVupt frame on its own — concatenated output
|
||||
* is a valid .zupt file that vv_decompress handles natively as a
|
||||
* multi-frame stream).
|
||||
*
|
||||
* Requires the library to be built with VV_ENABLE_THREADS (and
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
* VaptVupt — tANS Entropy Codec (v2: sparse header + 4-way interleaved)
|
||||
*
|
||||
* Standalone: define VV_ANS_STANDALONE to use without VaptVupt.
|
||||
* ZUPT-COMPAT: this header has zero VaptVupt dependencies when standalone.
|
||||
* EMBED-COMPAT: this header has zero VaptVupt dependencies when standalone.
|
||||
*
|
||||
* v0.6 changes:
|
||||
* - Adaptive sparse/dense header (Item 1): 3× smaller on typical data
|
||||
|
|
@ -28,7 +28,7 @@ extern "C" {
|
|||
#define VVA_HDR_SINGLE 0x01 /* Single symbol: 0-bit encoding */
|
||||
#define VVA_HDR_SPARSE 0x02 /* ≤32 active symbols: (sym,freq) pairs */
|
||||
#define VVA_HDR_DENSE 0x03 /* >32 active symbols: max_sym + freq array */
|
||||
/* ZUPT-COMPAT: v0.5 legacy format detected by first byte being 0x00-0xFF
|
||||
/* EMBED-COMPAT: v0.5 legacy format detected by first byte being 0x00-0xFF
|
||||
* without matching any HDR_* code — fall back to old read path. */
|
||||
#define VVA_HDR_LEGACY 0x00 /* v0.5 format: [max_sym] [2B×(max_sym+1)] */
|
||||
|
||||
|
|
@ -86,7 +86,7 @@ vva_error_t vva_decode_ctx(const uint8_t *src, size_t src_len,
|
|||
size_t num_literals, size_t *src_consumed);
|
||||
|
||||
/* ═══ Sequence coding (tag 'S', v0.8+) ═══
|
||||
* ZUPT-COMPAT: available when VV_ANS_STANDALONE is defined.
|
||||
* EMBED-COMPAT: available when VV_ANS_STANDALONE is defined.
|
||||
*
|
||||
* Encodes an LZ token stream using 3 ANS tables: literals, match-length
|
||||
* codes (36 symbols), and offset codes (24 symbols). Replaces raw varint
|
||||
|
|
|
|||
57
include/vv_bcj.h
Normal file
57
include/vv_bcj.h
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* VaptVupt — BCJ branch filters (see src/vv_bcj.c).
|
||||
*
|
||||
* Reversible, architecture-specific branch converters that improve the
|
||||
* compression of machine code by turning relative call targets into an
|
||||
* absolute form. Each is an exact bijection on arbitrary input, so a file
|
||||
* filtered with the wrong architecture (or no machine code at all) still
|
||||
* round-trips byte-for-byte.
|
||||
*/
|
||||
#ifndef VV_BCJ_H
|
||||
#define VV_BCJ_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/*
|
||||
* x86 / x86-64 BCJ. Converts near CALL (0xE8) and JMP (0xE9) relative
|
||||
* displacements to/from absolute. encoding != 0 = forward (compress-side),
|
||||
* 0 = inverse (decode-side). `ip` is the stream offset of byte 0 (use 0 for
|
||||
* whole-buffer transforms). Returns the prefix length that may have been
|
||||
* modified. vv_bcj_x86(b,n,0,0) undoes vv_bcj_x86(b,n,0,1).
|
||||
*/
|
||||
size_t vv_bcj_x86(uint8_t *data, size_t size, uint32_t ip, int encoding);
|
||||
|
||||
/*
|
||||
* AArch64 (ARM64) BL + ADRP filter. Converts BL (call) 26-bit relative word
|
||||
* offsets and ADRP (PC-relative page address) 21-bit page offsets to/from an
|
||||
* absolute form, each modulo its immediate width. Same calling convention as
|
||||
* vv_bcj_x86. Only BL (opcode 100101) and ADRP (1xx10000) are touched;
|
||||
* opcode and register bits are preserved, so the transform is an exact
|
||||
* bijection on arbitrary input. vv_bcj_arm64(b,n,0,0) undoes
|
||||
* vv_bcj_arm64(b,n,0,1).
|
||||
*/
|
||||
size_t vv_bcj_arm64(uint8_t *data, size_t size, uint32_t ip, int encoding);
|
||||
|
||||
/* Which branch filter best fits a buffer, by sniffing an executable header. */
|
||||
typedef enum {
|
||||
VV_FILTER_NONE = 0,
|
||||
VV_FILTER_X86 = 1,
|
||||
VV_FILTER_ARM64 = 2
|
||||
} vv_filter_kind_t;
|
||||
|
||||
/*
|
||||
* Inspect the first bytes of `data` for an ELF, PE (MZ/PE), or Mach-O header
|
||||
* and return the BCJ filter that matches its machine type:
|
||||
* - x86 / x86-64 (and 32-bit x86) -> VV_FILTER_X86
|
||||
* - AArch64 (ARM64) -> VV_FILTER_ARM64
|
||||
* - anything else, or no recognised header-> VV_FILTER_NONE
|
||||
* Fully bounds-checked: safe on truncated or arbitrary input. Detection
|
||||
* errors are never correctness bugs — a missed match just means no filter,
|
||||
* and a spurious match still round-trips (the filters are bijections), it
|
||||
* merely may not improve the ratio.
|
||||
*/
|
||||
vv_filter_kind_t vv_bcj_detect(const uint8_t *data, size_t size);
|
||||
|
||||
#endif /* VV_BCJ_H */
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
* VaptVupt — Canonical Huffman Codec
|
||||
*
|
||||
* Standalone header: can be used independently with VV_HUFFMAN_STANDALONE.
|
||||
* Designed for embedding in Zupt or any other LZ codec.
|
||||
* Designed for embedding in a host application or any other LZ codec.
|
||||
*
|
||||
* API:
|
||||
* vvh_encode() — compress raw literals into Huffman bitstream
|
||||
|
|
|
|||
150
include/zupt.h
150
include/zupt.h
|
|
@ -30,9 +30,47 @@
|
|||
#define zupt_mkdir(p) mkdir(p, 0755)
|
||||
#endif
|
||||
|
||||
#define ZUPT_VERSION_STRING "2.2.3"
|
||||
/* ─── Product identity ─────────────────────────────────────────────
|
||||
*
|
||||
* v3.0.0 (INPI Brasil trademark rename):
|
||||
* - Product name is now "VaptVupt" (was "Zupt"). The earlier name
|
||||
* conflicted with a software trademark already registered at INPI
|
||||
* Brasil under "Zupt".
|
||||
* - File extension stays `.zupt` for archive-format continuity:
|
||||
* v1.0–v2.4.x archives remain readable, the magic bytes
|
||||
* `\x5A\x55\x50\x54\x1A\x00` ("ZUPT" + sub-version) are unchanged.
|
||||
* - C identifier prefix stays `zupt_` / `ZUPT_` for ABI continuity
|
||||
* with libzuptsdk and existing callers. Only user-visible strings
|
||||
* (binary name, banner, help text, package names) change.
|
||||
* - The binary is now `vaptvupt`. Distro packages may ship a
|
||||
* compatibility symlink `zupt -> vaptvupt` for one major version.
|
||||
*/
|
||||
#define ZUPT_PRODUCT_NAME "VaptVupt"
|
||||
#define ZUPT_PRODUCT_NAME_LC "vaptvupt" /* lowercase: binary name */
|
||||
#define ZUPT_PRODUCT_EXTENSION ".zupt" /* on-disk archive extension (kept stable) */
|
||||
#define ZUPT_PRODUCT_TAGLINE "Post-quantum backup compression"
|
||||
|
||||
#define ZUPT_VERSION_STRING "4.0.0"
|
||||
/* Vendored codec release (upstream tag) — single source for display strings.
|
||||
* The codec's own VV_VERSION_* is its internal API version, not the release. */
|
||||
#define ZUPT_CODEC_RELEASE "2.60.4"
|
||||
#define ZUPT_FORMAT_MAJOR 1
|
||||
#define ZUPT_FORMAT_MINOR 4
|
||||
#define ZUPT_FORMAT_MINOR 6
|
||||
|
||||
/* F-08 of v2.3.0: archive-integrity trailer.
|
||||
*
|
||||
* v1.5 archives append a 32-byte trailing field AFTER the 32-byte footer.
|
||||
* Encrypted modes store HMAC-SHA256(mac_key, hdr[0..63] || footer[0..23]).
|
||||
* Plaintext modes store XXH64(...) in the first 8 bytes, zeros in the rest.
|
||||
*
|
||||
* The MAC input excludes footer[24..31] (the "ZEND" magic and footer_version)
|
||||
* to keep the field stable across format-version transitions. Both bytes are
|
||||
* structurally validated by read_footer().
|
||||
*
|
||||
* Read path falls back to v1.4 layout (no trailer) when the footer magic is
|
||||
* found at EOF-32 instead of EOF-64. */
|
||||
#define ZUPT_AIT_SIZE 32
|
||||
#define ZUPT_AIT_MAC_INPUT_LEN (sizeof(zupt_archive_header_t) + 24)
|
||||
|
||||
#define ZUPT_MAGIC_0 0x5A
|
||||
#define ZUPT_MAGIC_1 0x55
|
||||
|
|
@ -58,21 +96,58 @@
|
|||
#define ZUPT_FLAG_FORMAT_STABLE (1u << 4) /* v1.0: format frozen */
|
||||
#define ZUPT_FLAG_DEDUP (1u << 7) /* Block-level deduplication enabled */
|
||||
#define ZUPT_FLAG_AAD_SEQ (1u << 8) /* MAC binds block_seq as AAD (anti-reorder) */
|
||||
#define ZUPT_FLAG_AAD_PREFACE (1u << 9) /* v1.6: MAC also binds per-block frame preface (F-09) */
|
||||
|
||||
/* Encryption types (stored in encryption header block) */
|
||||
#define ZUPT_ENC_PBKDF2 0x01 /* Password-based: PBKDF2 → AES-256-CTR + HMAC */
|
||||
#define ZUPT_ENC_PQ_HYBRID 0x02 /* ML-KEM-768 + X25519 hybrid KEM (legacy XOR+SHA3) */
|
||||
#define ZUPT_ENC_PQ_SDK_V2 0x03 /* libzuptsdk v2 header: HKDF combiner + commitment + HPKE binding */
|
||||
#define ZUPT_ENC_PW_ARGON2 0x04 /* Password-based via libzuptsdk: Argon2id + XChaCha20-Poly1305 */
|
||||
#define ZUPT_ENC_PQ_BOX_V1 0x05 /* libpqvaptvupt sealed box: HKDF-SHA256 domain-separated combiner */
|
||||
|
||||
/* Argon2id KDF profile descriptor (v3.4.0).
|
||||
*
|
||||
* The 0x04 Argon2id enc-header historically recorded only [type|salt|
|
||||
* nonce] (33 bytes) and said nothing about the KDF cost parameters,
|
||||
* unlike the PBKDF2 header which records its iteration count. That made
|
||||
* an 0x04 archive non-self-describing: if the underlying Argon2id cost
|
||||
* preset ever changed, old archives could become undecryptable with no
|
||||
* way for a reader to know which cost produced them.
|
||||
*
|
||||
* v3.4.0 appends ONE descriptor byte at offset 33 naming the KDF profile
|
||||
* that produced the archive. Readers that understand the byte can select
|
||||
* the matching derivation; the legacy reader (which checks enc_hdr_len
|
||||
* >= 33 and reads fixed offsets) simply ignores the trailing byte, so
|
||||
* existing 33-byte archives and new 34-byte archives both decrypt. The
|
||||
* descriptor is covered by the archive-integrity trailer (F-08), so it
|
||||
* cannot be stripped or forged without failing authentication.
|
||||
*
|
||||
* Profile 0 (implicit, absent byte) == the historical libzuptsdk
|
||||
* "MODERATE" Argon2id preset reached via zuptsdk_easy_derive_key.
|
||||
* Profile 1 is the same derivation with the descriptor made explicit so
|
||||
* future profiles (should the cost change) get distinct IDs. */
|
||||
#define ZUPT_ARGON2_PROFILE_LEGACY 0x00 /* implicit: pre-3.4.0, no descriptor byte */
|
||||
#define ZUPT_ARGON2_PROFILE_MODERATE 0x01 /* explicit: libzuptsdk MODERATE preset */
|
||||
#define ZUPT_ARGON2_HDR_LEN_V1 33 /* [type|salt16|nonce16] */
|
||||
#define ZUPT_ARGON2_HDR_LEN_V2 34 /* + [profile1] */
|
||||
|
||||
/* Block types */
|
||||
#define ZUPT_BLOCK_DATA 0x00
|
||||
#define ZUPT_BLOCK_INDEX 0x02
|
||||
#define ZUPT_BLOCK_ENC_HEADER 0x03
|
||||
#define ZUPT_BLOCK_DEDUP_REF 0x04 /* Dedup reference: payload = 8B offset of original block */
|
||||
#define ZUPT_BLOCK_COMMENT 0x05 /* v2.4.3: free-form UTF-8 comment, encrypted same as data blocks */
|
||||
|
||||
#define ZUPT_MAX_COMMENT_LEN 4096 /* Maximum comment payload size (bytes). */
|
||||
|
||||
/* Block flags */
|
||||
#define ZUPT_BFLAG_ENCRYPTED (1u << 0)
|
||||
/* No per-block flag for F-09 — the v1.6 preface-AAD policy is anchored at
|
||||
* archive level via ZUPT_FLAG_AAD_PREFACE in global_flags. That flag is
|
||||
* itself MAC-protected by the v1.5 archive-integrity-trailer (F-08), so an
|
||||
* attacker can't clear it to downgrade. A per-block flag here would be
|
||||
* unauthenticated until the per-block MAC was checked, creating a chicken-
|
||||
* and-egg gap. */
|
||||
|
||||
/* Codec IDs */
|
||||
#define ZUPT_CODEC_STORE 0x0000
|
||||
|
|
@ -82,6 +157,20 @@
|
|||
#define ZUPT_CODEC_VAPTVUPT 0x0010 /* VAPTVUPT: VaptVupt LZ + ANS entropy codec */
|
||||
#define ZUPT_CODEC_AUTO 0xFFFF /* Auto-detect: VaptVupt if AVX2, else LZHP */
|
||||
|
||||
/* SIMD decode over-copy guard (bytes).
|
||||
*
|
||||
* The VaptVupt codec's AVX2 decode hot path over-writes up to 32 bytes
|
||||
* past the logical output end (vaptvupt.h: "Copy exactly n bytes, may
|
||||
* over-read/write by up to 32 bytes. Caller must ensure sufficient slack
|
||||
* in destination."). Every decode output buffer is over-allocated by
|
||||
* this many bytes and the padded capacity is passed to the codec so the
|
||||
* over-copy lands in owned memory. The reported uncompressed size is
|
||||
* unchanged; the slack is never part of the output. 64 > 32 leaves
|
||||
* margin for any future SIMD store-width increase (AVX-512 = 64 B).
|
||||
* Used by both the single-threaded (zupt_format.c) and parallel
|
||||
* (zupt_parallel.c) decode paths. */
|
||||
#define ZUPT_VV_DECODE_SLACK 64
|
||||
|
||||
/* Crypto */
|
||||
#define ZUPT_SALT_SIZE 32
|
||||
#define ZUPT_NONCE_SIZE 16
|
||||
|
|
@ -147,6 +236,7 @@ typedef struct {
|
|||
uint32_t iterations;
|
||||
int active;
|
||||
uint64_t canary_tail; /* Must equal ZUPT_CANARY */
|
||||
int use_preface_aad; /* F-09 of v2.3.1: appended after canary so existing field layout is preserved */
|
||||
} zupt_keyring_t;
|
||||
|
||||
/* Check keyring canaries — abort on buffer overflow */
|
||||
|
|
@ -174,9 +264,13 @@ typedef struct {
|
|||
int verbose, encrypt, quiet, solid, threads;
|
||||
int pq_mode; /* 1 = post-quantum hybrid KEM mode */
|
||||
int sdk_mode; /* 1 = use libzuptsdk-backed v3 crypto (HKDF combiner + commitment + HPKE) */
|
||||
int box_mode; /* 1 = libpqvaptvupt sealed-box mode (ZUPT_ENC_PQ_BOX_V1) */
|
||||
int dedup; /* 1 = block-level deduplication enabled */
|
||||
int kdf_legacy_pbkdf2; /* v2.4.1: 1 = force PBKDF2-SHA256 enc-header (compat with v2.4.0 and older readers). Default 0 = Argon2id. */
|
||||
char password[256];
|
||||
char keyfile[ZUPT_MAX_PATH]; /* Path to .zupt-key file */
|
||||
char comment[ZUPT_MAX_COMMENT_LEN]; /* v2.4.3: free-form archive comment, encrypted on write if -e */
|
||||
int has_comment; /* v2.4.3: 1 = a comment was supplied (write side) or read from archive (read side) */
|
||||
zupt_keyring_t keyring;
|
||||
} zupt_options_t;
|
||||
|
||||
|
|
@ -263,6 +357,12 @@ void zupt_sha256_init(zupt_sha256_ctx *c);
|
|||
void zupt_sha256_update(zupt_sha256_ctx *c, const uint8_t *d, size_t n);
|
||||
void zupt_sha256_final(zupt_sha256_ctx *c, uint8_t h[32]);
|
||||
void zupt_sha256(const uint8_t *d, size_t n, uint8_t h[32]);
|
||||
/* SHA-NI hardware compression function (x86_64; src/zupt_sha256_shani.c).
|
||||
* Processes `blocks` full 64-byte blocks, updating state[8] in place.
|
||||
* Internal: called by zupt_sha256_update() only when zupt_cpu.has_shani. */
|
||||
#if defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || defined(_M_IX86)
|
||||
void zupt_sha256_transform_shani(uint32_t state[8], const uint8_t *data, size_t blocks);
|
||||
#endif
|
||||
|
||||
/* ─── AES-256 ─── */
|
||||
typedef struct { uint32_t rk[60]; } zupt_aes256_ctx;
|
||||
|
|
@ -271,11 +371,50 @@ void zupt_aes256_encrypt_block(const zupt_aes256_ctx *c, const uint8_t in[16], u
|
|||
|
||||
/* ─── Crypto ops ─── */
|
||||
void zupt_hmac_sha256(const uint8_t *key, size_t klen, const uint8_t *data, size_t dlen, uint8_t mac[32]);
|
||||
|
||||
/* Incremental HMAC-SHA256 (RFC 2104).
|
||||
*
|
||||
* For repeated MACs under the SAME key (the per-block Encrypt-then-MAC
|
||||
* hot path), this folds the ipad/opad key-prefix compression ONCE in
|
||||
* _init and lets the caller stream the message via _update — avoiding
|
||||
* both the per-call key-pad recompute and any concat/copy buffer for
|
||||
* the message segments. Bit-identical output to the one-shot
|
||||
* zupt_hmac_sha256 (which is itself implemented on top of this). */
|
||||
typedef struct {
|
||||
zupt_sha256_ctx inner; /* SHA-256 state seeded with the ipad block */
|
||||
zupt_sha256_ctx outer; /* SHA-256 state seeded with the opad block */
|
||||
} zupt_hmac_ctx;
|
||||
void zupt_hmac_sha256_init(zupt_hmac_ctx *c, const uint8_t *key, size_t klen);
|
||||
void zupt_hmac_sha256_update(zupt_hmac_ctx *c, const uint8_t *data, size_t dlen);
|
||||
void zupt_hmac_sha256_final(zupt_hmac_ctx *c, uint8_t mac[32]);
|
||||
|
||||
/* Constant-time buffer equality. Returns 1 if equal, 0 otherwise, in
|
||||
* time dependent only on n (not contents / mismatch position). The single
|
||||
* audited MAC-tag comparison primitive; timing-verified by the
|
||||
* dudect-style test in tests/test_ct_timing.c. CT-REQUIRED. */
|
||||
int zupt_ct_memeq(const void *a, const void *b, size_t n);
|
||||
void zupt_pbkdf2_sha256(const uint8_t *pw, size_t pwlen, const uint8_t *salt, size_t slen, uint32_t iter, uint8_t *out, size_t olen);
|
||||
void zupt_aes256_ctr(const uint8_t key[32], const uint8_t nonce[16], const uint8_t *in, uint8_t *out, size_t len);
|
||||
void zupt_derive_keys(zupt_keyring_t *kr, const char *pw, const uint8_t salt[32], const uint8_t nonce[16], uint32_t iter);
|
||||
uint8_t *zupt_encrypt_buffer(const zupt_keyring_t *kr, const uint8_t *plain, size_t plen, uint64_t seq, size_t *olen);
|
||||
uint8_t *zupt_decrypt_buffer(const zupt_keyring_t *kr, const uint8_t *pkg, size_t pkglen, uint64_t seq, size_t *olen);
|
||||
|
||||
/* F-09 of v2.3.1: extended-AAD variants. The MAC input becomes
|
||||
* aad_extra || nonce || ciphertext || aad_seq, which lets the caller bind
|
||||
* the per-block frame preface (block_type, codec_id, block_flags, sizes,
|
||||
* checksum) into the per-block HMAC without changing the on-disk payload
|
||||
* layout. v1.6 archives use these; older archives keep using the original
|
||||
* functions. */
|
||||
uint8_t *zupt_encrypt_buffer_aad(const zupt_keyring_t *kr,
|
||||
const uint8_t *plain, size_t plen,
|
||||
uint64_t seq,
|
||||
const uint8_t *aad_extra, size_t aad_extra_len,
|
||||
size_t *olen);
|
||||
uint8_t *zupt_decrypt_buffer_aad(const zupt_keyring_t *kr,
|
||||
const uint8_t *pkg, size_t pkglen,
|
||||
uint64_t seq,
|
||||
const uint8_t *aad_extra, size_t aad_extra_len,
|
||||
size_t *olen);
|
||||
void zupt_random_bytes(uint8_t *buf, size_t len);
|
||||
|
||||
/* ─── Memory locking for key material ─── */
|
||||
|
|
@ -335,6 +474,13 @@ int zupt_sdk_hybrid_encrypt_init(zupt_keyring_t *kr, const char *pubkeyfile,
|
|||
uint8_t *enc_hdr, size_t *enc_hdr_len);
|
||||
int zupt_sdk_hybrid_decrypt_init(zupt_keyring_t *kr, const char *privkeyfile,
|
||||
const uint8_t *enc_hdr, size_t enc_hdr_len);
|
||||
|
||||
/* pq-box mode (ZUPT_ENC_PQ_BOX_V1, vendored libpqvaptvupt) */
|
||||
int zupt_pqbox_keygen(const char *privkeyfile, const char *pubkeyfile);
|
||||
int zupt_pqbox_encrypt_init(zupt_keyring_t *kr, const char *pubkeyfile,
|
||||
uint8_t *enc_hdr, size_t *enc_hdr_len);
|
||||
int zupt_pqbox_decrypt_init(zupt_keyring_t *kr, const char *privkeyfile,
|
||||
const uint8_t *payload, size_t payload_len);
|
||||
int zupt_sdk_password_encrypt_init(zupt_keyring_t *kr, const char *password,
|
||||
uint8_t *enc_hdr, size_t *enc_hdr_len);
|
||||
int zupt_sdk_password_decrypt_init(zupt_keyring_t *kr, const char *password,
|
||||
|
|
|
|||
|
|
@ -15,14 +15,16 @@ typedef struct {
|
|||
int has_pclmul; /* CPUID.01H:ECX[1] — CLMUL (carry-less multiply) */
|
||||
int has_avx2; /* CPUID.07H:EBX[5] — AVX2 (256-bit SIMD) */
|
||||
int has_sse41; /* CPUID.01H:ECX[19] — SSE4.1 */
|
||||
int has_shani; /* CPUID.07H:EBX[29] — SHA-NI (SHA-1/SHA-256 ext) */
|
||||
} zupt_cpu_features_t;
|
||||
|
||||
/*@ assigns f->has_aesni, f->has_avx, f->has_pclmul, f->has_avx2, f->has_sse41;
|
||||
/*@ assigns f->has_aesni, f->has_avx, f->has_pclmul, f->has_avx2, f->has_sse41, f->has_shani;
|
||||
@ ensures f->has_aesni == 0 || f->has_aesni == 1;
|
||||
@ ensures f->has_avx == 0 || f->has_avx == 1;
|
||||
@ ensures f->has_pclmul == 0 || f->has_pclmul == 1;
|
||||
@ ensures f->has_avx2 == 0 || f->has_avx2 == 1;
|
||||
@ ensures f->has_sse41 == 0 || f->has_sse41 == 1;
|
||||
@ ensures f->has_shani == 0 || f->has_shani == 1;
|
||||
*/
|
||||
void zupt_detect_cpu(zupt_cpu_features_t *f);
|
||||
|
||||
|
|
|
|||
|
|
@ -62,4 +62,9 @@ int zupt_mlkem768_decaps(uint8_t ss[MLKEM_SSBYTES],
|
|||
const uint8_t ct[MLKEM_CIPHERTEXTBYTES],
|
||||
const uint8_t sk[MLKEM_SECRETKEYBYTES]);
|
||||
|
||||
/* Self-test: NTT/iNTT roundtrip + CBD-sampler range invariants.
|
||||
* Returns 1 on pass, 0 on fail. Called from test_vectors.c case 14
|
||||
* (F-04, Zupt 2.2.4). */
|
||||
int zupt_mlkem768_selftest(void);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Reference in a new issue