-
Notifications
You must be signed in to change notification settings - Fork 25.6k
Use readFully() to read exactly the number of bytes we expect from the Stream
#28515
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
Changes from 5 commits
2ea6d2a
8241318
63a4fcd
4cc56cf
13ae59b
248c73d
a31ab4f
e3d79a5
f3274bb
0526e03
f93e9bf
460ee62
e853a6a
3c56cfd
1539793
9b90e9b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,6 +19,7 @@ | |
|
|
||
| package org.elasticsearch.common.settings; | ||
|
|
||
|
|
||
| import javax.crypto.Cipher; | ||
| import javax.crypto.CipherInputStream; | ||
| import javax.crypto.CipherOutputStream; | ||
|
|
@@ -31,6 +32,7 @@ | |
| import java.io.ByteArrayOutputStream; | ||
| import java.io.DataInputStream; | ||
| import java.io.DataOutputStream; | ||
| import java.io.EOFException; | ||
| import java.io.IOException; | ||
| import java.io.InputStream; | ||
| import java.nio.ByteBuffer; | ||
|
|
@@ -317,38 +319,33 @@ public void decrypt(char[] password) throws GeneralSecurityException, IOExceptio | |
| DataInputStream input = new DataInputStream(bytesStream)) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should add a test that would have failed using the old method. This would probably require a small refactoring to do this; I am thinking instead of
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think we need to override any methods. We can form streams of bytes in whatever order we want in tests. I do think we should have a test to ensure there cannot be garbage after the encrypted data.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Revisiting this after coming back.
Care to elaborate @rjernst ? You mean attempting to
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I mean adding a test with garbage at the end, so that we know the last readFully logic works. The intermediate readFully's are harder, as I described above.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
If I understand correctly, we should fail if there is garbage right? Currently this is not the case at least based on the test @jkakavas added.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That was not my intention. I meant it the way @jaymode suggested: we should fail when trailing garbage is present, and have a test for that. |
||
| int saltLen = input.readInt(); | ||
| salt = new byte[saltLen]; | ||
| if (input.read(salt) != saltLen) { | ||
| throw new SecurityException("Keystore has been corrupted or tampered with"); | ||
| } | ||
| input.readFully(salt); | ||
| int ivLen = input.readInt(); | ||
| iv = new byte[ivLen]; | ||
| if (input.read(iv) != ivLen) { | ||
| throw new SecurityException("Keystore has been corrupted or tampered with"); | ||
| } | ||
| input.readFully(iv); | ||
| int encryptedLen = input.readInt(); | ||
| encryptedBytes = new byte[encryptedLen]; | ||
| if (input.read(encryptedBytes) != encryptedLen) { | ||
| throw new SecurityException("Keystore has been corrupted or tampered with"); | ||
| } | ||
| input.readFully(encryptedBytes); | ||
| } catch (EOFException e) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. While the exception message was the same in all cases before, we still would have been able to tell where the failed read happened (ie where in the bytes the corrupt/tamped data occurs). Are we sure we want to drop the root cause exception here? I think that at least warrants a comment here as to why.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're right, any of the readX can throw an EOFException and I would have masked it. We should know where the failure occurred. I passed the original exception as a cause to SecurityException to fix that |
||
| throw new SecurityException("Keystore has been corrupted or tampered with"); | ||
| } | ||
|
|
||
| Cipher cipher = createCipher(Cipher.DECRYPT_MODE, password, salt, iv); | ||
| try (ByteArrayInputStream bytesStream = new ByteArrayInputStream(encryptedBytes); | ||
| CipherInputStream cipherStream = new CipherInputStream(bytesStream, cipher); | ||
| DataInputStream input = new DataInputStream(cipherStream)) { | ||
|
|
||
| entries.set(new HashMap<>()); | ||
| int numEntries = input.readInt(); | ||
| while (numEntries-- > 0) { | ||
| String setting = input.readUTF(); | ||
| EntryType entryType = EntryType.valueOf(input.readUTF()); | ||
| int entrySize = input.readInt(); | ||
| byte[] entryBytes = new byte[entrySize]; | ||
| if (input.read(entryBytes) != entrySize) { | ||
| throw new SecurityException("Keystore has been corrupted or tampered with"); | ||
| } | ||
| input.readFully(entryBytes); | ||
| entries.get().put(setting, new Entry(entryType, entryBytes)); | ||
| } | ||
| } catch (EOFException e) { | ||
| throw new SecurityException("Keystore has been corrupted or tampered with"); | ||
|
||
| } | ||
| } | ||
|
|
||
|
|
@@ -360,7 +357,6 @@ private byte[] encrypt(char[] password, byte[] salt, byte[] iv) throws GeneralSe | |
| Cipher cipher = createCipher(Cipher.ENCRYPT_MODE, password, salt, iv); | ||
| try (CipherOutputStream cipherStream = new CipherOutputStream(bytes, cipher); | ||
| DataOutputStream output = new DataOutputStream(cipherStream)) { | ||
|
|
||
| output.writeInt(entries.get().size()); | ||
| for (Map.Entry<String, Entry> mapEntry : entries.get().entrySet()) { | ||
| output.writeUTF(mapEntry.getKey()); | ||
|
|
@@ -370,7 +366,6 @@ private byte[] encrypt(char[] password, byte[] salt, byte[] iv) throws GeneralSe | |
| output.write(entry.bytes); | ||
| } | ||
| } | ||
|
|
||
| return bytes.toByteArray(); | ||
| } | ||
|
|
||
|
|
||
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: unnecessary change?