1 /*
2  * Copyright (C) 2015 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.annotation.Nullable;
21 import android.hardware.security.keymint.KeyParameter;
22 import android.security.KeyStoreException;
23 import android.security.KeyStoreOperation;
24 import android.security.keymaster.KeymasterDefs;
25 import android.security.keystore.ArrayUtils;
26 import android.security.keystore.KeyProperties;
27 import android.security.keystore2.KeyStoreCryptoOperationChunkedStreamer.Stream;
28 
29 import libcore.util.EmptyArray;
30 
31 import java.io.ByteArrayOutputStream;
32 import java.io.IOException;
33 import java.security.AlgorithmParameters;
34 import java.security.InvalidAlgorithmParameterException;
35 import java.security.InvalidKeyException;
36 import java.security.Key;
37 import java.security.NoSuchAlgorithmException;
38 import java.security.ProviderException;
39 import java.security.spec.AlgorithmParameterSpec;
40 import java.security.spec.InvalidParameterSpecException;
41 import java.util.Arrays;
42 import java.util.List;
43 
44 import javax.crypto.CipherSpi;
45 import javax.crypto.spec.GCMParameterSpec;
46 
47 /**
48  * Base class for Android Keystore authenticated AES {@link CipherSpi} implementations.
49  *
50  * @hide
51  */
52 abstract class AndroidKeyStoreAuthenticatedAESCipherSpi extends AndroidKeyStoreCipherSpiBase {
53 
54     abstract static class GCM extends AndroidKeyStoreAuthenticatedAESCipherSpi {
55         static final int MIN_SUPPORTED_TAG_LENGTH_BITS = 96;
56         private static final int MAX_SUPPORTED_TAG_LENGTH_BITS = 128;
57         private static final int DEFAULT_TAG_LENGTH_BITS = 128;
58         private static final int IV_LENGTH_BYTES = 12;
59 
60         private int mTagLengthBits = DEFAULT_TAG_LENGTH_BITS;
61 
GCM(int keymasterPadding)62         GCM(int keymasterPadding) {
63             super(KeymasterDefs.KM_MODE_GCM, keymasterPadding);
64         }
65 
66         @Override
getTransform()67         protected final String getTransform() {
68             return "AES/GCM/NoPadding";
69         }
70 
71         @Override
resetAll()72         protected final void resetAll() {
73             mTagLengthBits = DEFAULT_TAG_LENGTH_BITS;
74             super.resetAll();
75         }
76 
77         @Override
resetWhilePreservingInitState()78         protected final void resetWhilePreservingInitState() {
79             super.resetWhilePreservingInitState();
80         }
81 
82         @Override
initAlgorithmSpecificParameters()83         protected final void initAlgorithmSpecificParameters() throws InvalidKeyException {
84             if (!isEncrypting()) {
85                 throw new InvalidKeyException("IV required when decrypting"
86                         + ". Use IvParameterSpec or AlgorithmParameters to provide it.");
87             }
88         }
89 
90         @Override
initAlgorithmSpecificParameters(AlgorithmParameterSpec params)91         protected final void initAlgorithmSpecificParameters(AlgorithmParameterSpec params)
92                 throws InvalidAlgorithmParameterException {
93             // IV is used
94             if (params == null) {
95                 if (!isEncrypting()) {
96                     // IV must be provided by the caller
97                     throw new InvalidAlgorithmParameterException(
98                             "GCMParameterSpec must be provided when decrypting");
99                 }
100                 return;
101             }
102             if (!(params instanceof GCMParameterSpec)) {
103                 throw new InvalidAlgorithmParameterException("Only GCMParameterSpec supported");
104             }
105             GCMParameterSpec spec = (GCMParameterSpec) params;
106             byte[] iv = spec.getIV();
107             if (iv == null) {
108                 throw new InvalidAlgorithmParameterException("Null IV in GCMParameterSpec");
109             } else if (iv.length != IV_LENGTH_BYTES) {
110                 throw new InvalidAlgorithmParameterException("Unsupported IV length: "
111                         + iv.length + " bytes. Only " + IV_LENGTH_BYTES
112                         + " bytes long IV supported");
113             }
114             int tagLengthBits = spec.getTLen();
115             if ((tagLengthBits < MIN_SUPPORTED_TAG_LENGTH_BITS)
116                     || (tagLengthBits > MAX_SUPPORTED_TAG_LENGTH_BITS)
117                     || ((tagLengthBits % 8) != 0)) {
118                 throw new InvalidAlgorithmParameterException(
119                         "Unsupported tag length: " + tagLengthBits + " bits"
120                         + ". Supported lengths: 96, 104, 112, 120, 128");
121             }
122             setIv(iv);
123             mTagLengthBits = tagLengthBits;
124         }
125 
126         @Override
initAlgorithmSpecificParameters(AlgorithmParameters params)127         protected final void initAlgorithmSpecificParameters(AlgorithmParameters params)
128                 throws InvalidAlgorithmParameterException {
129             if (params == null) {
130                 if (!isEncrypting()) {
131                     // IV must be provided by the caller
132                     throw new InvalidAlgorithmParameterException("IV required when decrypting"
133                             + ". Use GCMParameterSpec or GCM AlgorithmParameters to provide it.");
134                 }
135                 return;
136             }
137 
138             if (!"GCM".equalsIgnoreCase(params.getAlgorithm())) {
139                 throw new InvalidAlgorithmParameterException(
140                         "Unsupported AlgorithmParameters algorithm: " + params.getAlgorithm()
141                         + ". Supported: GCM");
142             }
143 
144             GCMParameterSpec spec;
145             try {
146                 spec = params.getParameterSpec(GCMParameterSpec.class);
147             } catch (InvalidParameterSpecException e) {
148                 if (!isEncrypting()) {
149                     // IV must be provided by the caller
150                     throw new InvalidAlgorithmParameterException("IV and tag length required when"
151                             + " decrypting, but not found in parameters: " + params, e);
152                 }
153                 setIv(null);
154                 return;
155             }
156             initAlgorithmSpecificParameters(spec);
157         }
158 
159         @Nullable
160         @Override
engineGetParameters()161         protected final AlgorithmParameters engineGetParameters() {
162             byte[] iv = getIv();
163             if ((iv != null) && (iv.length > 0)) {
164                 try {
165                     AlgorithmParameters params = AlgorithmParameters.getInstance("GCM");
166                     params.init(new GCMParameterSpec(mTagLengthBits, iv));
167                     return params;
168                 } catch (NoSuchAlgorithmException e) {
169                     throw new ProviderException(
170                             "Failed to obtain GCM AlgorithmParameters", e);
171                 } catch (InvalidParameterSpecException e) {
172                     throw new ProviderException(
173                             "Failed to initialize GCM AlgorithmParameters", e);
174                 }
175             }
176             return null;
177         }
178 
179         @NonNull
180         @Override
createMainDataStreamer( KeyStoreOperation operation)181         protected KeyStoreCryptoOperationStreamer createMainDataStreamer(
182                 KeyStoreOperation operation) {
183             KeyStoreCryptoOperationStreamer streamer = new KeyStoreCryptoOperationChunkedStreamer(
184                     new KeyStoreCryptoOperationChunkedStreamer.MainDataStream(
185                             operation), 0);
186             if (isEncrypting()) {
187                 return streamer;
188             } else {
189                 // When decrypting, to avoid leaking unauthenticated plaintext, do not return any
190                 // plaintext before ciphertext is authenticated by KeyStore.finish.
191                 return new BufferAllOutputUntilDoFinalStreamer(streamer);
192             }
193         }
194 
195         @NonNull
196         @Override
createAdditionalAuthenticationDataStreamer( KeyStoreOperation operation)197         protected final KeyStoreCryptoOperationStreamer createAdditionalAuthenticationDataStreamer(
198                 KeyStoreOperation operation) {
199             return new KeyStoreCryptoOperationChunkedStreamer(
200                     new AdditionalAuthenticationDataStream(operation), 0);
201         }
202 
203         @Override
getAdditionalEntropyAmountForBegin()204         protected final int getAdditionalEntropyAmountForBegin() {
205             if ((getIv() == null) && (isEncrypting())) {
206                 // IV will need to be generated
207                 return IV_LENGTH_BYTES;
208             }
209 
210             return 0;
211         }
212 
213         @Override
getAdditionalEntropyAmountForFinish()214         protected final int getAdditionalEntropyAmountForFinish() {
215             return 0;
216         }
217 
218         @Override
addAlgorithmSpecificParametersToBegin( @onNull List<KeyParameter> parameters)219         protected final void addAlgorithmSpecificParametersToBegin(
220                 @NonNull List<KeyParameter> parameters) {
221             super.addAlgorithmSpecificParametersToBegin(parameters);
222             parameters.add(KeyStore2ParameterUtils.makeInt(
223                     KeymasterDefs.KM_TAG_MAC_LENGTH,
224                     mTagLengthBits
225             ));
226         }
227 
getTagLengthBits()228         protected final int getTagLengthBits() {
229             return mTagLengthBits;
230         }
231 
232         public static final class NoPadding extends GCM {
NoPadding()233             public NoPadding() {
234                 super(KeymasterDefs.KM_PAD_NONE);
235             }
236 
237             @Override
engineGetOutputSize(int inputLen)238             protected final int engineGetOutputSize(int inputLen) {
239                 int tagLengthBytes = (getTagLengthBits() + 7) / 8;
240                 long result;
241                 if (isEncrypting()) {
242                     result = getConsumedInputSizeBytes() - getProducedOutputSizeBytes() + inputLen
243                             + tagLengthBytes;
244                 } else {
245                     result = getConsumedInputSizeBytes() - getProducedOutputSizeBytes() + inputLen
246                             - tagLengthBytes;
247                 }
248                 if (result < 0) {
249                     return 0;
250                 } else if (result > Integer.MAX_VALUE) {
251                     return Integer.MAX_VALUE;
252                 }
253                 return (int) result;
254             }
255         }
256     }
257 
258     private static final int BLOCK_SIZE_BYTES = 16;
259 
260     private final int mKeymasterBlockMode;
261     private final int mKeymasterPadding;
262 
263     private byte[] mIv;
264 
265     /** Whether the current {@code #mIv} has been used by the underlying crypto operation. */
266     private boolean mIvHasBeenUsed;
267 
AndroidKeyStoreAuthenticatedAESCipherSpi( int keymasterBlockMode, int keymasterPadding)268     AndroidKeyStoreAuthenticatedAESCipherSpi(
269             int keymasterBlockMode,
270             int keymasterPadding) {
271         mKeymasterBlockMode = keymasterBlockMode;
272         mKeymasterPadding = keymasterPadding;
273     }
274 
275     @Override
resetAll()276     protected void resetAll() {
277         mIv = null;
278         mIvHasBeenUsed = false;
279         super.resetAll();
280     }
281 
282     @Override
initKey(int opmode, Key key)283     protected final void initKey(int opmode, Key key) throws InvalidKeyException {
284         if (!(key instanceof AndroidKeyStoreSecretKey)) {
285             throw new InvalidKeyException(
286                     "Unsupported key: " + ((key != null) ? key.getClass().getName() : "null"));
287         }
288         if (!KeyProperties.KEY_ALGORITHM_AES.equalsIgnoreCase(key.getAlgorithm())) {
289             throw new InvalidKeyException(
290                     "Unsupported key algorithm: " + key.getAlgorithm() + ". Only " +
291                     KeyProperties.KEY_ALGORITHM_AES + " supported");
292         }
293         setKey((AndroidKeyStoreSecretKey) key);
294     }
295 
296     @Override
addAlgorithmSpecificParametersToBegin( @onNull List<KeyParameter> parameters)297     protected void addAlgorithmSpecificParametersToBegin(
298             @NonNull List<KeyParameter> parameters) {
299         if ((isEncrypting()) && (mIvHasBeenUsed)) {
300             // IV is being reused for encryption: this violates security best practices.
301             throw new IllegalStateException(
302                     "IV has already been used. Reusing IV in encryption mode violates security best"
303                     + " practices.");
304         }
305         parameters.add(KeyStore2ParameterUtils.makeEnum(
306                 KeymasterDefs.KM_TAG_ALGORITHM,
307                 KeymasterDefs.KM_ALGORITHM_AES
308         ));
309         parameters.add(KeyStore2ParameterUtils.makeEnum(
310                 KeymasterDefs.KM_TAG_BLOCK_MODE,
311                 mKeymasterBlockMode
312         ));
313         parameters.add(KeyStore2ParameterUtils.makeEnum(
314                 KeymasterDefs.KM_TAG_PADDING,
315                 mKeymasterPadding
316         ));
317 
318         if (mIv != null) {
319             parameters.add(KeyStore2ParameterUtils.makeBytes(KeymasterDefs.KM_TAG_NONCE, mIv));
320         }
321     }
322 
323     @Override
loadAlgorithmSpecificParametersFromBeginResult( KeyParameter[] parameters)324     protected final void loadAlgorithmSpecificParametersFromBeginResult(
325             KeyParameter[] parameters) {
326         mIvHasBeenUsed = true;
327 
328         // NOTE: Keymaster doesn't always return an IV, even if it's used.
329         byte[] returnedIv = null;
330         if (parameters != null) {
331             for (KeyParameter p : parameters) {
332                 if (p.tag == KeymasterDefs.KM_TAG_NONCE) {
333                     returnedIv = p.value.getBlob();
334                     break;
335                 }
336             }
337         }
338 
339         if (mIv == null) {
340             mIv = returnedIv;
341         } else if ((returnedIv != null) && (!Arrays.equals(returnedIv, mIv))) {
342             throw new ProviderException("IV in use differs from provided IV");
343         }
344     }
345 
346     @Override
engineGetBlockSize()347     protected final int engineGetBlockSize() {
348         return BLOCK_SIZE_BYTES;
349     }
350 
351     @Override
engineGetIV()352     protected final byte[] engineGetIV() {
353         return ArrayUtils.cloneIfNotEmpty(mIv);
354     }
355 
setIv(byte[] iv)356     protected void setIv(byte[] iv) {
357         mIv = iv;
358     }
359 
getIv()360     protected byte[] getIv() {
361         return mIv;
362     }
363 
364     /**
365      * {@link KeyStoreCryptoOperationStreamer} which buffers all output until {@code doFinal} from
366      * which it returns all output in one go, provided {@code doFinal} succeeds.
367      */
368     private static class BufferAllOutputUntilDoFinalStreamer
369         implements KeyStoreCryptoOperationStreamer {
370 
371         private final KeyStoreCryptoOperationStreamer mDelegate;
372         private ByteArrayOutputStream mBufferedOutput = new ByteArrayOutputStream();
373         private long mProducedOutputSizeBytes;
374 
BufferAllOutputUntilDoFinalStreamer(KeyStoreCryptoOperationStreamer delegate)375         private BufferAllOutputUntilDoFinalStreamer(KeyStoreCryptoOperationStreamer delegate) {
376             mDelegate = delegate;
377         }
378 
379         @Override
update(byte[] input, int inputOffset, int inputLength)380         public byte[] update(byte[] input, int inputOffset, int inputLength)
381                 throws KeyStoreException {
382             byte[] output = mDelegate.update(input, inputOffset, inputLength);
383             if (output != null) {
384                 try {
385                     mBufferedOutput.write(output);
386                 } catch (IOException e) {
387                     throw new ProviderException("Failed to buffer output", e);
388                 }
389             }
390             return EmptyArray.BYTE;
391         }
392 
393         @Override
doFinal(byte[] input, int inputOffset, int inputLength, byte[] signature)394         public byte[] doFinal(byte[] input, int inputOffset, int inputLength,
395                 byte[] signature) throws KeyStoreException {
396             byte[] output = mDelegate.doFinal(input, inputOffset, inputLength, signature);
397             if (output != null) {
398                 try {
399                     mBufferedOutput.write(output);
400                 } catch (IOException e) {
401                     throw new ProviderException("Failed to buffer output", e);
402                 }
403             }
404             byte[] result = mBufferedOutput.toByteArray();
405             mBufferedOutput.reset();
406             mProducedOutputSizeBytes += result.length;
407             return result;
408         }
409 
410         @Override
getConsumedInputSizeBytes()411         public long getConsumedInputSizeBytes() {
412             return mDelegate.getConsumedInputSizeBytes();
413         }
414 
415         @Override
getProducedOutputSizeBytes()416         public long getProducedOutputSizeBytes() {
417             return mProducedOutputSizeBytes;
418         }
419     }
420 
421     /**
422      * Additional Authentication Data (AAD) stream via a KeyStore streaming operation. This stream
423      * sends AAD into the KeyStore.
424      */
425     private static class AdditionalAuthenticationDataStream implements Stream {
426 
427         private final KeyStoreOperation mOperation;
428 
AdditionalAuthenticationDataStream(KeyStoreOperation operation)429         private AdditionalAuthenticationDataStream(KeyStoreOperation operation) {
430             mOperation = operation;
431         }
432 
433         @Override
update(byte[] input)434         public byte[] update(byte[] input) throws KeyStoreException {
435             mOperation.updateAad(input);
436             return null;
437         }
438 
439         @Override
finish(byte[] input, byte[] signature)440         public byte[] finish(byte[] input, byte[] signature) {
441             return null;
442         }
443     }
444 }