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;
26import libcore.java.security.StandardNames;
27
28public class SecretKeyFactoryTest extends TestCase {
29
30    private static final char[] PASSWORD = "google".toCharArray();
31    /**
32     * Salts should be random to reduce effectiveness of dictionary
33     * attacks, but need not be kept secret from attackers. For more
34     * information, see http://en.wikipedia.org/wiki/Salt_(cryptography)
35     */
36    private static final byte[] SALT = {0, 1, 2, 3, 4, 5, 6, 7};
37    /**
38     * The number of iterations should be higher for production
39     * strength protection. The tolerable value may vary from device
40     * to device, but 8192 should be acceptable for PBKDF2 on a Nexus One.
41     */
42    private static final int ITERATIONS = 1024;
43    private static final int KEY_LENGTH = 128;
44
45    public void test_PBKDF2_required_parameters() throws Exception {
46        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
47
48        // PBEKeySpec validates arguments most to be non-null, non-empty, postive, etc.
49        // Focus on insufficient PBEKeySpecs
50
51        // PBEKeySpecs password only constructor
52        try {
53            KeySpec ks = new PBEKeySpec(null);
54            factory.generateSecret(ks);
55            fail();
56        } catch (InvalidKeySpecException expected) {
57        }
58        try {
59            KeySpec ks = new PBEKeySpec(new char[0]);
60            factory.generateSecret(ks);
61            fail();
62        } catch (InvalidKeySpecException expected) {
63        }
64        try {
65            KeySpec ks = new PBEKeySpec(PASSWORD);
66            factory.generateSecret(ks);
67            fail();
68        } catch (InvalidKeySpecException expected) {
69        }
70
71
72        // PBEKeySpecs constructor without key length
73        try {
74            KeySpec ks = new PBEKeySpec(null, SALT, ITERATIONS);
75            factory.generateSecret(ks);
76            fail();
77        } catch (InvalidKeySpecException expected) {
78        }
79        try {
80            KeySpec ks = new PBEKeySpec(new char[0], SALT, ITERATIONS);
81            factory.generateSecret(ks);
82            fail();
83        } catch (InvalidKeySpecException expected) {
84        }
85        try {
86            KeySpec ks = new PBEKeySpec(PASSWORD, SALT, ITERATIONS);
87            factory.generateSecret(ks);
88            fail();
89        } catch (InvalidKeySpecException expected) {
90        }
91
92        try {
93            KeySpec ks = new PBEKeySpec(null, SALT, ITERATIONS, KEY_LENGTH);
94            factory.generateSecret(ks);
95            fail();
96        } catch (IllegalArgumentException expected) {
97        }
98
99        try {
100            KeySpec ks = new PBEKeySpec(new char[0], SALT, ITERATIONS, KEY_LENGTH);
101            factory.generateSecret(ks);
102            fail();
103        } catch (IllegalArgumentException expected) {
104        }
105
106        KeySpec ks = new PBEKeySpec(PASSWORD, SALT, ITERATIONS, KEY_LENGTH);
107        factory.generateSecret(ks);
108    }
109
110    public void test_PBKDF2_b3059950() throws Exception {
111        byte[] expected = 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        test_PBKDF2_UTF8(PASSWORD, SALT, ITERATIONS, KEY_LENGTH, expected);
118        test_PBKDF2_8BIT(PASSWORD, SALT, ITERATIONS, KEY_LENGTH, expected);
119    }
120
121    /**
122     * 64-bit Test vector from RFC 3211
123     *
124     * See also org.bouncycastle.crypto.test.PKCS5Test
125     */
126    public void test_PBKDF2_rfc3211_64() throws Exception {
127        char[] password = "password".toCharArray();
128        byte[] salt = new byte[] {
129            (byte)0x12, (byte)0x34, (byte)0x56, (byte)0x78,
130            (byte)0x78, (byte)0x56, (byte)0x34, (byte)0x12
131        };
132        int iterations = 5;
133        int keyLength = 64;
134        byte[] expected = new byte[] {
135            (byte)0xD1, (byte)0xDA, (byte)0xA7, (byte)0x86,
136            (byte)0x15, (byte)0xF2, (byte)0x87, (byte)0xE6
137        };
138        test_PBKDF2_UTF8(password, salt, iterations, keyLength, expected);
139        test_PBKDF2_8BIT(password, salt, iterations, keyLength, expected);
140    }
141
142    /**
143     * 192-bit Test vector from RFC 3211
144     *
145     * See also org.bouncycastle.crypto.test.PKCS5Test
146     */
147    public void test_PBKDF2_rfc3211_192() throws Exception {
148        char[] password = ("All n-entities must communicate with other "
149                           + "n-entities via n-1 entiteeheehees").toCharArray();
150        byte[] salt = new byte[] {
151            (byte)0x12, (byte)0x34, (byte)0x56, (byte)0x78,
152            (byte)0x78, (byte)0x56, (byte)0x34, (byte)0x12
153        };
154        int iterations = 500;
155        int keyLength = 192;
156        byte[] expected = new byte[] {
157            (byte)0x6a, (byte)0x89, (byte)0x70, (byte)0xbf, (byte)0x68, (byte)0xc9,
158            (byte)0x2c, (byte)0xae, (byte)0xa8, (byte)0x4a, (byte)0x8d, (byte)0xf2,
159            (byte)0x85, (byte)0x10, (byte)0x85, (byte)0x86, (byte)0x07, (byte)0x12,
160            (byte)0x63, (byte)0x80, (byte)0xcc, (byte)0x47, (byte)0xab, (byte)0x2d
161        };
162        test_PBKDF2_UTF8(password, salt, iterations, keyLength, expected);
163        test_PBKDF2_8BIT(password, salt, iterations, keyLength, expected);
164    }
165
166   /**
167    * Unicode Test vector for b/8312059.
168    *
169    * See also https://code.google.com/p/android/issues/detail?id=40578
170    */
171    public void test_PBKDF2_b8312059() throws Exception {
172
173        char[] password = "\u0141\u0142".toCharArray();
174        byte[] salt = "salt".getBytes();
175        int iterations = 4096;
176        int keyLength = 160;
177        byte[] expected_utf8 = new byte[] {
178            (byte)0x4c, (byte)0xe0, (byte)0x6a, (byte)0xb8, (byte)0x48, (byte)0x04,
179            (byte)0xb7, (byte)0xe7, (byte)0x72, (byte)0xf2, (byte)0xaf, (byte)0x5e,
180            (byte)0x54, (byte)0xe9, (byte)0x03, (byte)0xad, (byte)0x59, (byte)0x64,
181            (byte)0x8b, (byte)0xab
182        };
183        byte[] expected_8bit = new byte[] {
184            (byte)0x6e, (byte)0x43, (byte)0xe0, (byte)0x18, (byte)0xc5, (byte)0x50,
185            (byte)0x0d, (byte)0xa7, (byte)0xfe, (byte)0x7a, (byte)0x44, (byte)0x4d,
186            (byte)0x99, (byte)0x5d, (byte)0x8c, (byte)0xae, (byte)0xc1, (byte)0xc9,
187            (byte)0x17, (byte)0xce
188        };
189        test_PBKDF2_UTF8(password, salt, iterations, keyLength, expected_utf8);
190        test_PBKDF2_8BIT(password, salt, iterations, keyLength, expected_8bit);
191    }
192
193    private void test_PBKDF2_8BIT(char[] password, byte[] salt, int iterations, int keyLength,
194                                  byte[] expected) throws Exception {
195        if (!StandardNames.IS_RI) {
196            SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1And8bit");
197            KeySpec ks = new PBEKeySpec(password, salt, iterations, keyLength);
198            SecretKey key = factory.generateSecret(ks);
199            assertTrue(Arrays.equals(expected, key.getEncoded()));
200        }
201
202    }
203
204    private void test_PBKDF2_UTF8(char[] password, byte[] salt, int iterations, int keyLength,
205                                  byte[] expected) throws Exception {
206        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
207        KeySpec ks = new PBEKeySpec(password, salt, iterations, keyLength);
208        SecretKey key = factory.generateSecret(ks);
209        assertTrue(Arrays.equals(expected, key.getEncoded()));
210
211    }
212}
213