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