1 /*
2  * Copyright (C) 2022 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 com.android.server.wifi.util;
18 
19 import android.security.keystore.AndroidKeyStoreProvider;
20 import android.security.keystore.BackendBusyException;
21 import android.security.keystore.KeyGenParameterSpec;
22 import android.security.keystore.KeyProperties;
23 import android.util.Log;
24 
25 import com.android.modules.utils.build.SdkLevel;
26 
27 import java.security.InvalidAlgorithmParameterException;
28 import java.security.InvalidKeyException;
29 import java.security.Key;
30 import java.security.KeyStore;
31 import java.security.KeyStoreException;
32 import java.security.NoSuchAlgorithmException;
33 import java.security.NoSuchProviderException;
34 import java.security.ProviderException;
35 import java.security.UnrecoverableKeyException;
36 
37 import javax.crypto.KeyGenerator;
38 import javax.crypto.Mac;
39 import javax.crypto.SecretKey;
40 
41 /**
42  * Mockable wrapper that interacts with Android KeyStore to get hash function used for MAC
43  * randomization.
44  */
45 public class KeystoreWrapper {
46     private static final String TAG = "KeystoreWrapper";
47 
48     /**
49      * Gets the hash function used to calculate persistent randomized MAC address from the KeyStore.
50      */
getHmacSHA256ForUid(int uid, String alias)51     public Mac getHmacSHA256ForUid(int uid, String alias) {
52         try {
53             KeyStore keyStore = AndroidKeyStoreProvider.getKeyStoreForUid(uid);
54             // tries to retrieve the secret, and generate a new one if it's unavailable.
55             Key key = keyStore.getKey(alias, null);
56             if (key == null) {
57                 key = generateAndPersistNewMacRandomizationSecret(uid, alias);
58                 if (key == null) {
59                     Log.e(TAG, "Failed to generate secret for " + alias);
60                     return null;
61                 }
62             }
63             Mac result = Mac.getInstance("HmacSHA256");
64             result.init(key);
65             return result;
66         } catch (KeyStoreException | NoSuchAlgorithmException | InvalidKeyException
67                 | UnrecoverableKeyException | NoSuchProviderException e) {
68             Log.e(TAG, "Failure in getHmacSHA256ForUid", e);
69             return null;
70         } catch (Exception e) {
71             if (SdkLevel.isAtLeastS() && e instanceof BackendBusyException) {
72                 Log.e(TAG, "Failure in getHmacSHA256ForUid", e);
73                 return null;
74             }
75             Log.e(TAG, "Unexpected exception caught in getHmacSHA256ForUid", e);
76             return null;
77         }
78     }
79 
80     /**
81      * Generates and returns a secret key to use for Mac randomization.
82      * Will also persist the generated secret inside KeyStore, accessible in the
83      * future with KeyGenerator#getKey.
84      */
generateAndPersistNewMacRandomizationSecret(int uid, String alias)85     private SecretKey generateAndPersistNewMacRandomizationSecret(int uid, String alias) {
86         try {
87             KeyGenerator keyGenerator = KeyGenerator.getInstance(
88                     KeyProperties.KEY_ALGORITHM_HMAC_SHA256, "AndroidKeyStore");
89             keyGenerator.init(
90                     new KeyGenParameterSpec.Builder(alias,
91                             KeyProperties.PURPOSE_SIGN)
92                             .setUid(uid)
93                             .build());
94             return keyGenerator.generateKey();
95         } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException
96                 | NoSuchProviderException | ProviderException e) {
97             Log.e(TAG, "Failure in generateMacRandomizationSecret", e);
98             return null;
99         }
100     }
101 }
102