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 if (nik == null) return false; 122 SecretKeySpec spec = new SecretKeySpec(nik, "HmacSHA256"); 123 124 try { 125 Mac hash = Mac.getInstance("HmacSHA256"); 126 hash.init(spec); 127 hash.update(mac); 128 hash.update(nonce); 129 byte[] message = Arrays.copyOf(hash.doFinal(), TAG_SIZE_IN_BYTE); 130 if (Arrays.equals(tag, message)) { 131 return true; 132 } 133 134 } catch (NoSuchAlgorithmException | InvalidKeyException e) { 135 Log.e(TAG, "Unexpected exception caught in getPairedDeviceAlias", e); 136 } 137 return false; 138 } 139 140 /** 141 * Get the cached security info fo the target alias. 142 */ getSecurityInfoPairedDevice(String alias)143 public PairingSecurityAssociationInfo getSecurityInfoPairedDevice(String alias) { 144 return mAliasToSecurityInfoMap.get(alias); 145 } 146 147 /** 148 * Add the NAN pairing sercurity info to the cache. 149 */ addPairedDeviceSecurityAssociation(String packageName, String alias, PairingSecurityAssociationInfo info)150 public void addPairedDeviceSecurityAssociation(String packageName, String alias, 151 PairingSecurityAssociationInfo info) { 152 if (info == null) { 153 return; 154 } 155 Set<String> pairedDevices = mPerAppPairedAliasMap.get(packageName); 156 if (pairedDevices == null) { 157 pairedDevices = new HashSet<>(); 158 } 159 pairedDevices.add(alias); 160 mAliasToNikMap.put(alias, info.mPeerNik); 161 mAliasToSecurityInfoMap.put(alias, info); 162 } 163 164 /** 165 * Remove all the caches related to the target calling App 166 */ removePackage(String packageName)167 public void removePackage(String packageName) { 168 mPackageNameToNikMap.remove(packageName); 169 Set<String> aliasSet = mPerAppPairedAliasMap.remove(packageName); 170 if (aliasSet == null) { 171 return; 172 } 173 for (String alias : aliasSet) { 174 mAliasToNikMap.remove(alias); 175 mAliasToSecurityInfoMap.remove(alias); 176 } 177 } 178 179 /** 180 * Remove the caches related to the target alias 181 */ removePairedDevice(String packageName, String alias)182 public void removePairedDevice(String packageName, String alias) { 183 mAliasToNikMap.remove(alias); 184 mAliasToSecurityInfoMap.remove(alias); 185 Set<String> aliasSet = mPerAppPairedAliasMap.remove(packageName); 186 if (aliasSet == null) { 187 return; 188 } 189 aliasSet.remove(alias); 190 } 191 192 /** 193 * Get all paired devices alias for target calling app 194 */ getAllPairedDevices(String callingPackage)195 public List<String> getAllPairedDevices(String callingPackage) { 196 return new ArrayList<>(mPerAppPairedAliasMap.get(callingPackage)); 197 } 198 } 199