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.aware;
18 
19 import android.util.Log;
20 
21 import java.nio.ByteBuffer;
22 import java.security.InvalidKeyException;
23 import java.security.NoSuchAlgorithmException;
24 import java.security.SecureRandom;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.HashMap;
28 import java.util.HashSet;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Random;
32 import java.util.Set;
33 
34 import javax.crypto.Mac;
35 import javax.crypto.spec.SecretKeySpec;
36 
37 /**
38  * The manager to store and maintain the NAN identity key and NPK/NIK caching
39  */
40 public class PairingConfigManager {
41 
42     private static final String TAG = "AwarePairingManager";
43 
44     private static final int NIK_SIZE_IN_BYTE = 16;
45     private static final int TAG_SIZE_IN_BYTE = 8;
46 
47     /**
48      * Store the NPKSA from the NAN Pairing confirmation
49      */
50     public static class PairingSecurityAssociationInfo {
51         public final byte[] mPeerNik;
52         public final byte[] mNpk;
53         public final int mAkm;
54         public final byte[] mLocalNik;
55 
56         public final int mCipherSuite;
57 
PairingSecurityAssociationInfo(byte[] peerNik, byte[] localNik, byte[] npk, int akm, int cipherSuite)58         public PairingSecurityAssociationInfo(byte[] peerNik, byte[] localNik, byte[] npk,
59                 int akm, int cipherSuite) {
60             mPeerNik = peerNik;
61             mNpk = npk;
62             mAkm = akm;
63             mLocalNik = localNik;
64             mCipherSuite = cipherSuite;
65         }
66     }
67 
68     private final Map<String, byte[]> mPackageNameToNikMap = new HashMap<>();
69     private final Map<String, Set<String>> mPerAppPairedAliasMap = new HashMap<>();
70     private final Map<String, byte[]> mAliasToNikMap = new HashMap<>();
71     private final Map<String, PairingSecurityAssociationInfo> mAliasToSecurityInfoMap =
72             new HashMap<>();
PairingConfigManager()73     public PairingConfigManager() {
74     }
75 
createRandomNik()76     private byte[] createRandomNik() {
77         long first, second;
78 
79         Random mRandom = new SecureRandom();
80         first = mRandom.nextLong();
81         second = mRandom.nextLong();
82         ByteBuffer buffer = ByteBuffer.allocate(NIK_SIZE_IN_BYTE);
83         buffer.putLong(first);
84         buffer.putLong(second);
85         return buffer.array();
86     }
87 
88     /**
89      * Get the NAN identity key of the app
90      * @param packageName the package name of the calling App
91      * @return the NAN identity key for this app
92      */
getNikForCallingPackage(String packageName)93     public byte[] getNikForCallingPackage(String packageName) {
94         if (mPackageNameToNikMap.get(packageName) != null) {
95             return mPackageNameToNikMap.get(packageName);
96         }
97 
98         byte[] nik = createRandomNik();
99         mPackageNameToNikMap.put(packageName, nik);
100         return nik;
101     }
102 
103     /**
104      * Get the matched pairing device's alias set by the app
105      */
getPairedDeviceAlias(String packageName, byte[] nonce, byte[] tag, byte[] mac)106     public String getPairedDeviceAlias(String packageName, byte[] nonce, byte[] tag, byte[] mac) {
107         Set<String> aliasSet = mPerAppPairedAliasMap.get(packageName);
108         if (aliasSet == null) {
109             return null;
110         }
111         for (String alias : aliasSet) {
112             if (checkMatchAlias(alias, nonce, tag, mac)) {
113                 return alias;
114             }
115         }
116         return null;
117     }
118 
checkMatchAlias(String alias, byte[] nonce, byte[] tag, byte[] mac)119     private boolean checkMatchAlias(String alias, byte[] nonce, byte[] tag, byte[] mac) {
120         byte[] nik = mAliasToNikMap.get(alias);
121         byte[] nir = {'N', 'I', 'R'};
122         if (nik == null) return false;
123         SecretKeySpec spec = new SecretKeySpec(nik, "HmacSHA256");
124 
125         try {
126             Mac hash = Mac.getInstance("HmacSHA256");
127             hash.init(spec);
128             hash.update(nir);
129             hash.update(mac);
130             hash.update(nonce);
131             byte[] message = Arrays.copyOf(hash.doFinal(), TAG_SIZE_IN_BYTE);
132             if (Arrays.equals(tag, message)) {
133                 return true;
134             }
135 
136         } catch (NoSuchAlgorithmException | InvalidKeyException e) {
137             Log.e(TAG, "Unexpected exception caught in getPairedDeviceAlias", e);
138         }
139         return false;
140     }
141 
142     /**
143      * Get the cached security info fo the target alias.
144      */
getSecurityInfoPairedDevice(String alias)145     public PairingSecurityAssociationInfo getSecurityInfoPairedDevice(String alias) {
146         return mAliasToSecurityInfoMap.get(alias);
147     }
148 
149     /**
150      * Add the NAN pairing sercurity info to the cache.
151      */
addPairedDeviceSecurityAssociation(String packageName, String alias, PairingSecurityAssociationInfo info)152     public void addPairedDeviceSecurityAssociation(String packageName, String alias,
153             PairingSecurityAssociationInfo info) {
154         if (info == null) {
155             return;
156         }
157         Set<String> pairedDevices = mPerAppPairedAliasMap.get(packageName);
158         if (pairedDevices == null) {
159             pairedDevices = new HashSet<>();
160             mPerAppPairedAliasMap.put(packageName, pairedDevices);
161         }
162         pairedDevices.add(alias);
163         mAliasToNikMap.put(alias, info.mPeerNik);
164         mAliasToSecurityInfoMap.put(alias, info);
165     }
166 
167     /**
168      * Remove all the caches related to the target calling App
169      */
removePackage(String packageName)170     public void removePackage(String packageName) {
171         mPackageNameToNikMap.remove(packageName);
172         Set<String> aliasSet = mPerAppPairedAliasMap.remove(packageName);
173         if (aliasSet == null) {
174             return;
175         }
176         for (String alias : aliasSet) {
177             mAliasToNikMap.remove(alias);
178             mAliasToSecurityInfoMap.remove(alias);
179         }
180     }
181 
182     /**
183      * Remove the caches related to the target alias
184      */
removePairedDevice(String packageName, String alias)185     public void removePairedDevice(String packageName, String alias) {
186         mAliasToNikMap.remove(alias);
187         mAliasToSecurityInfoMap.remove(alias);
188         Set<String> aliasSet = mPerAppPairedAliasMap.remove(packageName);
189         if (aliasSet == null) {
190             return;
191         }
192         aliasSet.remove(alias);
193     }
194 
195     /**
196      * Get all paired devices alias for target calling app
197      */
getAllPairedDevices(String callingPackage)198     public List<String> getAllPairedDevices(String callingPackage) {
199         return new ArrayList<>(mPerAppPairedAliasMap.get(callingPackage));
200     }
201 }
202