ab_riscv_interpreter/rv32/zk/zkn/zknh/zknh_helpers.rs
1//! Opaque helpers for RV32 Zknh extension
2
3// Each function implements the exact 32-bit formula from the RISC-V scalar crypto specification
4// (Volume I, Zknh section), expressed directly in terms of the rs1 and rs2 register values as
5// the executor passes them.
6//
7// The spec uses asymmetric register conventions across the six instructions. For all six the
8// 64-bit operand is assembled from the two source registers, but the high/low assignment
9// differs by group:
10//
11// sha512sig0l, sha512sig1l : rs1 = LOW 32 bits, rs2 = HIGH 32 bits
12// sha512sig0h, sha512sig1h : rs1 = HIGH 32 bits, rs2 = LOW 32 bits
13// sha512sum0r, sha512sum1r : rs1 = LOW 32 bits, rs2 = HIGH 32 bits
14//
15// For sum0r/sum1r the Sail pseudocode reads:
16// let x : bits(64) = X(rs2) @ X(rs1); -- rs2 is the HIGH half, rs1 is the LOW half
17// rd = result[31..0];
18//
19// The _l and _sum*r instructions place the "primary" (low) operand half in rs1 and produce
20// the low result half. The _h instructions place the high-operand half in rs1 and produce
21// the high-result half.
22//
23// For pure rotation terms ROR64(x, n) the half formulas are (hi = high word, lo = low word):
24// n < 32: .hi = (hi>>n) ^ (lo<<(32-n)) .lo = (lo>>n) ^ (hi<<(32-n))
25// n >= 32: .hi = (lo>>(n-32)) ^ (hi<<(64-n)) .lo = (hi>>(n-32)) ^ (lo<<(64-n))
26//
27// For SHR64(x, n) with n < 32 the low half picks up cross-boundary bits from hi:
28// .hi = hi>>n (no lo contribution)
29// .lo = (lo>>n) ^ (hi<<(32-n))
30
31// SHA-512 sigma0: ROR64(x,1) ^ ROR64(x,8) ^ SHR64(x,7)
32
33/// High 32 bits of SHA-512 sigma0. rs1 = HIGH word, rs2 = LOW word.
34///
35/// ```text
36/// ROR64(x,1).hi = (rs1>>1) ^ (rs2<<31)
37/// ROR64(x,8).hi = (rs1>>8) ^ (rs2<<24)
38/// SHR64(x,7).hi = rs1>>7 <- shift: no rs2 contribution
39/// ```
40#[inline(always)]
41#[doc(hidden)]
42pub fn sha512sig0h(rs1: u32, rs2: u32) -> u32 {
43 (rs1 >> 1) ^ (rs2 << 31) ^ (rs1 >> 8) ^ (rs2 << 24) ^ (rs1 >> 7)
44}
45
46/// Low 32 bits of SHA-512 sigma0. rs1 = LOW word, rs2 = HIGH word.
47///
48/// ```text
49/// ROR64(x,1).lo = (rs1>>1) ^ (rs2<<31)
50/// ROR64(x,8).lo = (rs1>>8) ^ (rs2<<24)
51/// SHR64(x,7).lo = (rs1>>7) ^ (rs2<<25) <- cross-boundary bits from hi
52/// ```
53#[inline(always)]
54#[doc(hidden)]
55pub fn sha512sig0l(rs1: u32, rs2: u32) -> u32 {
56 (rs1 >> 1) ^ (rs2 << 31) ^ (rs1 >> 8) ^ (rs2 << 24) ^ (rs1 >> 7) ^ (rs2 << 25)
57}
58
59// SHA-512 sigma1: ROR64(x,19) ^ ROR64(x,61) ^ SHR64(x,6)
60
61/// High 32 bits of SHA-512 sigma1. rs1 = HIGH word, rs2 = LOW word.
62///
63/// ```text
64/// ROR64(x,19).hi = (rs1>>19) ^ (rs2<<13)
65/// ROR64(x,61).hi = ROR64(x,32+29).hi = (rs2>>29) ^ (rs1<<3)
66/// SHR64(x,6).hi = rs1>>6 <- shift: no rs2 contribution
67/// ```
68#[inline(always)]
69#[doc(hidden)]
70pub fn sha512sig1h(rs1: u32, rs2: u32) -> u32 {
71 (rs1 >> 19) ^ (rs2 << 13) ^ (rs2 >> 29) ^ (rs1 << 3) ^ (rs1 >> 6)
72}
73
74/// Low 32 bits of SHA-512 sigma1. rs1 = LOW word, rs2 = HIGH word.
75///
76/// ```text
77/// ROR64(x,19).lo = (rs1>>19) ^ (rs2<<13)
78/// ROR64(x,61).lo = ROR64(x,32+29).lo = (rs2>>29) ^ (rs1<<3)
79/// SHR64(x,6).lo = (rs1>>6) ^ (rs2<<26) <- cross-boundary bits from hi
80/// ```
81#[inline(always)]
82#[doc(hidden)]
83pub fn sha512sig1l(rs1: u32, rs2: u32) -> u32 {
84 (rs1 >> 19) ^ (rs2 << 13) ^ (rs2 >> 29) ^ (rs1 << 3) ^ (rs1 >> 6) ^ (rs2 << 26)
85}
86
87// SHA-512 Sum0: ROR64(x,28) ^ ROR64(x,34) ^ ROR64(x,39)
88//
89// Sail: let x = X(rs2) @ X(rs1) => x[63:32] = rs2 (HIGH), x[31:0] = rs1 (LOW)
90// sum0r produces the LOW half of the result.
91//
92// ROR64({hi=rs2, lo=rs1}, 28).lo = (rs1>>28) ^ (rs2<<4) [n=28 < 32]
93// ROR64({hi=rs2, lo=rs1}, 34).lo = (rs2>>2) ^ (rs1<<30) [n=34 = 32+2]
94// ROR64({hi=rs2, lo=rs1}, 39).lo = (rs2>>7) ^ (rs1<<25) [n=39 = 32+7]
95
96/// Low 32 bits of SHA-512 Sum0. rs1 = LOW word, rs2 = HIGH word.
97///
98/// All three terms are rotations, so no asymmetric shift contribution.
99///
100/// ```text
101/// ROR64(x,28).lo = (rs1>>28) ^ (rs2<<4)
102/// ROR64(x,34).lo = ROR64(x,32+2).lo = (rs2>>2) ^ (rs1<<30)
103/// ROR64(x,39).lo = ROR64(x,32+7).lo = (rs2>>7) ^ (rs1<<25)
104/// ```
105#[inline(always)]
106#[doc(hidden)]
107pub fn sha512sum0r(rs1: u32, rs2: u32) -> u32 {
108 (rs1 >> 28) ^ (rs2 << 4) ^ (rs2 >> 2) ^ (rs1 << 30) ^ (rs2 >> 7) ^ (rs1 << 25)
109}
110
111// SHA-512 Sum1: ROR64(x,14) ^ ROR64(x,18) ^ ROR64(x,41)
112//
113// Sail: let x = X(rs2) @ X(rs1) => x[63:32] = rs2 (HIGH), x[31:0] = rs1 (LOW)
114// sum1r produces the LOW half of the result.
115//
116// ROR64({hi=rs2, lo=rs1}, 14).lo = (rs1>>14) ^ (rs2<<18) [n=14 < 32]
117// ROR64({hi=rs2, lo=rs1}, 18).lo = (rs1>>18) ^ (rs2<<14) [n=18 < 32]
118// ROR64({hi=rs2, lo=rs1}, 41).lo = (rs2>>9) ^ (rs1<<23) [n=41 = 32+9]
119
120/// Low 32 bits of SHA-512 Sum1. rs1 = LOW word, rs2 = HIGH word.
121///
122/// All three terms are rotations.
123///
124/// ```text
125/// ROR64(x,14).lo = (rs1>>14) ^ (rs2<<18)
126/// ROR64(x,18).lo = (rs1>>18) ^ (rs2<<14)
127/// ROR64(x,41).lo = ROR64(x,32+9).lo = (rs2>>9) ^ (rs1<<23)
128/// ```
129#[inline(always)]
130#[doc(hidden)]
131pub fn sha512sum1r(rs1: u32, rs2: u32) -> u32 {
132 (rs1 >> 14) ^ (rs2 << 18) ^ (rs1 >> 18) ^ (rs2 << 14) ^ (rs2 >> 9) ^ (rs1 << 23)
133}
134
135/// Only here to prevent compiler warnings about unused `zknh_helpers` module.
136#[doc(hidden)]
137pub const PLACEHOLDER: () = ();