Skip to content

Commit

Permalink
add CommissionableDataProvider on android platform (project-chip#16475)
Browse files Browse the repository at this point in the history
* add CommissionableDataProvider on android platform

* fix restyled-io and ci errors

* remove commented

* fix restyled-io and ci errors

* fix PR comment

* fix restyled-io and ci errors

* fix PR comment

* fix restyled-io and ci errors

* fix restyled-io and ci errors
  • Loading branch information
xylophone21 authored and andrei-menzopol committed Apr 14, 2022
1 parent 851873f commit 5e7f5e7
Show file tree
Hide file tree
Showing 9 changed files with 331 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@

public class MatterServant {

public int testSetupPasscode = 20202021;
public int testDiscriminator = 0xF00;

private ChipAppServer chipAppServer;

private MatterServant() {}
Expand Down Expand Up @@ -97,6 +100,9 @@ public void init(@NonNull Context context) {
new ChipMdnsCallbackImpl(),
new DiagnosticDataProviderImpl(applicationContext));

chipPlatform.updateCommissionableDataProviderData(
null, null, 0, testSetupPasscode, testDiscriminator);

tvApp.postInit();

chipAppServer = new ChipAppServer();
Expand Down
3 changes: 3 additions & 0 deletions src/include/platform/CommissionableDataProvider.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ namespace chip {
// The largest value of the 12-bit Payload discriminator
constexpr uint16_t kMaxDiscriminatorValue = 0xFFF;

constexpr uint32_t kMinSetupPasscode = 1;
constexpr uint32_t kMaxSetupPasscode = 0x5F5E0FE;

namespace DeviceLayer {

class CommissionableDataProvider
Expand Down
2 changes: 1 addition & 1 deletion src/platform/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ if (chip_device_platform != "none") {
# === FOR TRANSITION UNTIL ALL EXAMPLES PROVIDE THEIR OWN ===
# Linux platform has already transitioned.
chip_use_transitional_commissionable_data_provider =
chip_device_platform != "linux"
chip_device_platform != "linux" && chip_device_platform != "android"

# lock tracking: none/log/fatal or auto for a platform-dependent choice
chip_stack_lock_tracking = "auto"
Expand Down
19 changes: 19 additions & 0 deletions src/platform/android/AndroidChipPlatform-JNI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
* Implementation of JNI bridge for CHIP Device Controller for Android apps
*
*/
#include <jni.h>
#include <lib/core/CHIPError.h>
#include <lib/support/CHIPJNIError.h>
#include <lib/support/CHIPMem.h>
Expand All @@ -35,6 +36,7 @@

#include "AndroidChipPlatform-JNI.h"
#include "BLEManagerImpl.h"
#include "CommissionableDataProviderImpl.h"
#include "DiagnosticDataProviderImpl.h"
#include "DnssdImpl.h"

Expand Down Expand Up @@ -255,3 +257,20 @@ static bool JavaBytesToUUID(JNIEnv * env, jbyteArray value, chip::Ble::ChipBleUU
return result;
}
#endif

// for CommissionableDataProvider
JNI_METHOD(jboolean, updateCommissionableDataProviderData)
(JNIEnv * env, jclass self, jstring spake2pVerifierBase64, jstring Spake2pSaltBase64, jint spake2pIterationCount,
jlong setupPasscode, jint discriminator)
{
chip::DeviceLayer::StackLock lock;
CHIP_ERROR err = CommissionableDataProviderMgrImpl().Update(env, spake2pVerifierBase64, Spake2pSaltBase64,
spake2pIterationCount, setupPasscode, discriminator);
if (err != CHIP_NO_ERROR)
{
ChipLogError(DeviceLayer, "Failed to update commissionable data provider data: %s", ErrorStr(err));
return false;
}

return true;
}
2 changes: 2 additions & 0 deletions src/platform/android/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ static_library("android") {
"BLEManagerImpl.h",
"BlePlatformConfig.h",
"CHIPDevicePlatformEvent.h",
"CommissionableDataProviderImpl.cpp",
"CommissionableDataProviderImpl.h",
"ConfigurationManagerImpl.cpp",
"ConfigurationManagerImpl.h",
"ConnectivityManagerImpl.cpp",
Expand Down
219 changes: 219 additions & 0 deletions src/platform/android/CommissionableDataProviderImpl.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
/*
*
* Copyright (c) 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 "CommissionableDataProviderImpl.h"

#include <cstdint>
#include <string.h>

#include <crypto/CHIPCryptoPAL.h>
#include <lib/support/Base64.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/JniTypeWrappers.h>
#include <lib/support/Span.h>
#include <lib/support/logging/CHIPLogging.h>
#include <platform/CHIPDeviceConfig.h>

using namespace chip;
using namespace chip::Crypto;

namespace {

CHIP_ERROR GeneratePaseSalt(std::vector<uint8_t> & spake2pSaltVector)
{
constexpr size_t kSaltLen = kSpake2p_Max_PBKDF_Salt_Length;
spake2pSaltVector.resize(kSaltLen);
return DRBG_get_bytes(spake2pSaltVector.data(), spake2pSaltVector.size());
}

} // namespace

CommissionableDataProviderImpl CommissionableDataProviderImpl::sInstance;

CHIP_ERROR CommissionableDataProviderImpl::Update(JNIEnv * env, jstring spake2pVerifierBase64, jstring Spake2pSaltBase64,
jint spake2pIterationCount, jlong setupPasscode, jint discriminator)
{
VerifyOrReturnLogError(discriminator <= chip::kMaxDiscriminatorValue, CHIP_ERROR_INVALID_ARGUMENT);

if (spake2pIterationCount == 0)
{
spake2pIterationCount = CHIP_DEVICE_CONFIG_USE_TEST_SPAKE2P_ITERATION_COUNT;
}
VerifyOrReturnLogError(static_cast<uint32_t>(spake2pIterationCount) >= kSpake2p_Min_PBKDF_Iterations,
CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnLogError(static_cast<uint32_t>(spake2pIterationCount) <= kSpake2p_Max_PBKDF_Iterations,
CHIP_ERROR_INVALID_ARGUMENT);

const bool havePaseVerifier = (spake2pVerifierBase64 != nullptr);
const bool havePaseSalt = (Spake2pSaltBase64 != nullptr);
VerifyOrReturnLogError(!havePaseVerifier || (havePaseVerifier && havePaseSalt), CHIP_ERROR_INVALID_ARGUMENT);

CHIP_ERROR err;
// read verifier from paramter is have
Spake2pVerifier providedVerifier;
std::vector<uint8_t> serializedSpake2pVerifier(kSpake2p_VerifierSerialized_Length);
if (havePaseVerifier)
{
chip::JniUtfString utfSpake2pVerifierBase64(env, spake2pVerifierBase64);

size_t maxBase64Size = BASE64_ENCODED_LEN(chip::Crypto::kSpake2p_VerifierSerialized_Length);
VerifyOrReturnLogError(static_cast<unsigned>(utfSpake2pVerifierBase64.size()) <= maxBase64Size,
CHIP_ERROR_INVALID_ARGUMENT);

size_t decodedLen = chip::Base64Decode32(utfSpake2pVerifierBase64.c_str(), utfSpake2pVerifierBase64.size(),
reinterpret_cast<uint8_t *>(serializedSpake2pVerifier.data()));
VerifyOrReturnLogError(decodedLen == chip::Crypto::kSpake2p_VerifierSerialized_Length, CHIP_ERROR_INVALID_ARGUMENT);

chip::MutableByteSpan verifierSpan{ serializedSpake2pVerifier.data(), decodedLen };
err = providedVerifier.Deserialize(verifierSpan);
VerifyOrReturnLogError(err == CHIP_NO_ERROR, err);

ChipLogProgress(Support, "Got externally provided verifier, using it.");
}

// read slat from paramter is have or generate one
std::vector<uint8_t> spake2pSalt(chip::Crypto::kSpake2p_Max_PBKDF_Salt_Length);
if (!havePaseSalt)
{
ChipLogProgress(Support, "LinuxCommissionableDataProvider didn't get a PASE salt, generating one.");
err = GeneratePaseSalt(spake2pSalt);
VerifyOrReturnLogError(err == CHIP_NO_ERROR, err);
}
else
{
chip::JniUtfString utfSpake2pSaltBase64(env, Spake2pSaltBase64);

size_t maxBase64Size = BASE64_ENCODED_LEN(chip::Crypto::kSpake2p_Max_PBKDF_Salt_Length);
VerifyOrReturnLogError(static_cast<unsigned>(utfSpake2pSaltBase64.size()) <= maxBase64Size, CHIP_ERROR_INVALID_ARGUMENT);

size_t decodedLen = chip::Base64Decode32(utfSpake2pSaltBase64.c_str(), utfSpake2pSaltBase64.size(),
reinterpret_cast<uint8_t *>(spake2pSalt.data()));
VerifyOrReturnLogError(decodedLen >= chip::Crypto::kSpake2p_Min_PBKDF_Salt_Length &&
decodedLen <= chip::Crypto::kSpake2p_Max_PBKDF_Salt_Length,
CHIP_ERROR_INVALID_ARGUMENT);
spake2pSalt.resize(decodedLen);
}

// generate verifier from passcode is have
const bool havePasscode = (setupPasscode > kMinSetupPasscode && setupPasscode < kMaxSetupPasscode);
Spake2pVerifier passcodeVerifier;
std::vector<uint8_t> serializedPasscodeVerifier(kSpake2p_VerifierSerialized_Length);
chip::MutableByteSpan saltSpan{ spake2pSalt.data(), spake2pSalt.size() };
if (havePasscode)
{
uint32_t u32SetupPasscode = static_cast<uint32_t>(setupPasscode);
err = passcodeVerifier.Generate(spake2pIterationCount, saltSpan, u32SetupPasscode);
VerifyOrReturnLogError(err == CHIP_NO_ERROR, err);

chip::MutableByteSpan verifierSpan{ serializedPasscodeVerifier.data(), serializedPasscodeVerifier.size() };
err = passcodeVerifier.Serialize(verifierSpan);
VerifyOrReturnLogError(err == CHIP_NO_ERROR, err);
}

// Make sure we actually have a verifier
VerifyOrReturnLogError(havePasscode || havePaseVerifier, CHIP_ERROR_INVALID_ARGUMENT);

// If both passcode and external verifier were provided, validate they match, otherwise
// it's ambiguous.
if (havePasscode && havePaseVerifier)
{
VerifyOrReturnLogError(serializedPasscodeVerifier == serializedSpake2pVerifier, CHIP_ERROR_INVALID_ARGUMENT);
ChipLogProgress(Support, "Validated externally provided passcode matches the one generated from provided passcode.");
}

// External PASE verifier takes precedence when present (even though it is identical to passcode-based
// one when the latter is present).
if (havePaseVerifier)
{
mSerializedPaseVerifier = std::move(serializedSpake2pVerifier);
}
else
{
mSerializedPaseVerifier = std::move(serializedPasscodeVerifier);
}
mDiscriminator = discriminator;
mPaseSalt = std::move(spake2pSalt);
mPaseIterationCount = spake2pIterationCount;
if (havePasscode)
{
mSetupPasscode.SetValue(setupPasscode);
}

// Set to global CommissionableDataProvider once success first time
if (!mFirstUpdated)
{
DeviceLayer::SetCommissionableDataProvider(this);
}
mFirstUpdated = true;

return CHIP_NO_ERROR;
}

CHIP_ERROR CommissionableDataProviderImpl::GetSetupDiscriminator(uint16_t & setupDiscriminator)
{
VerifyOrReturnError(mFirstUpdated, CHIP_ERROR_INCORRECT_STATE);
setupDiscriminator = mDiscriminator;
return CHIP_NO_ERROR;
}

CHIP_ERROR CommissionableDataProviderImpl::GetSpake2pIterationCount(uint32_t & iterationCount)
{
VerifyOrReturnLogError(mFirstUpdated, CHIP_ERROR_INCORRECT_STATE);
iterationCount = mPaseIterationCount;
return CHIP_NO_ERROR;
}

CHIP_ERROR CommissionableDataProviderImpl::GetSpake2pSalt(chip::MutableByteSpan & saltBuf)
{
VerifyOrReturnError(mFirstUpdated, CHIP_ERROR_INCORRECT_STATE);

VerifyOrReturnError(saltBuf.size() >= kSpake2p_Max_PBKDF_Salt_Length, CHIP_ERROR_BUFFER_TOO_SMALL);
memcpy(saltBuf.data(), mPaseSalt.data(), mPaseSalt.size());
saltBuf.reduce_size(mPaseSalt.size());

return CHIP_NO_ERROR;
}

CHIP_ERROR CommissionableDataProviderImpl::GetSpake2pVerifier(chip::MutableByteSpan & verifierBuf, size_t & outVerifierLen)
{
VerifyOrReturnError(mFirstUpdated, CHIP_ERROR_INCORRECT_STATE);

// By now, serialized verifier from Init should be correct size
VerifyOrReturnError(mSerializedPaseVerifier.size() == kSpake2p_VerifierSerialized_Length, CHIP_ERROR_INTERNAL);

outVerifierLen = mSerializedPaseVerifier.size();
VerifyOrReturnError(verifierBuf.size() >= outVerifierLen, CHIP_ERROR_BUFFER_TOO_SMALL);
memcpy(verifierBuf.data(), mSerializedPaseVerifier.data(), mSerializedPaseVerifier.size());
verifierBuf.reduce_size(mSerializedPaseVerifier.size());

return CHIP_NO_ERROR;
}

CHIP_ERROR CommissionableDataProviderImpl::GetSetupPasscode(uint32_t & setupPasscode)
{
VerifyOrReturnError(mFirstUpdated, CHIP_ERROR_INCORRECT_STATE);

// Pretend not implemented if we don't have a passcode value externally set
if (!mSetupPasscode.HasValue())
{
return CHIP_ERROR_NOT_IMPLEMENTED;
}

setupPasscode = mSetupPasscode.Value();
return CHIP_NO_ERROR;
}
63 changes: 63 additions & 0 deletions src/platform/android/CommissionableDataProviderImpl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
*
* Copyright (c) 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.
*/

#pragma once

#include <jni.h>
#include <lib/core/CHIPError.h>
#include <lib/core/Optional.h>
#include <platform/CommissionableDataProvider.h>
#include <stdint.h>
#include <vector>

class CommissionableDataProviderImpl : public chip::DeviceLayer::CommissionableDataProvider
{
public:
CHIP_ERROR Update(JNIEnv * env, jstring spake2pVerifierBase64, jstring Spake2pSaltBase64, jint spake2pIterationCount,
jlong setupPasscode, jint discriminator);
CHIP_ERROR GetSetupDiscriminator(uint16_t & setupDiscriminator) override;
CHIP_ERROR SetSetupDiscriminator(uint16_t setupDiscriminator) override
{
// We don't support overriding the discriminator post-init (it is deprecated!)
return CHIP_ERROR_NOT_IMPLEMENTED;
}
CHIP_ERROR GetSpake2pIterationCount(uint32_t & iterationCount) override;
CHIP_ERROR GetSpake2pSalt(chip::MutableByteSpan & saltBuf) override;
CHIP_ERROR GetSpake2pVerifier(chip::MutableByteSpan & verifierBuf, size_t & outVerifierLen) override;
CHIP_ERROR GetSetupPasscode(uint32_t & setupPasscode) override;
CHIP_ERROR SetSetupPasscode(uint32_t setupPasscode) override
{
// We don't support overriding the passcode post-init (it is deprecated!)
return CHIP_ERROR_NOT_IMPLEMENTED;
}

private:
friend CommissionableDataProviderImpl & CommissionableDataProviderMgrImpl();
static CommissionableDataProviderImpl sInstance;
bool mFirstUpdated = false;
std::vector<uint8_t> mSerializedPaseVerifier;
std::vector<uint8_t> mPaseSalt;
uint32_t mPaseIterationCount = 0;
chip::Optional<uint32_t> mSetupPasscode;
uint16_t mDiscriminator = 0;
};

inline CommissionableDataProviderImpl & CommissionableDataProviderMgrImpl()
{
return CommissionableDataProviderImpl::sInstance;
}
18 changes: 18 additions & 0 deletions src/platform/android/java/chip/platform/AndroidChipPlatform.java
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,22 @@ private native void nativeSetServiceResolver(
ServiceResolver resolver, ChipMdnsCallback chipMdnsCallback);

private native void setDiagnosticDataProviderManager(DiagnosticDataProvider dataProviderCallback);

/**
* update commission info
*
* @param spake2pVerifierBase64 base64 encoded spake2p verifier, ref
* CHIP_DEVICE_CONFIG_USE_TEST_SPAKE2P_VERIFIER. using null to generate it from passcode.
* @param Spake2pSaltBase64 base64 encoded spake2p salt, ref
* CHIP_DEVICE_CONFIG_USE_TEST_SPAKE2P_SALT. using null to generate a random one.
* @param spake2pIterationCount Spake2p iteration count, or 0 to use
* CHIP_DEVICE_CONFIG_USE_TEST_SPAKE2P_ITERATION_COUNT
* @return true on success of false on failed
*/
public native boolean updateCommissionableDataProviderData(
String spake2pVerifierBase64,
String Spake2pSaltBase64,
int spake2pIterationCount,
long setupPasscode,
int discriminator);
}
Loading

0 comments on commit 5e7f5e7

Please sign in to comment.