1 /* 2 * Copyright (C) 2017 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 package org.conscrypt; 17 18 import java.security.InvalidAlgorithmParameterException; 19 import java.security.InvalidParameterException; 20 import java.security.SecureRandom; 21 import java.security.spec.AlgorithmParameterSpec; 22 import javax.crypto.KeyGeneratorSpi; 23 import javax.crypto.SecretKey; 24 import javax.crypto.spec.DESedeKeySpec; 25 import javax.crypto.spec.SecretKeySpec; 26 27 /** 28 * An implementation of {@link javax.crypto.KeyGenerator} suitable for use with other Conscrypt 29 * algorithms. 30 * 31 * @hide 32 */ 33 @Internal 34 public abstract class KeyGeneratorImpl extends KeyGeneratorSpi { 35 private final String algorithm; 36 protected SecureRandom secureRandom; 37 private int keySizeBits; 38 39 private KeyGeneratorImpl(String algorithm, int defaultKeySizeBits) { 40 this.algorithm = algorithm; 41 this.keySizeBits = defaultKeySizeBits; 42 } 43 44 protected void checkKeySize(int keySize) { 45 if (keySize <= 0) { 46 throw new InvalidParameterException("Key size must be positive"); 47 } 48 } 49 50 @Override 51 protected void engineInit(SecureRandom secureRandom) { 52 this.secureRandom = secureRandom; 53 } 54 55 @Override 56 protected void engineInit(AlgorithmParameterSpec params, SecureRandom secureRandom) 57 throws InvalidAlgorithmParameterException { 58 if (params == null) { 59 throw new InvalidAlgorithmParameterException("No params provided"); 60 } else { 61 throw new InvalidAlgorithmParameterException( 62 "Unknown param type: " + params.getClass().getName()); 63 } 64 } 65 66 @Override 67 protected void engineInit(int keySize, SecureRandom secureRandom) { 68 checkKeySize(keySize); 69 this.keySizeBits = keySize; 70 this.secureRandom = secureRandom; 71 } 72 73 protected byte[] doKeyGeneration(int keyBytes) { 74 byte[] keyData = new byte[keyBytes]; 75 secureRandom.nextBytes(keyData); 76 return keyData; 77 } 78 79 @Override 80 protected SecretKey engineGenerateKey() { 81 if (secureRandom == null) { 82 secureRandom = new SecureRandom(); 83 } 84 85 return new SecretKeySpec(doKeyGeneration((keySizeBits + 7) / 8), algorithm); 86 } 87 88 // For HMAC, RFC 2104 recommends using the hash's output length as the key length 89 public static final class HmacMD5 extends KeyGeneratorImpl { 90 public HmacMD5() { 91 super("HmacMD5", 128); 92 } 93 } 94 95 public static final class HmacSHA1 extends KeyGeneratorImpl { 96 public HmacSHA1() { 97 super("HmacSHA1", 160); 98 } 99 } 100 101 public static final class HmacSHA224 extends KeyGeneratorImpl { 102 public HmacSHA224() { 103 super("HmacSHA224", 224); 104 } 105 } 106 107 public static final class HmacSHA256 extends KeyGeneratorImpl { 108 public HmacSHA256() { 109 super("HmacSHA256", 256); 110 } 111 } 112 113 public static final class HmacSHA384 extends KeyGeneratorImpl { 114 public HmacSHA384() { 115 super("HmacSHA384", 384); 116 } 117 } 118 119 public static final class HmacSHA512 extends KeyGeneratorImpl { 120 public HmacSHA512() { 121 super("HmacSHA512", 512); 122 } 123 } 124 125 public static final class DESEDE extends KeyGeneratorImpl { 126 public DESEDE() { 127 super("DESEDE", 192); 128 } 129 130 @Override 131 protected void checkKeySize(int keySize) { 132 if ((keySize != 112) && (keySize != 168)) { 133 throw new InvalidParameterException("Key size must be either 112 or 168 bits"); 134 } 135 } 136 137 @Override 138 protected byte[] doKeyGeneration(int keyBytes) { 139 byte[] keyData = new byte[DESedeKeySpec.DES_EDE_KEY_LEN]; 140 secureRandom.nextBytes(keyData); 141 // Set the parity bit for each byte 142 for (int i = 0; i < keyData.length; i++) { 143 if (Integer.bitCount(keyData[i]) % 2 == 0) { 144 keyData[i] = (byte) (keyData[i] ^ 1); 145 } 146 } 147 if (keyBytes == 14) { 148 // The user requested an A-B-A key 149 System.arraycopy(keyData, 0, keyData, 16, 8); 150 } 151 return keyData; 152 } 153 } 154 155 public static final class AES extends KeyGeneratorImpl { 156 public AES() { 157 super("AES", 128); 158 } 159 160 @Override 161 protected void checkKeySize(int keySize) { 162 if ((keySize != 128) && (keySize != 192) && (keySize != 256)) { 163 throw new InvalidParameterException( 164 "Key size must be either 128, 192, or 256 bits"); 165 } 166 } 167 } 168 169 public static final class ChaCha20 extends KeyGeneratorImpl { 170 public ChaCha20() { 171 super("ChaCha20", 256); 172 } 173 174 @Override 175 protected void checkKeySize(int keySize) { 176 if (keySize != 256) { 177 throw new InvalidParameterException("Key size must be 256 bits"); 178 } 179 } 180 } 181 182 public static final class ARC4 extends KeyGeneratorImpl { 183 public ARC4() { 184 super("ARC4", 128); 185 } 186 187 @Override 188 protected void checkKeySize(int keySize) { 189 if (keySize < 40 || 2048 < keySize) { 190 throw new InvalidParameterException("Key size must be between 40 and 2048 bits"); 191 } 192 } 193 } 194 } 195