1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package libcore.javax.crypto;
18
19import java.security.spec.InvalidKeySpecException;
20import java.security.spec.KeySpec;
21import java.util.Arrays;
22import javax.crypto.SecretKey;
23import javax.crypto.SecretKeyFactory;
24import javax.crypto.spec.PBEKeySpec;
25import junit.framework.TestCase;
26
27public class SecretKeyFactoryTest extends TestCase {
28
29    private static final char[] PASSWORD = "google".toCharArray();
30    /**
31     * Salts should be random to reduce effectiveness of dictionary
32     * attacks, but need not be kept secret from attackers. For more
33     * information, see http://en.wikipedia.org/wiki/Salt_(cryptography)
34     */
35    private static final byte[] SALT = {0, 1, 2, 3, 4, 5, 6, 7};
36    /**
37     * The number of iterations should be higher for production
38     * strength protection. The tolerable value may vary from device
39     * to device, but 8192 should be acceptable for PBKDF2 on a Nexus One.
40     */
41    private static final int ITERATIONS = 1024;
42    private static final int KEY_LENGTH = 128;
43
44    public void test_PBKDF2_required_parameters() throws Exception {
45        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
46
47        // PBEKeySpec validates arguments most to be non-null, non-empty, postive, etc.
48        // Focus on insufficient PBEKeySpecs
49
50        // PBEKeySpecs password only constructor
51        try {
52            KeySpec ks = new PBEKeySpec(null);
53            factory.generateSecret(ks);
54            fail();
55        } catch (InvalidKeySpecException expected) {
56        }
57        try {
58            KeySpec ks = new PBEKeySpec(new char[0]);
59            factory.generateSecret(ks);
60            fail();
61        } catch (InvalidKeySpecException expected) {
62        }
63        try {
64            KeySpec ks = new PBEKeySpec(PASSWORD);
65            factory.generateSecret(ks);
66            fail();
67        } catch (InvalidKeySpecException expected) {
68        }
69
70
71        // PBEKeySpecs constructor without key length
72        try {
73            KeySpec ks = new PBEKeySpec(null, SALT, ITERATIONS);
74            factory.generateSecret(ks);
75            fail();
76        } catch (InvalidKeySpecException expected) {
77        }
78        try {
79            KeySpec ks = new PBEKeySpec(new char[0], SALT, ITERATIONS);
80            factory.generateSecret(ks);
81            fail();
82        } catch (InvalidKeySpecException expected) {
83        }
84        try {
85            KeySpec ks = new PBEKeySpec(PASSWORD, SALT, ITERATIONS);
86            factory.generateSecret(ks);
87            fail();
88        } catch (InvalidKeySpecException expected) {
89        }
90
91        try {
92            KeySpec ks = new PBEKeySpec(null, SALT, ITERATIONS, KEY_LENGTH);
93            factory.generateSecret(ks);
94            fail();
95        } catch (IllegalArgumentException expected) {
96        }
97
98        try {
99            KeySpec ks = new PBEKeySpec(new char[0], SALT, ITERATIONS, KEY_LENGTH);
100            factory.generateSecret(ks);
101            fail();
102        } catch (IllegalArgumentException expected) {
103        }
104
105        KeySpec ks = new PBEKeySpec(PASSWORD, SALT, ITERATIONS, KEY_LENGTH);
106        factory.generateSecret(ks);
107    }
108
109    public void test_PBKDF2_b3059950() throws Exception {
110        test_PBKDF2(PASSWORD, SALT, ITERATIONS, KEY_LENGTH,
111                    new byte[] {
112                        (byte)0x70, (byte)0x74, (byte)0xdb, (byte)0x72,
113                        (byte)0x35, (byte)0xd4, (byte)0x11, (byte)0x68,
114                        (byte)0x83, (byte)0x7c, (byte)0x14, (byte)0x1f,
115                        (byte)0xf6, (byte)0x4a, (byte)0xb0, (byte)0x54
116                    });
117    }
118
119    /**
120     * 64-bit Test vector from RFC 3211
121     *
122     * See also org.bouncycastle.crypto.test.PKCS5Test
123     */
124    public void test_PBKDF2_rfc3211_64() throws Exception {
125        char[] password = "password".toCharArray();
126        byte[] salt = new byte[] {
127            (byte)0x12, (byte)0x34, (byte)0x56, (byte)0x78,
128            (byte)0x78, (byte)0x56, (byte)0x34, (byte)0x12
129        };
130        int iterations = 5;
131        int keyLength = 64;
132        byte[] expected = new byte[] {
133            (byte)0xD1, (byte)0xDA, (byte)0xA7, (byte)0x86,
134            (byte)0x15, (byte)0xF2, (byte)0x87, (byte)0xE6
135        };
136        test_PBKDF2(password, salt, iterations, keyLength, expected);
137    }
138
139    /**
140     * 192-bit Test vector from RFC 3211
141     *
142     * See also org.bouncycastle.crypto.test.PKCS5Test
143     */
144    public void test_PBKDF2_rfc3211_192() throws Exception {
145        char[] password = ("All n-entities must communicate with other "
146                           + "n-entities via n-1 entiteeheehees").toCharArray();
147        byte[] salt = new byte[] {
148            (byte)0x12, (byte)0x34, (byte)0x56, (byte)0x78,
149            (byte)0x78, (byte)0x56, (byte)0x34, (byte)0x12
150        };
151        int iterations = 500;
152        int keyLength = 192;
153        byte[] expected = new byte[] {
154            (byte)0x6a, (byte)0x89, (byte)0x70, (byte)0xbf, (byte)0x68, (byte)0xc9,
155            (byte)0x2c, (byte)0xae, (byte)0xa8, (byte)0x4a, (byte)0x8d, (byte)0xf2,
156            (byte)0x85, (byte)0x10, (byte)0x85, (byte)0x86, (byte)0x07, (byte)0x12,
157            (byte)0x63, (byte)0x80, (byte)0xcc, (byte)0x47, (byte)0xab, (byte)0x2d
158        };
159        test_PBKDF2(password, salt, iterations, keyLength, expected);
160    }
161
162    private void test_PBKDF2(char[] password, byte[] salt, int iterations, int keyLength,
163                             byte[] expected) throws Exception {
164        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
165        KeySpec ks = new PBEKeySpec(password, salt, iterations, keyLength);
166        SecretKey key = factory.generateSecret(ks);
167        assertTrue(Arrays.equals(expected, key.getEncoded()));
168
169    }
170}
171