diff --git a/examples/tv-app/android/App/app/src/main/java/com/tcl/chip/chiptvserver/service/MatterServant.java b/examples/tv-app/android/App/app/src/main/java/com/tcl/chip/chiptvserver/service/MatterServant.java index 1a1c6f914b0d57..18842aabde64b9 100644 --- a/examples/tv-app/android/App/app/src/main/java/com/tcl/chip/chiptvserver/service/MatterServant.java +++ b/examples/tv-app/android/App/app/src/main/java/com/tcl/chip/chiptvserver/service/MatterServant.java @@ -40,6 +40,9 @@ public class MatterServant { + public int testSetupPasscode = 20202021; + public int testDiscriminator = 0xF00; + private ChipAppServer chipAppServer; private MatterServant() {} @@ -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(); diff --git a/src/include/platform/CommissionableDataProvider.h b/src/include/platform/CommissionableDataProvider.h index aea04bc833b2c8..6a51023f1aef03 100644 --- a/src/include/platform/CommissionableDataProvider.h +++ b/src/include/platform/CommissionableDataProvider.h @@ -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 diff --git a/src/platform/BUILD.gn b/src/platform/BUILD.gn index 1ac8f1bba7f764..caed34b8755eeb 100644 --- a/src/platform/BUILD.gn +++ b/src/platform/BUILD.gn @@ -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" diff --git a/src/platform/android/AndroidChipPlatform-JNI.cpp b/src/platform/android/AndroidChipPlatform-JNI.cpp index e655b30140c4b2..0f1168401fa7e8 100644 --- a/src/platform/android/AndroidChipPlatform-JNI.cpp +++ b/src/platform/android/AndroidChipPlatform-JNI.cpp @@ -21,6 +21,7 @@ * Implementation of JNI bridge for CHIP Device Controller for Android apps * */ +#include #include #include #include @@ -35,6 +36,7 @@ #include "AndroidChipPlatform-JNI.h" #include "BLEManagerImpl.h" +#include "CommissionableDataProviderImpl.h" #include "DiagnosticDataProviderImpl.h" #include "DnssdImpl.h" @@ -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; +} diff --git a/src/platform/android/BUILD.gn b/src/platform/android/BUILD.gn index c6ab8699b96cf0..5f7acc2d6d81a6 100644 --- a/src/platform/android/BUILD.gn +++ b/src/platform/android/BUILD.gn @@ -36,6 +36,8 @@ static_library("android") { "BLEManagerImpl.h", "BlePlatformConfig.h", "CHIPDevicePlatformEvent.h", + "CommissionableDataProviderImpl.cpp", + "CommissionableDataProviderImpl.h", "ConfigurationManagerImpl.cpp", "ConfigurationManagerImpl.h", "ConnectivityManagerImpl.cpp", diff --git a/src/platform/android/CommissionableDataProviderImpl.cpp b/src/platform/android/CommissionableDataProviderImpl.cpp new file mode 100644 index 00000000000000..ebcd6c48a302a4 --- /dev/null +++ b/src/platform/android/CommissionableDataProviderImpl.cpp @@ -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 +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace chip; +using namespace chip::Crypto; + +namespace { + +CHIP_ERROR GeneratePaseSalt(std::vector & 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(spake2pIterationCount) >= kSpake2p_Min_PBKDF_Iterations, + CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnLogError(static_cast(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 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(utfSpake2pVerifierBase64.size()) <= maxBase64Size, + CHIP_ERROR_INVALID_ARGUMENT); + + size_t decodedLen = chip::Base64Decode32(utfSpake2pVerifierBase64.c_str(), utfSpake2pVerifierBase64.size(), + reinterpret_cast(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 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(utfSpake2pSaltBase64.size()) <= maxBase64Size, CHIP_ERROR_INVALID_ARGUMENT); + + size_t decodedLen = chip::Base64Decode32(utfSpake2pSaltBase64.c_str(), utfSpake2pSaltBase64.size(), + reinterpret_cast(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 serializedPasscodeVerifier(kSpake2p_VerifierSerialized_Length); + chip::MutableByteSpan saltSpan{ spake2pSalt.data(), spake2pSalt.size() }; + if (havePasscode) + { + uint32_t u32SetupPasscode = static_cast(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; +} diff --git a/src/platform/android/CommissionableDataProviderImpl.h b/src/platform/android/CommissionableDataProviderImpl.h new file mode 100644 index 00000000000000..ef3bec7516d77a --- /dev/null +++ b/src/platform/android/CommissionableDataProviderImpl.h @@ -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 +#include +#include +#include +#include +#include + +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 mSerializedPaseVerifier; + std::vector mPaseSalt; + uint32_t mPaseIterationCount = 0; + chip::Optional mSetupPasscode; + uint16_t mDiscriminator = 0; +}; + +inline CommissionableDataProviderImpl & CommissionableDataProviderMgrImpl() +{ + return CommissionableDataProviderImpl::sInstance; +} diff --git a/src/platform/android/java/chip/platform/AndroidChipPlatform.java b/src/platform/android/java/chip/platform/AndroidChipPlatform.java index da1ee88423872f..eaad0e6ed0d281 100644 --- a/src/platform/android/java/chip/platform/AndroidChipPlatform.java +++ b/src/platform/android/java/chip/platform/AndroidChipPlatform.java @@ -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); } diff --git a/src/platform/android/java/chip/platform/PreferencesConfigurationManager.java b/src/platform/android/java/chip/platform/PreferencesConfigurationManager.java index 6ead06082eadcd..0069f033c6d77b 100644 --- a/src/platform/android/java/chip/platform/PreferencesConfigurationManager.java +++ b/src/platform/android/java/chip/platform/PreferencesConfigurationManager.java @@ -85,12 +85,6 @@ public long readConfigValueLong(String namespace, String name) */ case kConfigNamespace_ChipFactory + ":" + kConfigKey_SoftwareVersion: return 1; - - case kConfigNamespace_ChipFactory + ":" + kConfigKey_SetupPinCode: - return 20202021; - - case kConfigNamespace_ChipFactory + ":" + kConfigKey_SetupDiscriminator: - return 0xF00; } if (preferences.contains(key)) {