use super::stack::{PushValue, StackOp};
use std::sync::OnceLock;
const K: [u32; 64] = [
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
];
fn u32_to_le(n: u32) -> Vec<u8> {
vec![
(n & 0xff) as u8,
((n >> 8) & 0xff) as u8,
((n >> 16) & 0xff) as u8,
((n >> 24) & 0xff) as u8,
]
}
struct Emitter {
ops: Vec<StackOp>,
depth: i64,
alt_depth: i64,
}
impl Emitter {
fn new(initial_depth: i64) -> Self {
Emitter {
ops: Vec::new(),
depth: initial_depth,
alt_depth: 0,
}
}
fn e_raw(&mut self, sop: StackOp) {
self.ops.push(sop);
}
fn oc(&mut self, code: &str) {
self.ops.push(StackOp::Opcode(code.to_string()));
}
fn push_i(&mut self, v: i128) {
self.ops.push(StackOp::Push(PushValue::Int(v)));
self.depth += 1;
}
fn push_b(&mut self, v: Vec<u8>) {
self.ops.push(StackOp::Push(PushValue::Bytes(v)));
self.depth += 1;
}
fn dup(&mut self) {
self.ops.push(StackOp::Dup);
self.depth += 1;
}
fn drop(&mut self) {
self.ops.push(StackOp::Drop);
self.depth -= 1;
}
fn swap(&mut self) {
self.ops.push(StackOp::Swap);
}
fn over(&mut self) {
self.ops.push(StackOp::Over);
self.depth += 1;
}
fn rot(&mut self) {
self.ops.push(StackOp::Rot);
}
fn pick(&mut self, d: usize) {
if d == 0 {
self.dup();
return;
}
if d == 1 {
self.over();
return;
}
self.push_i(d as i128);
self.ops.push(StackOp::Pick { depth: d });
}
fn roll(&mut self, d: usize) {
if d == 0 {
return;
}
if d == 1 {
self.swap();
return;
}
if d == 2 {
self.rot();
return;
}
self.push_i(d as i128);
self.ops.push(StackOp::Roll { depth: d });
self.depth -= 1; }
fn to_alt(&mut self) {
self.oc("OP_TOALTSTACK");
self.depth -= 1;
self.alt_depth += 1;
}
fn from_alt(&mut self) {
self.oc("OP_FROMALTSTACK");
self.depth += 1;
self.alt_depth -= 1;
}
fn bin_op(&mut self, code: &str) {
self.oc(code);
self.depth -= 1;
}
fn uni_op(&mut self, code: &str) {
self.oc(code);
}
fn dup2(&mut self) {
self.oc("OP_2DUP");
self.depth += 2;
}
fn split(&mut self) {
self.oc("OP_SPLIT");
}
fn split4(&mut self) {
self.push_i(4);
self.split();
}
fn assert_depth(&self, expected: i64, msg: &str) {
assert_eq!(
self.depth, expected,
"SHA256 codegen: {}. Expected depth {}, got {}",
msg, expected, self.depth
);
}
fn reverse_bytes4(&mut self) {
self.push_i(1);
self.split();
self.push_i(1);
self.split();
self.push_i(1);
self.split();
self.swap();
self.bin_op("OP_CAT");
self.swap();
self.bin_op("OP_CAT");
self.swap();
self.bin_op("OP_CAT");
}
fn le2num(&mut self) {
self.push_b(vec![0x00]); self.bin_op("OP_CAT");
self.uni_op("OP_BIN2NUM");
}
fn num2le(&mut self) {
self.push_i(5);
self.bin_op("OP_NUM2BIN"); self.push_i(4);
self.split(); self.drop(); }
fn add32(&mut self) {
self.le2num();
self.swap();
self.le2num();
self.bin_op("OP_ADD");
self.num2le();
}
fn add_n(&mut self, n: usize) {
if n < 2 {
return;
}
self.le2num();
for _ in 1..n {
self.swap();
self.le2num();
self.bin_op("OP_ADD");
}
self.num2le();
}
fn rotr_be(&mut self, n: usize) {
self.dup(); self.push_i(n as i128);
self.bin_op("OP_RSHIFT"); self.swap(); self.push_i((32 - n) as i128);
self.bin_op("OP_LSHIFT"); self.bin_op("OP_OR"); }
fn shr_be(&mut self, n: usize) {
self.push_i(n as i128);
self.bin_op("OP_RSHIFT");
}
fn big_sigma0(&mut self) {
self.reverse_bytes4(); self.dup();
self.dup();
self.rotr_be(2);
self.swap();
self.rotr_be(13);
self.bin_op("OP_XOR");
self.swap();
self.rotr_be(22);
self.bin_op("OP_XOR");
self.reverse_bytes4(); }
fn big_sigma1(&mut self) {
self.reverse_bytes4();
self.dup();
self.dup();
self.rotr_be(6);
self.swap();
self.rotr_be(11);
self.bin_op("OP_XOR");
self.swap();
self.rotr_be(25);
self.bin_op("OP_XOR");
self.reverse_bytes4();
}
fn small_sigma0(&mut self) {
self.reverse_bytes4();
self.dup();
self.dup();
self.rotr_be(7);
self.swap();
self.rotr_be(18);
self.bin_op("OP_XOR");
self.swap();
self.shr_be(3);
self.bin_op("OP_XOR");
self.reverse_bytes4();
}
fn small_sigma1(&mut self) {
self.reverse_bytes4();
self.dup();
self.dup();
self.rotr_be(17);
self.swap();
self.rotr_be(19);
self.bin_op("OP_XOR");
self.swap();
self.shr_be(10);
self.bin_op("OP_XOR");
self.reverse_bytes4();
}
fn ch(&mut self) {
self.rot();
self.dup();
self.uni_op("OP_INVERT");
self.rot();
self.bin_op("OP_AND");
self.to_alt();
self.bin_op("OP_AND");
self.from_alt();
self.bin_op("OP_XOR");
}
fn maj(&mut self) {
self.to_alt();
self.dup2();
self.bin_op("OP_AND");
self.to_alt();
self.bin_op("OP_XOR");
self.from_alt();
self.swap();
self.from_alt();
self.bin_op("OP_AND");
self.bin_op("OP_OR");
}
fn be_words_to_le(&mut self, n: usize) {
for _ in 0..n {
self.reverse_bytes4();
self.to_alt();
}
for _ in 0..n {
self.from_alt();
}
}
fn be_words_to_le_reversed8(&mut self) {
for i in (0..8).rev() {
self.roll(i);
self.reverse_bytes4();
self.to_alt();
}
for _ in 0..8 {
self.from_alt();
}
}
}
fn emit_round(em: &mut Emitter, t: usize) {
let d0 = em.depth;
em.pick(4); em.big_sigma1();
em.pick(5);
em.pick(7);
em.pick(9); em.ch();
em.pick(9); em.push_b(u32_to_le(K[t])); em.pick(75 - t);
em.add_n(5);
em.dup();
em.to_alt();
em.pick(1); em.big_sigma0();
em.pick(2);
em.pick(4);
em.pick(6); em.maj(); em.add32();
em.from_alt();
em.swap();
em.add32();
em.swap();
em.roll(5); em.add32();
em.roll(8);
em.drop();
em.swap();
em.roll(4);
em.roll(4);
em.roll(4);
em.roll(3);
em.assert_depth(d0, &format!("compress: after round {}", t));
}
fn generate_compress_ops() -> Vec<StackOp> {
let mut em = Emitter::new(2);
em.swap();
em.dup();
em.to_alt();
em.to_alt();
em.assert_depth(1, "compress: after state save");
for _ in 0..15 {
em.split4();
}
em.assert_depth(16, "compress: after block unpack");
em.be_words_to_le(16);
em.assert_depth(16, "compress: after block LE convert");
for _t in 16..64 {
em.over();
em.small_sigma1();
em.pick(6 + 1);
em.pick(14 + 2);
em.small_sigma0();
em.pick(15 + 3);
em.add_n(4);
}
em.assert_depth(64, "compress: after W expansion");
em.from_alt();
for _ in 0..7 {
em.split4();
}
em.assert_depth(72, "compress: after state unpack");
em.be_words_to_le_reversed8();
em.assert_depth(72, "compress: after state LE convert");
for t in 0..64 {
emit_round(&mut em, t);
}
em.from_alt();
em.assert_depth(73, "compress: before final add");
for _ in 0..7 {
em.split4();
}
em.be_words_to_le_reversed8();
em.assert_depth(80, "compress: after init unpack");
for i in 0..8 {
em.roll(8 - i);
em.add32();
em.to_alt();
}
em.assert_depth(64, "compress: after final add");
em.from_alt();
em.reverse_bytes4();
for _ in 1..8 {
em.from_alt();
em.reverse_bytes4();
em.swap();
em.bin_op("OP_CAT");
}
em.assert_depth(65, "compress: after pack");
for _ in 0..64 {
em.swap();
em.drop();
}
em.assert_depth(1, "compress: final");
em.ops
}
static COMPRESS_OPS: OnceLock<Vec<StackOp>> = OnceLock::new();
fn get_compress_ops() -> &'static Vec<StackOp> {
COMPRESS_OPS.get_or_init(generate_compress_ops)
}
pub fn emit_sha256_compress(emit: &mut dyn FnMut(StackOp)) {
for op in get_compress_ops() {
emit(op.clone());
}
}
pub fn emit_sha256_finalize(emit: &mut dyn FnMut(StackOp)) {
let mut em = Emitter::new(3);
em.push_i(9);
em.bin_op("OP_NUM2BIN"); em.push_i(8);
em.split(); em.drop(); em.push_i(4);
em.split(); em.reverse_bytes4(); em.swap();
em.reverse_bytes4(); em.bin_op("OP_CAT"); em.to_alt(); em.assert_depth(2, "finalize: after bitLen conversion");
em.push_b(vec![0x80]);
em.bin_op("OP_CAT");
em.oc("OP_SIZE");
em.depth += 1;
em.dup();
em.push_i(57);
em.bin_op("OP_LESSTHAN");
em.oc("OP_IF");
em.depth -= 1;
em.push_i(56);
em.swap();
em.bin_op("OP_SUB"); em.push_i(0);
em.swap();
em.bin_op("OP_NUM2BIN"); em.bin_op("OP_CAT"); em.from_alt(); em.bin_op("OP_CAT"); let compress_ops = get_compress_ops();
for op in compress_ops {
em.e_raw(op.clone());
}
em.depth = 1;
em.oc("OP_ELSE");
em.depth = 3;
em.push_i(120);
em.swap();
em.bin_op("OP_SUB"); em.push_i(0);
em.swap();
em.bin_op("OP_NUM2BIN"); em.bin_op("OP_CAT"); em.from_alt(); em.bin_op("OP_CAT");
em.push_i(64);
em.split(); em.to_alt();
for op in compress_ops {
em.e_raw(op.clone());
}
em.depth = 1;
em.from_alt(); for op in compress_ops {
em.e_raw(op.clone());
}
em.depth = 1;
em.oc("OP_ENDIF");
em.assert_depth(1, "finalize: final");
for op in em.ops {
emit(op);
}
}