- 
          
 - 
                Notifications
    
You must be signed in to change notification settings  - Fork 33
 
Use secure_string for more secure Password and SecretKey #328
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
base: master
Are you sure you want to change the base?
Conversation
- Updated struct to use SecureString for enhanced security. - Implemented PartialEq and Eq to be able to compare 2 Password instances - Added tests for equality comparison of instances.
…curity - Updated SecretKey struct to use SecureBytes for improved security handling. - Adjusted implementations of PartialEq and Debug to align with SecureBytes. - Implemented serialization for SecretKey since we cannot derive Serialize since SecureBytes doesn't implement it . - Included tests for JSON serialization of SecretKey.
          Codecov ReportAll modified and coverable lines are covered by tests ✅ 
 Flags with carried forward coverage won't be shown. Click here to find out more. 
 🚀 New features to boost your workflow:
  | 
    
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.
@m4tx can you look at my comments, I'd like a second pair of eyes.
| 
           @seqre What do you think about the miri thing, because that is a blocker for this PR?  | 
    
          
 Hey, thanks for your patience! Can you share more details on mocking a syscall for Miri? I didn't know it was possible. Otherwise, I think the only option is to ignore all the failing tests. Which would be kind of a sad thing to do, given how many there are.  | 
    
- Refactored Password and SecretKey structs to provide Miri-compatible versions, using String and Box<[u8]> respectively. - Adjusted methods for creating and accessing passwords and secret keys to ensure compatibility with Miri's limitations.
          
 I realize I didn't fully think through the mock idea - it's beyond my current skill level anyway. 
 I kept a normal implementation for miri, all should be good now, please have a look.  | 
    
| 
           I'm not sure if creating a special implementation just for Miri is the best approach. It forces us to maintain twice as much code. However, getting rid of it would force us to ignore a lot of only partially related tests from Miri testing, which is also not a good end result. I think that for now, we can leave that implementation be, and we can always reassess the situation in the future if it becomes a hassle. Would you agree @m4tx? @eibrahim95, on a side note, please add   | 
    
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.
One last request from me and it should be good in my eyes!
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.
LGTM
| #[cfg(miri)] | ||
| impl Debug for Password { | ||
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
| write!(f, "Password(***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.
Using non-rustic outputs like trailing * can make things more complicated for downstream users. I would rather use write!(f, "Password(_)"), write!(f, "Password(..)"), or (my preference, because that's what you are supposed to used, if I'm not mistaken) f.debug_struct("Password").finish_non_exhaustive().
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.
Thank you for pointing that out, TIL about it! @eibrahim95 could you change it, please?
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.
I made it like this so it is the same as secure-string, if you notice this one is for miri, the other implementation derives Debug, and depends on SecureString, which implements Debug similarly to the above If that is important, I could change it for both, miri and not(miri)
| mockall = "0.13" | ||
| multer = "3" | ||
| password-auth = { version = "1", default-features = false } | ||
| secure-string = "0.3.0" | 
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.
secure-string looks kinda dead. Why not use zeroize directly?
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.
zeroize is great at what it does, but it doesn't do enough. secure-string, apart from zeroing the memory, provides constant time equality (although as we looked before, it might just be claim not backed by code, I'll have to double check it) and protections against leaking into swap and core dumps. I believe that PR is good enough to merge it as is and we can always do some additional research and improve that part of the code later on, what do you think?
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.
provides constant time equality
@seqre this is untrue, as you (and I) pointed out. The PR uses subtle for this functionality.
I somewhat agree with @Kijewski that secure-string is not very well maintained. zeroize provides a good portion of the functionality, but mlocking the memory would be nice. We could implement this ourselves (which shouldn't be very difficult, looking at the code), so perhaps it would be good to get this right from the beginning (or just ignore the mlocking part for now). WDYT?
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.
I see a few solutions:
- As I mentioned above, we can merge it as is and improve in the future.
 - We can fork 
secure-string, update it, use that version, and try to upstream the improvements - it ain't perfect, but something like this: ISibboI/secure-string@main...seqre-contrib:secure-string:main - Its codebase is small, and it's licensed with 
UNLICENSE, so we can just pull that code into Cot. 
The 2nd might be the best, we get what we want, and we might even improve the ecosystem by upstreaming improvements. This way, we can fairly quickly get what we want, and the only change required here is to change the source of the dependency. Would you agree @m4tx?
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.
@seqre ah fair, I guess we could use a fork. Do you mind transferring it to the cot-rs organization, so that it's clear for people that we definitely have the will to maintain it? Similarly to https://github.com/cot-rs/openapi-guis
If we want to use the fork, we'll probably want to publish it on crates.io - at this point I don't think we should use any git dependencies. Of course, ideally, as you say, we should upstream the changes, but if we don't get a response for say, a week or two, we should probably stick to our fork.
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.
The repository has been transferred: https://github.com/cot-rs/secure-string
However, I think that using a git dependency, especially for a repo in our control, is fine for now, @m4tx. If we can't upstream the changes promptly, then sure, we can publish it to crates.io. Similarly to what we did before with other crates that we had to fix ourselves.
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.
Issues were disabled on the forked repo, so I have copy comment here:
<SecureVec<T> as PartialEq>::eq is not sane
I don't think the change in cot-rs/secure-string@004f3da is sane. For a T which contains any internal or trailing padding, e.g. #[repr(C)] struct T { x: u8, y: u16 }, the bytes in the padding cannot be relied on, and should not be read. Also, generic Ts might not have the same order as its transmuted [u8] slice.
The simplest solution would be to remove the generic SecureVec<T>, and invariantly set T = u8. Then you could concentrate on secure strings, without having to care about any and all types there could be.
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.
However, I think that using a git dependency, especially for a repo in our control, is fine for now, @m4tx.
@seqre we can't release the crate to crates.io with a git dependency. When published, the git dependency will be replaced with a reference to a crates.io-published version, so it just won't work.
@Kijewski yeah, I agree that the implementation is not correct, thanks for vigilance! I think we can just use [T]::ct_eq (it's a Vec<T> underneath anyway) and remove the overcomplicated implementation and the unsafe code. Setting T = u8 would also work. @seqre is there a reason you opted for this solution?
I also re-enabled Issues on the repo.
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.
It was the quickest thing I could do to show a proof of concept in a few minutes; it wasn't intended to be the final solution. If we want to go forward with that fork, then we should spend more time on it.
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.
Ah sure, that makes sense!
This implements #47
cot::common_types::Passwordstruct to use SecureString for enhanced security.cot::common_types::Passwordto be able to compare 2 Password instances