Repository: RustCrypto/utils (Rust)

Issue:
`ZeroizeOnDrop` derivation does not work for a `Box<T>` field
The code 
```rust
#[derive(ZeroizeOnDrop)]
pub struct SecretBox<T: Zeroize>(Box<T>);
```
leads to the following compilation error (rustc 1.61):
```
error[E0367]: `Drop` impl requires `T: Drop` but the struct it is implemented for does not
 --> src/main.rs:3:10
  |
3 | #[derive(ZeroizeOnDrop)]
  |          ^^^^^^^^^^^^^
  |
note: the implementor must specify the same requirement
 --> src/main.rs:4:1
  |
4 | pub struct SecretBox<T: Zeroize>(Box<T>);
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  = note: this error originates in the derive macro `ZeroizeOnDrop` (in Nightly builds, run with -Z macro-backtrace for more info)
```

Meanwhile, a trivial drop implementation compiles fine:
```rust
impl<T: Zeroize> Drop for SecretBox<T>
{
    fn drop(&mut self) {
        self.0.zeroize()
    }
}

impl<T: Zeroize> ZeroizeOnDrop for SecretBox<T> {}
```
What can be the reason the automatic derivation does not work then? I am not sure how to interpret the compiler error.


Discussion:
`Box` is an unusual special case in Rust in general, and there's unfortunately a lot of magic involved in how `ZeroizeOnDrop` is impl'd.

That said, I'd call this a bug, but I'm not sure there's a good fix.

cc @daxpedda 
I mentioned this somewhere before, but probably using `synstructure` isn't ideal. In this case it's constraining the generics to implement `Drop`. I believe there is no case where this is necessary.
I also just noticed that `Zeroize` isn't implemented for `Box<T>` where `T: Zeroize`, is there a reason for this?

EDIT: Because it is handled through `DerefMut`.
I believe this applies to all generic objects.

```rs

#[derive(Zeroize, ZeroizeOnDrop)]
pub struct SecretShareMachine<C: Curve> {
  params: FrostParams,
  context: String,
  coefficients: Vec<C::F>,
  our_commitments: Vec<C::G>,
}
```

```
error[E0367]: `Drop` impl requires `Vec<<C as curve::Curve>::F>: Drop` but the struct it is implemented for does not
   --> crypto/frost/src/key_gen.rs:267:19
    |
267 | #[derive(Zeroize, ZeroizeOnDrop)]
    |                   ^^^^^^^^^^^^^
    |
note: the implementor must specify the same requirement
   --> crypto/frost/src/key_gen.rs:269:1
    |
269 | / pub struct SecretShareMachine<C: Curve> {
270 | |   params: FrostParams,
271 | |   context: String,
272 | |   coefficients: Vec<C::F>,
273 | |   our_commitments: Vec<C::G>,
274 | | }
    | |_^
    = note: this error originates in the derive macro `ZeroizeOnDrop` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0367]: `Drop` impl requires `C: Drop` but the struct it is implemented for does not
   --> crypto/frost/src/key_gen.rs:267:19
    |
267 | #[derive(Zeroize, ZeroizeOnDrop)]
    |                   ^^^^^^^^^^^^^
    |
note: the implementor must specify the same requirement
   --> crypto/frost/src/key_gen.rs:269:1
    |
269 | / pub struct SecretShareMachine<C: Curve> {
270 | |   params: FrostParams,
271 | |   context: String,
272 | |   coefficients: Vec<C::F>,
273 | |   our_commitments: Vec<C::G>,
274 | | }
    | |_^
    = note: this error originates in the derive macro `ZeroizeOnDrop` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0367]: `Drop` impl requires `Vec<<C as curve::Curve>::G>: Drop` but the struct it is implemented for does not
   --> crypto/frost/src/key_gen.rs:267:19
    |
267 | #[derive(Zeroize, ZeroizeOnDrop)]
    |                   ^^^^^^^^^^^^^
```

Considering I'm stuck on 1.3 due to dalek, would manually implementing drop to call zeroize be sufficient?
This should also be fixed by #787. Just in case I added a test-case.

> Considering I'm stuck on 1.3 due to dalek, would manually implementing drop to call zeroize be sufficient?

I believe in 1.3 you can use `#[zeroize(drop)]`, see https://docs.rs/zeroize/1.3.0/zeroize/index.html#custom-derive-support. But manually implementing works too.
I assumed zeroize(drop) would have the same behavior as OnDrop. No?
Yes. I mean `ZeroizeOnDrop` just allows you to implement `Drop` without implementing `Zeroize`, `#[zeroize(drop)]` can't do that.



Patch:
diff --git a/zeroize/derive/src/lib.rs b/zeroize/derive/src/lib.rs
index 1efc3b4d..baf69901 100644
--- a/zeroize/derive/src/lib.rs
+++ b/zeroize/derive/src/lib.rs
@@ -71,7 +71,7 @@ fn derive_zeroize(mut s: synstructure::Structure<'_>) -> TokenStream {
 fn derive_zeroize_on_drop(mut s: synstructure::Structure<'_>) -> TokenStream {
     let zeroizers = generate_fields(&mut s, quote! { zeroize_or_on_drop });
 
-    let drop_impl = s.gen_impl(quote! {
+    let drop_impl = s.add_bounds(AddBounds::None).gen_impl(quote! {
         gen impl Drop for @Self {
             fn drop(&mut self) {
                 use zeroize::__internal::AssertZeroize;


Test patch:
diff --git a/zeroize/tests/zeroize_derive.rs b/zeroize/tests/zeroize_derive.rs
index 4e0fdc61..96c10c32 100644
--- a/zeroize/tests/zeroize_derive.rs
+++ b/zeroize/tests/zeroize_derive.rs
@@ -315,3 +315,13 @@ fn derive_deref() {
     }
     assert_eq!(&value.0 .0, &[0, 0, 0])
 }
+
+#[test]
+#[cfg(feature = "alloc")]
+fn derive_zeroize_on_drop_generic() {
+    #[derive(ZeroizeOnDrop)]
+    struct Y<T: Zeroize>(Box<T>);
+
+    #[derive(ZeroizeOnDrop)]
+    struct Z<T: Zeroize>(Vec<T>);
+}