-
Notifications
You must be signed in to change notification settings - Fork 112
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 P11Cipher AES/ECB/PKCS5Padding array index out of range issue #576
base: openj9
Are you sure you want to change the base?
Conversation
@alon-sh pls review |
Pls add an IBM copyright to the modified file. |
src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Cipher.java
Outdated
Show resolved
Hide resolved
@alon-sh Can you please share your opinions on this? |
@taoliult i dont understand this fix - if you say the padding buffer is written to 17 times and u changed it so its 16 times. how come was it not failing when trying to buffer 17 times due to the padding buffer being only 16 bytes long? why did it only fail in the final() call? |
@taoliult any update? |
The test case will read each byte from the ByteArrayInputStream, and the ByteArrayInputStream come from a byte array, which size is 3 times of the blockSize, that is 3*16=48 for AES. And then, it calls the CipherOutputStream’s write and flush method. And when running the CipherOutputStream.write(), it will finally call the method "implUpdate(byte[] in, int inOfs, int inLen, byte[] out, int outOfs, int outLen)" in P11Cipher. And in implUpdate() method, for AES/ECB/PKCS5Padding, it will go into the below codes, because there is no native padding, it will use the P11Cipher its own padding implementation.
When call the write method for the first byte, padBufferLen == 0, so it will not fall into the above "if" condition. And when call for the second byte, padBufferLen == 1, then it will fall into the above "if" condition. "inLen" is always 1 because the test code call the write method for each byte. "padBuffer.length" is 16 because the AES block size is 16. "padBufferLen" will increase 1 each time when the test code call the write method for each byte. So, it will go from 1 to 16, the 16 times. And when padBufferLen is 16, bufCapacity will be 16-16=0, then the bytes in buffer will be processed and the padBufferLen will be set to 0 again. "bufCapacity" will from 16-1=15 to 16-16=0, totally 16 times. And why 16 times, because if (inLen > bufCapacity), and inLen is alway 1 in this test case, bufCapacity will from 15 to 0, 16 times. And then, 1 > 0 is true, so the byte will not be copy into the buffer array, instead, those 16 bytes will be processed. And, before go into the "if" condition, there is one byte be processed when padBufferLen == 0. So, if there are 17 bytes, all of these 17 bytes will be processed. But if there are 16 bytes, then only the first byte will be processed, all the other 15 bytes will be copy into a buffer array without be processed. And at this time, because the padBufferLen will increase 1 after the above codes, so after the last byte copy into the buffer array, the padBufferLen will become 16. And for the error “ArrayIndexOutOfBoundsException: Array index out of range: 32”, it happens when call the setPaddingBytes() method from line 808 to line 95, codes below.
The padBuffer length is 16, startOff is 16, and requiredOutLen - bytesBuffered is 16. And there is an out of range check in the Arrays.fill() method, so if the startOff + requiredOutLen - bytesBuffered > padBuffer length, that mean 16 + 16 > 16 is true, then the out of range error happens. And why startOff is 16, because “startOff = padBufferLen;” and padBufferLen is 16 when input byte array size is 16. So, my understanding, when AES block size is 16, that mean, the 16 or the multiple of 16 can be processed. But in the current code, 17 bytes can be processed but not 16. @alon-sh please advice. |
fcdcb26
to
ca0d683
Compare
Once we agree on a solution here, we will want equivalent changes for |
@@ -604,7 +611,7 @@ private int implUpdate(byte[] in, int inOfs, int inLen, | |||
if (padBufferLen != 0) { | |||
if (padBufferLen != padBuffer.length) { | |||
int bufCapacity = padBuffer.length - padBufferLen; | |||
if (inLen > bufCapacity) { | |||
if (inLen >= bufCapacity) { |
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.
If I understand your explanation of the problem, we have the same issue on line 715 in implUpdate(ByteBuffer inBuffer, ByteBuffer outBuffer)
.
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.
@keithc-ca Working on it, will update shortly.
Convert this PR back to draft, since need more time to investigate. |
Signed-off-by: Tao Liu <[email protected]>
Fix the issue https://github.ibm.com/runtimes/backlog/issues/853, when running CipherOutputStream tests in FIPS mode.
This issue happens when using Cipher Algorithm AES as parameter to initialize the P11Cipher, it will by default, initialize the P11Cipher with "AES/ECB/PKCS5Padding”, which failed due to ArrayIndexOutOfBoundsException.
In P11Cipher method “implUpdate()”, when there is no native padding support, like for AES-ECB, it will then go into its own padding implementation. And because the method is called for each byte from testing code ByteArrayInputStream for AES-ECB, so the “inLen = 1”, “padBuffer.length = 16” and “padBufferLen” start from 0 and increase by 1 when each implUpdate() invoke due to the “inLen = 1”. So, “bufCapacity” will from 16-1=15 to 16-16=0, for 16 times loop. And in the problem code, there is one more time when the “padBufferLen == 0”, so totally, there will be 16+1=17 times, which is wrong and cause the issue. For block size 16, there should be multiple of 16 times calls in the below part of codes.
So, I change the “ if (inLen > bufCapacity)” to “ if (inLen >= bufCapacity)”, the in loop times change to 15, then the totally calls change to 16 for each block size 16 for AES.
It works for the failed test cases. And I also tried using the byte[] directly instead of the ByteArrayInputStream, it also works in the test case. Following are the Grinder tests results after the code fix.
https://hyc-runtimes-jenkins.swg-devops.com/view/Test_grinder/job/Grinder/27352/
https://hyc-runtimes-jenkins.swg-devops.com/view/Test_grinder/job/Grinder/27353/
https://hyc-runtimes-jenkins.swg-devops.com/view/Test_grinder/job/Grinder/27455/
https://hyc-runtimes-jenkins.swg-devops.com/view/Test_grinder/job/Grinder/27456/
https://hyc-runtimes-jenkins.swg-devops.com/view/Test_grinder/job/Grinder/27457/
And for why in non-FIPS testing, we did not found this issue, that is because in the non-FIPS testing, SunJCE provider will be used instead of the SunPKCS11.
Signed-off-by: Tao Liu [email protected]