/* * * Copyright (c) 2021-2022 Project CHIP Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include using namespace chip; using namespace chip::Crypto; using namespace chip::Credentials; namespace chip { namespace DeviceLayer { namespace NetworkCommissioning { #if CHIP_DEVICE_CONFIG_ENABLE_WPA // TODO(#14172): Here, most interfaces are just calling ConnectivityManager interfaces, this is because the ConnectivityProvides // some bootstrap code for the wpa_supplicant. However, we can wrap the wpa_supplicant dbus api directly (and remove the related // code in ConnectivityManagerImpl). namespace { constexpr char kWiFiSSIDKeyName[] = "wifi-ssid"; constexpr char kWiFiCredentialsKeyName[] = "wifi-pass"; #if CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC constexpr char kWifiNetworkIdentityKeyName[] = "wifi-ni"; constexpr char kWifiClientIdentityKeyName[] = "wifi-ci"; constexpr char kWifiClientIdentityKeypairKeyName[] = "wifi-cik"; #endif // CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC inline CHIP_ERROR IgnoreNotFound(CHIP_ERROR err) { return (err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND) ? CHIP_NO_ERROR : err; } } // namespace // NOTE: For WiFiDriver, we uses two network configs, one is mSavedNetwork, and another is mStagingNetwork, during init, it will // load the network config from k-v storage, and loads it into both mSavedNetwork and mStagingNetwork. When updating the networks, // all changed are made on the staging network, and when the network is committed, it will update the mSavedNetwork to // mStagingNetwork and persist the changes. // NOTE: LinuxWiFiDriver uses network config with empty ssid (ssidLen = 0) for empty network config. // NOTE: For now, the LinuxWiFiDriver only supports one network, this can be fixed by using the wpa_supplicant API directly (then // wpa_supplicant will manage the networks for us.) CHIP_ERROR LinuxWiFiDriver::Init(BaseDriver::NetworkStatusChangeCallback * networkStatusChangeCallback) { CHIP_ERROR err; WiFiNetwork network; size_t valueLen = 0; auto & kvs = PersistedStorage::KeyValueStoreMgr(); SuccessOrExit(err = IgnoreNotFound(kvs.Get(kWiFiSSIDKeyName, network.ssid, sizeof(network.ssid), &valueLen))); if (valueLen != 0) { network.ssidLen = valueLen; err = kvs.Get(kWiFiCredentialsKeyName, network.credentials, sizeof(network.credentials), &valueLen); #if CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC if (err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND) { SuccessOrExit( err = kvs.Get(kWifiNetworkIdentityKeyName, network.networkIdentity, sizeof(network.networkIdentity), &valueLen)); VerifyOrExit(valueLen > 0, err = CHIP_ERROR_INTEGRITY_CHECK_FAILED); network.networkIdentityLen = valueLen; SuccessOrExit( err = kvs.Get(kWifiClientIdentityKeyName, network.clientIdentity, sizeof(network.clientIdentity), &valueLen)); VerifyOrExit(valueLen > 0, err = CHIP_ERROR_INTEGRITY_CHECK_FAILED); network.clientIdentityLen = valueLen; P256SerializedKeypair serializedKeypair; SuccessOrExit(err = kvs.Get(kWifiClientIdentityKeypairKeyName, serializedKeypair.Bytes(), serializedKeypair.Capacity(), &valueLen)); serializedKeypair.SetLength(valueLen); network.clientIdentityKeypair = Platform::MakeShared(); SuccessOrExit(err = network.clientIdentityKeypair->Deserialize(serializedKeypair)); } else #endif // CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC { SuccessOrExit(err); network.credentialsLen = valueLen; } mStagingNetwork = mSavedNetwork = network; } ConnectivityMgrImpl().SetNetworkStatusChangeCallback(networkStatusChangeCallback); return CHIP_NO_ERROR; exit: ChipLogProgress(NetworkProvisioning, "LinuxWiFiDriver: Failed to load network configuration: %" CHIP_ERROR_FORMAT, err.Format()); return err; } void LinuxWiFiDriver::Shutdown() { ConnectivityMgrImpl().SetNetworkStatusChangeCallback(nullptr); } CHIP_ERROR LinuxWiFiDriver::CommitConfiguration() { auto & kvs = PersistedStorage::KeyValueStoreMgr(); ReturnErrorOnFailure(kvs.Put(kWiFiSSIDKeyName, mStagingNetwork.ssid, mStagingNetwork.ssidLen)); #if CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC if (mStagingNetwork.UsingPDC()) { ReturnErrorOnFailure(IgnoreNotFound(kvs.Delete(kWiFiCredentialsKeyName))); ReturnErrorOnFailure( kvs.Put(kWifiNetworkIdentityKeyName, mStagingNetwork.networkIdentity, mStagingNetwork.networkIdentityLen)); ReturnErrorOnFailure( kvs.Put(kWifiClientIdentityKeyName, mStagingNetwork.clientIdentity, mStagingNetwork.clientIdentityLen)); P256SerializedKeypair serializedKeypair; ReturnErrorOnFailure(mStagingNetwork.clientIdentityKeypair->Serialize(serializedKeypair)); ReturnErrorOnFailure( kvs.Put(kWifiClientIdentityKeypairKeyName, serializedKeypair.ConstBytes(), serializedKeypair.Length())); } else { ReturnErrorOnFailure(IgnoreNotFound(kvs.Delete(kWifiNetworkIdentityKeyName))); ReturnErrorOnFailure(IgnoreNotFound(kvs.Delete(kWifiClientIdentityKeyName))); ReturnErrorOnFailure(IgnoreNotFound(kvs.Delete(kWifiClientIdentityKeypairKeyName))); #else // CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC { #endif // CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC ReturnErrorOnFailure(kvs.Put(kWiFiCredentialsKeyName, mStagingNetwork.credentials, mStagingNetwork.credentialsLen)); } ReturnErrorOnFailure(ConnectivityMgrImpl().CommitConfig()); mSavedNetwork = mStagingNetwork; return CHIP_NO_ERROR; } CHIP_ERROR LinuxWiFiDriver::RevertConfiguration() { mStagingNetwork = mSavedNetwork; return CHIP_NO_ERROR; } Status LinuxWiFiDriver::AddOrUpdateNetwork(ByteSpan ssid, ByteSpan credentials, MutableCharSpan & outDebugText, uint8_t & outNetworkIndex) { outDebugText.reduce_size(0); outNetworkIndex = 0; VerifyOrReturnError(mStagingNetwork.Empty() || mStagingNetwork.Matches(ssid), Status::kBoundsExceeded); // Do the check before setting the values, so the data is not updated on error. VerifyOrReturnError(credentials.size() <= sizeof(mStagingNetwork.credentials), Status::kOutOfRange); VerifyOrReturnError(!ssid.empty() && ssid.size() <= sizeof(mStagingNetwork.ssid), Status::kOutOfRange); memcpy(mStagingNetwork.credentials, credentials.data(), credentials.size()); mStagingNetwork.credentialsLen = static_cast(credentials.size()); memcpy(mStagingNetwork.ssid, ssid.data(), ssid.size()); mStagingNetwork.ssidLen = static_cast(ssid.size()); #if CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC mStagingNetwork.networkIdentityLen = 0; mStagingNetwork.clientIdentityLen = 0; mStagingNetwork.clientIdentityKeypair.reset(); #endif // CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC return Status::kSuccess; } Status LinuxWiFiDriver::RemoveNetwork(ByteSpan networkId, MutableCharSpan & outDebugText, uint8_t & outNetworkIndex) { outDebugText.reduce_size(0); outNetworkIndex = 0; VerifyOrReturnError(mStagingNetwork.Matches(networkId), Status::kNetworkIDNotFound); // Use empty ssid for representing invalid network mStagingNetwork.ssidLen = 0; return Status::kSuccess; } Status LinuxWiFiDriver::ReorderNetwork(ByteSpan networkId, uint8_t index, MutableCharSpan & outDebugText) { outDebugText.reduce_size(0); VerifyOrReturnError(mStagingNetwork.Matches(networkId), Status::kNetworkIDNotFound); VerifyOrReturnError(index == 0, Status::kOutOfRange); // We only support one network, so reorder is actually no-op. return Status::kSuccess; } void LinuxWiFiDriver::ConnectNetwork(ByteSpan networkId, ConnectCallback * callback) { CHIP_ERROR err = CHIP_NO_ERROR; Status networkingStatus = Status::kSuccess; const auto & network = mStagingNetwork; VerifyOrExit(network.Matches(networkId), networkingStatus = Status::kNetworkIDNotFound); #if CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC if (network.UsingPDC()) { ChipLogProgress(NetworkProvisioning, "LinuxWiFiDriver: ConnectNetwork (PDC) '%.*s'", network.ssidLen, network.ssid); err = ConnectivityMgrImpl().ConnectWiFiNetworkWithPDCAsync( ByteSpan(network.ssid, network.ssidLen), ByteSpan(network.networkIdentity, network.networkIdentityLen), ByteSpan(network.clientIdentity, network.clientIdentityLen), *network.clientIdentityKeypair, callback); } else #endif // CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC { ChipLogProgress(NetworkProvisioning, "LinuxWiFiDriver: ConnectNetwork '%.*s'", network.ssidLen, network.ssid); err = ConnectivityMgrImpl().ConnectWiFiNetworkAsync(ByteSpan(network.ssid, network.ssidLen), ByteSpan(network.credentials, network.credentialsLen), callback); } exit: if (err != CHIP_NO_ERROR) { networkingStatus = Status::kUnknownError; } if (networkingStatus != Status::kSuccess) { ChipLogError(NetworkProvisioning, "Failed to connect to WiFi network: %" CHIP_ERROR_FORMAT, err.Format()); callback->OnResult(networkingStatus, CharSpan(), 0); } } void LinuxWiFiDriver::ScanNetworks(ByteSpan ssid, WiFiDriver::ScanCallback * callback) { CHIP_ERROR err = DeviceLayer::ConnectivityMgrImpl().StartWiFiScan(ssid, callback); if (err != CHIP_NO_ERROR) { callback->OnFinished(Status::kUnknownError, CharSpan(), nullptr); } } size_t LinuxWiFiDriver::WiFiNetworkIterator::Count() { return driver->mStagingNetwork.Empty() ? 0 : 1; } bool LinuxWiFiDriver::WiFiNetworkIterator::Next(Network & item) { if (exhausted || driver->mStagingNetwork.Empty()) { return false; } memcpy(item.networkID, driver->mStagingNetwork.ssid, driver->mStagingNetwork.ssidLen); item.networkIDLen = driver->mStagingNetwork.ssidLen; item.connected = false; exhausted = true; Network configuredNetwork; CHIP_ERROR err = DeviceLayer::ConnectivityMgrImpl().GetConfiguredNetwork(configuredNetwork); if (err == CHIP_NO_ERROR) { if (DeviceLayer::ConnectivityMgrImpl().IsWiFiStationConnected() && configuredNetwork.networkIDLen == item.networkIDLen && memcmp(configuredNetwork.networkID, item.networkID, item.networkIDLen) == 0) { item.connected = true; } } return true; } #if CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC CHIP_ERROR LinuxWiFiDriver::AddOrUpdateNetworkWithPDC(ByteSpan ssid, ByteSpan networkIdentity, Optional clientIdentityNetworkIndex, Status & outStatus, MutableCharSpan & outDebugText, MutableByteSpan & outClientIdentity, uint8_t & outNetworkIndex) { CHIP_ERROR err = CHIP_NO_ERROR; outStatus = Status::kUnknownError; VerifyOrExit(mStagingNetwork.Empty() || mStagingNetwork.Matches(ssid), outStatus = Status::kBoundsExceeded); VerifyOrExit(!ssid.empty() && ssid.size() <= sizeof(WiFiNetwork::ssid), outStatus = Status::kOutOfRange); VerifyOrExit(!networkIdentity.empty() && networkIdentity.size() <= sizeof(WiFiNetwork::networkIdentity), outStatus = Status::kOutOfRange); VerifyOrExit(!clientIdentityNetworkIndex.HasValue() || (clientIdentityNetworkIndex.Value() == 0 && mStagingNetwork.UsingPDC()), outStatus = Status::kOutOfRange); { WiFiNetwork network = mStagingNetwork; // update a copy first in case of errors memcpy(network.ssid, ssid.data(), network.ssidLen = ssid.size()); memcpy(network.networkIdentity, networkIdentity.data(), network.networkIdentityLen = networkIdentity.size()); // If an existing client identity is being reused, we would need to copy it here, // but since we're only supporting a single network we simply don't overwrite it. if (!clientIdentityNetworkIndex.HasValue()) { network.clientIdentityKeypair = Platform::MakeShared(); SuccessOrExit(err = network.clientIdentityKeypair->Initialize(ECPKeyTarget::ECDSA)); MutableByteSpan clientIdentity(network.clientIdentity); SuccessOrExit(err = NewChipNetworkIdentity(*network.clientIdentityKeypair, clientIdentity)); network.clientIdentityLen = clientIdentity.size(); } network.credentialsLen = 0; SuccessOrExit(err = CopySpanToMutableSpan(ByteSpan(network.clientIdentity, network.clientIdentityLen), outClientIdentity)); mStagingNetwork = network; outNetworkIndex = 0; outStatus = Status::kSuccess; } exit: outDebugText.reduce_size(0); return err; } CHIP_ERROR LinuxWiFiDriver::GetNetworkIdentity(uint8_t networkIndex, MutableByteSpan & outNetworkIdentity) { VerifyOrReturnError(!mStagingNetwork.Empty() && networkIndex == 0, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(mStagingNetwork.UsingPDC(), CHIP_ERROR_INVALID_ARGUMENT); return CopySpanToMutableSpan(ByteSpan(mStagingNetwork.networkIdentity, mStagingNetwork.networkIdentityLen), outNetworkIdentity); } CHIP_ERROR LinuxWiFiDriver::GetClientIdentity(uint8_t networkIndex, MutableByteSpan & outClientIdentity) { VerifyOrReturnError(!mStagingNetwork.Empty() && networkIndex == 0, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(mStagingNetwork.UsingPDC(), CHIP_ERROR_INVALID_ARGUMENT); return CopySpanToMutableSpan(ByteSpan(mStagingNetwork.clientIdentity, mStagingNetwork.clientIdentityLen), outClientIdentity); } CHIP_ERROR LinuxWiFiDriver::SignWithClientIdentity(uint8_t networkIndex, const ByteSpan & message, P256ECDSASignature & outSignature) { VerifyOrReturnError(!mStagingNetwork.Empty() && networkIndex == 0, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(mStagingNetwork.UsingPDC(), CHIP_ERROR_INVALID_ARGUMENT); return mStagingNetwork.clientIdentityKeypair->ECDSA_sign_msg(message.data(), message.size(), outSignature); } #endif // CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC #endif // CHIP_DEVICE_CONFIG_ENABLE_WPA } // namespace NetworkCommissioning } // namespace DeviceLayer } // namespace chip