Skip to main content

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: () = ();