Skip to main content

miri/intrinsics/x86/
sha.rs

1//! Implements sha256 SIMD instructions of x86 targets
2//!
3//! The functions that actually compute SHA256 were copied from [RustCrypto's sha256 module].
4//!
5//! [RustCrypto's sha256 module]: https://github.com/RustCrypto/hashes/blob/6be8466247e936c415d8aafb848697f39894a386/sha2/src/sha256/soft.rs
6
7use rustc_span::Symbol;
8
9use crate::intrinsics::math::sha256;
10use crate::*;
11
12impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
13pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
14    fn emulate_x86_sha_intrinsic(
15        &mut self,
16        link_name: Symbol,
17        args: &[OpTy<'tcx>],
18        dest: &MPlaceTy<'tcx>,
19    ) -> InterpResult<'tcx, EmulateItemResult> {
20        let this = self.eval_context_mut();
21        this.expect_target_feature_for_intrinsic(link_name, "sha")?;
22        // Prefix should have already been checked.
23        let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.sha").unwrap();
24
25        fn read<'c>(ecx: &mut MiriInterpCx<'c>, vec: &OpTy<'c>) -> InterpResult<'c, [u32; 4]> {
26            let mut res = [0; 4];
27            // We reverse the order because x86 is little endian but the copied implementation uses
28            // big endian.
29            for (i, dst) in res.iter_mut().rev().enumerate() {
30                let projected = &ecx.project_index(vec, i.try_into().unwrap())?;
31                *dst = ecx.read_scalar(projected)?.to_u32()?
32            }
33            interp_ok(res)
34        }
35
36        fn write<'c>(
37            ecx: &mut MiriInterpCx<'c>,
38            dest: &MPlaceTy<'c>,
39            val: [u32; 4],
40        ) -> InterpResult<'c, ()> {
41            // We reverse the order because x86 is little endian but the copied implementation uses
42            // big endian.
43            for (i, part) in val.into_iter().rev().enumerate() {
44                let projected = &ecx.project_index(dest, i.to_u64())?;
45                ecx.write_scalar(Scalar::from_u32(part), projected)?;
46            }
47            interp_ok(())
48        }
49
50        match unprefixed_name {
51            // Used to implement the _mm_sha256rnds2_epu32 function.
52            "256rnds2" => {
53                let [a, b, k] = this.check_shim_sig_unadjusted(link_name, args)?;
54
55                let (a, a_len) = this.project_to_simd(a)?;
56                let (b, b_len) = this.project_to_simd(b)?;
57                let (k, k_len) = this.project_to_simd(k)?;
58                let (dest, dest_len) = this.project_to_simd(dest)?;
59
60                assert_eq!(a_len, 4);
61                assert_eq!(b_len, 4);
62                assert_eq!(k_len, 4);
63                assert_eq!(dest_len, 4);
64
65                let a = read(this, &a)?;
66                let b = read(this, &b)?;
67                let k = read(this, &k)?;
68
69                let result = sha256_digest_round_x2(a, b, k);
70                write(this, &dest, result)?;
71            }
72            // Used to implement the _mm_sha256msg1_epu32 function.
73            "256msg1" => {
74                let [a, b] = this.check_shim_sig_unadjusted(link_name, args)?;
75
76                let (a, a_len) = this.project_to_simd(a)?;
77                let (b, b_len) = this.project_to_simd(b)?;
78                let (dest, dest_len) = this.project_to_simd(dest)?;
79
80                assert_eq!(a_len, 4);
81                assert_eq!(b_len, 4);
82                assert_eq!(dest_len, 4);
83
84                let a = read(this, &a)?;
85                let b = read(this, &b)?;
86
87                let result = sha256msg1(a, b);
88                write(this, &dest, result)?;
89            }
90            // Used to implement the _mm_sha256msg2_epu32 function.
91            "256msg2" => {
92                let [a, b] = this.check_shim_sig_unadjusted(link_name, args)?;
93
94                let (a, a_len) = this.project_to_simd(a)?;
95                let (b, b_len) = this.project_to_simd(b)?;
96                let (dest, dest_len) = this.project_to_simd(dest)?;
97
98                assert_eq!(a_len, 4);
99                assert_eq!(b_len, 4);
100                assert_eq!(dest_len, 4);
101
102                let a = read(this, &a)?;
103                let b = read(this, &b)?;
104
105                let result = sha256msg2(a, b);
106                write(this, &dest, result)?;
107            }
108            _ => return interp_ok(EmulateItemResult::NotSupported),
109        }
110        interp_ok(EmulateItemResult::NeedsReturn)
111    }
112}
113
114fn sha256load(v2: [u32; 4], v3: [u32; 4]) -> [u32; 4] {
115    [v3[3], v2[0], v2[1], v2[2]]
116}
117
118fn sha256_digest_round_x2(cdgh: [u32; 4], abef: [u32; 4], wk: [u32; 4]) -> [u32; 4] {
119    // `sha256rnds2`: two rounds on the abef/cdgh permutation
120    // Ref: https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sha256rnds2
121    let [_, _, wk1, wk0] = wk;
122    let [a0, b0, e0, f0] = abef;
123    let [c0, d0, g0, h0] = cdgh;
124
125    let state = sha256::round([a0, b0, c0, d0, e0, f0, g0, h0], wk0);
126    let state = sha256::round(state, wk1);
127
128    [state[0], state[1], state[4], state[5]]
129}
130
131fn sha256msg1(v0: [u32; 4], v1: [u32; 4]) -> [u32; 4] {
132    let x = sha256load(v0, v1);
133    [
134        v0[0].wrapping_add(sha256::sigma0(x[0])),
135        v0[1].wrapping_add(sha256::sigma0(x[1])),
136        v0[2].wrapping_add(sha256::sigma0(x[2])),
137        v0[3].wrapping_add(sha256::sigma0(x[3])),
138    ]
139}
140
141fn sha256msg2(v4: [u32; 4], v3: [u32; 4]) -> [u32; 4] {
142    let [x3, x2, x1, x0] = v4;
143    let [w15, w14, _, _] = v3;
144
145    let w16 = x0.wrapping_add(sha256::sigma1(w14));
146    let w17 = x1.wrapping_add(sha256::sigma1(w15));
147    let w18 = x2.wrapping_add(sha256::sigma1(w16));
148    let w19 = x3.wrapping_add(sha256::sigma1(w17));
149
150    [w19, w18, w17, w16]
151}