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

Implement zeroize for secret values #6

Merged
merged 1 commit into from
Apr 22, 2024
Merged

Implement zeroize for secret values #6

merged 1 commit into from
Apr 22, 2024

Conversation

moCello
Copy link
Member

@moCello moCello commented Feb 8, 2024

Resolves #5

@moCello
Copy link
Member Author

moCello commented Feb 19, 2024

This is a tricky situation because even when we implement Zeroize on Drop for SecretKey, the memory still won't be erased:

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

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

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn zeroize() {
        let sk = SecretKey::from(BlsScalar::from(42));
        let ptr = sk.as_ref().0.as_ptr();
        drop(sk);

        // We would expect that the memory is erased during `drop` but it is still there
        unsafe {
            assert_eq!(
                core::slice::from_raw_parts(ptr, 4),
                BlsScalar::from(42).0
            );
        };

        // Let's try again and call zeroize explicitly:
        let mut sk = SecretKey::from(BlsScalar::from(42));
        let ptr = sk.as_ref().0.as_ptr();
        sk.zeroize();
        drop(sk);

        // now the memory is zeroed
        unsafe {
            assert_eq!(core::slice::from_raw_parts(ptr, 4), [0; 4]);
        };

    }
}

As this blogpost describes this has to do with the memory being copied under the hood

The answer is that in Rust, moving a value compiles into a memory copy in the general case. Sometimes, the compiler can be smart and optimize away a memory copy, but other times it is impossible. For example [..], in the manual drop() the value is moved into the drop() function, but the value is copied in memory while doing so.

This means that the copied value in drop is zeroized, but not the value in our program (the value we want to be zeroized).

One solution, as suggested in the blogpost, is to Box the BlsScalar in the SecretKey and therefore have it heap-allocated. This seems to erase the allocated memory even without calling zeroized on drop. This approach comes with a performance penalty.

The other is to leave it up to the user to call zeroize on any SecretKey before it goes out of scope.

And in any case the Copy trait should not be derived on any struct that carries sensitive data.

@moCello moCello force-pushed the mocello/5_zeroize branch 2 times, most recently from 0ba8be8 to ab04a5e Compare February 23, 2024 13:48
@moCello moCello closed this Apr 17, 2024
@moCello moCello force-pushed the mocello/5_zeroize branch from ab04a5e to 40a2a5d Compare April 17, 2024 14:52
@moCello moCello reopened this Apr 17, 2024
@moCello moCello marked this pull request as ready for review April 17, 2024 14:58
Copy link
Member

@herr-seppia herr-seppia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wasn't the issue about adding zeroize to secret data?
Is there a reason to derive zeroize for public structure (like public key, apk etc)

@moCello
Copy link
Member Author

moCello commented Apr 18, 2024

Wasn't the issue about adding zeroize to secret data? Is there a reason to derive zeroize for public structure (like public key, apk etc)

the issue was regarding secret values indeed. But while implementing I figured that it also doesn't hurt to implement it for other types as well.
Imagine for example someone being able to link a public key to a specific person because it has not been erased from a shared machine.

Copy link
Member

@ureeves ureeves left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm totally fine with introducing this and remove the Copy trait. I always found it strange that SecretKeys would be Copy. That said, while it does mitigate the problem, it doesn't solve it, since a move will produce the same results. It is, however, the best we can do for now.

@moCello moCello force-pushed the mocello/5_zeroize branch from 256b4ad to 9c65d8a Compare April 22, 2024 11:18
@moCello
Copy link
Member Author

moCello commented Apr 22, 2024

In my opinion it doesn't make sense to have both the Copy and Zerioze traits on types where the user is responsible to make sure to erase the data locally before it goes out of scope.

I understand that removing the Copy on the public types (PublicKey, APK, Signature) simply as a precaution so we can add Zeroize might be overkill for now and can cause issues downstream. So I decided to implement Zeroize (and remove Copy) only for SecretKey.

If, at a later point, we decide that we need Zeroize on the other types too, we can still add it then.

Copy link
Member

@herr-seppia herr-seppia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@moCello moCello merged commit aa0d537 into main Apr 22, 2024
5 checks passed
@moCello moCello deleted the mocello/5_zeroize branch April 22, 2024 11:35
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

Successfully merging this pull request may close these issues.

Secret velues not erased
4 participants