Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Why DefaultIsZeroes requires Copy? #1062

Closed
dima74 opened this issue Apr 15, 2024 · 4 comments
Closed

Why DefaultIsZeroes requires Copy? #1062

dima74 opened this issue Apr 15, 2024 · 4 comments

Comments

@dima74
Copy link

dima74 commented Apr 15, 2024

pub trait DefaultIsZeroes: Copy + Default + Sized {}

What is recommended approach to implement Zeroize for non-copy-types? Consider some library has non-copy type:

// somelib.rs
#[derive(Clone)]
pub struct SecretKey([u8; 32]);

And in user code we have wrapper around that which we would like to zeroize on drop:

// main.rs
#[derive(Clone)]
struct MyKey(SecretKey);

One approach would be to implement Default for MyKey and add impl DefaultIsZeroes for MyKey {} so MyKey will implement Zeroize, but it is not possible because of Copy constraint on DefaultIsZeroes

@tarcieri
Copy link
Member

DefaultIsZeroes underlies the core zeroization behavior for integer types, which is performed using core::ptr::write_volatile.

A Copy bound is the simplest way we can avoid making DefaultIsZeroes an unsafe trait:

  • We have special case impls of Zeroize for [Z] where Z: DefaultIsZeroes, which performs a memset-like operation underneath which requires the source type is Copy, since it makes a bunch of copies of it, which makes it possible to optimize zeroization of e.g. [u8] (though we haven't yet implemented an optimized version, see zeroize: use asm! to improve performance #841). With a Copy bound on DefaultIsZeros, we can make such impls safe.
  • It ensures the type is !Drop, which is necessary because write_volatile will bypass the drop handler on the target value.

What is recommended approach to implement Zeroize for non-copy-types?

For your example:

impl Zeroize for SecretKey {
    fn zeroize(&mut self) {
        self.0.zeroize();
    }
}

Though you'd likely be better zeroizing in a drop handler for such a use case, to avoid use-after-zeroize problems:

impl Drop for SecretKey {
    fn drop(&mut self) {
        self.0.zeroize();
    }
}

impl ZeroizeOnDrop for SecretKey {}

@dima74
Copy link
Author

dima74 commented Apr 16, 2024

Thanks for the detailed answer!

impl Zeroize for SecretKey {
    fn zeroize(&mut self) {
        self.0.zeroize();
    }
}

If this can't be used because SecretKey is from (external) library which doesn't use zeroize, and SecretKey doesn't implement Copy, are there any possibilities to use zeroize on the wrapper (MyKey), or the only solution would be to approach library author and suggest to use zeroize?

@tarcieri
Copy link
Member

There's no safe way to zeroize a foreign type.

#1045 added a zeroize_flat_type function which could potentially apply to these cases, although it's a bit dangerous to use on a type you don't control as its internal structure could change.

@dima74
Copy link
Author

dima74 commented Apr 22, 2024

Thanks for the explanation 👍

@dima74 dima74 closed this as completed Apr 22, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants