On Mon, 22 Mar 2021 21:43:31 GMT, Valerie Peng <valer...@openjdk.org> 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