-
Notifications
You must be signed in to change notification settings - Fork 20
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
Make sure secret data is zeroized on drop and not copied around #53
Conversation
7c74216
to
4f4f110
Compare
Codecov Report
@@ Coverage Diff @@
## master #53 +/- ##
==========================================
+ Coverage 83.77% 84.56% +0.79%
==========================================
Files 11 12 +1
Lines 912 946 +34
==========================================
+ Hits 764 800 +36
+ Misses 148 146 -2
Continue to review full report at Codecov.
|
Hard to answer wrt Python scope & rust... IMHO, I think attempting to zeroize is a solid idea. I think limiting the API surface to just the rust bits is the way to go about it. This PR looks to do that, so it's fine.
IMHO, yes. See my note above about safe API constructions for handling secret things. It's mostly just misuse prevention and will help developers know (and feel) that they're doing something weird and smelly.
It definitely doesn't hurt.
Same my above response, I don't think it would hurt to do it. 🤷 |
Would making the serialization method explicit for secret objects (like
I understand in general that that was the intention, but what specific scenario is prevented by the signer consuming the key? |
@tuxxy wrapped every secret data I could think of in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is very far from my league as I'm completely new to Rust, but seems a sensible approach. Thanks for taking care of this!
@@ -30,10 +30,12 @@ API reference | |||
|
|||
Returns a public key corresponding to this secret key. | |||
|
|||
.. py:method:: __bytes__() -> bytes | |||
.. py:method:: to_secret_bytes() -> bytes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the context of the old pyUmbral, we called this to_bytes()
. I think we may have simular occurrences in nucypher
, with the same intention (i.e., to avoid unintentional serialization of something sensitive)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to_bytes()
feels like it should just be an alias for __bytes__()
, I wanted to emphasize the "secretness" more. In nucypher
, I always thought that to_bytes()
was used when there was a need for additional serialization arguments; I didn't realize it was supposed to mean that the bytes are secret.
@@ -168,7 +174,7 @@ impl Capsule { | |||
} | |||
|
|||
let pub_key = receiving_sk.public_key().to_point(); | |||
let dh_point = &precursor * &receiving_sk.to_secret_scalar(); | |||
let dh_point = &precursor * receiving_sk.to_secret_scalar().as_secret(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just curious: why not using &
here on receiving_sk
? Is it because the new SecretBox
wrapper?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Initially, &
was applied to the whole receiving_sk.to_secret_scalar()
, which returns the scalar itself; the arithmetic operator needs a reference. as_secret()
gives a reference already (which means that the object itself is kept in the heap, wherever the wrapping Box
put it), and the secret is not copied to the stack as it was before.
(It is a naming convention in Rust that to_*
means that an object is returned, and as_*
means that a reference is returned)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At least, not copied to the stack in our code. The *
implementation may still make copies, and that's something we can't prevent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
…is cleared on drop
Ok, going to merge with |
Fixes #8 (to the extent Rust allows, at least)
The main principles of keeping secret data secret in Rust are:
Box
to prevent stack allocation (which may lead to unwanted copies left on stack)Drop
implementation that zeroizes secret dataWe have secret data belonging to three kinds of foreign types:
Zeroize
is defined, but which are not zeroized on drop by default (e.g. RustCryptoScalar
)Zeroize
is not defined (e.g.GenericArray
)Zeroize
is not defined, but which are zeroized on drop (e.g. RustCryptoSecretKey
). Note that RustCryptoSigningKey
was not zeroized until recently - it is sinceecdsa
0.12.2Plus, of course, our own types, but for them at least we can define traits.
Changes in the PR:
SecretBox
struct, a wrapper that ensures that its contents follow the principles aboveCanBeZeroizedOnDrop
helper trait, to generalize the behavior of all the kinds of types aboveSerializableToSecretArray
trait returning aSecretBox<GenericArray>
, as opposed toSerializableToArray
. It will be used for objects containing secret data.SecretKey
methods are wrapped inSecretBox
on exposurePartialEq
implementation removed fromSecretKey
,SecretKeyFactory
, andSigner
(kept for tests only)SecretBox
is used for the internals ofSecretKey
andSecretKeyFactory
(Signer
gets the benefits automatically by usingSecretKey
)SerializableToSecretArray
+ missing equality for secret objects)to_secret_bytes()
in Python andtoSecretBytes()
in WASM)kdf
,DEM
andCapsule
internallyFor reviewers:
Signer
consume the secret key and not be cloneable, like the backendSigningKey
in RustCrypto? What does it achieve?