Workspace of 9 crates (noxis, noxis-core, noxis-classfile, noxis-analysis, noxis-passes, noxis-runtime, noxis-jar, noxis-verify, noxis-structure-check). AGPL-3.0-only license, Rust 2024 edition, unsafe_code denied.
245 lines
8.3 KiB
Rust
245 lines
8.3 KiB
Rust
//! Ветвь `xtea-ctr`: XTEA в режиме счётчика, побайтовый XOR на месте.
|
||
//!
|
||
//! Зеркало `scheme::xtea_ctr` побитово: ключ — четыре `u32` из key/nonce,
|
||
//! счётчик — номер чанка, все правые сдвиги — логические (`u32` в Rust).
|
||
|
||
use noxis_classfile::{Arith, ArrayKind, BranchCond, Cond, Convert, Insn, Prim, SlotKind};
|
||
|
||
use super::kdf::{Gen, c_int, load, store};
|
||
|
||
/// XTEA delta — знаковое представление для `ldc_w`.
|
||
const DELTA: i32 = 0x9E37_79B9u32 as i32;
|
||
|
||
/// Расшифровать `data` на месте; `len` — длина массива.
|
||
pub(super) fn emit_xtea(asm: &mut Gen, key: u16, nonce: u16, data: u16, len: u16) {
|
||
let slots = Slots::alloc(asm);
|
||
emit_key_array(asm, key, nonce, &slots);
|
||
asm.emit(c_int(0));
|
||
asm.emit(store(slots.chunk, SlotKind::Int));
|
||
let outer = asm.label();
|
||
let done = asm.label();
|
||
asm.mark(outer);
|
||
asm.emit(load(slots.chunk, SlotKind::Int));
|
||
asm.emit(load(len, SlotKind::Int));
|
||
asm.emit(Insn::Branch(BranchCond::Int(Cond::Ge), done));
|
||
emit_counter(asm, &slots);
|
||
emit_rounds(asm, &slots);
|
||
emit_xor_bytes(asm, data, len, &slots);
|
||
asm.emit(load(slots.chunk, SlotKind::Int));
|
||
asm.emit(c_int(8));
|
||
asm.emit(int_op(Arith::Add));
|
||
asm.emit(store(slots.chunk, SlotKind::Int));
|
||
asm.emit(Insn::Goto(outer));
|
||
asm.mark(done);
|
||
}
|
||
|
||
/// Слоты ветви.
|
||
struct Slots {
|
||
keys: u16,
|
||
chunk: u16,
|
||
value0: u16,
|
||
value1: u16,
|
||
sum: u16,
|
||
round: u16,
|
||
counter: u16,
|
||
tmp_a: u16,
|
||
tmp_b: u16,
|
||
src: u16,
|
||
shift: u16,
|
||
sub: u16,
|
||
index: u16,
|
||
}
|
||
|
||
impl Slots {
|
||
fn alloc(asm: &mut Gen) -> Self {
|
||
Self {
|
||
keys: asm.alloc(false),
|
||
chunk: asm.alloc(false),
|
||
value0: asm.alloc(false),
|
||
value1: asm.alloc(false),
|
||
sum: asm.alloc(false),
|
||
round: asm.alloc(false),
|
||
counter: asm.alloc(false),
|
||
tmp_a: asm.alloc(false),
|
||
tmp_b: asm.alloc(false),
|
||
src: asm.alloc(false),
|
||
shift: asm.alloc(false),
|
||
sub: asm.alloc(false),
|
||
index: asm.alloc(false),
|
||
}
|
||
}
|
||
}
|
||
|
||
/// `k = [key.lo, key.hi, nonce.lo, nonce.hi]` как `int[4]`.
|
||
fn emit_key_array(asm: &mut Gen, key: u16, nonce: u16, slots: &Slots) {
|
||
asm.emit(c_int(4));
|
||
asm.emit(Insn::NewArray(ArrayKind::Int));
|
||
asm.emit(store(slots.keys, SlotKind::Ref));
|
||
for (position, (source, high)) in [(key, false), (key, true), (nonce, false), (nonce, true)]
|
||
.into_iter()
|
||
.enumerate()
|
||
{
|
||
asm.emit(load(slots.keys, SlotKind::Ref));
|
||
asm.emit(c_int(position as i32));
|
||
asm.emit(load(source, SlotKind::Long));
|
||
if high {
|
||
asm.emit(c_int(32));
|
||
asm.emit(long_shift(Arith::Ushr));
|
||
asm.emit(Insn::Convert(Convert::L2I));
|
||
} else {
|
||
asm.emit(Insn::Convert(Convert::L2I));
|
||
}
|
||
asm.emit(Insn::ArrayStore(ArrayKind::Int));
|
||
}
|
||
}
|
||
|
||
/// Счётчик чанка: `v0 = 0`, `v1 = chunk`, `sum = round = 0`.
|
||
fn emit_counter(asm: &mut Gen, slots: &Slots) {
|
||
asm.emit(load(slots.chunk, SlotKind::Int));
|
||
asm.emit(c_int(3));
|
||
asm.emit(int_op(Arith::Ushr));
|
||
asm.emit(store(slots.counter, SlotKind::Int));
|
||
asm.emit(c_int(0));
|
||
asm.emit(store(slots.value0, SlotKind::Int));
|
||
asm.emit(load(slots.counter, SlotKind::Int));
|
||
asm.emit(store(slots.value1, SlotKind::Int));
|
||
asm.emit(c_int(0));
|
||
asm.emit(store(slots.sum, SlotKind::Int));
|
||
asm.emit(c_int(0));
|
||
asm.emit(store(slots.round, SlotKind::Int));
|
||
}
|
||
|
||
/// 32 раунда XTEA над `(v0, v1, sum)`.
|
||
fn emit_rounds(asm: &mut Gen, slots: &Slots) {
|
||
let again = asm.label();
|
||
let done = asm.label();
|
||
asm.mark(again);
|
||
asm.emit(load(slots.round, SlotKind::Int));
|
||
asm.emit(c_int(32));
|
||
asm.emit(Insn::Branch(BranchCond::Int(Cond::Ge), done));
|
||
mixing(asm, slots.value0, slots.value1, 0, slots);
|
||
asm.emit(load(slots.sum, SlotKind::Int));
|
||
asm.emit(c_int(DELTA));
|
||
asm.emit(int_op(Arith::Add));
|
||
asm.emit(store(slots.sum, SlotKind::Int));
|
||
mixing(asm, slots.value1, slots.value0, 11, slots);
|
||
asm.emit(Insn::Iinc {
|
||
index: slots.round,
|
||
delta: 1,
|
||
});
|
||
asm.emit(Insn::Goto(again));
|
||
asm.mark(done);
|
||
}
|
||
|
||
/// Половина раунда: `dst += (((src<<4)^(src>>>5)) + src) ^ (sum + k[(sum>>>s)&3])`.
|
||
fn mixing(asm: &mut Gen, dst: u16, src: u16, key_shift: i32, slots: &Slots) {
|
||
asm.emit(load(src, SlotKind::Int));
|
||
asm.emit(c_int(4));
|
||
asm.emit(int_op(Arith::Shl));
|
||
asm.emit(store(slots.tmp_a, SlotKind::Int));
|
||
asm.emit(load(src, SlotKind::Int));
|
||
asm.emit(c_int(5));
|
||
asm.emit(int_op(Arith::Ushr));
|
||
asm.emit(store(slots.tmp_b, SlotKind::Int));
|
||
asm.emit(load(slots.tmp_a, SlotKind::Int));
|
||
asm.emit(load(slots.tmp_b, SlotKind::Int));
|
||
asm.emit(int_op(Arith::Xor));
|
||
asm.emit(load(src, SlotKind::Int));
|
||
asm.emit(int_op(Arith::Add));
|
||
asm.emit(store(slots.tmp_a, SlotKind::Int));
|
||
asm.emit(load(slots.sum, SlotKind::Int));
|
||
asm.emit(load(slots.keys, SlotKind::Ref));
|
||
asm.emit(load(slots.sum, SlotKind::Int));
|
||
// Первая половина: `sum & 3` без сдвига; вторая: `(sum >>> s) & 3`.
|
||
if key_shift != 0 {
|
||
asm.emit(c_int(key_shift));
|
||
asm.emit(int_op(Arith::Ushr));
|
||
}
|
||
asm.emit(c_int(3));
|
||
asm.emit(int_op(Arith::And));
|
||
asm.emit(Insn::ArrayLoad(ArrayKind::Int));
|
||
asm.emit(int_op(Arith::Add));
|
||
asm.emit(store(slots.tmp_b, SlotKind::Int));
|
||
asm.emit(load(slots.tmp_a, SlotKind::Int));
|
||
asm.emit(load(slots.tmp_b, SlotKind::Int));
|
||
asm.emit(int_op(Arith::Xor));
|
||
asm.emit(load(dst, SlotKind::Int));
|
||
asm.emit(int_op(Arith::Add));
|
||
asm.emit(store(dst, SlotKind::Int));
|
||
}
|
||
|
||
/// XOR 8 байт чанка со стримом `(v0, v1)`; хвост короче 8 — частично.
|
||
fn emit_xor_bytes(asm: &mut Gen, data: u16, len: u16, slots: &Slots) {
|
||
asm.emit(c_int(0));
|
||
asm.emit(store(slots.index, SlotKind::Int));
|
||
let again = asm.label();
|
||
let done = asm.label();
|
||
let use_v0 = asm.label();
|
||
let have_src = asm.label();
|
||
asm.mark(again);
|
||
asm.emit(load(slots.index, SlotKind::Int));
|
||
asm.emit(c_int(8));
|
||
asm.emit(Insn::Branch(BranchCond::Int(Cond::Ge), done));
|
||
asm.emit(load(len, SlotKind::Int));
|
||
asm.emit(load(slots.chunk, SlotKind::Int));
|
||
asm.emit(load(slots.index, SlotKind::Int));
|
||
asm.emit(int_op(Arith::Add));
|
||
asm.emit(Insn::Branch(BranchCond::Int(Cond::Le), done));
|
||
asm.emit(load(slots.index, SlotKind::Int));
|
||
asm.emit(c_int(4));
|
||
asm.emit(int_op(Arith::And));
|
||
asm.emit(Insn::Branch(BranchCond::Zero(Cond::Eq), use_v0));
|
||
asm.emit(load(slots.value1, SlotKind::Int));
|
||
asm.emit(Insn::Goto(have_src));
|
||
asm.mark(use_v0);
|
||
asm.emit(load(slots.value0, SlotKind::Int));
|
||
asm.mark(have_src);
|
||
asm.emit(store(slots.src, SlotKind::Int));
|
||
asm.emit(load(slots.index, SlotKind::Int));
|
||
asm.emit(c_int(3));
|
||
asm.emit(int_op(Arith::And));
|
||
asm.emit(store(slots.tmp_a, SlotKind::Int));
|
||
asm.emit(c_int(3));
|
||
asm.emit(load(slots.tmp_a, SlotKind::Int));
|
||
asm.emit(int_op(Arith::Sub));
|
||
asm.emit(c_int(3));
|
||
asm.emit(int_op(Arith::Shl));
|
||
asm.emit(store(slots.shift, SlotKind::Int));
|
||
asm.emit(load(slots.src, SlotKind::Int));
|
||
asm.emit(load(slots.shift, SlotKind::Int));
|
||
asm.emit(int_op(Arith::Ushr));
|
||
asm.emit(c_int(0xFF));
|
||
asm.emit(int_op(Arith::And));
|
||
asm.emit(store(slots.sub, SlotKind::Int));
|
||
asm.emit(load(data, SlotKind::Ref));
|
||
asm.emit(load(slots.chunk, SlotKind::Int));
|
||
asm.emit(load(slots.index, SlotKind::Int));
|
||
asm.emit(int_op(Arith::Add));
|
||
asm.emit(Insn::Dup2);
|
||
asm.emit(Insn::ArrayLoad(ArrayKind::Byte));
|
||
asm.emit(load(slots.sub, SlotKind::Int));
|
||
asm.emit(int_op(Arith::Xor));
|
||
asm.emit(Insn::ArrayStore(ArrayKind::Byte));
|
||
asm.emit(Insn::Iinc {
|
||
index: slots.index,
|
||
delta: 1,
|
||
});
|
||
asm.emit(Insn::Goto(again));
|
||
asm.mark(done);
|
||
}
|
||
|
||
/// Сдвиг `long` на `int` (`lshl`/`lushr`).
|
||
fn long_shift(op: Arith) -> Insn {
|
||
Insn::Binary {
|
||
prim: Prim::Long,
|
||
op,
|
||
}
|
||
}
|
||
|
||
/// `iadd/isub/...` для `int`.
|
||
fn int_op(op: Arith) -> Insn {
|
||
Insn::Binary {
|
||
prim: Prim::Int,
|
||
op,
|
||
}
|
||
}
|