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