1 /*
2  * Copyright (C) 2018 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 
17 package android.security.keystore2;
18 
19 import android.annotation.NonNull;
20 import android.hardware.security.keymint.KeyParameter;
21 import android.security.keymaster.KeymasterDefs;
22 import android.security.keystore.ArrayUtils;
23 import android.security.keystore.KeyProperties;
24 
25 import java.security.AlgorithmParameters;
26 import java.security.InvalidAlgorithmParameterException;
27 import java.security.InvalidKeyException;
28 import java.security.Key;
29 import java.security.NoSuchAlgorithmException;
30 import java.security.ProviderException;
31 import java.security.spec.AlgorithmParameterSpec;
32 import java.security.spec.InvalidParameterSpecException;
33 import java.util.Arrays;
34 import java.util.List;
35 
36 import javax.crypto.CipherSpi;
37 import javax.crypto.spec.IvParameterSpec;
38 
39 /**
40  * Base class for Android Keystore 3DES {@link CipherSpi} implementations.
41  *
42  * @hide
43  */
44 public abstract class AndroidKeyStore3DESCipherSpi extends AndroidKeyStoreCipherSpiBase {
45 
46     private static final int BLOCK_SIZE_BYTES = 8;
47 
48     private final int mKeymasterBlockMode;
49     private final int mKeymasterPadding;
50     /** Whether this transformation requires an IV. */
51     private final boolean mIvRequired;
52 
53     private byte[] mIv;
54 
55     /** Whether the current {@code #mIv} has been used by the underlying crypto operation. */
56     private boolean mIvHasBeenUsed;
57 
AndroidKeyStore3DESCipherSpi( int keymasterBlockMode, int keymasterPadding, boolean ivRequired)58     AndroidKeyStore3DESCipherSpi(
59             int keymasterBlockMode,
60             int keymasterPadding,
61             boolean ivRequired) {
62         mKeymasterBlockMode = keymasterBlockMode;
63         mKeymasterPadding = keymasterPadding;
64         mIvRequired = ivRequired;
65     }
66 
67     abstract static class ECB extends AndroidKeyStore3DESCipherSpi {
ECB(int keymasterPadding)68         protected ECB(int keymasterPadding) {
69             super(KeymasterDefs.KM_MODE_ECB, keymasterPadding, false);
70         }
71 
72         public static class NoPadding extends ECB {
NoPadding()73             public NoPadding() {
74                 super(KeymasterDefs.KM_PAD_NONE);
75             }
76 
77             @Override
getTransform()78             protected final String getTransform() {
79                 return "DESede/ECB/NoPadding";
80             }
81         }
82 
83         public static class PKCS7Padding extends ECB {
PKCS7Padding()84             public PKCS7Padding() {
85                 super(KeymasterDefs.KM_PAD_PKCS7);
86             }
87 
88             @Override
getTransform()89             protected final String getTransform() {
90                 return "DESede/ECB/PKCS7Padding";
91             }
92         }
93     }
94 
95     abstract static class CBC extends AndroidKeyStore3DESCipherSpi {
CBC(int keymasterPadding)96         protected CBC(int keymasterPadding) {
97             super(KeymasterDefs.KM_MODE_CBC, keymasterPadding, true);
98         }
99 
100         public static class NoPadding extends CBC {
NoPadding()101             public NoPadding() {
102                 super(KeymasterDefs.KM_PAD_NONE);
103             }
104 
105             @Override
getTransform()106             protected final String getTransform() {
107                 return "DESede/CBC/NoPadding";
108             }
109 
110         }
111 
112         public static class PKCS7Padding extends CBC {
PKCS7Padding()113             public PKCS7Padding() {
114                 super(KeymasterDefs.KM_PAD_PKCS7);
115             }
116 
117             @Override
getTransform()118             protected final String getTransform() {
119                 return "DESede/CBC/PKCS7Padding";
120             }
121         }
122     }
123 
124     @Override
initKey(int i, Key key)125     protected void initKey(int i, Key key) throws InvalidKeyException {
126         if (!(key instanceof AndroidKeyStoreSecretKey)) {
127             throw new InvalidKeyException(
128                     "Unsupported key: " + ((key != null) ? key.getClass().getName() : "null"));
129         }
130         if (!KeyProperties.KEY_ALGORITHM_3DES.equalsIgnoreCase(key.getAlgorithm())) {
131             throw new InvalidKeyException(
132                     "Unsupported key algorithm: " + key.getAlgorithm() + ". Only " +
133                             KeyProperties.KEY_ALGORITHM_3DES + " supported");
134         }
135         setKey((AndroidKeyStoreSecretKey) key);
136     }
137 
138     @Override
engineGetBlockSize()139     protected int engineGetBlockSize() {
140         return BLOCK_SIZE_BYTES;
141     }
142 
143     @Override
engineGetOutputSize(int inputLen)144     protected int engineGetOutputSize(int inputLen) {
145         return inputLen + 3 * BLOCK_SIZE_BYTES;
146     }
147 
148     @Override
engineGetIV()149     protected final byte[] engineGetIV() {
150         return ArrayUtils.cloneIfNotEmpty(mIv);
151     }
152 
153     @Override
engineGetParameters()154     protected AlgorithmParameters engineGetParameters() {
155         if (!mIvRequired) {
156             return null;
157         }
158         if ((mIv != null) && (mIv.length > 0)) {
159             try {
160                 AlgorithmParameters params = AlgorithmParameters.getInstance("DESede");
161                 params.init(new IvParameterSpec(mIv));
162                 return params;
163             } catch (NoSuchAlgorithmException e) {
164                 throw new ProviderException(
165                         "Failed to obtain 3DES AlgorithmParameters", e);
166             } catch (InvalidParameterSpecException e) {
167                 throw new ProviderException(
168                         "Failed to initialize 3DES AlgorithmParameters with an IV",
169                         e);
170             }
171         }
172         return null;
173     }
174 
175     @Override
initAlgorithmSpecificParameters()176     protected void initAlgorithmSpecificParameters() throws InvalidKeyException {
177         if (!mIvRequired) {
178             return;
179         }
180 
181         // IV is used
182         if (!isEncrypting()) {
183             throw new InvalidKeyException("IV required when decrypting"
184                     + ". Use IvParameterSpec or AlgorithmParameters to provide it.");
185         }
186     }
187 
188     @Override
initAlgorithmSpecificParameters(AlgorithmParameterSpec params)189     protected void initAlgorithmSpecificParameters(AlgorithmParameterSpec params)
190             throws InvalidAlgorithmParameterException {
191         if (!mIvRequired) {
192             if (params != null) {
193                 throw new InvalidAlgorithmParameterException("Unsupported parameters: " + params);
194             }
195             return;
196         }
197 
198         // IV is used
199         if (params == null) {
200             if (!isEncrypting()) {
201                 // IV must be provided by the caller
202                 throw new InvalidAlgorithmParameterException(
203                         "IvParameterSpec must be provided when decrypting");
204             }
205             return;
206         }
207         if (!(params instanceof IvParameterSpec)) {
208             throw new InvalidAlgorithmParameterException("Only IvParameterSpec supported");
209         }
210         mIv = ((IvParameterSpec) params).getIV();
211         if (mIv == null) {
212             throw new InvalidAlgorithmParameterException("Null IV in IvParameterSpec");
213         }
214     }
215 
216     @Override
initAlgorithmSpecificParameters(AlgorithmParameters params)217     protected void initAlgorithmSpecificParameters(AlgorithmParameters params)
218             throws InvalidAlgorithmParameterException {
219         if (!mIvRequired) {
220             if (params != null) {
221                 throw new InvalidAlgorithmParameterException("Unsupported parameters: " + params);
222             }
223             return;
224         }
225 
226         // IV is used
227         if (params == null) {
228             if (!isEncrypting()) {
229                 // IV must be provided by the caller
230                 throw new InvalidAlgorithmParameterException("IV required when decrypting"
231                         + ". Use IvParameterSpec or AlgorithmParameters to provide it.");
232             }
233             return;
234         }
235 
236         if (!"DESede".equalsIgnoreCase(params.getAlgorithm())) {
237             throw new InvalidAlgorithmParameterException(
238                     "Unsupported AlgorithmParameters algorithm: " + params.getAlgorithm()
239                             + ". Supported: DESede");
240         }
241 
242         IvParameterSpec ivSpec;
243         try {
244             ivSpec = params.getParameterSpec(IvParameterSpec.class);
245         } catch (InvalidParameterSpecException e) {
246             if (!isEncrypting()) {
247                 // IV must be provided by the caller
248                 throw new InvalidAlgorithmParameterException("IV required when decrypting"
249                         + ", but not found in parameters: " + params, e);
250             }
251             mIv = null;
252             return;
253         }
254         mIv = ivSpec.getIV();
255         if (mIv == null) {
256             throw new InvalidAlgorithmParameterException("Null IV in AlgorithmParameters");
257         }
258     }
259 
260     @Override
getAdditionalEntropyAmountForBegin()261     protected final int getAdditionalEntropyAmountForBegin() {
262         if ((mIvRequired) && (mIv == null) && (isEncrypting())) {
263             // IV will need to be generated
264             return BLOCK_SIZE_BYTES;
265         }
266 
267         return 0;
268     }
269 
270     @Override
getAdditionalEntropyAmountForFinish()271     protected int getAdditionalEntropyAmountForFinish() {
272         return 0;
273     }
274 
275     @Override
addAlgorithmSpecificParametersToBegin(@onNull List<KeyParameter> parameters)276     protected void addAlgorithmSpecificParametersToBegin(@NonNull List<KeyParameter> parameters) {
277         if ((isEncrypting()) && (mIvRequired) && (mIvHasBeenUsed)) {
278             // IV is being reused for encryption: this violates security best practices.
279             throw new IllegalStateException(
280                     "IV has already been used. Reusing IV in encryption mode violates security best"
281                             + " practices.");
282         }
283 
284         parameters.add(KeyStore2ParameterUtils.makeEnum(
285                 KeymasterDefs.KM_TAG_ALGORITHM,
286                 KeymasterDefs.KM_ALGORITHM_3DES
287         ));
288         parameters.add(KeyStore2ParameterUtils.makeEnum(
289                 KeymasterDefs.KM_TAG_BLOCK_MODE,
290                 mKeymasterBlockMode
291         ));
292         parameters.add(KeyStore2ParameterUtils.makeEnum(
293                 KeymasterDefs.KM_TAG_PADDING,
294                 mKeymasterPadding
295         ));
296 
297         if (mIvRequired && (mIv != null)) {
298             parameters.add(KeyStore2ParameterUtils.makeBytes(KeymasterDefs.KM_TAG_NONCE, mIv));
299         }
300     }
301 
302     @Override
loadAlgorithmSpecificParametersFromBeginResult( KeyParameter[] parameters)303     protected void loadAlgorithmSpecificParametersFromBeginResult(
304             KeyParameter[] parameters) {
305         mIvHasBeenUsed = true;
306 
307         // NOTE: Keymaster doesn't always return an IV, even if it's used.
308         byte[] returnedIv = null;
309         if (parameters != null) {
310             for (KeyParameter p : parameters) {
311                 if (p.tag == KeymasterDefs.KM_TAG_NONCE) {
312                     returnedIv = p.value.getBlob();
313                     break;
314                 }
315             }
316         }
317 
318         if (mIvRequired) {
319             if (mIv == null) {
320                 mIv = returnedIv;
321             } else if ((returnedIv != null) && (!Arrays.equals(returnedIv, mIv))) {
322                 throw new ProviderException("IV in use differs from provided IV");
323             }
324         } else {
325             if (returnedIv != null) {
326                 throw new ProviderException(
327                         "IV in use despite IV not being used by this transformation");
328             }
329         }
330     }
331 
332     @Override
resetAll()333     protected final void resetAll() {
334         mIv = null;
335         mIvHasBeenUsed = false;
336         super.resetAll();
337     }
338 }
339