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

add CommissionableDataProvider on android platform #16475

Merged
Show file tree
Hide file tree
Changes from all 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
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,
andy31415 marked this conversation as resolved.
Show resolved Hide resolved
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;
}
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