166ff1d62c02cd70d660b0fa99715c711faa635e9Artem Iglikov/* 266ff1d62c02cd70d660b0fa99715c711faa635e9Artem Iglikov * Copyright (C) 2017 The Android Open Source Project 366ff1d62c02cd70d660b0fa99715c711faa635e9Artem Iglikov * 466ff1d62c02cd70d660b0fa99715c711faa635e9Artem Iglikov * Licensed under the Apache License, Version 2.0 (the "License"); 566ff1d62c02cd70d660b0fa99715c711faa635e9Artem Iglikov * you may not use this file except in compliance with the License. 666ff1d62c02cd70d660b0fa99715c711faa635e9Artem Iglikov * You may obtain a copy of the License at 766ff1d62c02cd70d660b0fa99715c711faa635e9Artem Iglikov * 866ff1d62c02cd70d660b0fa99715c711faa635e9Artem Iglikov * http://www.apache.org/licenses/LICENSE-2.0 966ff1d62c02cd70d660b0fa99715c711faa635e9Artem Iglikov * 1066ff1d62c02cd70d660b0fa99715c711faa635e9Artem Iglikov * Unless required by applicable law or agreed to in writing, software 1166ff1d62c02cd70d660b0fa99715c711faa635e9Artem Iglikov * distributed under the License is distributed on an "AS IS" BASIS, 1266ff1d62c02cd70d660b0fa99715c711faa635e9Artem Iglikov * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1366ff1d62c02cd70d660b0fa99715c711faa635e9Artem Iglikov * See the License for the specific language governing permissions and 1466ff1d62c02cd70d660b0fa99715c711faa635e9Artem Iglikov * limitations under the License 1566ff1d62c02cd70d660b0fa99715c711faa635e9Artem Iglikov */ 1666ff1d62c02cd70d660b0fa99715c711faa635e9Artem Iglikov 17603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikovpackage com.android.server.backup.utils; 18603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov 19fe4ae0c5b1bc3b31adc4cc2c5a0197e29e97b6bcMichal Karpinskiimport static com.android.server.backup.BackupManagerService.TAG; 20603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov 21dbe68324801cfd45d0d1116c9da983b8ebe651aeArtem Iglikovimport android.util.Slog; 22603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov 23603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikovimport java.security.Key; 24603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikovimport java.security.NoSuchAlgorithmException; 25603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikovimport java.security.spec.InvalidKeySpecException; 26603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikovimport java.security.spec.KeySpec; 27603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov 28603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikovimport javax.crypto.SecretKey; 29603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikovimport javax.crypto.SecretKeyFactory; 30603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikovimport javax.crypto.spec.PBEKeySpec; 31603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov 32603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov/** 33603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov * Passwords related utility methods. 34603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov */ 35603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikovpublic class PasswordUtils { 36603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov // Configuration of PBKDF2 that we use for generating pw hashes and intermediate keys 37603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov public static final int PBKDF2_HASH_ROUNDS = 10000; 38603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov private static final int PBKDF2_KEY_SIZE = 256; // bits 39603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov public static final int PBKDF2_SALT_SIZE = 512; // bits 40603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov public static final String ENCRYPTION_ALGORITHM_NAME = "AES-256"; 41603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov 42603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov /** 43603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov * Creates {@link SecretKey} instance from given parameters. 44603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov * 45603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov * @param algorithm - key generation algorithm. 46603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov * @param pw - password. 47603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov * @param salt - salt. 48603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov * @param rounds - number of rounds to run in key generation. 49603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov * @return {@link SecretKey} instance or null in case of an error. 50603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov */ 51603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov public static SecretKey buildPasswordKey(String algorithm, String pw, byte[] salt, int rounds) { 52603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov return buildCharArrayKey(algorithm, pw.toCharArray(), salt, rounds); 53603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov } 54603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov 55603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov /** 56603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov * Generates {@link SecretKey} instance from given parameters and returns it's hex 57603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov * representation. 58603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov * 59603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov * @param algorithm - key generation algorithm. 60603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov * @param pw - password. 61603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov * @param salt - salt. 62603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov * @param rounds - number of rounds to run in key generation. 63603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov * @return Hex representation of the generated key, or null if generation failed. 64603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov */ 65603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov public static String buildPasswordHash(String algorithm, String pw, byte[] salt, int rounds) { 66603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov SecretKey key = buildPasswordKey(algorithm, pw, salt, rounds); 67603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov if (key != null) { 68603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov return byteArrayToHex(key.getEncoded()); 69603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov } 70603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov return null; 71603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov } 72603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov 73603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov /** 74603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov * Creates hex string representation of the byte array. 75603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov */ 76603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov public static String byteArrayToHex(byte[] data) { 77603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov StringBuilder buf = new StringBuilder(data.length * 2); 78603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov for (int i = 0; i < data.length; i++) { 79603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov buf.append(Byte.toHexString(data[i], true)); 80603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov } 81603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov return buf.toString(); 82603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov } 83603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov 84603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov /** 85603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov * Creates byte array from it's hex string representation. 86603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov */ 87603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov public static byte[] hexToByteArray(String digits) { 88603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov final int bytes = digits.length() / 2; 89603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov if (2 * bytes != digits.length()) { 90603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov throw new IllegalArgumentException("Hex string must have an even number of digits"); 91603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov } 92603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov 93603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov byte[] result = new byte[bytes]; 94603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov for (int i = 0; i < digits.length(); i += 2) { 95603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov result[i / 2] = (byte) Integer.parseInt(digits.substring(i, i + 2), 16); 96603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov } 97603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov return result; 98603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov } 99603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov 100603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov /** 101603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov * Generates {@link SecretKey} instance from given parameters and returns it's checksum. 102603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov * 103603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov * Current implementation returns the key in its primary encoding format. 104603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov * 105603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov * @param algorithm - key generation algorithm. 106603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov * @param pwBytes - password. 107603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov * @param salt - salt. 108603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov * @param rounds - number of rounds to run in key generation. 109603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov * @return Hex representation of the generated key, or null if generation failed. 110603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov */ 111603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov public static byte[] makeKeyChecksum(String algorithm, byte[] pwBytes, byte[] salt, 112603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov int rounds) { 113603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov char[] mkAsChar = new char[pwBytes.length]; 114603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov for (int i = 0; i < pwBytes.length; i++) { 115603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov mkAsChar[i] = (char) pwBytes[i]; 116603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov } 117603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov 118603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov Key checksum = buildCharArrayKey(algorithm, mkAsChar, salt, rounds); 119603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov return checksum.getEncoded(); 120603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov } 121603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov 122603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov private static SecretKey buildCharArrayKey(String algorithm, char[] pwArray, byte[] salt, 123603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov int rounds) { 124603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov try { 125603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(algorithm); 1269699fe319e78c681e638e8d5dee7954e15f8a96bRobert Berry KeySpec ks = new PBEKeySpec(pwArray, salt, rounds, PBKDF2_KEY_SIZE); 127603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov return keyFactory.generateSecret(ks); 128603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov } catch (InvalidKeySpecException e) { 129dbe68324801cfd45d0d1116c9da983b8ebe651aeArtem Iglikov Slog.e(TAG, "Invalid key spec for PBKDF2!"); 130603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov } catch (NoSuchAlgorithmException e) { 131dbe68324801cfd45d0d1116c9da983b8ebe651aeArtem Iglikov Slog.e(TAG, "PBKDF2 unavailable!"); 132603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov } 133603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov return null; 134603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov } 135603bcb7b88062e0b12589e90a73c792dfaa2b04bArtem Iglikov} 136