On Mon, 22 Mar 2021 21:43:31 GMT, Valerie Peng <[email protected]> wrote:
>> This change updates SunJCE provider as below:
>> - updated existing AESWrap support with AES/KW/NoPadding cipher
>> transformation.
>> - added support for AES/KWP/NoPadding and AES/KW/PKCS5Padding.
>>
>> Existing AESWrap impl, i.e. AESWrapCipher class, is re-factored and renamed
>> to KeyWrapCipher class. The W and W_inverse functions are moved to KWUtil
>> class. The KW and KWP support are in the new AESKeyWrap and AESKeyWrapPadded
>> classes which extend FeedbackCipher and used in KeyWrapCipher class. To
>> minimize data copying, AESKeyWrap and AESKeyWrapPadded will do the crypto
>> operation over the same input buffer which is allocated and managed by
>> KeyWrapCipher class.
>>
>> Also note that existing AESWrap impl does not take IV. However, the
>> corresponding PKCS#11 mechanisms do, so I added support for accepting IVs to
>> both KW and KWP.
>>
>> Thanks,
>> Valerie
>
> Valerie Peng has updated the pull request incrementally with one additional
> commit since the last revision:
>
> Changed AlgorithmParameters impls to register under AES/KW/NoPadding and
> AES/KWP/NoPadding
src/java.base/share/classes/com/sun/crypto/provider/AESKeyWrap.java line 41:
> 39: * and represents AES cipher in KW mode.
> 40: */
> 41: class AESKeyWrap extends FeedbackCipher {
I see lots of unsupported operations and `encrypt/decryptFinal` ignores their
output parameters. Should we be extending `FeedbackCipher` if so much doesn't
seem to quite fit?
src/java.base/share/classes/com/sun/crypto/provider/AESKeyWrap.java line 155:
> 153: " be at least 16 bytes and multiples of 8");
> 154: }
> 155: // assert ptOfs == 0; ct == pt; ctOfs == 0;
Is this a missing code assertion?
src/java.base/share/classes/com/sun/crypto/provider/AESKeyWrap.java line 193:
> 191: if (!Arrays.equals(ivOut, 0, ICV1.length, this.iv, 0,
> ICV1.length)) {
> 192: throw new IllegalBlockSizeException("Integrity check
> failed");
> 193: }
It feels like an odd asymmetry that we validate the IV upon decryption in
`AESKeyWrap` but the IV is prepended in `KeyWrapCipher.writeIvSemiBlock`. I'm
worried that by separating this logic like this it is easier for us to get it
wrong (or break it in the future).
src/java.base/share/classes/com/sun/crypto/provider/AESKeyWrapPadded.java line
69:
> 67: if (!Arrays.equals(ivAndLen, 0, ICV2.length, icv, 0,
> ICV2.length)) {
> 68: throw new IllegalBlockSizeException("Integrity check failed");
> 69: }
While I cannot find any public discussion of this, I'm always uncomfortable
checking the plaintext (prior to authentication) against a known value in
non-constant time. I'm worried that this (and the equivalent in the unpadded
version) might be a problem in the future.
src/java.base/share/classes/com/sun/crypto/provider/AESKeyWrapPadded.java line
246:
> 244: int outLen = validateIV(ivAndLen, this.iv);
> 245: // check padding bytes
> 246: int padLen = ctLen - outLen;
Can we add an explicit check that `0 <= padLen < SEMI_BLKSIZE`?
src/java.base/share/classes/com/sun/crypto/provider/KWUtil.java line 87:
> 85: */
> 86: static final void W_INV(byte[] in, int inLen, byte[] ivOut,
> 87: SymmetricCipher cipher) {
The asymmetry between `W` not taking an IV but `W_INV` returning an IV also
bothers me and seems to make this harder to use safely.
src/java.base/share/classes/com/sun/crypto/provider/KeyWrapCipher.java line 507:
> 505: } else {
> 506: outLen = cipher.encryptFinal(dataBuf, 0, dataIdx, null,
> -1);
> 507: }
Can we extract this shared logic (among the different versions of
`engineDoFinal`) into a separate helper method so that the different
`engineDoFinal` methods just need to handle their specific differences (such as
returning `Arrays.copyOf(dataBuf, outLen)` or calling
`System.arraycopy(dataBuf, 0, out, outOfs, outLen); return outLen;`).
src/java.base/share/classes/com/sun/crypto/provider/KeyWrapCipher.java line 660:
> 658: @Override
> 659: protected byte[] engineWrap(Key key)
> 660: throws IllegalBlockSizeException, InvalidKeyException {
Is it okay that we can call `cipher.init(Cipher.ENCRYPT_MODE, ...)` and then
use it with `cipher.wrap()`?
Also, can we simply delegate the heavy lifting to `engineDoFinal()` (or the new
suggested helper method) rather than duplicating this logic here?
test/jdk/com/sun/crypto/provider/Cipher/KeyWrap/NISTWrapKAT.java line 105:
> 103:
> 104: @DataProvider
> 105: public Object[][] testData() {
Can we please add test cases for `AES/KWP/NoPadding` where the input is an even
multiple of 8? We've [seen bugs in this case
before](https://github.com/pyca/cryptography/issues/4156).
test/jdk/com/sun/crypto/provider/Cipher/KeyWrap/TestGeneral.java line 50:
> 48:
> 49: System.out.println("input len: " + inLen);
> 50: c.init(Cipher.ENCRYPT_MODE, KEY, new IvParameterSpec(in, 0,
> ivLen));
Do we have tests for no IV (and thus the default)?
Do we have tests that the null (default) IV matches the results from an
explicitly provided default ID?
test/jdk/com/sun/crypto/provider/Cipher/KeyWrap/TestGeneral.java line 179:
> 177: System.out.println("Testing " + ALGO);
> 178: c = Cipher.getInstance(ALGO, "SunJCE");
> 179: for (int i = 0; i < MAX_KWP_PAD_LEN; i++) {
I see that here (and earlier) we do test all padding lengths. I'd still like
some KATs generated by a known good implementation to ensure that we are not
just compatible with ourselves.
-------------
PR: https://git.openjdk.java.net/jdk/pull/2404