Skip to content

Commit

Permalink
[Android] Separate Operational Credentials Issuer (#14871)
Browse files Browse the repository at this point in the history
* Seperate OperationalCredentialsIssuer in Android

* Restyled by whitespace

* Restyled by clang-format

* Remove unused code

Co-authored-by: Restyled.io <commits@restyled.io>
  • Loading branch information
2 people authored and pull[bot] committed Aug 5, 2023
1 parent 1166a48 commit f6d66fc
Show file tree
Hide file tree
Showing 6 changed files with 334 additions and 187 deletions.
170 changes: 22 additions & 148 deletions src/controller/java/AndroidDeviceControllerWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,6 @@ using namespace chip::Controller;
using namespace chip::Credentials;
using namespace TLV;

constexpr const char kOperationalCredentialsIssuerKeypairStorage[] = "AndroidDeviceControllerKey";
constexpr const char kOperationalCredentialsRootCertificateStorage[] = "AndroidCARootCert";
AndroidDeviceControllerWrapper::~AndroidDeviceControllerWrapper()
{
if ((mJavaVM != nullptr) && (mJavaObjectRef != nullptr))
Expand All @@ -62,122 +60,12 @@ void AndroidDeviceControllerWrapper::CallJavaMethod(const char * methodName, jin
argument);
}

CHIP_ERROR AndroidDeviceControllerWrapper::GenerateNOCChainAfterValidation(NodeId nodeId, FabricId fabricId,
const Crypto::P256PublicKey & pubkey,
MutableByteSpan & rcac, MutableByteSpan & icac,
MutableByteSpan & noc)
{
ChipDN noc_dn;
noc_dn.AddAttribute(chip::ASN1::kOID_AttributeType_ChipFabricId, fabricId);
noc_dn.AddAttribute(chip::ASN1::kOID_AttributeType_ChipNodeId, nodeId);
ChipDN rcac_dn;
rcac_dn.AddAttribute(chip::ASN1::kOID_AttributeType_ChipRootId, mIssuerId);
rcac_dn.AddAttribute(chip::ASN1::kOID_AttributeType_ChipFabricId, fabricId);

ChipLogProgress(Controller, "Generating NOC");
chip::Credentials::X509CertRequestParams noc_request = { 1, mNow, mNow + mValidity, noc_dn, rcac_dn };
ReturnErrorOnFailure(NewNodeOperationalX509Cert(noc_request, pubkey, mIssuer, noc));
icac.reduce_size(0);

uint16_t rcacBufLen = static_cast<uint16_t>(std::min(rcac.size(), static_cast<size_t>(UINT16_MAX)));
CHIP_ERROR err = CHIP_NO_ERROR;
PERSISTENT_KEY_OP(fabricId, kOperationalCredentialsRootCertificateStorage, key,
err = SyncGetKeyValue(key, rcac.data(), rcacBufLen));
if (err == CHIP_NO_ERROR)
{
// Found root certificate in the storage.
rcac.reduce_size(rcacBufLen);
return CHIP_NO_ERROR;
}

ChipLogProgress(Controller, "Generating RCAC");
chip::Credentials::X509CertRequestParams rcac_request = { 0, mNow, mNow + mValidity, rcac_dn, rcac_dn };
ReturnErrorOnFailure(NewRootX509Cert(rcac_request, mIssuer, rcac));

VerifyOrReturnError(CanCastTo<uint16_t>(rcac.size()), CHIP_ERROR_INTERNAL);
PERSISTENT_KEY_OP(fabricId, kOperationalCredentialsRootCertificateStorage, key,
err = SyncSetKeyValue(key, rcac.data(), static_cast<uint16_t>(rcac.size())));

return err;
}

// TODO Refactor this API to match latest spec, so that GenerateNodeOperationalCertificate receives the full CSR Elements data
// payload.
CHIP_ERROR AndroidDeviceControllerWrapper::GenerateNOCChain(const ByteSpan & csrElements, const ByteSpan & attestationSignature,
const ByteSpan & DAC, const ByteSpan & PAI, const ByteSpan & PAA,
Callback::Callback<OnNOCChainGeneration> * onCompletion)
{
jmethodID method;
CHIP_ERROR err = CHIP_NO_ERROR;
err = JniReferences::GetInstance().FindMethod(JniReferences::GetInstance().GetEnvForCurrentThread(), mJavaObjectRef,
"onOpCSRGenerationComplete", "([B)V", &method);
if (err != CHIP_NO_ERROR)
{
ChipLogError(Controller, "Error invoking onOpCSRGenerationComplete: %" CHIP_ERROR_FORMAT, err.Format());
return err;
}

NodeId assignedId;
if (mNodeIdRequested)
{
assignedId = mNextRequestedNodeId;
mNodeIdRequested = false;
}
else
{
assignedId = mNextAvailableNodeId++;
}

TLVReader reader;
reader.Init(csrElements);

if (reader.GetType() == kTLVType_NotSpecified)
{
ReturnErrorOnFailure(reader.Next());
}

VerifyOrReturnError(reader.GetType() == kTLVType_Structure, CHIP_ERROR_WRONG_TLV_TYPE);
VerifyOrReturnError(reader.GetTag() == AnonymousTag(), CHIP_ERROR_UNEXPECTED_TLV_ELEMENT);

TLVType containerType;
ReturnErrorOnFailure(reader.EnterContainer(containerType));
ReturnErrorOnFailure(reader.Next(kTLVType_ByteString, TLV::ContextTag(1)));

ByteSpan csr(reader.GetReadPoint(), reader.GetLength());
reader.ExitContainer(containerType);

P256PublicKey pubkey;
ReturnErrorOnFailure(VerifyCertificateSigningRequest(csr.data(), csr.size(), pubkey));

ChipLogProgress(chipTool, "VerifyCertificateSigningRequest");

Platform::ScopedMemoryBuffer<uint8_t> noc;
ReturnErrorCodeIf(!noc.Alloc(kMaxCHIPDERCertLength), CHIP_ERROR_NO_MEMORY);
MutableByteSpan nocSpan(noc.Get(), kMaxCHIPDERCertLength);

Platform::ScopedMemoryBuffer<uint8_t> rcac;
ReturnErrorCodeIf(!rcac.Alloc(kMaxCHIPDERCertLength), CHIP_ERROR_NO_MEMORY);
MutableByteSpan rcacSpan(rcac.Get(), kMaxCHIPDERCertLength);

MutableByteSpan icacSpan;

ReturnErrorOnFailure(GenerateNOCChainAfterValidation(assignedId, mNextFabricId, pubkey, rcacSpan, icacSpan, nocSpan));

onCompletion->mCall(onCompletion->mContext, CHIP_NO_ERROR, nocSpan, ByteSpan(), rcacSpan, Optional<AesCcm128KeySpan>(),
Optional<NodeId>());

jbyteArray javaCsr;
JniReferences::GetInstance().GetEnvForCurrentThread()->ExceptionClear();
JniReferences::GetInstance().N2J_ByteArray(JniReferences::GetInstance().GetEnvForCurrentThread(), csrElements.data(),
csrElements.size(), javaCsr);
JniReferences::GetInstance().GetEnvForCurrentThread()->CallVoidMethod(mJavaObjectRef, method, javaCsr);
return CHIP_NO_ERROR;
}

AndroidDeviceControllerWrapper * AndroidDeviceControllerWrapper::AllocateNew(
JavaVM * vm, jobject deviceControllerObj, chip::NodeId nodeId, chip::System::Layer * systemLayer,
chip::Inet::EndPointManager<Inet::TCPEndPoint> * tcpEndPointManager,
chip::Inet::EndPointManager<Inet::UDPEndPoint> * udpEndPointManager, CHIP_ERROR * errInfoOnFailure)
AndroidDeviceControllerWrapper *
AndroidDeviceControllerWrapper::AllocateNew(JavaVM * vm, jobject deviceControllerObj, chip::NodeId nodeId,
chip::System::Layer * systemLayer,
chip::Inet::EndPointManager<Inet::TCPEndPoint> * tcpEndPointManager,
chip::Inet::EndPointManager<Inet::UDPEndPoint> * udpEndPointManager,
AndroidOperationalCredentialsIssuerPtr opCredsIssuerPtr, CHIP_ERROR * errInfoOnFailure)
{
if (errInfoOnFailure == nullptr)
{
Expand Down Expand Up @@ -212,10 +100,13 @@ AndroidDeviceControllerWrapper * AndroidDeviceControllerWrapper::AllocateNew(
*errInfoOnFailure = CHIP_ERROR_NO_MEMORY;
return nullptr;
}
std::unique_ptr<AndroidDeviceControllerWrapper> wrapper(new AndroidDeviceControllerWrapper(std::move(controller)));
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();
Expand All @@ -235,9 +126,9 @@ AndroidDeviceControllerWrapper * AndroidDeviceControllerWrapper::AllocateNew(
initParams.listenPort = CHIP_PORT + 1;
setupParams.storageDelegate = wrapper.get();
setupParams.pairingDelegate = wrapper.get();
setupParams.operationalCredentialsDelegate = wrapper.get();
setupParams.operationalCredentialsDelegate = opCredsIssuer;

wrapper->InitializeOperationalCredentialsIssuer();
opCredsIssuer->Initialize(*wrapper.get(), wrapper.get()->mJavaObjectRef);

Platform::ScopedMemoryBuffer<uint8_t> noc;
if (!noc.Alloc(kMaxCHIPDERCertLength))
Expand All @@ -247,7 +138,14 @@ AndroidDeviceControllerWrapper * AndroidDeviceControllerWrapper::AllocateNew(
}
MutableByteSpan nocSpan(noc.Get(), kMaxCHIPDERCertLength);

MutableByteSpan icacSpan;
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))
Expand All @@ -264,7 +162,8 @@ AndroidDeviceControllerWrapper * AndroidDeviceControllerWrapper::AllocateNew(
return nullptr;
}

*errInfoOnFailure = wrapper->GenerateNOCChainAfterValidation(nodeId, 0, ephemeralKey.Pubkey(), rcacSpan, icacSpan, nocSpan);
*errInfoOnFailure =
opCredsIssuer->GenerateNOCChainAfterValidation(nodeId, 0, ephemeralKey.Pubkey(), rcacSpan, icacSpan, nocSpan);
if (*errInfoOnFailure != CHIP_NO_ERROR)
{
return nullptr;
Expand Down Expand Up @@ -404,31 +303,6 @@ void AndroidDeviceControllerWrapper::OnCommissioningComplete(NodeId deviceId, CH
}
}

CHIP_ERROR AndroidDeviceControllerWrapper::InitializeOperationalCredentialsIssuer()
{
chip::Crypto::P256SerializedKeypair serializedKey;
uint16_t keySize = static_cast<uint16_t>(sizeof(serializedKey));

// TODO: Use Android keystore system instead of direct storage of private key and add specific errors to check if a specified
// item is not found in the keystore.
if (SyncGetKeyValue(kOperationalCredentialsIssuerKeypairStorage, &serializedKey, keySize) != CHIP_NO_ERROR)
{
// If storage doesn't have an existing keypair, create one and add it to the storage.
ReturnErrorOnFailure(mIssuer.Initialize());
ReturnErrorOnFailure(mIssuer.Serialize(serializedKey));
keySize = static_cast<uint16_t>(sizeof(serializedKey));
SyncSetKeyValue(kOperationalCredentialsIssuerKeypairStorage, &serializedKey, keySize);
}
else
{
// Use the keypair from the storage
ReturnErrorOnFailure(mIssuer.Deserialize(serializedKey));
}

mInitialized = true;
return CHIP_NO_ERROR;
}

CHIP_ERROR AndroidDeviceControllerWrapper::SyncGetKeyValue(const char * key, void * value, uint16_t & size)
{
ChipLogProgress(chipTool, "KVS: Getting key %s", key);
Expand Down
44 changes: 9 additions & 35 deletions src/controller/java/AndroidDeviceControllerWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,18 @@
#include <jni.h>

#include <controller/CHIPDeviceController.h>
#include <controller/ExampleOperationalCredentialsIssuer.h>
#include <lib/support/TimeUtils.h>
#include <platform/internal/DeviceNetworkInfo.h>

#include "AndroidOperationalCredentialsIssuer.h"

/**
* This class contains all relevant information for the JNI view of CHIPDeviceController
* to handle all controller-related processing.
*
* Generally it contains the DeviceController class itself, plus any related delegates/callbacks.
*/
class AndroidDeviceControllerWrapper : public chip::Controller::DevicePairingDelegate,
public chip::Controller::OperationalCredentialsDelegate,
public chip::PersistentStorageDelegate,
public chip::FabricStorage
{
Expand All @@ -61,19 +61,6 @@ class AndroidDeviceControllerWrapper : public chip::Controller::DevicePairingDel
void OnPairingDeleted(CHIP_ERROR error) override;
void OnCommissioningComplete(chip::NodeId deviceId, CHIP_ERROR error) override;

// OperationalCredentialsDelegate implementation
CHIP_ERROR GenerateNOCChain(const chip::ByteSpan & csrElements, const chip::ByteSpan & attestationSignature,
const chip::ByteSpan & DAC, const chip::ByteSpan & PAI, const chip::ByteSpan & PAA,
chip::Callback::Callback<chip::Controller::OnNOCChainGeneration> * onCompletion) override;

void SetNodeIdForNextNOCRequest(chip::NodeId nodeId) override
{
mNextRequestedNodeId = nodeId;
mNodeIdRequested = true;
}

void SetFabricIdForNextNOCRequest(chip::FabricId fabricId) override { mNextFabricId = fabricId; }

// PersistentStorageDelegate implementation
CHIP_ERROR SyncSetKeyValue(const char * key, const void * value, uint16_t size) override;
CHIP_ERROR SyncGetKeyValue(const char * key, void * buffer, uint16_t & size) override;
Expand All @@ -89,36 +76,24 @@ class AndroidDeviceControllerWrapper : public chip::Controller::DevicePairingDel
return reinterpret_cast<AndroidDeviceControllerWrapper *>(handle);
}

using AndroidOperationalCredentialsIssuerPtr = std::unique_ptr<chip::Controller::AndroidOperationalCredentialsIssuer>;

static AndroidDeviceControllerWrapper * AllocateNew(JavaVM * vm, jobject deviceControllerObj, chip::NodeId nodeId,
chip::System::Layer * systemLayer,
chip::Inet::EndPointManager<chip::Inet::TCPEndPoint> * tcpEndPointManager,
chip::Inet::EndPointManager<chip::Inet::UDPEndPoint> * udpEndPointManager,
AndroidOperationalCredentialsIssuerPtr opCredsIssuer,
CHIP_ERROR * errInfoOnFailure);

CHIP_ERROR GenerateNOCChainAfterValidation(chip::NodeId nodeId, chip::FabricId fabricId,
const chip::Crypto::P256PublicKey & ephemeralKey, chip::MutableByteSpan & rcac,
chip::MutableByteSpan & icac, chip::MutableByteSpan & noc);

private:
using ChipDeviceControllerPtr = std::unique_ptr<chip::Controller::DeviceCommissioner>;
chip::Crypto::P256Keypair mIssuer;
bool mInitialized = false;
uint32_t mIssuerId = 0;
uint32_t mNow = 0;
uint32_t mValidity = 10 * chip::kSecondsPerStandardYear;

ChipDeviceControllerPtr mController;
chip::Controller::ExampleOperationalCredentialsIssuer mOpCredsIssuer;
AndroidOperationalCredentialsIssuerPtr mOpCredsIssuer;

JavaVM * mJavaVM = nullptr;
jobject mJavaObjectRef = nullptr;

chip::NodeId mNextAvailableNodeId = 1;

chip::NodeId mNextRequestedNodeId = 1;
chip::FabricId mNextFabricId = 0;
bool mNodeIdRequested = false;

// These fields allow us to release the string/byte array memory later.
jstring ssidStr = nullptr;
jstring passwordStr = nullptr;
Expand All @@ -127,10 +102,9 @@ class AndroidDeviceControllerWrapper : public chip::Controller::DevicePairingDel
jbyteArray operationalDatasetBytes = nullptr;
jbyte * operationalDataset = nullptr;

AndroidDeviceControllerWrapper(ChipDeviceControllerPtr controller) : mController(std::move(controller))
{
chip::CalendarToChipEpochTime(2021, 06, 10, 0, 0, 0, mNow);
}
AndroidDeviceControllerWrapper(ChipDeviceControllerPtr controller, AndroidOperationalCredentialsIssuerPtr opCredsIssuer) :
mController(std::move(controller)), mOpCredsIssuer(std::move(opCredsIssuer))
{}
};

inline jlong AndroidDeviceControllerWrapper::ToJNIHandle()
Expand Down
Loading

0 comments on commit f6d66fc

Please sign in to comment.