I'm trying to learn FFI by starting with something simple (and with a practical use), but this doesn't seem to work:
mod bindings {
::windows::include_bindings!();
}
use std::{convert::TryFrom, ptr};
use bindings::{
windows::win32::security::CryptUnprotectData,
windows::win32::security::CRYPTOAPI_BLOB
};
// Powershell code to generate the token
// $pw = read-host "Enter Token" -AsSecureString
// ConvertFrom-SecureString $pw
fn main() -> windows::Result<()> {
// The encrypted string is 'foobar'
let encrypted_token = "01000000d08c9ddf0115d1118c7a00c04fc297eb01000000c336dca1c99b7d40ae3f797c2b5d2951000000000200000000001066000000010000200000007a87d6ac2fc8037bef45e3dbcb0b652432a22a9b48fc5fa3e4fcfd9aaf922949000000000e8000000002000020000000eeaa76a44b6cd5da837f4b0f7040de8e2795ed846f8abe2c7f2d2365d00cf89c1000000069fcaa7fa475178d623f4adab1b08ac4400000008af807014cba53ed2f1e7b8a54c6ad89ff57f0ee3d8c51ecd8c5b48e99b58d0e738c9fae9fc41b4280938865a047f2724106d34313c88a0f3852d5ba9d75abfd";
let mut et_bytes = hex::decode(encrypted_token).unwrap();
let size = u32::try_from(et_bytes.len()).unwrap();
let mut decrypted = vec![0u8; et_bytes.len()];
let dt_bytes = &mut decrypted;
let mut p_data_in = CRYPTOAPI_BLOB {
cb_data: size,
pb_data: et_bytes.as_mut_ptr(),
};
let mut p_data_out = CRYPTOAPI_BLOB {
cb_data: size,
pb_data: dt_bytes.as_mut_ptr(),
};
let pin = &mut p_data_in;
let pout = &mut p_data_out;
unsafe {
let result = CryptUnprotectData(
pin,
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
0,
pout
);
println!("{:?}, {:?}", dt_bytes, result);
}
Ok(())
}
Basically it returns the all zero array, but the result of the CryptUnprotectData returns 1, which according to the docs means success: https://docs.microsoft.com/en-us/windows/win32/api/dpapi/nf-dpapi-cryptunprotectdata
I've verified that by trying to mangle the hex string thus corrupting the encrypted data, which causes it to return 0. I'm not sure if it's writing to the wrong location or something, but presumably the success condition means it wrote somewhere.
DATA_BLOBvariable that, on successful return, receives the decrypted data. The hint is in the parameter description where it says that you need to free the storage usingLocalFree. - IInspectableto_string_lossy()should really replaced byto_string(), and propagate the error information. Otherwisedecryptwill report success even when the input doesn't meet the expectations, causing errors at a later point, with nothing pointing to the root cause anymore. And the library's interface should be based on&[u8]slices. The CryptoAPI operates on bytes, and having this library only support some byte sequences unduly limits its utility. - IInspectable