Skip to content

Commit 79d904b

Browse files
authored
Avoid AEAD in-place slice aliasing (#21)
1 parent 465e6ce commit 79d904b

1 file changed

Lines changed: 111 additions & 21 deletions

File tree

src/aead.rs

Lines changed: 111 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,107 @@
66
use rustls::crypto::cipher::NONCE_LEN;
77
use rustls::Error;
88
use windows::core::Owned;
9+
use windows::Win32::Foundation::NTSTATUS;
910
use windows::Win32::Security::Cryptography::{
10-
BCryptDecrypt, BCryptEncrypt, BCryptGenerateSymmetricKey, BCRYPT_AES_GCM_ALG_HANDLE,
11-
BCRYPT_ALG_HANDLE, BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO,
12-
BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO_VERSION, BCRYPT_CHACHA20_POLY1305_ALG_HANDLE,
13-
BCRYPT_FLAGS, BCRYPT_KEY_HANDLE,
11+
BCryptGenerateSymmetricKey, BCRYPT_AES_GCM_ALG_HANDLE, BCRYPT_ALG_HANDLE,
12+
BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO, BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO_VERSION,
13+
BCRYPT_CHACHA20_POLY1305_ALG_HANDLE, BCRYPT_FLAGS, BCRYPT_KEY_HANDLE,
1414
};
1515

1616
/// The tag length is 16 bytes for all supported ciphers.
1717
pub(crate) const TAG_LEN: usize = 16;
1818

19+
type BcryptInPlaceFn = unsafe fn(
20+
BCRYPT_KEY_HANDLE,
21+
*mut u8,
22+
u32,
23+
*mut BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO,
24+
*mut u32,
25+
BCRYPT_FLAGS,
26+
) -> NTSTATUS;
27+
28+
const _: BcryptInPlaceFn = bcrypt_encrypt_in_place;
29+
const _: BcryptInPlaceFn = bcrypt_decrypt_in_place;
30+
31+
#[link(name = "bcrypt")]
32+
extern "system" {
33+
#[link_name = "BCryptEncrypt"]
34+
fn bcrypt_encrypt_raw(
35+
hkey: BCRYPT_KEY_HANDLE,
36+
pbinput: *const u8,
37+
cbinput: u32,
38+
ppaddinginfo: *const core::ffi::c_void,
39+
pbiv: *mut u8,
40+
cbiv: u32,
41+
pboutput: *mut u8,
42+
cboutput: u32,
43+
pcbresult: *mut u32,
44+
dwflags: BCRYPT_FLAGS,
45+
) -> NTSTATUS;
46+
47+
#[link_name = "BCryptDecrypt"]
48+
fn bcrypt_decrypt_raw(
49+
hkey: BCRYPT_KEY_HANDLE,
50+
pbinput: *const u8,
51+
cbinput: u32,
52+
ppaddinginfo: *const core::ffi::c_void,
53+
pbiv: *mut u8,
54+
cbiv: u32,
55+
pboutput: *mut u8,
56+
cboutput: u32,
57+
pcbresult: *mut u32,
58+
dwflags: BCRYPT_FLAGS,
59+
) -> NTSTATUS;
60+
}
61+
62+
unsafe fn bcrypt_encrypt_in_place(
63+
hkey: BCRYPT_KEY_HANDLE,
64+
buffer: *mut u8,
65+
len: u32,
66+
info: *mut BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO,
67+
pcbresult: *mut u32,
68+
dwflags: BCRYPT_FLAGS,
69+
) -> NTSTATUS {
70+
unsafe {
71+
bcrypt_encrypt_raw(
72+
hkey,
73+
buffer as *const u8,
74+
len,
75+
info as *const core::ffi::c_void,
76+
std::ptr::null_mut(),
77+
0,
78+
buffer,
79+
len,
80+
pcbresult,
81+
dwflags,
82+
)
83+
}
84+
}
85+
86+
unsafe fn bcrypt_decrypt_in_place(
87+
hkey: BCRYPT_KEY_HANDLE,
88+
buffer: *mut u8,
89+
len: u32,
90+
info: *mut BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO,
91+
pcbresult: *mut u32,
92+
dwflags: BCRYPT_FLAGS,
93+
) -> NTSTATUS {
94+
unsafe {
95+
bcrypt_decrypt_raw(
96+
hkey,
97+
buffer as *const u8,
98+
len,
99+
info as *const core::ffi::c_void,
100+
std::ptr::null_mut(),
101+
0,
102+
buffer,
103+
len,
104+
pcbresult,
105+
dwflags,
106+
)
107+
}
108+
}
109+
19110
#[derive(Debug, Clone, Copy)]
20111
pub(crate) struct Algorithm {
21112
handle: BCRYPT_ALG_HANDLE,
@@ -101,7 +192,7 @@ impl AeadKey {
101192
let mut tag = [0u8; TAG_LEN];
102193

103194
// https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/ns-bcrypt-bcrypt_authenticated_cipher_mode_info
104-
let info = BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO {
195+
let mut info = BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO {
105196
cbSize: core::mem::size_of::<BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO>() as u32,
106197
dwInfoVersion: BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO_VERSION,
107198
pbNonce: nonce.as_ptr().cast_mut(),
@@ -114,16 +205,16 @@ impl AeadKey {
114205
};
115206

116207
unsafe {
117-
// SAFETY: CNG supports in-place encryption, so the input and output buffers can be the same.
208+
// SAFETY: CNG supports in-place encryption, and the raw helper accepts one buffer pointer
209+
// so Rust does not materialize overlapping input and output slice references.
118210
let mut size = 0u32;
119-
let input = std::slice::from_raw_parts(data.as_ptr().cast(), data.len());
211+
let len = data.len().try_into().unwrap();
120212

121-
BCryptEncrypt(
213+
bcrypt_encrypt_in_place(
122214
*self.handle,
123-
Some(input),
124-
Some(std::ptr::from_ref(&info) as *mut _),
125-
None,
126-
Some(data),
215+
data.as_mut_ptr(),
216+
len,
217+
std::ptr::from_mut(&mut info),
127218
&mut size,
128219
BCRYPT_FLAGS::default(),
129220
)
@@ -147,7 +238,7 @@ impl AeadKey {
147238
let (ciphertext, tag) = data.split_at_mut(payload_len - TAG_LEN);
148239

149240
// https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/ns-bcrypt-bcrypt_authenticated_cipher_mode_info
150-
let info = BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO {
241+
let mut info = BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO {
151242
cbSize: core::mem::size_of::<BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO>() as u32,
152243
dwInfoVersion: BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO_VERSION,
153244
pbNonce: nonce.as_ptr().cast_mut(),
@@ -162,16 +253,15 @@ impl AeadKey {
162253
let mut size = 0u32;
163254

164255
unsafe {
165-
// SAFETY: CNG supports in-place decryption, so the input and output buffers can be the same.
166-
167-
let input = std::slice::from_raw_parts(ciphertext.as_ptr().cast(), ciphertext.len());
256+
// SAFETY: CNG supports in-place decryption, and the raw helper accepts one buffer pointer
257+
// so Rust does not materialize overlapping input and output slice references.
258+
let len = ciphertext.len().try_into().unwrap();
168259

169-
BCryptDecrypt(
260+
bcrypt_decrypt_in_place(
170261
*self.handle,
171-
Some(input),
172-
Some(std::ptr::from_ref(&info) as *mut _),
173-
None,
174-
Some(ciphertext),
262+
ciphertext.as_mut_ptr(),
263+
len,
264+
std::ptr::from_mut(&mut info),
175265
&mut size,
176266
BCRYPT_FLAGS::default(),
177267
)

0 commit comments

Comments
 (0)