-
Notifications
You must be signed in to change notification settings - Fork 273
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
[COMPRESS-633] Add encryption support for SevenZ #332
[COMPRESS-633] Add encryption support for SevenZ #332
Conversation
src/main/java/org/apache/commons/compress/archivers/sevenz/AES256SHA256Decoder.java
Fixed
Show fixed
Hide fixed
Codecov Report
@@ Coverage Diff @@
## master #332 +/- ##
============================================
+ Coverage 80.22% 80.25% +0.03%
- Complexity 6640 6657 +17
============================================
Files 341 342 +1
Lines 25184 25266 +82
Branches 4102 4112 +10
============================================
+ Hits 20203 20278 +75
- Misses 3402 3404 +2
- Partials 1579 1584 +5
📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more |
I pushed an alternate implementation to throw out use of |
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.
Hi @Dougniel
Thanks a lot for your contribution.
I normally don't contribute much to Commons Compress, so I just did a quick review, and left some comments. But some of these are not necessarily must-haves (e.g. questions about storing password, random). Take a look at the other ones for misspellings, and javadocs 👍
I stopped before taking a look at AES256SHA256Decoder
as I realized it would take a bit longer to understand the change there.
Cheers
-Bruno
src/test/java/org/apache/commons/compress/archivers/sevenz/SevenZOutputFileTest.java
Outdated
Show resolved
Hide resolved
src/main/java/org/apache/commons/compress/archivers/sevenz/SevenZOutputFile.java
Outdated
Show resolved
Hide resolved
src/main/java/org/apache/commons/compress/archivers/sevenz/SevenZOutputFile.java
Outdated
Show resolved
Hide resolved
src/main/java/org/apache/commons/compress/archivers/sevenz/SevenZOutputFile.java
Outdated
Show resolved
Hide resolved
src/main/java/org/apache/commons/compress/archivers/sevenz/SevenZFile.java
Show resolved
Hide resolved
src/main/java/org/apache/commons/compress/archivers/sevenz/AES256Options.java
Outdated
Show resolved
Hide resolved
int numCyclesPower = 19; | ||
|
||
public AES256Options(byte[] password) { | ||
this.password = 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.
I don't know the Commons Compress API well enough, but I guess it'd be better if the password were never stored in an object in memory or serialized, if possible. This means instead of storing the object in the class, instead trying to design a method public void decompress(byte[] data, byte[] password) {...}
that never stores the password.
But if other classes are already doing that, then I guess that should be fine. (Didn't have time to check, sorry)
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 consider myself not enought skilled in security concerns, but I asked myself about that too when I saw this mechanism that clears password as soon as possible 🤔.
I have chosen to implement the encryption like decryption done in SevenZFile.java
(that stores password too) for homogeneous reasons 🤷.
By the way, as the password is required to decrypt each file independently (the sevenZ archive is not entirely encrypted - we can access metadata like file names without password - only files are encrypted). I don't know how to manage that without storing it somewhere in memory - asking user to fulfill it for each file to compress/decompress ? Even in this way it will be stored inside the Input/OutputStream used by the library user. (Update: I checked through my debugger and I'm not so sure about that 😅)
👉🏼 What I could try is to store it in less places (I stored it in AES256Options
as in SevenZOutputFile
) and even try to initialise a Cipher
early as it seems a secure place for it
src/main/java/org/apache/commons/compress/archivers/sevenz/AES256Options.java
Outdated
Show resolved
Hide resolved
cipher.init(Cipher.ENCRYPT_MODE, aesKey, new IvParameterSpec(opts.iv)); | ||
} catch (final GeneralSecurityException generalSecurityException) { | ||
throw new IOException( | ||
"Encryption error " + "(do you have the JCE Unlimited Strength Jurisdiction Policy Files installed?)", |
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.
Unnecessary string concatenation?
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.
Right, a stupid copy/paste that I made. Maybe it was done that way for formatting purposes
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.
Why is a new Random instance allocated each time?
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 rework the implementation and now I only use SecureRandom one time
As in Java 8 it's not clear that SecureRandom is thread safe or not, I didn't put In a static member (even if the JavaDocs 9 seems to tell that it could be ThreadSafe)
What do you think ?
(Also needs a JIRA issue for the changelog) |
@kinow It seems like you have tagged the wrong person. |
Hi @kinow, Thank you for your rigorous and interesting review, I will correct these and respond The JIRA in description is not right for the purpose of changelog ? (https://issues.apache.org/jira/browse/COMPRESS-633) Cheers, |
src/main/java/org/apache/commons/compress/archivers/sevenz/AES256Options.java
Outdated
Show resolved
Hide resolved
import java.util.Random; | ||
|
||
public class AES256Options { | ||
byte[] 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.
Instance variables should be private and final when possible.
src/main/java/org/apache/commons/compress/archivers/sevenz/SevenZOutputFile.java
Outdated
Show resolved
Hide resolved
src/test/java/org/apache/commons/compress/archivers/sevenz/SevenZOutputFileTest.java
Outdated
Show resolved
Hide resolved
cipher.init(Cipher.ENCRYPT_MODE, aesKey, new IvParameterSpec(opts.iv)); | ||
} catch (final GeneralSecurityException generalSecurityException) { | ||
throw new IOException( | ||
"Encryption error " + "(do you have the JCE Unlimited Strength Jurisdiction Policy Files installed?)", |
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.
Why is a new Random instance allocated each time?
src/test/java/org/apache/commons/compress/archivers/sevenz/SevenZOutputFileTest.java
Outdated
Show resolved
Hide resolved
try { | ||
digest = MessageDigest.getInstance("SHA-256"); | ||
} catch (final NoSuchAlgorithmException noSuchAlgorithmException) { | ||
throw new IOException("SHA-256 is unsupported by your Java implementation", noSuchAlgorithmException); |
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 not possible since SHA-256 is required by the JRE specification, see https://docs.oracle.com/javase/8/docs/api/java/security/MessageDigest.html, therefore this could be an IllegalStateException
.
Thanks for your reviews, I just pushed corrections that you suggested and I changed the place where With these changes, the implementation between decryption and encryption is less homogeneous. In my opinion the decryption could be aligned in the encryption way for the same concern about password storage, but also to reduce the complexity of decode method (ex: replace Cheers, |
Hi Daniel,\My bad, I hadn't seen that there was already a JIRA created for this issue. Thanks for updating the PR! |
src/main/java/org/apache/commons/compress/archivers/sevenz/AES256SHA256Decoder.java
Outdated
Show resolved
Hide resolved
src/main/java/org/apache/commons/compress/archivers/sevenz/SevenZFile.java
Outdated
Show resolved
Hide resolved
@@ -266,4 +270,24 @@ private static void checkReadLength(final int length) { | |||
throw new IllegalArgumentException("Can't read more than eight bytes into a long value"); | |||
} | |||
} | |||
|
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 looks like this method is only used from the sevenz
package so let's keep it package-private there and avoid increasing the public API surface.
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.
Right, I move it on AES256SHA256Decoder
because it is used to convert password from char to byte for encryption/decryption
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 one comment about the VSCode files. But everything else looks OK to me. I would need to find some spare time now to sit down, read some of the surrounding API in Compress, and then really understand what's going on in order to do a proper review 👍 Thanks!
.vscode/settings.json
Outdated
@@ -0,0 +1,3 @@ | |||
{ | |||
"java.configuration.updateBuildConfiguration": "automatic" | |||
} |
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 file does not need to be added. I believe the repository does not include anything specific to Eclipse/IntelliJ/etc. If you use this file locally, you can add a global .gitignore
to prevent Git from including it with your changes (or just ignore it when committing your changes).
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.
Right, It was a mistake 👍
It seems that there is an issue with |
Just rebase on git master to test with the latest, that will help us get closer ;-) |
Implementation of password-based encryption for 7z compressor COMPRESS-633
As `AES/CBC/PKCS5Padding` is raised as weak of security, a manual implementation to fill cither block size is done COMPRESS-633
- implementation without storing password in a clear way - several corrections suggeested by reviewers COMPRESS-633
typo COMPRESS-633
Avoid incrasing the public API surface with uneccessary method COMPRESS-633
no IDE specifi config files COMPRESS-633
03eaf41
to
c2d1d1d
Compare
private final int numCyclesPower; | ||
private final Cipher cipher; | ||
|
||
/** |
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.
Please complete the Javadoc comments. You need a starting sentence.
@Dougniel |
Thank you for your rigor, all is ok 👍 I noticed that you switched |
@Dougniel |
Hi @garydgregory, do you have an idea of next release date ? |
Nope, we have other components to work through and Commons Net just had a release. |
👉🏼 The purpose is to provide password based encryption for 7z compression in the same way of decryption already supported, so go forward of one know limitation
☝️In this way, I would like to submit my contribution based on the existing implementation of decryption and the C++ implementation of 7z
✅ I added one unit test
Implementation of password-based encryption for 7z compressor
Let me know what do you think about this implementation 👍
https://issues.apache.org/jira/browse/COMPRESS-633