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