miri/intrinsics/x86/aesni.rs
1use rustc_middle::ty::Ty;
2use rustc_span::Symbol;
3
4use crate::*;
5
6impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
7pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
8 fn emulate_x86_aesni_intrinsic(
9 &mut self,
10 link_name: Symbol,
11 args: &[OpTy<'tcx>],
12 dest: &MPlaceTy<'tcx>,
13 ) -> InterpResult<'tcx, EmulateItemResult> {
14 let this = self.eval_context_mut();
15 this.expect_target_feature_for_intrinsic(link_name, "aes")?;
16 // Prefix should have already been checked.
17 let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.aesni.").unwrap();
18
19 match unprefixed_name {
20 // Used to implement the _mm_aesdec_si128, _mm256_aesdec_epi128
21 // and _mm512_aesdec_epi128 functions.
22 // Performs one round of an AES decryption on each 128-bit word of
23 // `state` with the corresponding 128-bit key of `key`.
24 // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesdec_si128
25 "aesdec" | "aesdec.256" | "aesdec.512" => {
26 let [state, key] = this.check_shim_sig_unadjusted(link_name, args)?;
27 aes_round(this, state, key, dest, |state, key| {
28 let key = aes::Block::from(key.to_le_bytes());
29 let mut state = aes::Block::from(state.to_le_bytes());
30 // `aes::hazmat::equiv_inv_cipher_round` documentation states that
31 // it performs the same operation as the x86 aesdec instruction.
32 aes::hazmat::equiv_inv_cipher_round(&mut state, &key);
33 u128::from_le_bytes(state.into())
34 })?;
35 }
36 // Used to implement the _mm_aesdeclast_si128, _mm256_aesdeclast_epi128
37 // and _mm512_aesdeclast_epi128 functions.
38 // Performs last round of an AES decryption on each 128-bit word of
39 // `state` with the corresponding 128-bit key of `key`.
40 // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesdeclast_si128
41 "aesdeclast" | "aesdeclast.256" | "aesdeclast.512" => {
42 let [state, key] = this.check_shim_sig_unadjusted(link_name, args)?;
43
44 aes_round(this, state, key, dest, |state, key| {
45 let mut state = aes::Block::from(state.to_le_bytes());
46 // `aes::hazmat::equiv_inv_cipher_round` does the following operations:
47 // state = InvShiftRows(state)
48 // state = InvSubBytes(state)
49 // state = InvMixColumns(state)
50 // state = state ^ key
51 // But we need to skip the InvMixColumns.
52 // First, use a zeroed key to skip the XOR.
53 aes::hazmat::equiv_inv_cipher_round(&mut state, &aes::Block::from([0; 16]));
54 // Then, undo the InvMixColumns with MixColumns.
55 aes::hazmat::mix_columns(&mut state);
56 // Finally, do the XOR.
57 u128::from_le_bytes(state.into()) ^ key
58 })?;
59 }
60 // Used to implement the _mm_aesenc_si128, _mm256_aesenc_epi128
61 // and _mm512_aesenc_epi128 functions.
62 // Performs one round of an AES encryption on each 128-bit word of
63 // `state` with the corresponding 128-bit key of `key`.
64 // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesenc_si128
65 "aesenc" | "aesenc.256" | "aesenc.512" => {
66 let [state, key] = this.check_shim_sig_unadjusted(link_name, args)?;
67 aes_round(this, state, key, dest, |state, key| {
68 let key = aes::Block::from(key.to_le_bytes());
69 let mut state = aes::Block::from(state.to_le_bytes());
70 // `aes::hazmat::cipher_round` documentation states that
71 // it performs the same operation as the x86 aesenc instruction.
72 aes::hazmat::cipher_round(&mut state, &key);
73 u128::from_le_bytes(state.into())
74 })?;
75 }
76 // Used to implement the _mm_aesenclast_si128, _mm256_aesenclast_epi128
77 // and _mm512_aesenclast_epi128 functions.
78 // Performs last round of an AES encryption on each 128-bit word of
79 // `state` with the corresponding 128-bit key of `key`.
80 // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesenclast_si128
81 "aesenclast" | "aesenclast.256" | "aesenclast.512" => {
82 let [state, key] = this.check_shim_sig_unadjusted(link_name, args)?;
83 aes_round(this, state, key, dest, |state, key| {
84 let mut state = aes::Block::from(state.to_le_bytes());
85 // `aes::hazmat::cipher_round` does the following operations:
86 // state = ShiftRows(state)
87 // state = SubBytes(state)
88 // state = MixColumns(state)
89 // state = state ^ key
90 // But we need to skip the MixColumns.
91 // First, use a zeroed key to skip the XOR.
92 aes::hazmat::cipher_round(&mut state, &aes::Block::from([0; 16]));
93 // Then, undo the MixColumns with InvMixColumns.
94 aes::hazmat::inv_mix_columns(&mut state);
95 // Finally, do the XOR.
96 u128::from_le_bytes(state.into()) ^ key
97 })?;
98 }
99 // Used to implement the _mm_aesimc_si128 function.
100 // Performs the AES InvMixColumns operation on `op`
101 "aesimc" => {
102 let [op] = this.check_shim_sig_unadjusted(link_name, args)?;
103 // Transmute to `u128`
104 let op = op.transmute(this.machine.layouts.u128, this)?;
105 let dest = dest.transmute(this.machine.layouts.u128, this)?;
106
107 let state = this.read_scalar(&op)?.to_u128()?;
108 let mut state = aes::Block::from(state.to_le_bytes());
109 aes::hazmat::inv_mix_columns(&mut state);
110
111 this.write_scalar(Scalar::from_u128(u128::from_le_bytes(state.into())), &dest)?;
112 }
113 // TODO: Implement the `llvm.x86.aesni.aeskeygenassist` when possible
114 // with an external crate.
115 _ => return interp_ok(EmulateItemResult::NotSupported),
116 }
117 interp_ok(EmulateItemResult::NeedsReturn)
118 }
119}
120
121// Performs an AES round (given by `f`) on each 128-bit word of
122// `state` with the corresponding 128-bit key of `key`.
123fn aes_round<'tcx>(
124 ecx: &mut crate::MiriInterpCx<'tcx>,
125 state: &OpTy<'tcx>,
126 key: &OpTy<'tcx>,
127 dest: &MPlaceTy<'tcx>,
128 f: impl Fn(u128, u128) -> u128,
129) -> InterpResult<'tcx, ()> {
130 assert_eq!(dest.layout.size, state.layout.size);
131 assert_eq!(dest.layout.size, key.layout.size);
132
133 // Transmute arguments to arrays of `u128`.
134 assert_eq!(dest.layout.size.bytes() % 16, 0);
135 let len = dest.layout.size.bytes() / 16;
136
137 let u128_array_layout = ecx.layout_of(Ty::new_array(ecx.tcx.tcx, ecx.tcx.types.u128, len))?;
138
139 let state = state.transmute(u128_array_layout, ecx)?;
140 let key = key.transmute(u128_array_layout, ecx)?;
141 let dest = dest.transmute(u128_array_layout, ecx)?;
142
143 for i in 0..len {
144 let state = ecx.read_scalar(&ecx.project_index(&state, i)?)?.to_u128()?;
145 let key = ecx.read_scalar(&ecx.project_index(&key, i)?)?.to_u128()?;
146 let dest = ecx.project_index(&dest, i)?;
147
148 let res = f(state, key);
149
150 ecx.write_scalar(Scalar::from_u128(res), &dest)?;
151 }
152
153 interp_ok(())
154}