blob: f2c1e74e6cb3dce2ff9fbdb3465ab78ae13a434a [file]
/*
*
* Copyright (c) 2026 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 "AliroDelegate.h"
#include <crypto/CHIPCryptoPAL.h>
#include <lib/core/CHIPEncoding.h>
#include <lib/support/Span.h>
#include <lib/support/logging/CHIPLogging.h>
using namespace chip;
using namespace chip::app::Clusters::DoorLock;
AliroDelegate AliroDelegate::sInstance;
AliroDelegate::AliroDelegate()
{
CHIP_ERROR err = Crypto::DRBG_get_bytes(mAliroReaderGroupSubIdentifier, sizeof(mAliroReaderGroupSubIdentifier));
if (err != CHIP_NO_ERROR)
{
ChipLogError(Zcl, "Failed to init Aliro sub-identifier: %" CHIP_ERROR_FORMAT, err.Format());
}
}
// ---------------------------------------------------------------------------
// DoorLock::Delegate — Aliro provisioning attributes
// ---------------------------------------------------------------------------
CHIP_ERROR AliroDelegate::GetAliroReaderVerificationKey(MutableByteSpan & verificationKey)
{
if (!mAliroStateInitialized)
{
verificationKey.reduce_size(0);
return CHIP_NO_ERROR;
}
return CopySpanToMutableSpan(ByteSpan(mAliroReaderVerificationKey), verificationKey);
}
CHIP_ERROR AliroDelegate::GetAliroReaderGroupIdentifier(MutableByteSpan & groupIdentifier)
{
if (!mAliroStateInitialized)
{
groupIdentifier.reduce_size(0);
return CHIP_NO_ERROR;
}
return CopySpanToMutableSpan(ByteSpan(mAliroReaderGroupIdentifier), groupIdentifier);
}
CHIP_ERROR AliroDelegate::GetAliroReaderGroupSubIdentifier(MutableByteSpan & groupSubIdentifier)
{
return CopySpanToMutableSpan(ByteSpan(mAliroReaderGroupSubIdentifier), groupSubIdentifier);
}
CHIP_ERROR AliroDelegate::CopyProtocolVersionIntoSpan(uint16_t protocolVersionValue, MutableByteSpan & protocolVersion)
{
static_assert(sizeof(protocolVersionValue) == kAliroProtocolVersionSize);
if (protocolVersion.size() < kAliroProtocolVersionSize)
{
return CHIP_ERROR_INVALID_ARGUMENT;
}
// Per Aliro spec, protocol version encoding is big-endian.
Encoding::BigEndian::Put16(protocolVersion.data(), protocolVersionValue);
protocolVersion.reduce_size(kAliroProtocolVersionSize);
return CHIP_NO_ERROR;
}
CHIP_ERROR AliroDelegate::GetAliroExpeditedTransactionSupportedProtocolVersionAtIndex(size_t index,
MutableByteSpan & protocolVersion)
{
// Only claim support for the one known protocol version for now: 0x0100.
constexpr uint16_t knownProtocolVersion = 0x0100;
if (index > 0)
{
return CHIP_ERROR_PROVIDER_LIST_EXHAUSTED;
}
return CopyProtocolVersionIntoSpan(knownProtocolVersion, protocolVersion);
}
CHIP_ERROR AliroDelegate::GetAliroGroupResolvingKey(MutableByteSpan & groupResolvingKey)
{
if (!mAliroStateInitialized)
{
groupResolvingKey.reduce_size(0);
return CHIP_NO_ERROR;
}
return CopySpanToMutableSpan(ByteSpan(mAliroGroupResolvingKey), groupResolvingKey);
}
CHIP_ERROR AliroDelegate::GetAliroSupportedBLEUWBProtocolVersionAtIndex(size_t index, MutableByteSpan & protocolVersion)
{
// Only claim support for the one known protocol version for now: 0x0100.
constexpr uint16_t knownProtocolVersion = 0x0100;
if (index > 0)
{
return CHIP_ERROR_PROVIDER_LIST_EXHAUSTED;
}
return CopyProtocolVersionIntoSpan(knownProtocolVersion, protocolVersion);
}
uint8_t AliroDelegate::GetAliroBLEAdvertisingVersion()
{
// For now the only defined value of the BLE advertising version for Aliro is 0.
return 0;
}
uint16_t AliroDelegate::GetNumberOfAliroCredentialIssuerKeysSupported()
{
return APP_MAX_CREDENTIAL;
}
uint16_t AliroDelegate::GetNumberOfAliroEndpointKeysSupported()
{
return APP_MAX_CREDENTIAL;
}
CHIP_ERROR AliroDelegate::SetAliroReaderConfig(const ByteSpan & signingKey, const ByteSpan & verificationKey,
const ByteSpan & groupIdentifier, const Optional<ByteSpan> & groupResolvingKey)
{
// We ignore the signing key, since we never do anything with it.
(void) signingKey;
VerifyOrReturnError(verificationKey.size() == sizeof(mAliroReaderVerificationKey), CHIP_ERROR_INVALID_ARGUMENT);
memcpy(mAliroReaderVerificationKey, verificationKey.data(), sizeof(mAliroReaderVerificationKey));
VerifyOrReturnError(groupIdentifier.size() == sizeof(mAliroReaderGroupIdentifier), CHIP_ERROR_INVALID_ARGUMENT);
memcpy(mAliroReaderGroupIdentifier, groupIdentifier.data(), sizeof(mAliroReaderGroupIdentifier));
if (groupResolvingKey.HasValue())
{
VerifyOrReturnError(groupResolvingKey.Value().size() == sizeof(mAliroGroupResolvingKey), CHIP_ERROR_INVALID_ARGUMENT);
memcpy(mAliroGroupResolvingKey, groupResolvingKey.Value().data(), sizeof(mAliroGroupResolvingKey));
}
mAliroStateInitialized = true;
return CHIP_NO_ERROR;
}
CHIP_ERROR AliroDelegate::ClearAliroReaderConfig()
{
// A real implementation would clear out key data from persistent storage.
mAliroStateInitialized = false;
return CHIP_NO_ERROR;
}
// ---------------------------------------------------------------------------
// Aliro credential storage
// ---------------------------------------------------------------------------
/* static */ bool AliroDelegate::IsAliroCredentialType(CredentialTypeEnum type)
{
switch (type)
{
case CredentialTypeEnum::kAliroCredentialIssuerKey:
case CredentialTypeEnum::kAliroEvictableEndpointKey:
case CredentialTypeEnum::kAliroNonEvictableEndpointKey:
return true;
default:
return false;
}
}
AliroDelegate::CredentialSlot * AliroDelegate::SlotArrayForType(CredentialTypeEnum type)
{
switch (type)
{
case CredentialTypeEnum::kAliroCredentialIssuerKey:
return mIssuerKeys;
case CredentialTypeEnum::kAliroEvictableEndpointKey:
return mEvictableEndpointKeys;
case CredentialTypeEnum::kAliroNonEvictableEndpointKey:
return mNonEvictableEndpointKeys;
default:
return nullptr;
}
}
bool AliroDelegate::GetCredential(uint16_t index, CredentialTypeEnum type, EmberAfPluginDoorLockCredentialInfo & out)
{
CredentialSlot * slots = SlotArrayForType(type);
VerifyOrReturnValue(slots != nullptr, false);
auto & slot = slots[index];
out.status = slot.status;
out.credentialType = type;
out.createdBy = slot.createdBy;
out.lastModifiedBy = slot.lastModifiedBy;
out.creationSource = DlAssetSource::kMatterIM;
out.modificationSource = DlAssetSource::kMatterIM;
out.credentialData = chip::ByteSpan{ slot.data, slot.dataSize };
ChipLogProgress(Zcl, "AliroDelegate::GetCredential [type=%u,index=%u,status=%d]", to_underlying(type), index,
(int) slot.status);
return true;
}
bool AliroDelegate::SetCredential(uint16_t index, chip::FabricIndex creator, chip::FabricIndex modifier, DlCredentialStatus status,
CredentialTypeEnum type, const chip::ByteSpan & data)
{
CredentialSlot * slots = SlotArrayForType(type);
VerifyOrReturnValue(slots != nullptr, false);
auto & slot = slots[index];
if (status == DlCredentialStatus::kAvailable)
{
slot.status = DlCredentialStatus::kAvailable;
slot.dataSize = 0;
slot.createdBy = creator;
slot.lastModifiedBy = modifier;
return true;
}
VerifyOrReturnValue(data.size() <= kAliroCredentialMaxSize, false);
memcpy(slot.data, data.data(), data.size());
slot.dataSize = data.size();
slot.status = status;
slot.createdBy = creator;
slot.lastModifiedBy = modifier;
ChipLogProgress(Zcl, "AliroDelegate::SetCredential [type=%u,index=%u,dataSize=%u]", to_underlying(type), index,
static_cast<unsigned int>(data.size()));
return true;
}