blob: d0fe68ae3f61305beb044574abed103bab6ec911 [file] [log] [blame]
/*
* Copyright (c) 2020-2022 Project CHIP Authors
* All rights reserved.
*
* 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 "AndroidDeviceControllerWrapper.h"
#include <lib/support/CHIPJNIError.h>
#include <algorithm>
#include <memory>
#include <lib/support/CodeUtils.h>
#include <lib/support/JniReferences.h>
#include <controller/CHIPDeviceControllerFactory.h>
#include <credentials/attestation_verifier/DefaultDeviceAttestationVerifier.h>
#include <credentials/attestation_verifier/DeviceAttestationVerifier.h>
#include <lib/core/CHIPTLV.h>
#include <lib/support/PersistentStorageMacros.h>
#include <lib/support/SafeInt.h>
#include <lib/support/ScopedBuffer.h>
#include <lib/support/TestGroupData.h>
#include <lib/support/ThreadOperationalDataset.h>
#include <platform/KeyValueStoreManager.h>
using namespace chip;
using namespace chip::Controller;
using namespace chip::Credentials;
using namespace TLV;
AndroidDeviceControllerWrapper::~AndroidDeviceControllerWrapper()
{
if ((mJavaVM != nullptr) && (mJavaObjectRef != nullptr))
{
JniReferences::GetInstance().GetEnvForCurrentThread()->DeleteGlobalRef(mJavaObjectRef);
}
mController->Shutdown();
}
void AndroidDeviceControllerWrapper::SetJavaObjectRef(JavaVM * vm, jobject obj)
{
mJavaVM = vm;
mJavaObjectRef = JniReferences::GetInstance().GetEnvForCurrentThread()->NewGlobalRef(obj);
}
void AndroidDeviceControllerWrapper::CallJavaMethod(const char * methodName, jint argument)
{
JniReferences::GetInstance().CallVoidInt(JniReferences::GetInstance().GetEnvForCurrentThread(), mJavaObjectRef, methodName,
argument);
}
AndroidDeviceControllerWrapper *
AndroidDeviceControllerWrapper::AllocateNew(JavaVM * vm, jobject deviceControllerObj, chip::NodeId nodeId,
const chip::CATValues & cats, chip::System::Layer * systemLayer,
chip::Inet::EndPointManager<Inet::TCPEndPoint> * tcpEndPointManager,
chip::Inet::EndPointManager<Inet::UDPEndPoint> * udpEndPointManager,
AndroidOperationalCredentialsIssuerPtr opCredsIssuerPtr, CHIP_ERROR * errInfoOnFailure)
{
if (errInfoOnFailure == nullptr)
{
ChipLogError(Controller, "Missing error info");
return nullptr;
}
if (systemLayer == nullptr)
{
ChipLogError(Controller, "Missing system layer");
*errInfoOnFailure = CHIP_ERROR_INVALID_ARGUMENT;
return nullptr;
}
if (tcpEndPointManager == nullptr)
{
ChipLogError(Controller, "Missing TCP layer");
*errInfoOnFailure = CHIP_ERROR_INVALID_ARGUMENT;
return nullptr;
}
if (udpEndPointManager == nullptr)
{
ChipLogError(Controller, "Missing UDP layer");
*errInfoOnFailure = CHIP_ERROR_INVALID_ARGUMENT;
return nullptr;
}
*errInfoOnFailure = CHIP_NO_ERROR;
std::unique_ptr<DeviceCommissioner> controller(new DeviceCommissioner());
if (!controller)
{
*errInfoOnFailure = CHIP_ERROR_NO_MEMORY;
return nullptr;
}
std::unique_ptr<AndroidDeviceControllerWrapper> wrapper(
new AndroidDeviceControllerWrapper(std::move(controller), std::move(opCredsIssuerPtr)));
wrapper->SetJavaObjectRef(vm, deviceControllerObj);
chip::Controller::AndroidOperationalCredentialsIssuer * opCredsIssuer = wrapper->mOpCredsIssuer.get();
// Initialize device attestation verifier
// TODO: Replace testingRootStore with a AttestationTrustStore that has the necessary official PAA roots available
const chip::Credentials::AttestationTrustStore * testingRootStore = chip::Credentials::GetTestAttestationTrustStore();
SetDeviceAttestationVerifier(GetDefaultDACVerifier(testingRootStore));
chip::Controller::FactoryInitParams initParams;
chip::Controller::SetupParams setupParams;
initParams.systemLayer = systemLayer;
initParams.tcpEndPointManager = tcpEndPointManager;
initParams.udpEndPointManager = udpEndPointManager;
// move bleLayer into platform/android to share with app server
#if CONFIG_NETWORK_LAYER_BLE
initParams.bleLayer = DeviceLayer::ConnectivityMgr().GetBleLayer();
#endif
initParams.listenPort = CHIP_PORT + 1;
setupParams.pairingDelegate = wrapper.get();
setupParams.operationalCredentialsDelegate = opCredsIssuer;
initParams.fabricIndependentStorage = wrapper.get();
wrapper->mGroupDataProvider.SetStorageDelegate(wrapper.get());
CHIP_ERROR err = wrapper->mGroupDataProvider.Init();
if (err != CHIP_NO_ERROR)
{
*errInfoOnFailure = err;
return nullptr;
}
initParams.groupDataProvider = &wrapper->mGroupDataProvider;
// TODO: Init IPK Epoch Key in opcreds issuer, so that commissionees get the right IPK
opCredsIssuer->Initialize(*wrapper.get(), wrapper.get()->mJavaObjectRef);
Platform::ScopedMemoryBuffer<uint8_t> noc;
if (!noc.Alloc(kMaxCHIPDERCertLength))
{
*errInfoOnFailure = CHIP_ERROR_NO_MEMORY;
return nullptr;
}
MutableByteSpan nocSpan(noc.Get(), kMaxCHIPDERCertLength);
Platform::ScopedMemoryBuffer<uint8_t> icac;
if (!icac.Alloc(kMaxCHIPDERCertLength))
{
*errInfoOnFailure = CHIP_ERROR_NO_MEMORY;
return nullptr;
}
MutableByteSpan icacSpan(icac.Get(), kMaxCHIPDERCertLength);
Platform::ScopedMemoryBuffer<uint8_t> rcac;
if (!rcac.Alloc(kMaxCHIPDERCertLength))
{
*errInfoOnFailure = CHIP_ERROR_NO_MEMORY;
return nullptr;
}
MutableByteSpan rcacSpan(rcac.Get(), kMaxCHIPDERCertLength);
Crypto::P256Keypair ephemeralKey;
*errInfoOnFailure = ephemeralKey.Initialize();
if (*errInfoOnFailure != CHIP_NO_ERROR)
{
return nullptr;
}
*errInfoOnFailure = opCredsIssuer->GenerateNOCChainAfterValidation(nodeId, /* fabricId = */ 1, cats, ephemeralKey.Pubkey(),
rcacSpan, icacSpan, nocSpan);
if (*errInfoOnFailure != CHIP_NO_ERROR)
{
return nullptr;
}
setupParams.operationalKeypair = &ephemeralKey;
setupParams.controllerRCAC = rcacSpan;
setupParams.controllerICAC = icacSpan;
setupParams.controllerNOC = nocSpan;
*errInfoOnFailure = DeviceControllerFactory::GetInstance().Init(initParams);
if (*errInfoOnFailure != CHIP_NO_ERROR)
{
return nullptr;
}
*errInfoOnFailure = DeviceControllerFactory::GetInstance().SetupCommissioner(setupParams, *wrapper->Controller());
if (*errInfoOnFailure != CHIP_NO_ERROR)
{
return nullptr;
}
// Setup IPK
chip::FabricInfo * fabricInfo = wrapper->Controller()->GetFabricInfo();
if (fabricInfo == nullptr)
{
*errInfoOnFailure = CHIP_ERROR_INTERNAL;
return nullptr;
}
uint8_t compressedFabricId[sizeof(uint64_t)] = { 0 };
chip::MutableByteSpan compressedFabricIdSpan(compressedFabricId);
*errInfoOnFailure = fabricInfo->GetCompressedId(compressedFabricIdSpan);
if (*errInfoOnFailure != CHIP_NO_ERROR)
{
return nullptr;
}
ChipLogProgress(Support, "Setting up group data for Fabric Index %u with Compressed Fabric ID:",
static_cast<unsigned>(fabricInfo->GetFabricIndex()));
ChipLogByteSpan(Support, compressedFabricIdSpan);
chip::ByteSpan defaultIpk = chip::GroupTesting::DefaultIpkValue::GetDefaultIpk();
*errInfoOnFailure = chip::Credentials::SetSingleIpkEpochKey(&wrapper->mGroupDataProvider, fabricInfo->GetFabricIndex(),
defaultIpk, compressedFabricIdSpan);
if (*errInfoOnFailure != CHIP_NO_ERROR)
{
return nullptr;
}
return wrapper.release();
}
CHIP_ERROR AndroidDeviceControllerWrapper::ApplyNetworkCredentials(chip::Controller::CommissioningParameters & params,
jobject networkCredentials)
{
chip::DeviceLayer::StackUnlock unlock;
CHIP_ERROR err = CHIP_NO_ERROR;
// Retrieve WiFi or Thread credentials from the NetworkCredentials Java object, and set them in the commissioning params.
JNIEnv * env = chip::JniReferences::GetInstance().GetEnvForCurrentThread();
jmethodID getWiFiCredentials;
err = chip::JniReferences::GetInstance().FindMethod(env, networkCredentials, "getWiFiCredentials",
"()Lchip/devicecontroller/NetworkCredentials$WiFiCredentials;",
&getWiFiCredentials);
VerifyOrReturnError(err == CHIP_NO_ERROR, err);
jobject wifiCredentialsJava = env->CallObjectMethod(networkCredentials, getWiFiCredentials);
jmethodID getThreadCredentials;
err = chip::JniReferences::GetInstance().FindMethod(env, networkCredentials, "getThreadCredentials",
"()Lchip/devicecontroller/NetworkCredentials$ThreadCredentials;",
&getThreadCredentials);
VerifyOrReturnError(err == CHIP_NO_ERROR, err);
jobject threadCredentialsJava = env->CallObjectMethod(networkCredentials, getThreadCredentials);
if (wifiCredentialsJava != nullptr)
{
jmethodID getSsid;
jmethodID getPassword;
err = chip::JniReferences::GetInstance().FindMethod(env, wifiCredentialsJava, "getSsid", "()Ljava/lang/String;", &getSsid);
VerifyOrReturnError(err == CHIP_NO_ERROR, err);
err = chip::JniReferences::GetInstance().FindMethod(env, wifiCredentialsJava, "getPassword", "()Ljava/lang/String;",
&getPassword);
VerifyOrReturnError(err == CHIP_NO_ERROR, err);
ssidStr = static_cast<jstring>(env->NewGlobalRef(env->CallObjectMethod(wifiCredentialsJava, getSsid)));
VerifyOrReturnError(ssidStr != nullptr && !env->ExceptionCheck(), CHIP_JNI_ERROR_EXCEPTION_THROWN);
passwordStr = static_cast<jstring>(env->NewGlobalRef(env->CallObjectMethod(wifiCredentialsJava, getPassword)));
VerifyOrReturnError(ssidStr != nullptr && !env->ExceptionCheck(), CHIP_JNI_ERROR_EXCEPTION_THROWN);
ssid = env->GetStringUTFChars(ssidStr, 0);
password = env->GetStringUTFChars(passwordStr, 0);
jsize ssidLength = env->GetStringUTFLength(ssidStr);
jsize passwordLength = env->GetStringUTFLength(passwordStr);
params.SetWiFiCredentials(
WiFiCredentials(chip::ByteSpan(reinterpret_cast<const uint8_t *>(ssid), static_cast<size_t>(ssidLength)),
chip::ByteSpan(reinterpret_cast<const uint8_t *>(password), static_cast<size_t>(passwordLength))));
}
else if (threadCredentialsJava != nullptr)
{
jmethodID getOperationalDataset;
err = chip::JniReferences::GetInstance().FindMethod(env, threadCredentialsJava, "getOperationalDataset", "()[B",
&getOperationalDataset);
VerifyOrReturnError(err == CHIP_NO_ERROR, err);
operationalDatasetBytes =
static_cast<jbyteArray>(env->NewGlobalRef(env->CallObjectMethod(threadCredentialsJava, getOperationalDataset)));
VerifyOrReturnError(operationalDatasetBytes != nullptr && !env->ExceptionCheck(), CHIP_JNI_ERROR_EXCEPTION_THROWN);
operationalDataset = env->GetByteArrayElements(operationalDatasetBytes, nullptr);
jsize length = env->GetArrayLength(operationalDatasetBytes);
params.SetThreadOperationalDataset(
chip::ByteSpan(reinterpret_cast<const uint8_t *>(operationalDataset), static_cast<size_t>(length)));
}
else
{
ChipLogError(Controller, "Both WiFi and Thread credentials were null in NetworkCredentials");
return CHIP_ERROR_INCORRECT_STATE;
}
return err;
}
void AndroidDeviceControllerWrapper::OnStatusUpdate(chip::Controller::DevicePairingDelegate::Status status)
{
chip::DeviceLayer::StackUnlock unlock;
CallJavaMethod("onStatusUpdate", static_cast<jint>(status));
}
void AndroidDeviceControllerWrapper::OnPairingComplete(CHIP_ERROR error)
{
chip::DeviceLayer::StackUnlock unlock;
CallJavaMethod("onPairingComplete", static_cast<jint>(error.AsInteger()));
}
void AndroidDeviceControllerWrapper::OnPairingDeleted(CHIP_ERROR error)
{
chip::DeviceLayer::StackUnlock unlock;
CallJavaMethod("onPairingDeleted", static_cast<jint>(error.AsInteger()));
}
void AndroidDeviceControllerWrapper::OnCommissioningComplete(NodeId deviceId, CHIP_ERROR error)
{
chip::DeviceLayer::StackUnlock unlock;
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
jmethodID onCommissioningCompleteMethod;
CHIP_ERROR err = JniReferences::GetInstance().FindMethod(env, mJavaObjectRef, "onCommissioningComplete", "(JI)V",
&onCommissioningCompleteMethod);
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Error finding Java method: %" CHIP_ERROR_FORMAT, err.Format()));
env->CallVoidMethod(mJavaObjectRef, onCommissioningCompleteMethod, static_cast<jlong>(deviceId), error.AsInteger());
if (ssidStr != nullptr)
{
env->ReleaseStringUTFChars(ssidStr, ssid);
env->DeleteGlobalRef(ssidStr);
}
if (passwordStr != nullptr)
{
env->ReleaseStringUTFChars(passwordStr, password);
env->DeleteGlobalRef(passwordStr);
}
if (operationalDatasetBytes != nullptr)
{
env->ReleaseByteArrayElements(operationalDatasetBytes, operationalDataset, 0);
env->DeleteGlobalRef(operationalDatasetBytes);
}
}
CHIP_ERROR AndroidDeviceControllerWrapper::SyncGetKeyValue(const char * key, void * value, uint16_t & size)
{
ChipLogProgress(chipTool, "KVS: Getting key %s", key);
size_t read_size = 0;
CHIP_ERROR err = chip::DeviceLayer::PersistedStorage::KeyValueStoreMgr().Get(key, value, size, &read_size);
size = static_cast<uint16_t>(read_size);
return err;
}
CHIP_ERROR AndroidDeviceControllerWrapper::SyncSetKeyValue(const char * key, const void * value, uint16_t size)
{
ChipLogProgress(chipTool, "KVS: Setting key %s", key);
return chip::DeviceLayer::PersistedStorage::KeyValueStoreMgr().Put(key, value, size);
}
CHIP_ERROR AndroidDeviceControllerWrapper::SyncDeleteKeyValue(const char * key)
{
ChipLogProgress(chipTool, "KVS: Deleting key %s", key);
return chip::DeviceLayer::PersistedStorage::KeyValueStoreMgr().Delete(key);
}