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

Support passing csr nonce into commissioning flow #7696

Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,8 @@ open class GenericChipDeviceListener : ChipDeviceController.CompletionListener {
override fun onError(error: Throwable?) {
// No op
}

override fun onOpCSRGenerationComplete(errorCode: ByteArray) {
// No op
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@ class DeviceProvisioningFragment : Fragment() {
}
}

override fun onOpCSRGenerationComplete(errorCode: ByteArray) {
Log.d(TAG,String(errorCode))
}

override fun onPairingDeleted(code: Int) {
Log.d(TAG, "onPairingDeleted: $code")
}
Expand Down
7 changes: 6 additions & 1 deletion src/controller/CHIPDevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,12 @@ class DLL_EXPORT Device : public Messaging::ExchangeDelegate, public SessionEsta

CASESession & GetCASESession() { return mCASESession; }

CHIP_ERROR GenerateCSRNonce() { return Crypto::DRBG_get_bytes(mCSRNonce, sizeof(mCSRNonce)); }
CHIP_ERROR SetCSRNonce(ByteSpan csrNonce)
{
VerifyOrReturnError(csrNonce.size() == sizeof(mCSRNonce), CHIP_ERROR_INVALID_ARGUMENT);
memcpy(mCSRNonce, csrNonce.data(), csrNonce.size());
return CHIP_NO_ERROR;
}

ByteSpan GetCSRNonce() const { return ByteSpan(mCSRNonce, sizeof(mCSRNonce)); }

Expand Down
13 changes: 12 additions & 1 deletion src/controller/CHIPDeviceController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -880,6 +880,18 @@ CHIP_ERROR DeviceCommissioner::PairDevice(NodeId remoteDeviceId, RendezvousParam
VerifyOrExit(mDeviceBeingPaired < kNumMaxActiveDevices, err = CHIP_ERROR_NO_MEMORY);
device = &mActiveDevices[mDeviceBeingPaired];

// If the CSRNonce is passed in, using that else using a random one..
if (params.HasCSRNonce())
{
VerifyOrReturnError(device->SetCSRNonce(params.GetCSRNonce().Value()) == CHIP_NO_ERROR, CHIP_ERROR_INVALID_ARGUMENT);
}
else
{
uint8_t mCSRNonce[kOpCSRNonceLength];
Crypto::DRBG_get_bytes(mCSRNonce, sizeof(mCSRNonce));
VerifyOrReturnError(device->SetCSRNonce(ByteSpan(mCSRNonce)) == CHIP_NO_ERROR, CHIP_ERROR_INVALID_ARGUMENT);
}

mIsIPRendezvous = (params.GetPeerAddress().GetTransportType() != Transport::Type::kBle);

err = mPairingSession.MessageDispatch().Init(mTransportMgr);
Expand Down Expand Up @@ -1166,7 +1178,6 @@ CHIP_ERROR DeviceCommissioner::SendOperationalCertificateSigningRequestCommand(D
Callback::Cancelable * successCallback = mOpCSRResponseCallback.Cancel();
Callback::Cancelable * failureCallback = mOnCSRFailureCallback.Cancel();

ReturnErrorOnFailure(device->GenerateCSRNonce());
ReturnErrorOnFailure(cluster.OpCSRRequest(successCallback, failureCallback, device->GetCSRNonce()));
ChipLogDetail(Controller, "Sent OpCSR request, waiting for the CSR");
return CHIP_NO_ERROR;
Expand Down
89 changes: 82 additions & 7 deletions src/controller/java/AndroidDeviceControllerWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,17 @@
#include <algorithm>
#include <memory>

#include "JniReferences.h"
#include <support/CodeUtils.h>

#include <platform/KeyValueStoreManager.h>
#include <support/ThreadOperationalDataset.h>

using namespace chip::Controller;

extern chip::Ble::BleLayer * GetJNIBleLayer();

constexpr const char kOperationalCredentialsIssuerKeypairStorage[] = "AndroidDeviceControllerKey";
AndroidDeviceControllerWrapper::~AndroidDeviceControllerWrapper()
{
if ((mJavaVM != nullptr) && (mJavaObjectRef != nullptr))
Expand All @@ -50,6 +54,15 @@ void AndroidDeviceControllerWrapper::CallJavaMethod(const char * methodName, jin
argument);
}

CHIP_ERROR AndroidDeviceControllerWrapper::GetRootCACertificate(chip::FabricId fabricId, uint8_t * certBuf, uint32_t certBufSize,
uint32_t & outCertLen)
{
Initialize();
woody-apple marked this conversation as resolved.
Show resolved Hide resolved
VerifyOrReturnError(mInitialized, CHIP_ERROR_INCORRECT_STATE);
chip::X509CertRequestParams newCertParams = { 0, mIssuerId, mNow, mNow + mValidity, true, fabricId, false, 0 };
return NewRootX509Cert(newCertParams, mIssuer, certBuf, certBufSize, outCertLen);
}

AndroidDeviceControllerWrapper * AndroidDeviceControllerWrapper::AllocateNew(JavaVM * vm, jobject deviceControllerObj,
pthread_mutex_t * stackLock, chip::NodeId nodeId,
chip::System::Layer * systemLayer,
Expand Down Expand Up @@ -90,20 +103,19 @@ AndroidDeviceControllerWrapper * AndroidDeviceControllerWrapper::AllocateNew(Jav

chip::Controller::CommissionerInitParams initParams;

initParams.storageDelegate = wrapper.get();
initParams.pairingDelegate = wrapper.get();
initParams.systemLayer = systemLayer;
initParams.inetLayer = inetLayer;
initParams.bleLayer = GetJNIBleLayer();
initParams.storageDelegate = wrapper.get();
initParams.pairingDelegate = wrapper.get();
initParams.operationalCredentialsDelegate = wrapper.get();
initParams.systemLayer = systemLayer;
initParams.inetLayer = inetLayer;
initParams.bleLayer = GetJNIBleLayer();

*errInfoOnFailure = wrapper->OpCredsIssuer().Initialize(*initParams.storageDelegate);
if (*errInfoOnFailure != CHIP_NO_ERROR)
{
return nullptr;
}

initParams.operationalCredentialsDelegate = &wrapper->OpCredsIssuer();

*errInfoOnFailure = wrapper->Controller()->Init(nodeId, initParams);

if (*errInfoOnFailure != CHIP_NO_ERROR)
Expand Down Expand Up @@ -139,6 +151,69 @@ void AndroidDeviceControllerWrapper::OnPairingDeleted(CHIP_ERROR error)
CallJavaMethod("onPairingDeleted", static_cast<jint>(error));
}

// TODO Refactor this API to match latest spec, so that GenerateNodeOperationalCertificate receives the full CSR Elements data
// payload.
CHIP_ERROR AndroidDeviceControllerWrapper::GenerateNodeOperationalCertificate(const chip::PeerId & peerId,
const chip::ByteSpan & csr, int64_t serialNumber,
uint8_t * certBuf, uint32_t certBufSize,
uint32_t & outCertLen)
{
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: %d", err);
return err;
}

// Initializing the KeyPair.
Initialize();

chip::X509CertRequestParams request = { serialNumber, mIssuerId, mNow, mNow + mValidity, true, peerId.GetFabricId(),
true, peerId.GetNodeId() };

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

ChipLogProgress(chipTool, "VerifyCertificateSigningRequest");

CHIP_ERROR generateCert = NewNodeOperationalX509Cert(request, chip::CertificateIssuerLevel::kIssuerIsRootCA, pubkey, mIssuer,
certBuf, certBufSize, outCertLen);
jbyteArray argument;
JniReferences::GetInstance().GetEnvForCurrentThread()->ExceptionClear();
JniReferences::GetInstance().N2J_ByteArray(JniReferences::GetInstance().GetEnvForCurrentThread(), csr.data(), csr.size(),
argument);
JniReferences::GetInstance().GetEnvForCurrentThread()->CallVoidMethod(mJavaObjectRef, method, argument);
return generateCert;
}

CHIP_ERROR AndroidDeviceControllerWrapper::Initialize()
{
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;
}

void AndroidDeviceControllerWrapper::OnMessage(chip::System::PacketBufferHandle && msg) {}

void AndroidDeviceControllerWrapper::OnStatusChange(void) {}
Expand Down
15 changes: 15 additions & 0 deletions src/controller/java/AndroidDeviceControllerWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <controller/CHIPDeviceController.h>
#include <controller/ExampleOperationalCredentialsIssuer.h>
#include <platform/internal/DeviceNetworkInfo.h>
#include <support/TimeUtils.h>

/**
* This class contains all relevant information for the JNI view of CHIPDeviceController
Expand All @@ -35,6 +36,7 @@
*/
class AndroidDeviceControllerWrapper : public chip::Controller::DevicePairingDelegate,
public chip::Controller::DeviceStatusDelegate,
public chip::Controller::OperationalCredentialsDelegate,
public chip::PersistentStorageDelegate
{
public:
Expand All @@ -47,12 +49,20 @@ class AndroidDeviceControllerWrapper : public chip::Controller::DevicePairingDel
jlong ToJNIHandle();

void CallJavaMethod(const char * methodName, jint argument);
CHIP_ERROR Initialize();

// DevicePairingDelegate implementation
void OnStatusUpdate(chip::Controller::DevicePairingDelegate::Status status) override;
void OnPairingComplete(CHIP_ERROR error) override;
void OnPairingDeleted(CHIP_ERROR error) override;

// OperationalCredentialsDelegate implementation
CHIP_ERROR GenerateNodeOperationalCertificate(const chip::PeerId & peerId, const chip::ByteSpan & csr, int64_t serialNumber,
uint8_t * certBuf, uint32_t certBufSize, uint32_t & outCertLen) override;

CHIP_ERROR GetRootCACertificate(chip::FabricId fabricId, uint8_t * certBuf, uint32_t certBufSize,
uint32_t & outCertLen) override;

// DeviceStatusDelegate implementation
void OnMessage(chip::System::PacketBufferHandle && msg) override;
void OnStatusChange(void) override;
Expand All @@ -73,6 +83,11 @@ class AndroidDeviceControllerWrapper : public chip::Controller::DevicePairingDel

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

ChipDeviceControllerPtr mController;
chip::Controller::ExampleOperationalCredentialsIssuer mOpCredsIssuer;
Expand Down
27 changes: 7 additions & 20 deletions src/controller/java/CHIPDeviceController-JNI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ static void HandleNewConnection(void * appState, const uint16_t discriminator);
static void ThrowError(JNIEnv * env, CHIP_ERROR errToThrow);
static void ReportError(JNIEnv * env, CHIP_ERROR cbErr, const char * cbName);
static void * IOThreadMain(void * arg);
static CHIP_ERROR N2J_ByteArray(JNIEnv * env, const uint8_t * inArray, uint32_t inArrayLen, jbyteArray & outArray);
static CHIP_ERROR N2J_Error(JNIEnv * env, CHIP_ERROR inErr, jthrowable & outEx);

namespace {
Expand Down Expand Up @@ -782,13 +781,13 @@ bool HandleSendCharacteristic(BLE_CONNECTION_OBJECT connObj, const uint8_t * svc
ChipLogProgress(Controller, "Received SendCharacteristic");
VerifyOrExit(env != NULL, err = CHIP_JNI_ERROR_NO_ENV);

err = N2J_ByteArray(env, svcId, 16, svcIdObj);
err = JniReferences::GetInstance().N2J_ByteArray(env, svcId, 16, svcIdObj);
SuccessOrExit(err);

err = N2J_ByteArray(env, charId, 16, charIdObj);
err = JniReferences::GetInstance().N2J_ByteArray(env, charId, 16, charIdObj);
SuccessOrExit(err);

err = N2J_ByteArray(env, characteristicData, characteristicDataLen, characteristicDataObj);
err = JniReferences::GetInstance().N2J_ByteArray(env, characteristicData, characteristicDataLen, characteristicDataObj);
SuccessOrExit(err);

method = env->GetStaticMethodID(sAndroidChipStackCls, "onSendCharacteristic", "(I[B[B[B)Z");
Expand Down Expand Up @@ -829,10 +828,10 @@ bool HandleSubscribeCharacteristic(BLE_CONNECTION_OBJECT connObj, const uint8_t
ChipLogProgress(Controller, "Received SubscribeCharacteristic");
VerifyOrExit(env != NULL, err = CHIP_JNI_ERROR_NO_ENV);

err = N2J_ByteArray(env, svcId, 16, svcIdObj);
err = JniReferences::GetInstance().N2J_ByteArray(env, svcId, 16, svcIdObj);
SuccessOrExit(err);

err = N2J_ByteArray(env, charId, 16, charIdObj);
err = JniReferences::GetInstance().N2J_ByteArray(env, charId, 16, charIdObj);
SuccessOrExit(err);

{
Expand Down Expand Up @@ -872,10 +871,10 @@ bool HandleUnsubscribeCharacteristic(BLE_CONNECTION_OBJECT connObj, const uint8_
ChipLogProgress(Controller, "Received UnsubscribeCharacteristic");
VerifyOrExit(env != NULL, err = CHIP_JNI_ERROR_NO_ENV);

err = N2J_ByteArray(env, svcId, 16, svcIdObj);
err = JniReferences::GetInstance().N2J_ByteArray(env, svcId, 16, svcIdObj);
SuccessOrExit(err);

err = N2J_ByteArray(env, charId, 16, charIdObj);
err = JniReferences::GetInstance().N2J_ByteArray(env, charId, 16, charIdObj);
SuccessOrExit(err);

method = env->GetStaticMethodID(sAndroidChipStackCls, "onUnsubscribeCharacteristic", "(I[B[B)Z");
Expand Down Expand Up @@ -1102,18 +1101,6 @@ void ThrowError(JNIEnv * env, CHIP_ERROR errToThrow)
}
}

CHIP_ERROR N2J_ByteArray(JNIEnv * env, const uint8_t * inArray, uint32_t inArrayLen, jbyteArray & outArray)
{
outArray = env->NewByteArray((int) inArrayLen);
VerifyOrReturnError(outArray != NULL, CHIP_ERROR_NO_MEMORY);

env->ExceptionClear();
env->SetByteArrayRegion(outArray, 0, inArrayLen, (jbyte *) inArray);
VerifyOrReturnError(!env->ExceptionCheck(), CHIP_JNI_ERROR_EXCEPTION_THROWN);

return CHIP_NO_ERROR;
}

CHIP_ERROR N2J_Error(JNIEnv * env, CHIP_ERROR inErr, jthrowable & outEx)
{
CHIP_ERROR err = CHIP_NO_ERROR;
Expand Down
15 changes: 15 additions & 0 deletions src/controller/java/JniReferences.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,21 @@ CHIP_ERROR JniReferences::GetClassRef(JNIEnv * env, const char * clsType, jclass
return err;
}

CHIP_ERROR JniReferences::N2J_ByteArray(JNIEnv * env, const uint8_t * inArray, uint32_t inArrayLen, jbyteArray & outArray)
{
CHIP_ERROR err = CHIP_NO_ERROR;

outArray = env->NewByteArray((int) inArrayLen);
VerifyOrReturnError(outArray != NULL, CHIP_ERROR_NO_MEMORY);

env->ExceptionClear();
env->SetByteArrayRegion(outArray, 0, inArrayLen, (jbyte *) inArray);
VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN);

exit:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer using Return macros instead of goto exit macros when there is no cleanup to be done in the exit block. Saves some scoping and helps readability.

return err;
}

CHIP_ERROR JniReferences::FindMethod(JNIEnv * env, jobject object, const char * methodName, const char * methodSignature,
jmethodID * methodId)
{
Expand Down
2 changes: 2 additions & 0 deletions src/controller/java/JniReferences.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ class JniReferences
jmethodID * methodId);
void CallVoidInt(JNIEnv * env, jobject object, const char * methodName, jint argument);

CHIP_ERROR N2J_ByteArray(JNIEnv * env, const uint8_t * inArray, uint32_t inArrayLen, jbyteArray & outArray);

private:
JniReferences() {}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,12 @@ public void onPairingComplete(int errorCode) {
}
}

public void onOpCSRGenerationComplete(byte[] errorCode) {
if (completionListener != null) {
completionListener.onOpCSRGenerationComplete(errorCode);
}
}

public void onPairingDeleted(int errorCode) {
if (completionListener != null) {
completionListener.onPairingDeleted(errorCode);
Expand Down Expand Up @@ -265,5 +271,8 @@ public interface CompletionListener {

/** Notifies the listener of the error. */
void onError(Throwable error);

/** Notifies the Commissioner when the OpCSR for the Comissionee is generated. */
void onOpCSRGenerationComplete(byte[] errorCode);
}
}
9 changes: 9 additions & 0 deletions src/protocols/secure_channel/RendezvousParameters.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,19 @@ class RendezvousParameters

bool HasPeerAddress() const { return mPeerAddress.IsInitialized(); }
Transport::PeerAddress GetPeerAddress() const { return mPeerAddress; }
const Optional<ByteSpan> GetCSRNonce() const { return mCSRNonce; }
RendezvousParameters & SetPeerAddress(const Transport::PeerAddress & peerAddress)
{
mPeerAddress = peerAddress;
return *this;
}

RendezvousParameters & SetCSRNonce(ByteSpan csrNonce)
ritikananda27 marked this conversation as resolved.
Show resolved Hide resolved
bzbarsky-apple marked this conversation as resolved.
Show resolved Hide resolved
{
mCSRNonce.SetValue(csrNonce);
return *this;
}

bool HasDiscriminator() const { return mDiscriminator <= kMaxRendezvousDiscriminatorValue; }
uint16_t GetDiscriminator() const { return mDiscriminator; }
RendezvousParameters & SetDiscriminator(uint16_t discriminator)
Expand All @@ -94,6 +101,7 @@ class RendezvousParameters
}

bool HasPASEVerifier() const { return mHasPASEVerifier; }
bool HasCSRNonce() const { return mCSRNonce.HasValue(); }
const PASEVerifier & GetPASEVerifier() const { return mPASEVerifier; }
RendezvousParameters & SetPASEVerifier(PASEVerifier & verifier)
{
Expand Down Expand Up @@ -138,6 +146,7 @@ class RendezvousParameters
Optional<NodeId> mRemoteNodeId; ///< the remote node id
uint32_t mSetupPINCode = 0; ///< the target peripheral setup PIN Code
uint16_t mDiscriminator = UINT16_MAX; ///< the target peripheral discriminator
Optional<ByteSpan> mCSRNonce; ///< CSR Nonce passed by the commissioner

PASEVerifier mPASEVerifier;
bool mHasPASEVerifier = false;
Expand Down