//! Integration tests for `zeroize_derive` proc macros #![cfg(feature = "zeroize_derive")] use zeroize::{Zeroize, ZeroizeOnDrop}; #[test] fn derive_tuple_struct_test() { #[derive(Zeroize, ZeroizeOnDrop)] struct Z([u8; 3]); let mut value = Z([1, 2, 3]); value.zeroize(); assert_eq!(&value.0, &[0, 0, 0]) } #[test] #[cfg(feature = "alloc")] fn derive_struct_test() { #[derive(Zeroize, ZeroizeOnDrop)] struct Z { string: String, vec: Vec, bytearray: [u8; 3], number: usize, boolean: bool, } let mut value = Z { string: String::from("Hello, world!"), vec: vec![1, 2, 3], bytearray: [4, 5, 6], number: 42, boolean: true, }; value.zeroize(); assert!(value.string.is_empty()); assert!(value.vec.is_empty()); assert_eq!(&value.bytearray, &[0, 0, 0]); assert_eq!(value.number, 0); assert!(!value.boolean); } #[test] fn derive_enum_test() { #[derive(Zeroize, ZeroizeOnDrop)] enum Z { #[allow(dead_code)] Variant1, Variant2(usize), } let mut value = Z::Variant2(26); value.zeroize(); assert!(matches!(value, Z::Variant2(0))); } /// Test that the custom macro actually derived `Drop` for `Z` #[test] fn derive_struct_drop() { #[derive(Zeroize, ZeroizeOnDrop)] struct Z([u8; 3]); assert!(std::mem::needs_drop::()); } /// Test that the custom macro actually derived `Drop` for `Z` #[test] fn derive_enum_drop() { #[allow(dead_code)] #[derive(Zeroize, ZeroizeOnDrop)] enum Z { Variant1, Variant2(usize), } assert!(std::mem::needs_drop::()); } /// Test that the custom macro actually derived `Drop` for `Z` #[test] fn derive_struct_only_drop() { #[derive(ZeroizeOnDrop)] struct Z([u8; 3]); assert!(std::mem::needs_drop::()); } /// Test that the custom macro actually derived `Drop` for `Z` #[test] fn derive_enum_only_drop() { #[allow(dead_code)] #[derive(ZeroizeOnDrop)] enum Z { Variant1, Variant2(usize), } assert!(std::mem::needs_drop::()); } /// Test that `Drop` is not derived in the following case by defining a /// `Drop` impl which should conflict if the custom derive defined one too #[allow(dead_code)] #[derive(Zeroize)] struct ZeroizeNoDropStruct([u8; 3]); impl Drop for ZeroizeNoDropStruct { fn drop(&mut self) {} } #[allow(dead_code)] #[derive(Zeroize)] enum ZeroizeNoDropEnum { Variant([u8; 3]), } impl Drop for ZeroizeNoDropEnum { fn drop(&mut self) {} } #[test] #[cfg(feature = "alloc")] fn derive_struct_skip() { #[derive(Zeroize, ZeroizeOnDrop)] struct Z { string: String, vec: Vec, #[zeroize(skip)] bytearray: [u8; 3], number: usize, boolean: bool, } let mut value = Z { string: String::from("Hello, world!"), vec: vec![1, 2, 3], bytearray: [4, 5, 6], number: 42, boolean: true, }; value.zeroize(); assert!(value.string.is_empty()); assert!(value.vec.is_empty()); assert_eq!(&value.bytearray, &[4, 5, 6]); assert_eq!(value.number, 0); assert!(!value.boolean); } #[test] #[cfg(feature = "alloc")] fn derive_enum_skip() { #[derive(Zeroize, ZeroizeOnDrop)] enum Z { #[allow(dead_code)] Variant1, #[zeroize(skip)] Variant2([u8; 3]), #[zeroize(skip)] Variant3 { string: String, vec: Vec, bytearray: [u8; 3], number: usize, boolean: bool, }, Variant4 { string: String, vec: Vec, #[zeroize(skip)] bytearray: [u8; 3], number: usize, boolean: bool, }, } let mut value = Z::Variant2([4, 5, 6]); value.zeroize(); assert!(matches!(&value, Z::Variant2([4, 5, 6]))); let mut value = Z::Variant3 { string: String::from("Hello, world!"), vec: vec![1, 2, 3], bytearray: [4, 5, 6], number: 42, boolean: true, }; value.zeroize(); assert!(matches!( &value, Z::Variant3 { string, vec, bytearray, number, boolean } if string == "Hello, world!" && vec == &[1, 2, 3] && bytearray == &[4, 5, 6] && *number == 42 && *boolean )); let mut value = Z::Variant4 { string: String::from("Hello, world!"), vec: vec![1, 2, 3], bytearray: [4, 5, 6], number: 42, boolean: true, }; value.zeroize(); assert!(matches!( &value, Z::Variant4 { string, vec, bytearray, number, boolean } if string.is_empty() && vec.is_empty() && bytearray == &[4, 5, 6] && *number == 0 && !boolean )); } #[test] fn derive_bound() { trait T: Zeroize {} impl T for u8 {} #[derive(Zeroize)] #[zeroize(bound = "X: T")] struct Z(X); let mut value = Z(5_u8); value.zeroize(); assert_eq!(value.0, 0); } #[test] fn derive_inherit_zeroize_on_drop() { #[derive(ZeroizeOnDrop)] struct X([u8; 3]); #[derive(ZeroizeOnDrop)] struct Z(X); let mut value = Z(X([1, 2, 3])); unsafe { std::ptr::drop_in_place(&mut value); } assert_eq!(&value.0 .0, &[0, 0, 0]) } #[test] fn derive_inherit_from_both() { #[derive(Zeroize, ZeroizeOnDrop)] struct X([u8; 3]); #[derive(ZeroizeOnDrop)] struct Z(X); let mut value = Z(X([1, 2, 3])); unsafe { std::ptr::drop_in_place(&mut value); } assert_eq!(&value.0 .0, &[0, 0, 0]) } #[test] fn derive_inherit_both() { #[derive(Zeroize, ZeroizeOnDrop)] struct X([u8; 3]); #[derive(Zeroize, ZeroizeOnDrop)] struct Z(X); let mut value = Z(X([1, 2, 3])); unsafe { std::ptr::drop_in_place(&mut value); } assert_eq!(&value.0 .0, &[0, 0, 0]) } #[test] fn derive_deref() { struct X([u8; 3]); impl std::ops::Deref for X { type Target = [u8]; fn deref(&self) -> &Self::Target { &self.0 } } impl std::ops::DerefMut for X { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } #[derive(Zeroize, ZeroizeOnDrop)] struct Z(X); let mut value = Z(X([1, 2, 3])); unsafe { std::ptr::drop_in_place(&mut value); } assert_eq!(&value.0 .0, &[0, 0, 0]) } #[test] #[cfg(feature = "alloc")] fn derive_zeroize_on_drop_generic() { #[derive(ZeroizeOnDrop)] struct Y(Box); #[derive(ZeroizeOnDrop)] struct Z(Vec); } #[test] fn derive_zeroize_unused_param() { #[derive(Zeroize)] struct Z { arr: [u32; 5], #[zeroize(skip)] skipped: T, } } #[test] // Issue #878 fn derive_zeroize_with_marker() { #[derive(ZeroizeOnDrop, Zeroize)] struct Test { #[zeroize(skip)] field: Option, } trait Secret: ZeroizeOnDrop + Zeroize {} impl Secret for Test {} trait Marker {} } #[test] // Issue #878 fn derive_zeroize_used_param() { #[derive(Zeroize)] struct Z { used: T, } }