-
Notifications
You must be signed in to change notification settings - Fork 123
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
fix: The final frame can not be larger than the Frame Length #166
Conversation
frame header does not exceed the frame size specified in the message header.
@@ -133,6 +133,11 @@ public ProcessingSummary processBytes(final byte[] in, final int off, final int | |||
int protectedContentLen = -1; | |||
if (currentFrameHeaders_.isFinalFrame()) { | |||
protectedContentLen = currentFrameHeaders_.getFrameContentLength(); | |||
|
|||
// The final frame should not be able to exceed the frameLength | |||
if(frameSize_ > 0 && protectedContentLen > frameSize_) { |
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.
frame size can be 0 and the final frame content length can be strictly equal to the frame size.
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'm only throwing the exception if the final frame size is strictly greater than the frame size. If the frame size is 0, then I can't compare the last frame size so I don't throw the exception in that case.
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.
According to
Line 122 in 163fd88
if (frameSize_ == 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.
@SalusaSecondus do you know if there a reason we allow the frame size in the header to be 0 even for framed data?
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 code should never execute if the frame length is 0.
encryption:
aws-encryption-sdk-java/src/main/java/com/amazonaws/encryptionsdk/internal/EncryptionHandler.java
Lines 120 to 146 in 163fd88
ContentType contentType; | |
if (frameSize > 0) { | |
contentType = ContentType.FRAME; | |
} else if (frameSize == 0) { | |
contentType = ContentType.SINGLEBLOCK; | |
} else { | |
throw Utils.cannotBeNegative("Frame size"); | |
} | |
final CiphertextHeaders unsignedHeaders = createCiphertextHeaders(contentType, frameSize); | |
try { | |
encryptionKey_ = cryptoAlgo_.getEncryptionKeyFromDataKey(result.getCleartextDataKey(), unsignedHeaders); | |
} catch (final InvalidKeyException ex) { | |
throw new AwsCryptoException(ex); | |
} | |
ciphertextHeaders_ = signCiphertextHeaders(unsignedHeaders); | |
ciphertextHeaderBytes_ = ciphertextHeaders_.toByteArray(); | |
byte[] messageId_ = ciphertextHeaders_.getMessageId(); | |
switch (contentType) { | |
case FRAME: | |
contentCryptoHandler_ = new FrameEncryptionHandler(encryptionKey_, nonceLen_, cryptoAlgo_, messageId_, | |
frameSize); | |
break; | |
case SINGLEBLOCK: | |
contentCryptoHandler_ = new BlockEncryptionHandler(encryptionKey_, nonceLen_, cryptoAlgo_, messageId_); | |
break; |
decryption:
aws-encryption-sdk-java/src/main/java/com/amazonaws/encryptionsdk/internal/DecryptionHandler.java
Lines 497 to 505 in 163fd88
switch (contentType) { | |
case FRAME: | |
contentCryptoHandler_ = new FrameDecryptionHandler(decryptionKey_, (byte) nonceLen, cryptoAlgo_, | |
messageId, frameLen); | |
break; | |
case SINGLEBLOCK: | |
contentCryptoHandler_ = new BlockDecryptionHandler(decryptionKey_, (byte) nonceLen, cryptoAlgo_, | |
messageId); | |
break; |
The comment on the quoted lines makes me think that this was something that was added in a long time ago, possibly for a very early version of the message format because the frame size is not included in the frame header.
Lines 123 to 124 in 163fd88
// if frame size in ciphertext headers is 0, the frame size | |
// will need to be parsed in individual frame headers. |
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 it possible for the ciphertext to be corrupted/manipulated in some way that leaves the frame size as 0, or would that be caught upstream and cause decryption to fail?
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've added validation to ensure the frame size is not zero if the data is framed
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.
From discussing with @SalusaSecondus, frame size = 0 in the header was intended to indicate a variable length encoding. This feature was not fully implemented and is not documented, so it should be removed, but there are more remnants of it scattered in other parts of the code, so I've opened #167 to track that. For this PR I will only validate the final frame length if the header frame length is > 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.
We also need a check on the encrypt path. Arguably, that's the more important path to cover.
@@ -133,6 +133,11 @@ public ProcessingSummary processBytes(final byte[] in, final int off, final int | |||
int protectedContentLen = -1; | |||
if (currentFrameHeaders_.isFinalFrame()) { | |||
protectedContentLen = currentFrameHeaders_.getFrameContentLength(); | |||
|
|||
// The final frame should not be able to exceed the frameLength | |||
if(frameSize_ > 0 && protectedContentLen > frameSize_) { |
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 code should never execute if the frame length is 0.
encryption:
aws-encryption-sdk-java/src/main/java/com/amazonaws/encryptionsdk/internal/EncryptionHandler.java
Lines 120 to 146 in 163fd88
ContentType contentType; | |
if (frameSize > 0) { | |
contentType = ContentType.FRAME; | |
} else if (frameSize == 0) { | |
contentType = ContentType.SINGLEBLOCK; | |
} else { | |
throw Utils.cannotBeNegative("Frame size"); | |
} | |
final CiphertextHeaders unsignedHeaders = createCiphertextHeaders(contentType, frameSize); | |
try { | |
encryptionKey_ = cryptoAlgo_.getEncryptionKeyFromDataKey(result.getCleartextDataKey(), unsignedHeaders); | |
} catch (final InvalidKeyException ex) { | |
throw new AwsCryptoException(ex); | |
} | |
ciphertextHeaders_ = signCiphertextHeaders(unsignedHeaders); | |
ciphertextHeaderBytes_ = ciphertextHeaders_.toByteArray(); | |
byte[] messageId_ = ciphertextHeaders_.getMessageId(); | |
switch (contentType) { | |
case FRAME: | |
contentCryptoHandler_ = new FrameEncryptionHandler(encryptionKey_, nonceLen_, cryptoAlgo_, messageId_, | |
frameSize); | |
break; | |
case SINGLEBLOCK: | |
contentCryptoHandler_ = new BlockEncryptionHandler(encryptionKey_, nonceLen_, cryptoAlgo_, messageId_); | |
break; |
decryption:
aws-encryption-sdk-java/src/main/java/com/amazonaws/encryptionsdk/internal/DecryptionHandler.java
Lines 497 to 505 in 163fd88
switch (contentType) { | |
case FRAME: | |
contentCryptoHandler_ = new FrameDecryptionHandler(decryptionKey_, (byte) nonceLen, cryptoAlgo_, | |
messageId, frameLen); | |
break; | |
case SINGLEBLOCK: | |
contentCryptoHandler_ = new BlockDecryptionHandler(decryptionKey_, (byte) nonceLen, cryptoAlgo_, | |
messageId); | |
break; |
The comment on the quoted lines makes me think that this was something that was added in a long time ago, possibly for a very early version of the message format because the frame size is not included in the frame header.
Lines 123 to 124 in 163fd88
// if frame size in ciphertext headers is 0, the frame size | |
// will need to be parsed in individual frame headers. |
I thought the decrypt path was needed because we are deserializing data that may have been manipulated or corrupted somehow. The encrypt path is in our control so as long we trust ourselves to write the correct size frame for the final frame I don't see why the check is necessary? |
if (frameSize_ == 0) { | ||
// if frame size in ciphertext headers is 0, the frame size | ||
// will need to be parsed in individual frame headers. | ||
currentFrameHeaders_.includeFrameSize(true); | ||
} |
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 are we removing this logic?
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.
After discussion, I'll revert this part of the change and have created a separate issue to track removing this code (which was a partially implemented variable frame length feature): #167
src/main/java/com/amazonaws/encryptionsdk/model/CiphertextHeaders.java
Outdated
Show resolved
Hide resolved
src/main/java/com/amazonaws/encryptionsdk/model/CiphertextHeaders.java
Outdated
Show resolved
Hide resolved
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
Will address frame lenghth = 0 in #167
* Add a basic example for encrypting and decrypting with a KMS CMK (#136) * *Issue #, if available:* #108 *Description of changes:* Add a basic example for encrypting and decrypting with a KMS CMK. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. # Check any applicable: - [ ] Were any files moved? Moving files changes their URL, which breaks all hyperlinks to the files. * Add test and Maven plugin to include examples directory as test source * Update docs in prep for 1.6.1 (#133) * Update docs in prep for 1.6.1 * Actually bump version for release * Fix for new versions of gpg * Refactor JceMasterKey to extract logic to be shared by raw keyrings. (#139) * Refactor JceMasterKey to extract logic to be shared by raw keyrings. *Issue #, if available:* #102 *Description of changes:* In anticipation of the RawAesKeyring and RawRsaKeyring needing logic currently embedded in the JceMasterKey, this change extracts that logic into the JceKeyCipher class so it may be shared. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. - [ ] Were any files moved? Moving files changes their URL, which breaks all hyperlinks to the files. * fix: The final frame can not be larger than the Frame Length (#166) * Add validation to ensure the length of the final frame in the final frame header does not exceed the frame size specified in the message header. * Validate that frame length is positive for framed data * Reverting removal of variable frame length code * Reverting removal of variable frame length code * Fix spacing after if Co-authored-by: SalusaSecondus <[email protected]> Co-authored-by: Greg Rubin <[email protected]>
Add validation to ensure the length of the final frame in the final frame header does not exceed the frame size specified in the message header.
See aws/aws-encryption-sdk-javascript#281
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
Check any applicable: