-
Notifications
You must be signed in to change notification settings - Fork 204
Conversation
@mvdnes Any thoughts on this PR? I'm willing to invest time and fix issues if needed. |
848325d
to
744bb41
Compare
Merged improvements from |
This is brilliant! I hope we can get this merged soon. Would it be possible to support Central Directory Encryption with this implementation? @rylev and I have recently been given permission to accept PRs, and we should be able to give this a proper review soon. |
Technically, yes. The ZipCrypto algorithm stands for itself and can be used to decrypt any binary stream, operating on the std::io::Read trait* (I also considered creating a separate crate just for ZipCrypto, but that's probably overkill). However, I am not sure if there is any ZIP software in existence that uses the weak ZipCrypto cipher to encrypt the Central Directory (relevant: zlib-ng/minizip-ng#141 ). A higher value task would be to support ZIP files with strong crypto like AES encryption which is believed to provide real security. It seems like Central Directory Encryption is often (always?) done with Strong Encryption. The encryption method of choice is almost always AES because all the alternatives in the specification are deprecated & insecure by now (RC2, RC4, DES, 3DES). AES encrypted ZIP files are somewhat common. However, I think Strong Encryption should be treated in a separate issue/PR because it's a separate subject that requires its own considerations. The restructuring of the reader that I've done in this PR can be used to hook any other crypto algorithm into the reader pipeline, so it's a good preparation for further work. *Also, encryption is almost finished, it just needs to get cryptographically secure randomness from somewhere. |
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! Could you run a cargo fmt
on the changes?
Thanks! Do you mean just running |
@Plecra Merged all the improvements from master. Applied |
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, sorry to be a pain, but I don't want to erase the commit history - I'd rather rebase the changes. I can handle that tomorrow if you'd like 😃
How does merging erase the commit history? The merge from I used to rebase my changes, but for |
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.
Some small things. We can merge this, but if you're up for it, it would be great to fix the nitpicks before that.
src/read.rs
Outdated
if file_number >= self.files.len() { | ||
return Err(ZipError::FileNotFound); | ||
} | ||
let data = &mut self.files[file_number]; | ||
let ref mut data = self.files[file_number]; |
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 think we usually use the previous syntax. Is there a reason this was changed?
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.
No, that was an oversight during the merge. Fixed.
src/read.rs
Outdated
self.by_name_internal(name, None) | ||
} | ||
|
||
fn by_name_internal<'a>( |
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'd rather call this by_name_with_optional_password
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.
Okay. It's a bit long, but the name is clearer. Changed it to by_name_with_optional_password
and by_index_with_optional_password
.
src/read.rs
Outdated
if data.encrypted { | ||
return unsupported_zip_error("Encrypted files are not supported"); | ||
if password == None { | ||
if data.encrypted { |
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 think this would be easier to follow like this:
match (password, data.encrypted) {
(None, true) => return Err(..),
(Some(_), false) => password = None,
_ => {}
}
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.
Back when I wrote this code, I didn't even know this was possible with matcher clauses (I only learned that a week ago). Looks elegant, I applied that change.
src/zipcrypto.rs
Outdated
} | ||
|
||
impl ZipCryptoKeys { | ||
//Used this paper to implement ZipCrypto algo |
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.
Can this be part of the module documentation?
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.
Improved & fixed docstrings throughout the entire zipcrypto.rs
document. Added module level documentation.
src/zipcrypto.rs
Outdated
|
||
impl<R: std::io::Read> ZipCryptoReader<R> { | ||
pub fn new(file: R, password: &[u8]) -> ZipCryptoReader<R> { | ||
//Note: The password is &[u8] and not &str because the documentation |
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.
nit: comments should have a space between the //
and the first character
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.
Fixed.
tests/zip_crypto.rs
Outdated
// No password | ||
let file = archive.by_index(0); | ||
assert!(file.is_err()); | ||
if let Err(error) = file { |
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.
You can match inside of the Err
as well so there's no need for the match expression below
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.
Turned if expressions into cleaner matcher clause.
tests/zip_crypto.rs
Outdated
if let Err(error) = file { | ||
match error { | ||
zip::result::ZipError::PasswordRequired => (), | ||
_ => panic!(), |
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.
Can we add some descriptive text to these panics?
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.
Added some meaningful panic messages.
tests/zip_crypto.rs
Outdated
|
||
{ | ||
// Wrong password | ||
let file = archive.by_index_decrypt(0, "wrong password".as_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.
nit: you can use b"wrong password"
instead of as_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.
Fixed.
…_index_internal` to `by_index_with_optional_password`
@rylev Thanks for the detailed review. I applied all your suggestions. |
Thanks for following up on that! I've created #157 to discuss more encryption features if you'd like to share your thoughts 😉 |
Fantastic! I'm very happy to see this PR accepted and merged 🎉 |
Added two new functions to the API ( resolves #64 ):
These functions behave identically to their already existing counterparts without
_decrypt
, but take a password in the form of&[u8]
. The password is not a&str
because the ZipCrypto standard does not define which encoding is used, so UTF-8 cannot represent all possible passwords (for details, please read the comments in zipcrypto.rs).The functions integrate seamlessly with the existing API. Substantial restructuring was necessary to insert the CryptoReader between the other readers. The code has been modified to facilitate the addition of further crypto or decompression routines.
Note that the API for
read_zipfile_from_stream
does not yet support ZipCrypto reading. However, this is not hard to add. Also, ZipCrypto writing support has not been added at this point in time. I want to make sure that my work is in line with what the authors and the users expect before I continue working on this.In addition to that, a ZipCrypto writer will need cryptographically secure randomness (even though the crypto is weak). Where this notoriously hard to get randomness comes from is up for discussion.