Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Android] Separate Operational Credentials Issuer #14871

Merged
merged 6 commits into from
Feb 11, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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>;
joonhaengHeo marked this conversation as resolved.
Show resolved Hide resolved

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