Skip to content

Commit 1a5a2b6

Browse files
author
Valerie Peng
committedSep 1, 2021
8271745: Correct block size for KW,KWP mode and use fixed IV for KWP mode for SunJCE
Reviewed-by: xuelei, mullan
1 parent 2f01a6f commit 1a5a2b6

File tree

3 files changed

+213
-150
lines changed

3 files changed

+213
-150
lines changed
 

‎src/java.base/share/classes/com/sun/crypto/provider/AESKeyWrapPadded.java

+7-4
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
package com.sun.crypto.provider;
2727

2828
import java.util.Arrays;
29+
import java.util.HexFormat;
2930
import java.security.*;
3031
import java.security.spec.*;
3132
import javax.crypto.*;
@@ -132,12 +133,14 @@ void init(boolean decrypting, String algorithm, byte[] key, byte[] iv)
132133
if (key == null) {
133134
throw new InvalidKeyException("Invalid null key");
134135
}
135-
if (iv != null && iv.length != ICV2.length) {
136-
throw new InvalidAlgorithmParameterException("Invalid IV length");
136+
// allow setting an iv but if non-null, must equal to ICV2
137+
if (iv != null && !Arrays.equals(iv, ICV2)) {
138+
HexFormat hf = HexFormat.of().withUpperCase();
139+
throw new InvalidAlgorithmParameterException("Invalid IV, got 0x" +
140+
hf.formatHex(iv) + " instead of 0x" + hf.formatHex(ICV2));
137141
}
138142
embeddedCipher.init(decrypting, algorithm, key);
139-
// iv is retrieved from IvParameterSpec.getIV() which is already cloned
140-
this.iv = (iv == null? ICV2 : iv);
143+
this.iv = ICV2;
141144
}
142145

143146
/**

‎src/java.base/share/classes/com/sun/crypto/provider/KeyWrapCipher.java

+6-8
Original file line numberDiff line numberDiff line change
@@ -70,28 +70,28 @@ public AES256_KW_NoPadding() {
7070
// for AES/KW/NoPadding
7171
public static final class AES_KW_PKCS5Padding extends KeyWrapCipher {
7272
public AES_KW_PKCS5Padding() {
73-
super(new AESKeyWrap(), new PKCS5Padding(16), -1);
73+
super(new AESKeyWrap(), new PKCS5Padding(8), -1);
7474
}
7575
}
7676

7777
// for AES_128/KW/NoPadding
7878
public static final class AES128_KW_PKCS5Padding extends KeyWrapCipher {
7979
public AES128_KW_PKCS5Padding() {
80-
super(new AESKeyWrap(), new PKCS5Padding(16), 16);
80+
super(new AESKeyWrap(), new PKCS5Padding(8), 16);
8181
}
8282
}
8383

8484
// for AES_192/KW/NoPadding
8585
public static final class AES192_KW_PKCS5Padding extends KeyWrapCipher {
8686
public AES192_KW_PKCS5Padding() {
87-
super(new AESKeyWrap(), new PKCS5Padding(16), 24);
87+
super(new AESKeyWrap(), new PKCS5Padding(8), 24);
8888
}
8989
}
9090

9191
// for AES_256/KW/NoPadding
9292
public static final class AES256_KW_PKCS5Padding extends KeyWrapCipher {
9393
public AES256_KW_PKCS5Padding() {
94-
super(new AESKeyWrap(), new PKCS5Padding(16), 32);
94+
super(new AESKeyWrap(), new PKCS5Padding(8), 32);
9595
}
9696
}
9797

@@ -230,13 +230,11 @@ protected void engineSetPadding(String padding)
230230
}
231231

232232
/**
233-
* Returns the block size (in bytes). i.e. 16 bytes.
234-
*
235-
* @return the block size (in bytes), i.e. 16 bytes.
233+
* @return the block size (in bytes)
236234
*/
237235
@Override
238236
protected int engineGetBlockSize() {
239-
return cipher.getBlockSize();
237+
return 8;
240238
}
241239

242240
/**

‎test/jdk/com/sun/crypto/provider/Cipher/KeyWrap/TestGeneral.java

+200-138
Original file line numberDiff line numberDiff line change
@@ -23,188 +23,250 @@
2323

2424
/*
2525
* @test
26-
* @bug 8248268 8268621
26+
* @bug 8248268 8268621 8271745
2727
* @summary Verify general properties of the AES/KW/NoPadding,
28-
* AES/KW/PKCS5Padding, and AES/KWP/NoPadding.
28+
* AES/KW/PKCS5Padding, and AES/KWP/NoPadding impls of SunJCE provider.
2929
* @run main TestGeneral
3030
*/
31+
import java.nio.ByteBuffer;
3132
import java.util.Arrays;
3233
import java.security.Key;
34+
import java.security.KeyPairGenerator;
35+
import java.security.PrivateKey;
3336
import java.security.InvalidAlgorithmParameterException;
37+
import java.security.AlgorithmParameters;
3438
import javax.crypto.*;
3539
import javax.crypto.spec.*;
3640

3741
public class TestGeneral {
3842

39-
private static final byte[] DATA_128 =
40-
Arrays.copyOf("1234567890123456789012345678901234".getBytes(), 128);
43+
private static final byte[] DATA_32 =
44+
Arrays.copyOf("1234567890123456789012345678901234".getBytes(), 32);
4145
private static final SecretKey KEY =
42-
new SecretKeySpec(DATA_128, 0, 16, "AES");
46+
new SecretKeySpec(DATA_32, 0, 16, "AES");
4347
private static final int KW_IV_LEN = 8;
4448
private static final int KWP_IV_LEN = 4;
45-
private static final int MAX_KW_PKCS5PAD_LEN = 16; // 1-16
46-
private static final int MAX_KWP_PAD_LEN = 7; // 0...7
47-
48-
public static void testEnc(Cipher c, byte[] in, int inLen, int ivLen,
49-
int maxPadLen) throws Exception {
50-
51-
System.out.println("input len: " + inLen);
52-
c.init(Cipher.ENCRYPT_MODE, KEY, new IvParameterSpec(in, 0, ivLen));
53-
54-
int estOutLen = c.getOutputSize(inLen);
55-
56-
byte[] out = c.doFinal(in, 0, inLen);
57-
58-
// for encryption output, the estimate should match the actual
59-
if (estOutLen != out.length) {
60-
System.out.println("=> estimated: " + estOutLen);
61-
System.out.println("=> actual enc out length: " + out.length);
62-
throw new RuntimeException("Failed enc output len check");
63-
}
64-
65-
// encryption outout should always be multiple of 8 and at least 8-byte
66-
// longer than input
67-
if ((out.length % 8 != 0) || (out.length - inLen < 8)) {
68-
throw new RuntimeException("Invalid length of encrypted data: " +
69-
out.length);
70-
}
71-
72-
c.init(Cipher.DECRYPT_MODE, KEY, new IvParameterSpec(in, 0, ivLen));
73-
estOutLen = c.getOutputSize(out.length);
74-
75-
byte[] in2 = c.doFinal(out);
76-
77-
// for decryption output, the estimate should match the actual for
78-
// AES/KW/NoPadding and slightly larger than the actual for the rest
79-
if (estOutLen < in2.length || (estOutLen - in2.length) > maxPadLen) {
80-
System.out.println("=> estimated: " + estOutLen);
81-
System.out.println("=> actual dec out length: " + in2.length);
82-
throw new RuntimeException("Failed dec output len check");
83-
}
84-
85-
if (!Arrays.equals(in, 0, inLen, in2, 0, inLen)) {
86-
throw new RuntimeException("Failed decrypted data check");
49+
private static final int MAX_KW_PKCS5PAD_LEN = 8; // 1-8
50+
private static final int MAX_KWP_PAD_LEN = 7; // 0-7
51+
52+
public static void testEnc(Cipher c, byte[] in, int startLen, int inc,
53+
IvParameterSpec[] ivs, int maxPadLen) throws Exception {
54+
55+
System.out.println("testEnc, input len=" + startLen + " w/ inc=" +
56+
inc);
57+
58+
for (IvParameterSpec iv : ivs) {
59+
System.out.print("\t=> w/ iv=" + iv);
60+
61+
for (int inLen = startLen; inLen < in.length; inLen+=inc) {
62+
c.init(Cipher.ENCRYPT_MODE, KEY, iv);
63+
64+
int estOutLen = c.getOutputSize(inLen);
65+
System.out.println(", inLen=" + inLen);
66+
byte[] out = c.doFinal(in, 0, inLen);
67+
68+
// check the length of encryption output
69+
if (estOutLen != out.length || (out.length % 8 != 0) ||
70+
(out.length - inLen < 8)) {
71+
System.out.println("=> estimated: " + estOutLen);
72+
System.out.println("=> actual: " + out.length);
73+
throw new RuntimeException("Failed enc output len check");
74+
}
75+
76+
c.init(Cipher.DECRYPT_MODE, KEY, iv);
77+
estOutLen = c.getOutputSize(out.length);
78+
byte[] recovered = new byte[estOutLen];
79+
80+
// do decryption using ByteBuffer and multi-part
81+
ByteBuffer outBB = ByteBuffer.wrap(out);
82+
ByteBuffer recoveredBB = ByteBuffer.wrap(recovered);
83+
int len = c.update(outBB, recoveredBB);
84+
len += c.doFinal(outBB, recoveredBB);
85+
86+
// check the length of decryption output
87+
if (estOutLen < len || (estOutLen - len) > maxPadLen) {
88+
System.out.println("=> estimated: " + estOutLen);
89+
System.out.println("=> actual: " + len);
90+
throw new RuntimeException("Failed dec output len check");
91+
}
92+
93+
if (!Arrays.equals(in, 0, inLen, recovered, 0, len)) {
94+
throw new RuntimeException("Failed decrypted data check");
95+
}
96+
}
8797
}
8898
}
8999

90-
public static void testWrap(Cipher c, byte[] in, int inLen, int ivLen,
100+
public static void testWrap(Cipher c, Key[] inKeys, IvParameterSpec[] ivs,
91101
int maxPadLen) throws Exception {
92102

93-
System.out.println("key len: " + inLen);
94-
c.init(Cipher.WRAP_MODE, KEY, new IvParameterSpec(in, 0, ivLen));
95-
96-
int estOutLen = c.getOutputSize(inLen);
97-
98-
byte[] out = c.wrap(new SecretKeySpec(in, 0, inLen, "Any"));
99-
100-
// for encryption output, the estimate should match the actual
101-
if (estOutLen != out.length) {
102-
System.out.println("=> estimated: " + estOutLen);
103-
System.out.println("=> actual wrap out length: " + out.length);
104-
throw new RuntimeException("Failed wrap output len check");
105-
}
106-
107-
// encryption outout should always be multiple of 8 and at least 8-byte
108-
// longer than input
109-
if ((out.length % 8 != 0) || (out.length - inLen < 8)) {
110-
throw new RuntimeException("Invalid length of encrypted data: " +
111-
out.length);
112-
}
113-
c.init(Cipher.UNWRAP_MODE, KEY, new IvParameterSpec(in, 0, ivLen));
114-
estOutLen = c.getOutputSize(out.length);
115-
116-
Key key2 = c.unwrap(out, "Any", Cipher.SECRET_KEY);
117-
118-
if (!(key2 instanceof SecretKey)) {
119-
throw new RuntimeException("Failed unwrap output type check");
103+
for (Key inKey : inKeys) {
104+
System.out.println("testWrap, key: " + inKey);
105+
for (IvParameterSpec iv : ivs) {
106+
System.out.println("\t=> w/ iv " + iv);
107+
108+
c.init(Cipher.WRAP_MODE, KEY, iv);
109+
110+
byte[] out = c.wrap(inKey);
111+
112+
// output should always be multiple of cipher block size
113+
if (out.length % c.getBlockSize() != 0) {
114+
throw new RuntimeException("Invalid wrap len: " +
115+
out.length);
116+
}
117+
118+
c.init(Cipher.UNWRAP_MODE, KEY, iv);
119+
120+
// SecretKey or PrivateKey
121+
int keyType = (inKey instanceof SecretKey? Cipher.SECRET_KEY :
122+
Cipher.PRIVATE_KEY);
123+
124+
int estOutLen = c.getOutputSize(out.length);
125+
Key key2 = c.unwrap(out, inKey.getAlgorithm(), keyType);
126+
127+
if ((keyType == Cipher.SECRET_KEY &&
128+
!(key2 instanceof SecretKey)) ||
129+
(keyType == Cipher.PRIVATE_KEY &&
130+
!(key2 instanceof PrivateKey))) {
131+
throw new RuntimeException("Failed unwrap type check");
132+
}
133+
134+
byte[] in2 = key2.getEncoded();
135+
// check decryption output length
136+
if (estOutLen < in2.length ||
137+
(estOutLen - in2.length) > maxPadLen) {
138+
System.out.println("=> estimated: " + estOutLen);
139+
System.out.println("=> actual: " + in2.length);
140+
throw new RuntimeException("Failed unwrap len check");
141+
}
142+
143+
if (!Arrays.equals(inKey.getEncoded(), in2) ||
144+
!(inKey.getAlgorithm().equalsIgnoreCase
145+
(key2.getAlgorithm()))) {
146+
throw new RuntimeException("Failed unwrap key check");
147+
}
148+
}
120149
}
150+
}
121151

122-
byte[] in2 = key2.getEncoded();
123-
// for decryption output, the estimate should match the actual for
124-
// AES/KW/NoPadding and slightly larger than the actual for the rest
125-
if (estOutLen < in2.length || (estOutLen - in2.length) > maxPadLen) {
126-
System.out.println("=> estimated: " + estOutLen);
127-
System.out.println("=> actual unwrap out length: " + in2.length);
128-
throw new RuntimeException("Failed unwrap output len check");
129-
}
152+
public static void testIv(Cipher c, int defIvLen, boolean allowCustomIv)
153+
throws Exception {
130154

131-
if (inLen != in2.length ||
132-
!Arrays.equals(in, 0, inLen, in2, 0, inLen)) {
133-
throw new RuntimeException("Failed unwrap data check");
134-
}
135-
}
155+
System.out.println("testIv: defIvLen = " + defIvLen +
156+
" allowCustomIv = " + allowCustomIv);
136157

137-
public static void testIv(Cipher c) throws Exception {
138158
// get a fresh Cipher instance so we can test iv with pre-init state
139-
Cipher c2 = Cipher.getInstance(c.getAlgorithm(), c.getProvider());
140-
if (c2.getIV() != null) {
159+
c = Cipher.getInstance(c.getAlgorithm(), c.getProvider());
160+
if (c.getIV() != null) {
141161
throw new RuntimeException("Expects null iv");
142162
}
143-
if (c2.getParameters() == null) {
163+
164+
AlgorithmParameters ivParams = c.getParameters();
165+
if (ivParams == null) {
144166
throw new RuntimeException("Expects non-null default parameters");
145167
}
146-
147-
c2.init(Cipher.ENCRYPT_MODE, KEY);
148-
byte[] defIv2 = c2.getIV();
168+
IvParameterSpec ivSpec =
169+
ivParams.getParameterSpec(IvParameterSpec.class);
170+
byte[] iv = ivSpec.getIV();
171+
// try through all opmodes
149172
c.init(Cipher.ENCRYPT_MODE, KEY);
173+
c.init(Cipher.DECRYPT_MODE, KEY);
174+
c.init(Cipher.WRAP_MODE, KEY);
175+
c.init(Cipher.UNWRAP_MODE, KEY);
176+
150177
byte[] defIv = c.getIV();
151-
if (!Arrays.equals(defIv, defIv2)) {
178+
179+
// try again through all opmodes
180+
c.init(Cipher.ENCRYPT_MODE, KEY);
181+
c.init(Cipher.DECRYPT_MODE, KEY);
182+
c.init(Cipher.WRAP_MODE, KEY);
183+
c.init(Cipher.UNWRAP_MODE, KEY);
184+
185+
byte[] defIv2 = c.getIV();
186+
if (iv.length != defIvLen || !Arrays.equals(iv, defIv) ||
187+
!Arrays.equals(defIv, defIv2)) {
152188
throw new RuntimeException("Failed default iv check");
153189
}
190+
if (defIv == defIv2) {
191+
throw new RuntimeException("Failed getIV copy check");
192+
}
193+
154194
// try init w/ an iv w/ invalid length
155195
try {
156196
c.init(Cipher.ENCRYPT_MODE, KEY, new IvParameterSpec(defIv, 0,
157-
defIv.length/2));
197+
defIv.length/2));
158198
throw new RuntimeException("Invalid iv accepted");
159199
} catch (InvalidAlgorithmParameterException iape) {
160200
System.out.println("Invalid IV rejected as expected");
161201
}
162-
Arrays.fill(defIv, (byte) 0xFF);
163-
c.init(Cipher.ENCRYPT_MODE, KEY, new IvParameterSpec(defIv));
164-
byte[] newIv = c.getIV();
165-
if (!Arrays.equals(newIv, defIv)) {
166-
throw new RuntimeException("Failed set iv check");
167-
}
168-
byte[] newIv2 = c.getIV();
169-
if (newIv == newIv2) {
170-
throw new RuntimeException("Failed getIV copy check");
202+
203+
if (allowCustomIv) {
204+
Arrays.fill(defIv, (byte) 0xFF);
205+
// try through all opmodes
206+
c.init(Cipher.ENCRYPT_MODE, KEY, new IvParameterSpec(defIv));
207+
c.init(Cipher.DECRYPT_MODE, KEY, new IvParameterSpec(defIv));
208+
c.init(Cipher.WRAP_MODE, KEY, new IvParameterSpec(defIv));
209+
c.init(Cipher.UNWRAP_MODE, KEY, new IvParameterSpec(defIv));
210+
211+
if (!Arrays.equals(defIv, c.getIV())) {
212+
throw new RuntimeException("Failed set iv check");
213+
}
171214
}
172215
}
173216

174217
public static void main(String[] argv) throws Exception {
175-
byte[] data = DATA_128;
176-
177-
String ALGO = "AES/KW/PKCS5Padding";
178-
System.out.println("Testing " + ALGO);
179-
Cipher c = Cipher.getInstance(ALGO, "SunJCE");
180-
181-
// test all possible pad lengths, i.e. 1 - 16
182-
for (int i = 1; i <= MAX_KW_PKCS5PAD_LEN; i++) {
183-
testEnc(c, data, data.length - i, KW_IV_LEN, MAX_KW_PKCS5PAD_LEN);
184-
testWrap(c, data, data.length - i, KW_IV_LEN, MAX_KW_PKCS5PAD_LEN);
218+
byte[] data = DATA_32;
219+
220+
SecretKey aes256 = new SecretKeySpec(DATA_32, "AES");
221+
SecretKey any256 = new SecretKeySpec(DATA_32, "ANY");
222+
PrivateKey priv = KeyPairGenerator.getInstance
223+
("RSA", "SunRsaSign").generateKeyPair().getPrivate();
224+
225+
String[] algos = {
226+
"AES/KW/PKCS5Padding", "AES/KW/NoPadding", "AES/KWP/NoPadding"
227+
};
228+
229+
for (String a : algos) {
230+
System.out.println("Testing " + a);
231+
Cipher c = Cipher.getInstance(a, "SunJCE");
232+
233+
int blkSize = c.getBlockSize();
234+
235+
// set the default based on AES/KWP/NoPadding, the other two
236+
// override as needed
237+
int startLen = data.length - blkSize;
238+
int inc = 1;
239+
IvParameterSpec[] ivs = new IvParameterSpec[] { null };
240+
int padLen = MAX_KWP_PAD_LEN;
241+
Key[] keys = new Key[] { aes256, any256, priv };
242+
int ivLen = KWP_IV_LEN;
243+
boolean allowCustomIv = false;
244+
245+
switch (a) {
246+
case "AES/KW/PKCS5Padding":
247+
ivs = new IvParameterSpec[] {
248+
null, new IvParameterSpec(DATA_32, 0, KW_IV_LEN) };
249+
padLen = MAX_KW_PKCS5PAD_LEN;
250+
ivLen = KW_IV_LEN;
251+
allowCustomIv = true;
252+
break;
253+
case "AES/KW/NoPadding":
254+
startLen = data.length >> 1;
255+
inc = blkSize;
256+
ivs = new IvParameterSpec[] {
257+
null, new IvParameterSpec(DATA_32, 0, KW_IV_LEN) };
258+
padLen = 0;
259+
keys = new Key[] { aes256, any256 };
260+
ivLen = KW_IV_LEN;
261+
allowCustomIv = true;
262+
break;
263+
}
264+
265+
// now test based on the configured arguments
266+
testEnc(c, data, startLen, inc, ivs, padLen);
267+
testWrap(c, keys, ivs, padLen);
268+
testIv(c, ivLen, allowCustomIv);
185269
}
186-
testIv(c);
187-
188-
ALGO = "AES/KW/NoPadding";
189-
System.out.println("Testing " + ALGO);
190-
c = Cipher.getInstance(ALGO, "SunJCE");
191-
testEnc(c, data, data.length, KW_IV_LEN, 0);
192-
testEnc(c, data, data.length >> 1, KW_IV_LEN, 0);
193-
testWrap(c, data, data.length, KW_IV_LEN, 0);
194-
testWrap(c, data, data.length >> 1, KW_IV_LEN, 0);
195-
testIv(c);
196-
197-
ALGO = "AES/KWP/NoPadding";
198-
System.out.println("Testing " + ALGO);
199-
c = Cipher.getInstance(ALGO, "SunJCE");
200-
201-
// test all possible pad lengths, i.e. 0 - 7
202-
for (int i = 0; i <= MAX_KWP_PAD_LEN; i++) {
203-
testEnc(c, data, data.length - i, KWP_IV_LEN, MAX_KWP_PAD_LEN);
204-
testWrap(c, data, data.length - i, KWP_IV_LEN, MAX_KWP_PAD_LEN);
205-
}
206-
testIv(c);
207-
208270
System.out.println("All Tests Passed");
209271
}
210272
}

0 commit comments

Comments
 (0)
Please sign in to comment.