diff --git a/src/credentials/CHIPCert.cpp b/src/credentials/CHIPCert.cpp index 159526e296f738..2af62ab2efe0f0 100644 --- a/src/credentials/CHIPCert.cpp +++ b/src/credentials/CHIPCert.cpp @@ -1203,6 +1203,48 @@ CHIP_ERROR ConvertECDSASignatureRawToDER(P256ECDSASignatureSpan rawSig, ASN1Writ return err; } +CHIP_ERROR ConvertECDSAKeypairRawToDER(const P256SerializedKeypair & rawKeypair, MutableByteSpan & outDerKeypair) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + // The raw key pair contains the public key followed by the private key + VerifyOrReturnError(rawKeypair.Length() == kP256_PublicKey_Length + kP256_PrivateKey_Length, CHIP_ERROR_INVALID_ARGUMENT); + FixedByteSpan publicKey(rawKeypair.ConstBytes()); + FixedByteSpan privateKey(rawKeypair.ConstBytes() + kP256_PublicKey_Length); + + ASN1Writer writer; + writer.Init(outDerKeypair); + + // ECPrivateKey ::= SEQUENCE + ASN1_START_SEQUENCE + { + // version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1) + ASN1_ENCODE_INTEGER(1); + + // privateKey OCTET STRING + ASN1_ENCODE_OCTET_STRING(privateKey.data(), privateKey.size()); + + // parameters [0] ECParameters {{ NamedCurve }} OPTIONAL + ASN1_START_CONSTRUCTED(kASN1TagClass_ContextSpecific, 0); + { + ASN1_ENCODE_OBJECT_ID(kOID_EllipticCurve_prime256v1); + } + ASN1_END_CONSTRUCTED; + + // publicKey [1] BIT STRING OPTIONAL + ASN1_START_CONSTRUCTED(kASN1TagClass_ContextSpecific, 1); + { + ReturnErrorOnFailure(writer.PutBitString(0, publicKey.data(), publicKey.size())); + } + ASN1_END_CONSTRUCTED; + } + ASN1_END_SEQUENCE; + + outDerKeypair.reduce_size(writer.GetLengthWritten()); +exit: + return err; +} + CHIP_ERROR ExtractNodeIdFabricIdFromOpCert(const ChipCertificateData & opcert, NodeId * outNodeId, FabricId * outFabricId) { // Since we assume the cert is pre-validated, we are going to assume that diff --git a/src/credentials/CHIPCert.h b/src/credentials/CHIPCert.h index 5c8372d34954e4..8eba5fed51fdb7 100644 --- a/src/credentials/CHIPCert.h +++ b/src/credentials/CHIPCert.h @@ -57,6 +57,9 @@ static constexpr uint32_t kMaxDERCertLength = 600; // As per spec section 11.24 (Wi-Fi Authentication with Per-Device Credentials) inline constexpr uint32_t kMaxCHIPCompactNetworkIdentityLength = 137; +// Length of a ASN.1 DER encoded ECPrivateKey structure (RFC 5915) for a P256 key pair. +inline constexpr uint32_t kP256ECPrivateKeyDERLength = 121; + /** Data Element Tags for the CHIP Certificate */ enum @@ -730,6 +733,17 @@ CHIP_ERROR ConvertECDSASignatureRawToDER(P256ECDSASignatureSpan rawSig, ASN1::AS */ CHIP_ERROR ConvertECDSASignatureDERToRaw(ASN1::ASN1Reader & reader, chip::TLV::TLVWriter & writer, uint64_t tag); +/** + * @brief Convert a raw ECDSA P256 key pair to an ASN.1 DER encoded ECPrivateKey structure (RFC 5915). + * + * @param rawKeypair The raw P256 key pair. + * @param outDerKeypair Output buffer to receive the ASN.1 DER encoded key pair. + * Must have a capacity of at least `kP256ECPrivateKeyDERLength` bytes. + * + * @retval #CHIP_NO_ERROR If the key pair was successfully converted, or a CHIP_ERROR otherwise. + */ +CHIP_ERROR ConvertECDSAKeypairRawToDER(const Crypto::P256SerializedKeypair & rawKeypair, MutableByteSpan & outDerKeypair); + /** * Extract the Fabric ID from an operational certificate that has already been * parsed. diff --git a/src/credentials/tests/CHIPCert_test_vectors.cpp b/src/credentials/tests/CHIPCert_test_vectors.cpp index 90de7a9a8c722f..e798a19cbd43a7 100644 --- a/src/credentials/tests/CHIPCert_test_vectors.cpp +++ b/src/credentials/tests/CHIPCert_test_vectors.cpp @@ -31,228 +31,105 @@ namespace chip { namespace TestCerts { -// clang-format off -extern const TestCert gTestCerts[] = { - TestCert::kRoot01, - TestCert::kRoot02, - TestCert::kRoot03, - TestCert::kICA01, - TestCert::kICA02, - TestCert::kICA01_1, - TestCert::kFWSign01, - TestCert::kNode01_01, - TestCert::kNode01_02, - TestCert::kNode02_01, - TestCert::kNode02_02, - TestCert::kNode02_03, - TestCert::kNode02_04, - TestCert::kNode02_05, - TestCert::kNode02_06, - TestCert::kNode02_07, - TestCert::kNode02_08, - TestCert::kPDCID01, -}; -// clang-format on - -extern const size_t gNumTestCerts = ArraySize(gTestCerts); +// X-Macro style list of TestCert names, without prefix +#define ENUMERATE_TEST_CERTS(X, ...) \ + X(Root01, ##__VA_ARGS__) \ + X(Root02, ##__VA_ARGS__) \ + X(Root03, ##__VA_ARGS__) \ + X(ICA01, ##__VA_ARGS__) \ + X(ICA02, ##__VA_ARGS__) \ + X(ICA01_1, ##__VA_ARGS__) \ + X(FWSign01, ##__VA_ARGS__) \ + X(Node01_01, ##__VA_ARGS__) \ + X(Node01_02, ##__VA_ARGS__) \ + X(Node02_01, ##__VA_ARGS__) \ + X(Node02_02, ##__VA_ARGS__) \ + X(Node02_03, ##__VA_ARGS__) \ + X(Node02_04, ##__VA_ARGS__) \ + X(Node02_05, ##__VA_ARGS__) \ + X(Node02_06, ##__VA_ARGS__) \ + X(Node02_07, ##__VA_ARGS__) \ + X(Node02_08, ##__VA_ARGS__) \ + X(PDCID01, ##__VA_ARGS__) + +#define TEST_CERT_ENUM_REF_ENTRY(NAME) TestCert::k##NAME, +extern const TestCert gTestCerts[] = { ENUMERATE_TEST_CERTS(TEST_CERT_ENUM_REF_ENTRY) }; +extern const size_t gNumTestCerts = ArraySize(gTestCerts); + +#define TEST_CERT_SELECT_NAME(NAME) \ + case TestCert::k##NAME: \ + return #NAME; + +#define TEST_CERT_SELECT_PROPERTY(NAME, PROP, OUT) \ + case TestCert::k##NAME: \ + OUT = sTestCert_##NAME##_##PROP; \ + return CHIP_NO_ERROR; + +#define TEST_CERT_SWITCH(EXPR, MACRO, ...) \ + switch (EXPR) \ + { \ + ENUMERATE_TEST_CERTS(MACRO, ##__VA_ARGS__) \ + default: \ + break; \ + } CHIP_ERROR GetTestCert(TestCert certType, BitFlags certLoadFlags, ByteSpan & cert) { - CHIP_ERROR err; - bool derForm = certLoadFlags.Has(TestCertLoadFlags::kDERForm); - -#define SELECT_CERT(NAME) \ - do \ - { \ - if (certType == TestCert::k##NAME) \ - { \ - cert = (derForm) ? sTestCert_##NAME##_DER : sTestCert_##NAME##_Chip; \ - ExitNow(err = CHIP_NO_ERROR); \ - } \ - } while (0) - - SELECT_CERT(Root01); - SELECT_CERT(Root02); - SELECT_CERT(Root03); - SELECT_CERT(ICA01); - SELECT_CERT(ICA02); - SELECT_CERT(ICA01_1); - SELECT_CERT(FWSign01); - SELECT_CERT(Node01_01); - SELECT_CERT(Node01_02); - SELECT_CERT(Node02_01); - SELECT_CERT(Node02_02); - SELECT_CERT(Node02_03); - SELECT_CERT(Node02_04); - SELECT_CERT(Node02_05); - SELECT_CERT(Node02_06); - SELECT_CERT(Node02_07); - SELECT_CERT(Node02_08); - SELECT_CERT(PDCID01); - -#undef SELECT_CERT - - err = CHIP_ERROR_CA_CERT_NOT_FOUND; - -exit: - return err; + if (certLoadFlags.Has(TestCertLoadFlags::kDERForm)) + { + TEST_CERT_SWITCH(certType, TEST_CERT_SELECT_PROPERTY, DER, cert); + } + else + { + TEST_CERT_SWITCH(certType, TEST_CERT_SELECT_PROPERTY, Chip, cert); + } + return CHIP_ERROR_NOT_FOUND; } const char * GetTestCertName(TestCert certType) { -#define NAME_CERT(NAME) \ - do \ - { \ - if (certType == TestCert::k##NAME) \ - { \ - return #NAME; \ - } \ - } while (0) - NAME_CERT(Root01); - NAME_CERT(Root02); - NAME_CERT(Root03); - NAME_CERT(ICA01); - NAME_CERT(ICA02); - NAME_CERT(ICA01_1); - NAME_CERT(FWSign01); - NAME_CERT(Node01_01); - NAME_CERT(Node01_02); - NAME_CERT(Node02_01); - NAME_CERT(Node02_02); - NAME_CERT(Node02_03); - NAME_CERT(Node02_04); - NAME_CERT(Node02_05); - NAME_CERT(Node02_06); - NAME_CERT(Node02_07); - NAME_CERT(Node02_08); - NAME_CERT(PDCID01); - -#undef NAME_CERT - + TEST_CERT_SWITCH(certType, TEST_CERT_SELECT_NAME); return nullptr; } CHIP_ERROR GetTestCertPubkey(TestCert certType, ByteSpan & pubkey) { - CHIP_ERROR err; - -#define SELECT_PUBKEY(NAME) \ - do \ - { \ - if (certType == TestCert::k##NAME) \ - { \ - pubkey = sTestCert_##NAME##_PublicKey; \ - ExitNow(err = CHIP_NO_ERROR); \ - } \ - } while (0) - - SELECT_PUBKEY(Root01); - SELECT_PUBKEY(Root02); - SELECT_PUBKEY(Root03); - SELECT_PUBKEY(ICA01); - SELECT_PUBKEY(ICA02); - SELECT_PUBKEY(ICA01_1); - SELECT_PUBKEY(FWSign01); - SELECT_PUBKEY(Node01_01); - SELECT_PUBKEY(Node01_02); - SELECT_PUBKEY(Node02_01); - SELECT_PUBKEY(Node02_02); - SELECT_PUBKEY(Node02_03); - SELECT_PUBKEY(Node02_04); - SELECT_PUBKEY(Node02_05); - SELECT_PUBKEY(Node02_06); - SELECT_PUBKEY(Node02_07); - SELECT_PUBKEY(Node02_08); - SELECT_PUBKEY(PDCID01); - -#undef SELECT_PUBKEY - - err = CHIP_ERROR_CA_CERT_NOT_FOUND; + TEST_CERT_SWITCH(certType, TEST_CERT_SELECT_PROPERTY, PublicKey, pubkey); + return CHIP_ERROR_NOT_FOUND; +} -exit: - return err; +CHIP_ERROR GetTestCertPrivkey(TestCert certType, ByteSpan & privkey) +{ + TEST_CERT_SWITCH(certType, TEST_CERT_SELECT_PROPERTY, PrivateKey, privkey) + return CHIP_ERROR_NOT_FOUND; } -CHIP_ERROR GetTestCertSKID(TestCert certType, ByteSpan & skid) +CHIP_ERROR GetTestCertKeypair(TestCert certType, Crypto::P256SerializedKeypair & keypair) { - CHIP_ERROR err; + ByteSpan pubkey; + ReturnErrorOnFailure(GetTestCertPubkey(certType, pubkey)); + VerifyOrReturnError(pubkey.size() == Crypto::kP256_PublicKey_Length, CHIP_ERROR_WRONG_KEY_TYPE); -#define SELECT_SKID(NAME) \ - do \ - { \ - if (certType == TestCert::k##NAME) \ - { \ - skid = sTestCert_##NAME##_SubjectKeyId; \ - ExitNow(err = CHIP_NO_ERROR); \ - } \ - } while (0) - - SELECT_SKID(Root01); - SELECT_SKID(Root02); - SELECT_SKID(Root03); - SELECT_SKID(ICA01); - SELECT_SKID(ICA02); - SELECT_SKID(ICA01_1); - SELECT_SKID(FWSign01); - SELECT_SKID(Node01_01); - SELECT_SKID(Node01_02); - SELECT_SKID(Node02_01); - SELECT_SKID(Node02_02); - SELECT_SKID(Node02_03); - SELECT_SKID(Node02_04); - SELECT_SKID(Node02_05); - SELECT_SKID(Node02_06); - SELECT_SKID(Node02_07); - SELECT_SKID(Node02_08); - SELECT_SKID(PDCID01); - -#undef SELECT_SKID - - err = CHIP_ERROR_CA_CERT_NOT_FOUND; + ByteSpan privkey; + ReturnErrorOnFailure(GetTestCertPrivkey(certType, privkey)); + VerifyOrReturnError(privkey.size() == Crypto::kP256_PrivateKey_Length, CHIP_ERROR_WRONG_KEY_TYPE); -exit: - return err; + ReturnErrorOnFailure(keypair.SetLength(Crypto::kP256_PublicKey_Length + Crypto::kP256_PrivateKey_Length)); + memcpy(keypair.Bytes(), pubkey.data(), Crypto::kP256_PublicKey_Length); + memcpy(keypair.Bytes() + Crypto::kP256_PublicKey_Length, privkey.data(), Crypto::kP256_PrivateKey_Length); + return CHIP_NO_ERROR; } -CHIP_ERROR GetTestCertAKID(TestCert certType, ByteSpan & akid) +CHIP_ERROR GetTestCertSKID(TestCert certType, ByteSpan & skid) { - CHIP_ERROR err; - -#define SELECT_AKID(NAME) \ - do \ - { \ - if (certType == TestCert::k##NAME) \ - { \ - akid = sTestCert_##NAME##_AuthorityKeyId; \ - ExitNow(err = CHIP_NO_ERROR); \ - } \ - } while (0) - - SELECT_AKID(Root01); - SELECT_AKID(Root02); - SELECT_AKID(Root03); - SELECT_AKID(ICA01); - SELECT_AKID(ICA02); - SELECT_AKID(ICA01_1); - SELECT_AKID(FWSign01); - SELECT_AKID(Node01_01); - SELECT_AKID(Node01_02); - SELECT_AKID(Node02_01); - SELECT_AKID(Node02_02); - SELECT_AKID(Node02_03); - SELECT_AKID(Node02_04); - SELECT_AKID(Node02_05); - SELECT_AKID(Node02_06); - SELECT_AKID(Node02_07); - SELECT_AKID(Node02_08); - SELECT_AKID(PDCID01); - -#undef SELECT_AKID - - err = CHIP_ERROR_CA_CERT_NOT_FOUND; + TEST_CERT_SWITCH(certType, TEST_CERT_SELECT_PROPERTY, SubjectKeyId, skid) + return CHIP_ERROR_NOT_FOUND; +} -exit: - return err; +CHIP_ERROR GetTestCertAKID(TestCert certType, ByteSpan & akid) +{ + TEST_CERT_SWITCH(certType, TEST_CERT_SELECT_PROPERTY, AuthorityKeyId, akid) + return CHIP_ERROR_NOT_FOUND; } CHIP_ERROR DecodeTestCert(ChipCertificateData & certData, TestCert certType) @@ -2577,5 +2454,14 @@ extern constexpr ByteSpan sTestCert_PDCID01_KeyId((const uint8_t[]){ 0x3a, 0x0e, 0x71, 0xe2, 0x09, 0x9a, 0x49, 0xda, 0xc9, 0x74, 0xfe, 0xd0, 0x5e, 0xa5, 0x3e, 0xba, 0xce, 0x29, 0x33, 0x90, }); +extern constexpr ByteSpan sTestCert_PDCID01_KeypairDER((const uint8_t[]){ + 0x30, 0x77, 0x02, 0x01, 0x01, 0x04, 0x20, 0x08, 0x0c, 0xa8, 0x62, 0xfc, 0x8b, 0x52, 0xca, 0x16, 0xea, 0x43, 0x82, 0x49, 0x78, + 0xda, 0x64, 0x20, 0x9f, 0x55, 0xd2, 0xde, 0x83, 0x1d, 0xee, 0x70, 0x84, 0x35, 0xc5, 0x04, 0xc1, 0x5e, 0xab, 0xa0, 0x0a, 0x06, + 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0xa1, 0x44, 0x03, 0x42, 0x00, 0x04, 0x39, 0xcc, 0xd4, 0xac, 0x7e, 0x59, + 0x68, 0xbd, 0xf1, 0xb0, 0x80, 0x11, 0x1d, 0x92, 0x53, 0x64, 0x94, 0xfc, 0x51, 0x62, 0x4c, 0x70, 0xaa, 0x6d, 0x73, 0x08, 0xda, + 0xed, 0xf3, 0xa1, 0x5e, 0x38, 0x69, 0x17, 0x7b, 0x1b, 0xf3, 0xd0, 0x90, 0x47, 0xeb, 0xf0, 0x6b, 0xe8, 0xdd, 0x17, 0xbe, 0x23, + 0xf2, 0xfb, 0x3d, 0x63, 0x90, 0xc6, 0xcf, 0x82, 0x80, 0x2f, 0x62, 0xd0, 0x53, 0x62, 0xc0, 0x08, +}); + } // namespace TestCerts } // namespace chip diff --git a/src/credentials/tests/CHIPCert_test_vectors.h b/src/credentials/tests/CHIPCert_test_vectors.h index 98f600a9384611..daacaed690c2b6 100644 --- a/src/credentials/tests/CHIPCert_test_vectors.h +++ b/src/credentials/tests/CHIPCert_test_vectors.h @@ -28,6 +28,7 @@ #include #include +#include #include namespace chip { @@ -72,6 +73,8 @@ enum class TestCertLoadFlags : uint8_t extern CHIP_ERROR GetTestCert(TestCert certType, BitFlags certLoadFlags, ByteSpan & cert); extern const char * GetTestCertName(TestCert certType); extern CHIP_ERROR GetTestCertPubkey(TestCert certType, ByteSpan & pubkey); +extern CHIP_ERROR GetTestCertPrivkey(TestCert certType, ByteSpan & privkey); +extern CHIP_ERROR GetTestCertKeypair(TestCert certType, Crypto::P256SerializedKeypair & keypair); extern CHIP_ERROR GetTestCertSKID(TestCert certType, ByteSpan & skid); extern CHIP_ERROR GetTestCertAKID(TestCert certType, ByteSpan & akid); @@ -213,6 +216,7 @@ extern const ByteSpan sTestCert_PDCID01_PrivateKey; extern const ByteSpan sTestCert_PDCID01_SubjectKeyId; // empty extern const ByteSpan sTestCert_PDCID01_AuthorityKeyId; // empty extern const ByteSpan sTestCert_PDCID01_KeyId; +extern const ByteSpan sTestCert_PDCID01_KeypairDER; } // namespace TestCerts } // namespace chip diff --git a/src/credentials/tests/TestChipCert.cpp b/src/credentials/tests/TestChipCert.cpp index 2179561367b9cc..2b0319f8ebb827 100644 --- a/src/credentials/tests/TestChipCert.cpp +++ b/src/credentials/tests/TestChipCert.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -2191,6 +2192,20 @@ static void TestChipCert_PDCIdentityGeneration(nlTestSuite * inSuite, void * inC NL_TEST_ASSERT(inSuite, ValidateChipNetworkIdentity(tlvCert) == CHIP_NO_ERROR); } +static void TestChipCert_KeypairConversion(nlTestSuite * inSuite, void * inContext) +{ + P256SerializedKeypair keypair; + NL_TEST_ASSERT_SUCCESS(inSuite, GetTestCertKeypair(kPDCID01, keypair)); + + uint8_t buffer[kP256ECPrivateKeyDERLength]; + MutableByteSpan keypairDer(buffer); + NL_TEST_ASSERT_SUCCESS(inSuite, ConvertECDSAKeypairRawToDER(keypair, keypairDer)); + + // Technically the curve name and public key are optional in the DER format, + // but both our code and standard tools include them, so we can just compare. + NL_TEST_ASSERT(inSuite, keypairDer.data_equal(sTestCert_PDCID01_KeypairDER)); +} + /** * Set up the test suite. */ @@ -2251,6 +2266,7 @@ static const nlTest sTests[] = { NL_TEST_DEF("Test extracting PublicKey and SKID from chip certificate", TestChipCert_ExtractPublicKeyAndSKID), NL_TEST_DEF("Test PDC Identity Validation", TestChipCert_PDCIdentityValidation), NL_TEST_DEF("Test PDC Identity Generation", TestChipCert_PDCIdentityGeneration), + NL_TEST_DEF("Test keypair conversion", TestChipCert_KeypairConversion), NL_TEST_SENTINEL() }; // clang-format on diff --git a/src/crypto/CHIPCryptoPAL.h b/src/crypto/CHIPCryptoPAL.h index 55ab402f8fd4b2..33192c0da6cb4e 100644 --- a/src/crypto/CHIPCryptoPAL.h +++ b/src/crypto/CHIPCryptoPAL.h @@ -484,6 +484,9 @@ struct alignas(size_t) P256KeypairContext uint8_t mBytes[kMAX_P256Keypair_Context_Size]; }; +/** + * A serialized P256 key pair is the concatenation of the public and private keys, in that order. + */ using P256SerializedKeypair = SensitiveDataBuffer; class P256KeypairBase : public ECPKeypair diff --git a/src/include/platform/NetworkCommissioning.h b/src/include/platform/NetworkCommissioning.h index 6c2741d81201c5..472ef6d6874d2c 100644 --- a/src/include/platform/NetworkCommissioning.h +++ b/src/include/platform/NetworkCommissioning.h @@ -351,7 +351,8 @@ class WiFiDriver : public Internal::WirelessDriver /** * @brief Signs the specified message with the private key of a Network Client Identity. */ - virtual CHIP_ERROR SignWithClientIdentity(uint8_t networkIndex, ByteSpan & message, Crypto::P256ECDSASignature & outSignature) + virtual CHIP_ERROR SignWithClientIdentity(uint8_t networkIndex, const ByteSpan & message, + Crypto::P256ECDSASignature & outSignature) { return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; } diff --git a/src/platform/Linux/BUILD.gn b/src/platform/Linux/BUILD.gn index 6fca28efb0ae3a..061f052869d883 100644 --- a/src/platform/Linux/BUILD.gn +++ b/src/platform/Linux/BUILD.gn @@ -73,7 +73,10 @@ static_library("Linux") { "SystemTimeSupport.cpp", ] - deps = [ "${chip_root}/src/setup_payload" ] + deps = [ + "${chip_root}/src/credentials:credentials_header", + "${chip_root}/src/setup_payload", + ] if (!chip_use_external_logging) { sources += [ "Logging.cpp" ] diff --git a/src/platform/Linux/ConnectivityManagerImpl.cpp b/src/platform/Linux/ConnectivityManagerImpl.cpp index 718824c6c428e5..a2566d20f35560 100644 --- a/src/platform/Linux/ConnectivityManagerImpl.cpp +++ b/src/platform/Linux/ConnectivityManagerImpl.cpp @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -40,6 +41,7 @@ #include #include +#include #include #include #include @@ -59,12 +61,14 @@ #endif #if CHIP_DEVICE_CONFIG_ENABLE_WPA +#include #include #include #endif using namespace ::chip; using namespace ::chip::TLV; +using namespace ::chip::Credentials; using namespace ::chip::DeviceLayer; using namespace ::chip::DeviceLayer::Internal; using namespace ::chip::app::Clusters::GeneralDiagnostics; @@ -977,25 +981,14 @@ void ConnectivityManagerImpl::DriveAPState(::chip::System::Layer * aLayer, void } CHIP_ERROR -ConnectivityManagerImpl::ConnectWiFiNetworkAsync(ByteSpan ssid, ByteSpan credentials, - NetworkCommissioning::Internal::WirelessDriver::ConnectCallback * apCallback) +ConnectivityManagerImpl::_ConnectWiFiNetworkAsync(GVariant * args, + NetworkCommissioning::Internal::WirelessDriver::ConnectCallback * apCallback) { + GAutoPtr argsDeleter(g_variant_ref_sink(args)); // args may be floating, ensure we don't leak it + CHIP_ERROR ret = CHIP_NO_ERROR; GAutoPtr err; - GVariant * args = nullptr; - GVariantBuilder builder; gboolean result; - char ssidStr[kMaxWiFiSSIDLength + 1u] = { 0 }; - char keyStr[kMaxWiFiKeyLength + 1u] = { 0 }; - - std::lock_guard lock(mWpaSupplicantMutex); - - VerifyOrReturnError(ssid.size() <= kMaxWiFiSSIDLength, CHIP_ERROR_INVALID_ARGUMENT); - VerifyOrReturnError(credentials.size() <= kMaxWiFiKeyLength, CHIP_ERROR_INVALID_ARGUMENT); - VerifyOrReturnError(mWpaSupplicant.iface != nullptr, CHIP_ERROR_INCORRECT_STATE); - - // There is another ongoing connect request, reject the new one. - VerifyOrReturnError(mpConnectCallback == nullptr, CHIP_ERROR_INCORRECT_STATE); const gchar * networkPath = wpa_fi_w1_wpa_supplicant1_interface_get_current_network(mWpaSupplicant.iface); @@ -1026,14 +1019,6 @@ ConnectivityManagerImpl::ConnectWiFiNetworkAsync(ByteSpan ssid, ByteSpan credent SuccessOrExit(ret); } - g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); - memcpy(ssidStr, ssid.data(), ssid.size()); - memcpy(keyStr, credentials.data(), credentials.size()); - g_variant_builder_add(&builder, "{sv}", "ssid", g_variant_new_string(ssidStr)); - g_variant_builder_add(&builder, "{sv}", "psk", g_variant_new_string(keyStr)); - g_variant_builder_add(&builder, "{sv}", "key_mgmt", g_variant_new_string("SAE WPA-PSK")); - args = g_variant_builder_end(&builder); - result = wpa_fi_w1_wpa_supplicant1_interface_call_add_network_sync(mWpaSupplicant.iface, args, &mWpaSupplicant.networkPath, nullptr, &MakeUniquePointerReceiver(err).Get()); @@ -1051,7 +1036,7 @@ ConnectivityManagerImpl::ConnectWiFiNetworkAsync(ByteSpan ssid, ByteSpan credent } else { - ChipLogProgress(DeviceLayer, "wpa_supplicant: failed to add network: %s", err ? err->message : "unknown error"); + ChipLogError(DeviceLayer, "wpa_supplicant: failed to add network: %s", err ? err->message : "unknown error"); if (mWpaSupplicant.networkPath) { @@ -1066,6 +1051,146 @@ ConnectivityManagerImpl::ConnectWiFiNetworkAsync(ByteSpan ssid, ByteSpan credent return ret; } +CHIP_ERROR +ConnectivityManagerImpl::ConnectWiFiNetworkAsync(ByteSpan ssid, ByteSpan credentials, + NetworkCommissioning::Internal::WirelessDriver::ConnectCallback * connectCallback) +{ + char ssidStr[kMaxWiFiSSIDLength + 1] = { 0 }; + char keyStr[kMaxWiFiKeyLength + 1] = { 0 }; + + VerifyOrReturnError(ssid.size() <= kMaxWiFiSSIDLength, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(credentials.size() <= kMaxWiFiKeyLength, CHIP_ERROR_INVALID_ARGUMENT); + + std::lock_guard lock(mWpaSupplicantMutex); + VerifyOrReturnError(mWpaSupplicant.iface != nullptr, CHIP_ERROR_INCORRECT_STATE); + + // There is another ongoing connect request, reject the new one. + VerifyOrReturnError(mpConnectCallback == nullptr, CHIP_ERROR_INCORRECT_STATE); + + GVariantBuilder builder; + g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); + memcpy(ssidStr, ssid.data(), ssid.size()); + memcpy(keyStr, credentials.data(), credentials.size()); + g_variant_builder_add(&builder, "{sv}", "ssid", g_variant_new_string(ssidStr)); + g_variant_builder_add(&builder, "{sv}", "psk", g_variant_new_string(keyStr)); + g_variant_builder_add(&builder, "{sv}", "key_mgmt", g_variant_new_string("SAE WPA-PSK")); + GVariant * args = g_variant_builder_end(&builder); + return _ConnectWiFiNetworkAsync(args, connectCallback); +} + +#if CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC +static CHIP_ERROR AddOrReplaceBlob(WpaFiW1Wpa_supplicant1Interface * iface, const char * nameOrRef, ByteSpan data) +{ + // Strip the blob:// prefix off the name (if present), so we don't need as many string constants. + constexpr auto refPrefix = "blob://"_span; + const char * name = (strncmp(nameOrRef, refPrefix.data(), refPrefix.size()) == 0) ? nameOrRef + refPrefix.size() : nameOrRef; + + GAutoPtr err; + if (!wpa_fi_w1_wpa_supplicant1_interface_call_remove_blob_sync(iface, name, nullptr, &MakeUniquePointerReceiver(err).Get())) + { + GAutoPtr remoteError(g_dbus_error_get_remote_error(err.get())); + if (!(remoteError && strcmp(remoteError.get(), kWpaSupplicantBlobUnknown) == 0)) + { + ChipLogError(DeviceLayer, "wpa_supplicant: failed to remove blob: %s", err ? err->message : "unknown error"); + return CHIP_ERROR_INTERNAL; + } + err.reset(); + } + if (!wpa_fi_w1_wpa_supplicant1_interface_call_add_blob_sync( + iface, name, g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, data.data(), data.size(), 1), nullptr, + &MakeUniquePointerReceiver(err).Get())) + { + ChipLogError(DeviceLayer, "wpa_supplicant: failed to add blob: %s", err ? err->message : "unknown error"); + return CHIP_ERROR_INTERNAL; + } + return CHIP_NO_ERROR; +} + +// Note: Static blob names assume we're only supporting a single network configuration. +static constexpr char kNetworkIdentityBlobRef[] = "blob://pdc-ni"; +static constexpr char kClientIdentityBlobRef[] = "blob://pdc-ci"; +static constexpr char kClientIdentityKeyBlobRef[] = "blob://pdc-cik"; + +CHIP_ERROR ConnectivityManagerImpl::ConnectWiFiNetworkWithPDCAsync( + ByteSpan ssid, ByteSpan networkIdentity, ByteSpan clientIdentity, const Crypto::P256Keypair & clientIdentityKeypair, + NetworkCommissioning::Internal::WirelessDriver::ConnectCallback * connectCallback) +{ + VerifyOrReturnError(ssid.size() <= kMaxWiFiSSIDLength, CHIP_ERROR_INVALID_ARGUMENT); + + std::lock_guard lock(mWpaSupplicantMutex); + VerifyOrReturnError(mWpaSupplicant.iface != nullptr, CHIP_ERROR_INCORRECT_STATE); + + // There is another ongoing connect request, reject the new one. + VerifyOrReturnError(mpConnectCallback == nullptr, CHIP_ERROR_INCORRECT_STATE); + + // Convert identities and our key pair to DER and add them to wpa_supplicant as blobs + { + constexpr size_t bufferSize = std::max(kMaxDERCertLength, kP256ECPrivateKeyDERLength); + Platform::ScopedMemoryBuffer buffer; + VerifyOrReturnError(buffer.Alloc(bufferSize), CHIP_ERROR_NO_MEMORY); + + MutableByteSpan networkIdentityDER(buffer.Get(), bufferSize); + ReturnErrorOnFailure(ConvertChipCertToX509Cert(networkIdentity, networkIdentityDER)); + ReturnErrorOnFailure(AddOrReplaceBlob(mWpaSupplicant.iface, kNetworkIdentityBlobRef, networkIdentityDER)); + + MutableByteSpan clientIdentityDER(buffer.Get(), bufferSize); + ReturnErrorOnFailure(ConvertChipCertToX509Cert(clientIdentity, clientIdentityDER)); + ReturnErrorOnFailure(AddOrReplaceBlob(mWpaSupplicant.iface, kClientIdentityBlobRef, clientIdentityDER)); + + Crypto::P256SerializedKeypair serializedKeypair; + MutableByteSpan clientIdentityKeypairDER(buffer.Get(), bufferSize); + ReturnErrorOnFailure(clientIdentityKeypair.Serialize(serializedKeypair)); + ReturnErrorOnFailure(ConvertECDSAKeypairRawToDER(serializedKeypair, clientIdentityKeypairDER)); + ReturnErrorOnFailure(AddOrReplaceBlob(mWpaSupplicant.iface, kClientIdentityKeyBlobRef, clientIdentityKeypairDER)); + } + + // Build the network configuration + GVariantBuilder builder; + g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); + + { + char ssidStr[kMaxWiFiSSIDLength + 1] = { 0 }; + memcpy(ssidStr, ssid.data(), ssid.size()); + g_variant_builder_add(&builder, "{sv}", "ssid", g_variant_new_string(ssidStr)); + } + + { + CertificateKeyIdStorage keyId; + ReturnErrorOnFailure(ExtractIdentifierFromChipNetworkIdentity(networkIdentity, keyId)); + + static constexpr char kNAIDomain[] = ".pdc.csa-iot.org"; + static constexpr auto keyIdHexSize = keyId.size() * 2; + char identityStr[1 + keyIdHexSize + sizeof(kNAIDomain)]; // sizeof(kNAIDomain) includes null terminator + + identityStr[0] = '@'; + ReturnErrorOnFailure(Encoding::BytesToUppercaseHexBuffer(keyId.data(), keyId.size(), &identityStr[1], keyIdHexSize)); + strcpy(&identityStr[1 + keyIdHexSize], kNAIDomain); + g_variant_builder_add(&builder, "{sv}", "identity", g_variant_new_string(identityStr)); + } + + // The configuration will become simpler once we add explicit Matter support to wpa_supplicant + g_variant_builder_add(&builder, "{sv}", "key_mgmt", g_variant_new_string("WPA-EAP-SHA256")); + g_variant_builder_add(&builder, "{sv}", "fallback_key_mgmt", g_variant_new_string("WPA-EAP-SHA256")); + g_variant_builder_add(&builder, "{sv}", "pairwise", g_variant_new_string("CCMP")); + g_variant_builder_add(&builder, "{sv}", "group", g_variant_new_string("CCMP")); + g_variant_builder_add(&builder, "{sv}", "ieee80211w", g_variant_new_int32(2)); + g_variant_builder_add(&builder, "{sv}", "eap", g_variant_new_string("TLS")); + g_variant_builder_add(&builder, "{sv}", "eap_workaround", g_variant_new_int32(0)); + + g_variant_builder_add( + &builder, "{sv}", "phase1", + g_variant_new_string("tls_disable_tlsv1_0=1,tls_disable_tlsv1_1=1,tls_disable_tlsv1_2=1,tls_disable_tlsv1_3=0")); + g_variant_builder_add(&builder, "{sv}", "openssl_ciphers", g_variant_new_string("TLS_AES_128_CCM_SHA256")); + g_variant_builder_add(&builder, "{sv}", "openssl_ecdh_curves", g_variant_new_string("P-256")); + + g_variant_builder_add(&builder, "{sv}", "ca_cert", g_variant_new_string(kNetworkIdentityBlobRef)); + g_variant_builder_add(&builder, "{sv}", "client_cert", g_variant_new_string(kClientIdentityBlobRef)); + g_variant_builder_add(&builder, "{sv}", "private_key", g_variant_new_string(kClientIdentityKeyBlobRef)); + GVariant * args = g_variant_builder_end(&builder); + return _ConnectWiFiNetworkAsync(args, connectCallback); +} +#endif // CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC + void ConnectivityManagerImpl::_ConnectWiFiNetworkAsyncCallback(GObject * source_object, GAsyncResult * res, gpointer user_data) { ConnectivityManagerImpl * this_ = reinterpret_cast(user_data); diff --git a/src/platform/Linux/ConnectivityManagerImpl.h b/src/platform/Linux/ConnectivityManagerImpl.h index 1325c6cd83f21d..930b84fb4d455b 100644 --- a/src/platform/Linux/ConnectivityManagerImpl.h +++ b/src/platform/Linux/ConnectivityManagerImpl.h @@ -122,17 +122,23 @@ class ConnectivityManagerImpl final : public ConnectivityManager, // the implementation methods provided by this class. friend class ConnectivityManager; -public: #if CHIP_DEVICE_CONFIG_ENABLE_WPA +public: void SetNetworkStatusChangeCallback(NetworkCommissioning::Internal::BaseDriver::NetworkStatusChangeCallback * statusChangeCallback) { mpStatusChangeCallback = statusChangeCallback; } + CHIP_ERROR ConnectWiFiNetworkAsync(ByteSpan ssid, ByteSpan credentials, NetworkCommissioning::Internal::WirelessDriver::ConnectCallback * connectCallback); +#if CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC + CHIP_ERROR ConnectWiFiNetworkWithPDCAsync(ByteSpan ssid, ByteSpan networkIdentity, ByteSpan clientIdentity, + const Crypto::P256Keypair & clientIdentityKeypair, + NetworkCommissioning::Internal::WirelessDriver::ConnectCallback * connectCallback); +#endif // CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC + void PostNetworkConnect(); - static void _ConnectWiFiNetworkAsyncCallback(GObject * source_object, GAsyncResult * res, gpointer user_data); CHIP_ERROR CommitConfig(); void StartWiFiManagement(); @@ -144,8 +150,15 @@ class ConnectivityManagerImpl final : public ConnectivityManager, CHIP_ERROR GetWiFiVersion(app::Clusters::WiFiNetworkDiagnostics::WiFiVersionEnum & wiFiVersion); CHIP_ERROR GetConfiguredNetwork(NetworkCommissioning::Network & network); CHIP_ERROR StartWiFiScan(ByteSpan ssid, NetworkCommissioning::WiFiDriver::ScanCallback * callback); + +private: + CHIP_ERROR _ConnectWiFiNetworkAsync(GVariant * networkArgs, + NetworkCommissioning::Internal::WirelessDriver::ConnectCallback * connectCallback) + CHIP_REQUIRES(mWpaSupplicantMutex); + static void _ConnectWiFiNetworkAsyncCallback(GObject * source_object, GAsyncResult * res, gpointer user_data); #endif +public: const char * GetEthernetIfName() { return (mEthIfName[0] == '\0') ? nullptr : mEthIfName; } #if CHIP_DEVICE_CONFIG_ENABLE_WIFI diff --git a/src/platform/Linux/ConnectivityUtils.h b/src/platform/Linux/ConnectivityUtils.h index e4884e935bd875..1ea060b0da7842 100644 --- a/src/platform/Linux/ConnectivityUtils.h +++ b/src/platform/Linux/ConnectivityUtils.h @@ -37,6 +37,7 @@ static constexpr uint16_t kWiFi_BAND_2_4_GHZ = 2400; static constexpr uint16_t kWiFi_BAND_5_0_GHZ = 5000; static constexpr char kWpaSupplicantServiceName[] = "fi.w1.wpa_supplicant1"; static constexpr char kWpaSupplicantObjectPath[] = "/fi/w1/wpa_supplicant1"; +static constexpr char kWpaSupplicantBlobUnknown[] = "fi.w1.wpa_supplicant1.BlobUnknown"; class ConnectivityUtils { diff --git a/src/platform/Linux/NetworkCommissioningDriver.h b/src/platform/Linux/NetworkCommissioningDriver.h index 7b3734d75f4bbe..96a647c2d22cbc 100644 --- a/src/platform/Linux/NetworkCommissioningDriver.h +++ b/src/platform/Linux/NetworkCommissioningDriver.h @@ -17,7 +17,12 @@ #pragma once +#include +#include +#include +#include #include + #include namespace chip { @@ -69,14 +74,6 @@ class LinuxWiFiDriver final : public WiFiDriver bool exhausted = false; }; - struct WiFiNetwork - { - uint8_t ssid[DeviceLayer::Internal::kMaxWiFiSSIDLength]; - uint8_t ssidLen = 0; - uint8_t credentials[DeviceLayer::Internal::kMaxWiFiKeyLength]; - uint8_t credentialsLen = 0; - }; - void Set5gSupport(bool is5gSupported) { mIs5gSupported = is5gSupported; } // BaseDriver @@ -111,8 +108,45 @@ class LinuxWiFiDriver final : public WiFiDriver return supportedBands; } +#if CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC + bool SupportsPerDeviceCredentials() override { return true; }; + CHIP_ERROR AddOrUpdateNetworkWithPDC(ByteSpan ssid, ByteSpan networkIdentity, Optional clientIdentityNetworkIndex, + Status & outStatus, MutableCharSpan & outDebugText, MutableByteSpan & outClientIdentity, + uint8_t & outNetworkIndex) override; + CHIP_ERROR GetNetworkIdentity(uint8_t networkIndex, MutableByteSpan & outNetworkIdentity) override; + CHIP_ERROR GetClientIdentity(uint8_t networkIndex, MutableByteSpan & outClientIdentity) override; + CHIP_ERROR SignWithClientIdentity(uint8_t networkIndex, const ByteSpan & message, + Crypto::P256ECDSASignature & outSignature) override; +#endif // CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC + private: - bool NetworkMatch(const WiFiNetwork & network, ByteSpan networkId); + struct WiFiNetwork + { + bool Empty() const { return ssidLen == 0; } + bool Matches(ByteSpan aSsid) const { return !Empty() && ByteSpan(ssid, ssidLen).data_equal(aSsid); } + + uint8_t ssid[DeviceLayer::Internal::kMaxWiFiSSIDLength]; + uint8_t ssidLen = 0; + static_assert(std::numeric_limits::max() >= sizeof(ssid)); + + uint8_t credentials[DeviceLayer::Internal::kMaxWiFiKeyLength]; + uint8_t credentialsLen = 0; + static_assert(std::numeric_limits::max() >= sizeof(credentials)); + +#if CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC + bool UsingPDC() const { return networkIdentityLen != 0; } + + uint8_t networkIdentity[Credentials::kMaxCHIPCompactNetworkIdentityLength]; + uint8_t networkIdentityLen = 0; + static_assert(std::numeric_limits::max() >= sizeof(networkIdentity)); + + uint8_t clientIdentity[Credentials::kMaxCHIPCompactNetworkIdentityLength]; + uint8_t clientIdentityLen = 0; + static_assert(std::numeric_limits::max() >= sizeof(clientIdentity)); + + Platform::SharedPtr clientIdentityKeypair; +#endif // CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC + }; WiFiNetwork mSavedNetwork; WiFiNetwork mStagingNetwork; diff --git a/src/platform/Linux/NetworkCommissioningWiFiDriver.cpp b/src/platform/Linux/NetworkCommissioningWiFiDriver.cpp index d1d40dbe3e3864..4b5b3c0b6b5e7d 100644 --- a/src/platform/Linux/NetworkCommissioningWiFiDriver.cpp +++ b/src/platform/Linux/NetworkCommissioningWiFiDriver.cpp @@ -15,6 +15,7 @@ * limitations under the License. */ +#include #include #include #include @@ -25,7 +26,8 @@ #include using namespace chip; -using namespace chip::Thread; +using namespace chip::Crypto; +using namespace chip::Credentials; namespace chip { namespace DeviceLayer { @@ -38,6 +40,16 @@ namespace NetworkCommissioning { namespace { constexpr char kWiFiSSIDKeyName[] = "wifi-ssid"; constexpr char kWiFiCredentialsKeyName[] = "wifi-pass"; +#if CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC +constexpr char kWifiNetworkIdentityKeyName[] = "wifi-ni"; +constexpr char kWifiClientIdentityKeyName[] = "wifi-ci"; +constexpr char kWifiClientIdentityKeypairKeyName[] = "wifi-cik"; + +inline CHIP_ERROR IgnoreNotFound(CHIP_ERROR err) +{ + return (err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND) ? CHIP_NO_ERROR : err; +} +#endif // CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC } // namespace // NOTE: For WiFiDriver, we uses two network configs, one is mSavedNetwork, and another is mStagingNetwork, during init, it will @@ -53,29 +65,54 @@ constexpr char kWiFiCredentialsKeyName[] = "wifi-pass"; CHIP_ERROR LinuxWiFiDriver::Init(BaseDriver::NetworkStatusChangeCallback * networkStatusChangeCallback) { CHIP_ERROR err; - size_t ssidLen = 0; - size_t credentialsLen = 0; + WiFiNetwork network; + size_t valueLen = 0; - err = PersistedStorage::KeyValueStoreMgr().Get(kWiFiCredentialsKeyName, mSavedNetwork.credentials, - sizeof(mSavedNetwork.credentials), &credentialsLen); - if (err == CHIP_ERROR_KEY_NOT_FOUND) - { - return CHIP_NO_ERROR; - } + auto & kvs = PersistedStorage::KeyValueStoreMgr(); - err = PersistedStorage::KeyValueStoreMgr().Get(kWiFiSSIDKeyName, mSavedNetwork.ssid, sizeof(mSavedNetwork.ssid), &ssidLen); - if (err == CHIP_ERROR_KEY_NOT_FOUND) + SuccessOrExit(err = IgnoreNotFound(kvs.Get(kWiFiSSIDKeyName, network.ssid, sizeof(network.ssid), &valueLen))); + if (valueLen != 0) { - return CHIP_NO_ERROR; - } + network.ssidLen = valueLen; - mSavedNetwork.credentialsLen = credentialsLen; - mSavedNetwork.ssidLen = ssidLen; + err = kvs.Get(kWiFiCredentialsKeyName, network.credentials, sizeof(network.credentials), &valueLen); +#if CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC + if (err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND) + { + SuccessOrExit( + err = kvs.Get(kWifiNetworkIdentityKeyName, network.networkIdentity, sizeof(network.networkIdentity), &valueLen)); + VerifyOrExit(valueLen > 0, err = CHIP_ERROR_INTEGRITY_CHECK_FAILED); + network.networkIdentityLen = valueLen; + + SuccessOrExit( + err = kvs.Get(kWifiClientIdentityKeyName, network.clientIdentity, sizeof(network.clientIdentity), &valueLen)); + VerifyOrExit(valueLen > 0, err = CHIP_ERROR_INTEGRITY_CHECK_FAILED); + network.clientIdentityLen = valueLen; + + P256SerializedKeypair serializedKeypair; + SuccessOrExit(err = kvs.Get(kWifiClientIdentityKeypairKeyName, serializedKeypair.Bytes(), serializedKeypair.Capacity(), + &valueLen)); + serializedKeypair.SetLength(valueLen); + network.clientIdentityKeypair = Platform::MakeShared(); + SuccessOrExit(err = network.clientIdentityKeypair->Deserialize(serializedKeypair)); + } + else +#endif // CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC + { + SuccessOrExit(err); + network.credentialsLen = valueLen; + } - mStagingNetwork = mSavedNetwork; + mStagingNetwork = mSavedNetwork = network; + } ConnectivityMgrImpl().SetNetworkStatusChangeCallback(networkStatusChangeCallback); return CHIP_NO_ERROR; + +exit: + ChipLogProgress(NetworkProvisioning, "LinuxWiFiDriver: Failed to load network configuration: %" CHIP_ERROR_FORMAT, + err.Format()); + return err; } void LinuxWiFiDriver::Shutdown() @@ -85,9 +122,33 @@ void LinuxWiFiDriver::Shutdown() CHIP_ERROR LinuxWiFiDriver::CommitConfiguration() { - ReturnErrorOnFailure(PersistedStorage::KeyValueStoreMgr().Put(kWiFiSSIDKeyName, mStagingNetwork.ssid, mStagingNetwork.ssidLen)); - ReturnErrorOnFailure(PersistedStorage::KeyValueStoreMgr().Put(kWiFiCredentialsKeyName, mStagingNetwork.credentials, - mStagingNetwork.credentialsLen)); + auto & kvs = PersistedStorage::KeyValueStoreMgr(); + ReturnErrorOnFailure(kvs.Put(kWiFiSSIDKeyName, mStagingNetwork.ssid, mStagingNetwork.ssidLen)); +#if CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC + if (mStagingNetwork.UsingPDC()) + { + ReturnErrorOnFailure(IgnoreNotFound(kvs.Delete(kWiFiCredentialsKeyName))); + ReturnErrorOnFailure( + kvs.Put(kWifiNetworkIdentityKeyName, mStagingNetwork.networkIdentity, mStagingNetwork.networkIdentityLen)); + ReturnErrorOnFailure( + kvs.Put(kWifiClientIdentityKeyName, mStagingNetwork.clientIdentity, mStagingNetwork.clientIdentityLen)); + + P256SerializedKeypair serializedKeypair; + ReturnErrorOnFailure(mStagingNetwork.clientIdentityKeypair->Serialize(serializedKeypair)); + ReturnErrorOnFailure( + kvs.Put(kWifiClientIdentityKeypairKeyName, serializedKeypair.ConstBytes(), serializedKeypair.Length())); + } + else + { + ReturnErrorOnFailure(IgnoreNotFound(kvs.Delete(kWifiNetworkIdentityKeyName))); + ReturnErrorOnFailure(IgnoreNotFound(kvs.Delete(kWifiClientIdentityKeyName))); + ReturnErrorOnFailure(IgnoreNotFound(kvs.Delete(kWifiClientIdentityKeypairKeyName))); +#else // CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC + { +#endif // CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC + ReturnErrorOnFailure(kvs.Put(kWiFiCredentialsKeyName, mStagingNetwork.credentials, mStagingNetwork.credentialsLen)); + } + ReturnErrorOnFailure(ConnectivityMgrImpl().CommitConfig()); mSavedNetwork = mStagingNetwork; return CHIP_NO_ERROR; @@ -99,26 +160,16 @@ CHIP_ERROR LinuxWiFiDriver::RevertConfiguration() return CHIP_NO_ERROR; } -bool LinuxWiFiDriver::NetworkMatch(const WiFiNetwork & network, ByteSpan networkId) -{ - return networkId.size() == network.ssidLen && memcmp(networkId.data(), network.ssid, network.ssidLen) == 0; -} - Status LinuxWiFiDriver::AddOrUpdateNetwork(ByteSpan ssid, ByteSpan credentials, MutableCharSpan & outDebugText, uint8_t & outNetworkIndex) { outDebugText.reduce_size(0); outNetworkIndex = 0; - VerifyOrReturnError(mStagingNetwork.ssidLen == 0 || NetworkMatch(mStagingNetwork, ssid), Status::kBoundsExceeded); - - static_assert(sizeof(WiFiNetwork::ssid) <= std::numeric_limits::max(), - "Max length of WiFi ssid exceeds the limit of ssidLen field"); - static_assert(sizeof(WiFiNetwork::credentials) <= std::numeric_limits::max(), - "Max length of WiFi credentials exceeds the limit of credentialsLen field"); + VerifyOrReturnError(mStagingNetwork.Empty() || mStagingNetwork.Matches(ssid), Status::kBoundsExceeded); // Do the check before setting the values, so the data is not updated on error. VerifyOrReturnError(credentials.size() <= sizeof(mStagingNetwork.credentials), Status::kOutOfRange); - VerifyOrReturnError(ssid.size() <= sizeof(mStagingNetwork.ssid), Status::kOutOfRange); + VerifyOrReturnError(!ssid.empty() && ssid.size() <= sizeof(mStagingNetwork.ssid), Status::kOutOfRange); memcpy(mStagingNetwork.credentials, credentials.data(), credentials.size()); mStagingNetwork.credentialsLen = static_cast(credentials.size()); @@ -126,6 +177,12 @@ Status LinuxWiFiDriver::AddOrUpdateNetwork(ByteSpan ssid, ByteSpan credentials, memcpy(mStagingNetwork.ssid, ssid.data(), ssid.size()); mStagingNetwork.ssidLen = static_cast(ssid.size()); +#if CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC + mStagingNetwork.networkIdentityLen = 0; + mStagingNetwork.clientIdentityLen = 0; + mStagingNetwork.clientIdentityKeypair.reset(); +#endif // CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC + return Status::kSuccess; } @@ -133,7 +190,7 @@ Status LinuxWiFiDriver::RemoveNetwork(ByteSpan networkId, MutableCharSpan & outD { outDebugText.reduce_size(0); outNetworkIndex = 0; - VerifyOrReturnError(NetworkMatch(mStagingNetwork, networkId), Status::kNetworkIDNotFound); + VerifyOrReturnError(mStagingNetwork.Matches(networkId), Status::kNetworkIDNotFound); // Use empty ssid for representing invalid network mStagingNetwork.ssidLen = 0; @@ -143,9 +200,9 @@ Status LinuxWiFiDriver::RemoveNetwork(ByteSpan networkId, MutableCharSpan & outD Status LinuxWiFiDriver::ReorderNetwork(ByteSpan networkId, uint8_t index, MutableCharSpan & outDebugText) { outDebugText.reduce_size(0); - VerifyOrReturnError(NetworkMatch(mStagingNetwork, networkId), Status::kNetworkIDNotFound); + VerifyOrReturnError(mStagingNetwork.Matches(networkId), Status::kNetworkIDNotFound); + VerifyOrReturnError(index == 0, Status::kOutOfRange); // We only support one network, so reorder is actually no-op. - return Status::kSuccess; } @@ -154,14 +211,26 @@ void LinuxWiFiDriver::ConnectNetwork(ByteSpan networkId, ConnectCallback * callb CHIP_ERROR err = CHIP_NO_ERROR; Status networkingStatus = Status::kSuccess; - VerifyOrExit(NetworkMatch(mStagingNetwork, networkId), networkingStatus = Status::kNetworkIDNotFound); + const auto & network = mStagingNetwork; + VerifyOrExit(network.Matches(networkId), networkingStatus = Status::kNetworkIDNotFound); + +#if CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC + if (network.UsingPDC()) + { + ChipLogProgress(NetworkProvisioning, "LinuxWiFiDriver: ConnectNetwork (PDC) '%.*s'", network.ssidLen, network.ssid); + err = ConnectivityMgrImpl().ConnectWiFiNetworkWithPDCAsync( + ByteSpan(network.ssid, network.ssidLen), ByteSpan(network.networkIdentity, network.networkIdentityLen), + ByteSpan(network.clientIdentity, network.clientIdentityLen), *network.clientIdentityKeypair, callback); + } + else +#endif // CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC + { + ChipLogProgress(NetworkProvisioning, "LinuxWiFiDriver: ConnectNetwork '%.*s'", network.ssidLen, network.ssid); - ChipLogProgress(NetworkProvisioning, "LinuxWiFiDriver: SSID: %.*s", static_cast(networkId.size()), - StringOrNullMarker((char *) networkId.data())); + err = ConnectivityMgrImpl().ConnectWiFiNetworkAsync(ByteSpan(network.ssid, network.ssidLen), + ByteSpan(network.credentials, network.credentialsLen), callback); + } - err = ConnectivityMgrImpl().ConnectWiFiNetworkAsync(ByteSpan(mStagingNetwork.ssid, mStagingNetwork.ssidLen), - ByteSpan(mStagingNetwork.credentials, mStagingNetwork.credentialsLen), - callback); exit: if (err != CHIP_NO_ERROR) { @@ -170,7 +239,7 @@ void LinuxWiFiDriver::ConnectNetwork(ByteSpan networkId, ConnectCallback * callb if (networkingStatus != Status::kSuccess) { - ChipLogError(NetworkProvisioning, "Failed to connect to WiFi network: %s", chip::ErrorStr(err)); + ChipLogError(NetworkProvisioning, "Failed to connect to WiFi network: %" CHIP_ERROR_FORMAT, err.Format()); callback->OnResult(networkingStatus, CharSpan(), 0); } } @@ -186,12 +255,12 @@ void LinuxWiFiDriver::ScanNetworks(ByteSpan ssid, WiFiDriver::ScanCallback * cal size_t LinuxWiFiDriver::WiFiNetworkIterator::Count() { - return driver->mStagingNetwork.ssidLen == 0 ? 0 : 1; + return driver->mStagingNetwork.Empty() ? 0 : 1; } bool LinuxWiFiDriver::WiFiNetworkIterator::Next(Network & item) { - if (exhausted || driver->mStagingNetwork.ssidLen == 0) + if (exhausted || driver->mStagingNetwork.Empty()) { return false; } @@ -214,6 +283,77 @@ bool LinuxWiFiDriver::WiFiNetworkIterator::Next(Network & item) return true; } +#if CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC +CHIP_ERROR LinuxWiFiDriver::AddOrUpdateNetworkWithPDC(ByteSpan ssid, ByteSpan networkIdentity, + Optional clientIdentityNetworkIndex, Status & outStatus, + MutableCharSpan & outDebugText, MutableByteSpan & outClientIdentity, + uint8_t & outNetworkIndex) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + outStatus = Status::kUnknownError; + VerifyOrExit(mStagingNetwork.Empty() || mStagingNetwork.Matches(ssid), outStatus = Status::kBoundsExceeded); + + VerifyOrExit(!ssid.empty() && ssid.size() <= sizeof(WiFiNetwork::ssid), outStatus = Status::kOutOfRange); + VerifyOrExit(!networkIdentity.empty() && networkIdentity.size() <= sizeof(WiFiNetwork::networkIdentity), + outStatus = Status::kOutOfRange); + VerifyOrExit(!clientIdentityNetworkIndex.HasValue() || (clientIdentityNetworkIndex.Value() == 0 && mStagingNetwork.UsingPDC()), + outStatus = Status::kOutOfRange); + + { + WiFiNetwork network = mStagingNetwork; // update a copy first in case of errors + + memcpy(network.ssid, ssid.data(), network.ssidLen = ssid.size()); + memcpy(network.networkIdentity, networkIdentity.data(), network.networkIdentityLen = networkIdentity.size()); + + // If an existing client identity is being reused, we would need to copy it here, + // but since we're only supporting a single network we simply don't overwrite it. + if (!clientIdentityNetworkIndex.HasValue()) + { + network.clientIdentityKeypair = Platform::MakeShared(); + SuccessOrExit(err = network.clientIdentityKeypair->Initialize(ECPKeyTarget::ECDSA)); + + MutableByteSpan clientIdentity(network.clientIdentity); + SuccessOrExit(err = NewChipNetworkIdentity(*network.clientIdentityKeypair, clientIdentity)); + network.clientIdentityLen = clientIdentity.size(); + } + + network.credentialsLen = 0; + + SuccessOrExit(err = CopySpanToMutableSpan(ByteSpan(network.clientIdentity, network.clientIdentityLen), outClientIdentity)); + + mStagingNetwork = network; + outNetworkIndex = 0; + outStatus = Status::kSuccess; + } + +exit: + outDebugText.reduce_size(0); + return err; +} + +CHIP_ERROR LinuxWiFiDriver::GetNetworkIdentity(uint8_t networkIndex, MutableByteSpan & outNetworkIdentity) +{ + VerifyOrReturnError(!mStagingNetwork.Empty() && networkIndex == 0, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(mStagingNetwork.UsingPDC(), CHIP_ERROR_INVALID_ARGUMENT); + return CopySpanToMutableSpan(ByteSpan(mStagingNetwork.networkIdentity, mStagingNetwork.networkIdentityLen), outNetworkIdentity); +} + +CHIP_ERROR LinuxWiFiDriver::GetClientIdentity(uint8_t networkIndex, MutableByteSpan & outClientIdentity) +{ + VerifyOrReturnError(!mStagingNetwork.Empty() && networkIndex == 0, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(mStagingNetwork.UsingPDC(), CHIP_ERROR_INVALID_ARGUMENT); + return CopySpanToMutableSpan(ByteSpan(mStagingNetwork.clientIdentity, mStagingNetwork.clientIdentityLen), outClientIdentity); +} + +CHIP_ERROR LinuxWiFiDriver::SignWithClientIdentity(uint8_t networkIndex, const ByteSpan & message, + P256ECDSASignature & outSignature) +{ + VerifyOrReturnError(!mStagingNetwork.Empty() && networkIndex == 0, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(mStagingNetwork.UsingPDC(), CHIP_ERROR_INVALID_ARGUMENT); + return mStagingNetwork.clientIdentityKeypair->ECDSA_sign_msg(message.data(), message.size(), outSignature); +} +#endif // CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC + #endif // CHIP_DEVICE_CONFIG_ENABLE_WPA } // namespace NetworkCommissioning diff --git a/src/platform/Linux/dbus/wpa/DBusWpaInterface.xml b/src/platform/Linux/dbus/wpa/DBusWpaInterface.xml index 61b4f56ba9b777..ef86bdd3b17032 100644 --- a/src/platform/Linux/dbus/wpa/DBusWpaInterface.xml +++ b/src/platform/Linux/dbus/wpa/DBusWpaInterface.xml @@ -30,7 +30,10 @@ - + + + + diff --git a/src/tools/chip-cert/Cmd_PrintCert.cpp b/src/tools/chip-cert/Cmd_PrintCert.cpp index 0c7f696bdb8a5d..de610c004f053f 100644 --- a/src/tools/chip-cert/Cmd_PrintCert.cpp +++ b/src/tools/chip-cert/Cmd_PrintCert.cpp @@ -207,9 +207,8 @@ bool PrintCert(const char * fileName, X509 * cert) bool res = true; CHIP_ERROR err = CHIP_NO_ERROR; FILE * file = nullptr; - ChipCertificateSet certSet; - const ChipCertificateData * certData; - chip::BitFlags decodeFlags; + ChipCertificateData certDataStorage; + const auto certData = &certDataStorage; uint8_t chipCertBuf[kMaxCHIPCertLength]; MutableByteSpan chipCert(chipCertBuf); int indent = 4; @@ -222,22 +221,13 @@ bool PrintCert(const char * fileName, X509 * cert) res = X509ToChipCert(cert, chipCert); VerifyTrueOrExit(res); - err = certSet.Init(1); - if (err != CHIP_NO_ERROR) - { - fprintf(stderr, "Failed to initialize certificate set: %s\n", chip::ErrorStr(err)); - ExitNow(res = false); - } - - err = certSet.LoadCert(chipCert, decodeFlags); + err = DecodeChipCert(chipCert, certDataStorage); if (err != CHIP_NO_ERROR) { fprintf(stderr, "Error reading %s: %s\n", fileName, chip::ErrorStr(err)); ExitNow(res = false); } - certData = certSet.GetLastCert(); - fprintf(file, "CHIP Certificate:\n"); Indent(file, indent);