Skip to content

Commit

Permalink
Add some APIs for defining local endpoints to Matter.framework. (#31673)
Browse files Browse the repository at this point in the history
* Add some APIs for defining local endpoints to Matter.framework.

These are not hooked up to anything yet, in the sense that you can create an
MTREndpointDescription but then can't do anything with it.  APIs on
MTRDeviceController that take an MTREndpointDescription and hook up the endpoint
to actually be exposed will come later.

* Address review comments.

* Fix some build/lint issues.

* Address more review comments.

* Addressing more review comments.
  • Loading branch information
bzbarsky-apple authored Jan 31, 2024
1 parent 4422581 commit 058f199
Show file tree
Hide file tree
Showing 16 changed files with 1,908 additions and 1 deletion.
5 changes: 5 additions & 0 deletions src/darwin/Framework/CHIP/Matter.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

#define MTR_INCLUDED_FROM_UMBRELLA_HEADER

#import <Matter/MTRAccessGrant.h>
#import <Matter/MTRAsyncCallbackWorkQueue.h>
#import <Matter/MTRBackwardsCompatShims.h>
#import <Matter/MTRBaseClusters.h>
Expand All @@ -45,6 +46,7 @@
#import <Matter/MTRDeviceControllerParameters.h>
#import <Matter/MTRDeviceControllerStartupParams.h>
#import <Matter/MTRDeviceControllerStorageDelegate.h>
#import <Matter/MTRDeviceTypeRevision.h>
#import <Matter/MTRDiagnosticLogsType.h>
#import <Matter/MTRError.h>
#import <Matter/MTRFabricInfo.h>
Expand All @@ -56,6 +58,9 @@
#import <Matter/MTROnboardingPayloadParser.h>
#import <Matter/MTROperationalCertificateIssuer.h>
#import <Matter/MTRQRCodeSetupPayloadParser.h>
#import <Matter/MTRServerAttribute.h>
#import <Matter/MTRServerCluster.h>
#import <Matter/MTRServerEndpoint.h>
#import <Matter/MTRSetupPayload.h>
#import <Matter/MTRStorage.h>
#import <Matter/MTRStructsObjc.h>
Expand Down
80 changes: 80 additions & 0 deletions src/darwin/Framework/CHIP/ServerEndpoint/MTRAccessGrant.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/**
* Copyright (c) 2024 Project CHIP Authors
*
* 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.
*/

#import <Foundation/Foundation.h>
#import <Matter/MTRBaseClusters.h>
#import <Matter/MTRDefines.h>

NS_ASSUME_NONNULL_BEGIN

/**
* An access grant, which can be represented as an entry in the Matter Access
* Control cluster.
*/
MTR_NEWLY_AVAILABLE
@interface MTRAccessGrant : NSObject <NSCopying>

- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)new NS_UNAVAILABLE;

/**
* Grant access at the provided level to a specific node on the fabric. The
* provided nodeID must be an operational node identifier.
*/
+ (nullable MTRAccessGrant *)accessGrantForNodeID:(NSNumber *)nodeID privilege:(MTRAccessControlEntryPrivilege)privilege;

/**
* Grant access to any node on the fabric that has a matching CASE Authenticated
* Tag in its operational certificate. The provided caseAuthenticatedTag must
* be a 32-bit unsigned integer with lower 16 bits not 0, per the Matter
* specification.
*/
+ (nullable MTRAccessGrant *)accessGrantForCASEAuthenticatedTag:(NSNumber *)caseAuthenticatedTag privilege:(MTRAccessControlEntryPrivilege)privilege;

/**
* Grant access to any node on the fabric that is communicating with us via
* group messages sent to the given group. The provided groupID must be a valid
* group identifier in the range 1-65535.
*/
+ (nullable MTRAccessGrant *)accessGrantForGroupID:(NSNumber *)groupID privilege:(MTRAccessControlEntryPrivilege)privilege;

/**
* Grant access to any node on the fabric, as long as it's communicating with us
* over a unicast authenticated channel.
*/
+ (MTRAccessGrant *)accessGrantForAllNodesWithPrivilege:(MTRAccessControlEntryPrivilege)privilege;

/**
* The matter access control subject ID that access has been granted for. Nil
* when access has been granted for all subjects (e.g. via initForAllNodesWithPrivilege).
*/
@property (nonatomic, copy, readonly, nullable) NSNumber * subjectID;

/**
* The privilege that has been granted
*/
@property (nonatomic, assign, readonly) MTRAccessControlEntryPrivilege grantedPrivilege;

/**
* The type of authentication mode the access grant is
* for. MTRAccessControlEntryAuthModeCASE for unicast messages and
* MTRAccessControlEntryAuthModeGroup for groupcast ones.
*/
@property (nonatomic, assign, readonly) MTRAccessControlEntryAuthMode authenticationMode;

@end

NS_ASSUME_NONNULL_END
155 changes: 155 additions & 0 deletions src/darwin/Framework/CHIP/ServerEndpoint/MTRAccessGrant.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
/**
* Copyright (c) 2024 Project CHIP Authors
*
* 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.
*/

#import "MTRDefines_Internal.h"
#import "MTRLogging_Internal.h"
#import <Matter/MTRAccessGrant.h>

#include <lib/core/CASEAuthTag.h>
#include <lib/core/GroupId.h>
#include <lib/core/NodeId.h>
#include <lib/support/SafeInt.h>

using namespace chip;

MTR_DIRECT_MEMBERS
@implementation MTRAccessGrant

+ (nullable MTRAccessGrant *)accessGrantForNodeID:(NSNumber *)nodeID privilege:(MTRAccessControlEntryPrivilege)privilege
{
NodeId id = nodeID.unsignedLongLongValue;
if (!IsOperationalNodeId(id)) {
MTR_LOG_ERROR("MTRAccessGrant provided non-operational node ID: 0x%llx", id);
return nil;
}

return [[MTRAccessGrant alloc] initWithSubject:[nodeID copy] privilege:privilege authenticationMode:MTRAccessControlEntryAuthModeCASE];
}

+ (nullable MTRAccessGrant *)accessGrantForCASEAuthenticatedTag:(NSNumber *)caseAuthenticatedTag privilege:(MTRAccessControlEntryPrivilege)privilege
{
auto value = caseAuthenticatedTag.unsignedLongLongValue;
if (!CanCastTo<CASEAuthTag>(value)) {
MTR_LOG_ERROR("MTRAccessGrant provided too-large CAT value: 0x%llx", value);
return nil;
}

CASEAuthTag tag = static_cast<CASEAuthTag>(value);
if (!IsValidCASEAuthTag(tag)) {
MTR_LOG_ERROR("MTRAccessGrant provided invalid CAT value: 0x%" PRIx32, tag);
return nil;
}

return [[MTRAccessGrant alloc] initWithSubject:@(NodeIdFromCASEAuthTag(tag)) privilege:privilege authenticationMode:MTRAccessControlEntryAuthModeCASE];
}

+ (nullable MTRAccessGrant *)accessGrantForGroupID:(NSNumber *)groupID privilege:(MTRAccessControlEntryPrivilege)privilege
{
auto value = groupID.unsignedLongLongValue;
if (!CanCastTo<GroupId>(value)) {
MTR_LOG_ERROR("MTRAccessGrant provided too-large group id: 0x%llx", value);
return nil;
}

GroupId id = static_cast<GroupId>(value);
if (!IsValidGroupId(id)) {
MTR_LOG_ERROR("MTRAccessGrant provided invalid group id: 0x%" PRIx32, id);
return nil;
}

return [[MTRAccessGrant alloc] initWithSubject:@(NodeIdFromGroupId(id)) privilege:privilege authenticationMode:MTRAccessControlEntryAuthModeGroup];
}

+ (MTRAccessGrant *)accessGrantForAllNodesWithPrivilege:(MTRAccessControlEntryPrivilege)privilege
{
return [[MTRAccessGrant alloc] initWithSubject:nil privilege:privilege authenticationMode:MTRAccessControlEntryAuthModeCASE];
}

// initWithSubject assumes that the subject has already been validated and, if
// needed, copied from the input.
- (nullable instancetype)initWithSubject:(nullable NSNumber *)subject privilege:(MTRAccessControlEntryPrivilege)privilege authenticationMode:(MTRAccessControlEntryAuthMode)authenticationMode
{
if (!(self = [super init])) {
return nil;
}

_subjectID = subject;
_grantedPrivilege = privilege;
_authenticationMode = authenticationMode;
return self;
}

- (id)copyWithZone:(NSZone *)zone
{
// We have no mutable state.
return self;
}

- (BOOL)isEqual:(id)object
{
if ([object class] != [self class]) {
return NO;
}

MTRAccessGrant * other = object;

BOOL sameSubjectID = (_subjectID == nil && other.subjectID == nil) || [_subjectID isEqual:other.subjectID];
return sameSubjectID && _grantedPrivilege == other.grantedPrivilege && _authenticationMode == other.authenticationMode;
}

- (NSUInteger)hash
{
return _subjectID.unsignedLongLongValue ^ _grantedPrivilege ^ _authenticationMode;
}

- (NSString *)description
{
NSString * privilege = @"Unknown";
switch (_grantedPrivilege) {
case MTRAccessControlEntryPrivilegeView:
privilege = @"View";
break;
case MTRAccessControlEntryPrivilegeProxyView:
privilege = @"ProxyView";
break;
case MTRAccessControlEntryPrivilegeOperate:
privilege = @"Operate";
break;
case MTRAccessControlEntryPrivilegeManage:
privilege = @"Manage";
break;
case MTRAccessControlEntryPrivilegeAdminister:
privilege = @"Administer";
break;
}

if (_subjectID == nil) {
return [NSString stringWithFormat:@"<%@ all nodes can %@>", self.class, privilege];
}

NodeId nodeId = static_cast<NodeId>(_subjectID.unsignedLongLongValue);
if (IsGroupId(nodeId)) {
return [NSString stringWithFormat:@"<%@ group 0x%x can %@>", self.class, GroupIdFromNodeId(nodeId), privilege];
}

if (IsCASEAuthTag(nodeId)) {
return [NSString stringWithFormat:@"<%@ nodes with CASE Authenticated Tag 0x%08x can %@>", self.class, CASEAuthTagFromNodeId(nodeId), privilege];
}

return [NSString stringWithFormat:@"<%@ node 0x%016llx can %@>", self.class, nodeId, privilege];
}

@end
45 changes: 45 additions & 0 deletions src/darwin/Framework/CHIP/ServerEndpoint/MTRDeviceTypeRevision.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* Copyright (c) 2024 Project CHIP Authors
*
* 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.
*/

#import <Foundation/Foundation.h>
#import <Matter/MTRDefines.h>

NS_ASSUME_NONNULL_BEGIN

/**
* A representation of a "device type revision" in the sense used in the Matter
* specification. This has an identifier and a version number.
*/
MTR_NEWLY_AVAILABLE
@interface MTRDeviceTypeRevision : NSObject <NSCopying>

- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)new NS_UNAVAILABLE;

/**
* The provided deviceTypeID must be in the range 0xVVVV0000-0xVVVVBFFF, where
* VVVV is the vendor identifier (0 for standard device types).
*
* The provided deviceTypeRevision must be in the range 1-65535.
*/
- (nullable instancetype)initWithDeviceTypeID:(NSNumber *)deviceTypeID revision:(NSNumber *)revision;

@property (nonatomic, copy, readonly) NSNumber * deviceTypeID;
@property (nonatomic, copy, readonly) NSNumber * deviceTypeRevision;

@end

NS_ASSUME_NONNULL_END
88 changes: 88 additions & 0 deletions src/darwin/Framework/CHIP/ServerEndpoint/MTRDeviceTypeRevision.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/**
* Copyright (c) 2024 Project CHIP Authors
*
* 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.
*/

#import "MTRDefines_Internal.h"
#import "MTRLogging_Internal.h"
#import <Matter/MTRDeviceTypeRevision.h>

#include <lib/core/CHIPError.h>
#include <lib/core/DataModelTypes.h>
#include <lib/support/SafeInt.h>

using namespace chip;

MTR_DIRECT_MEMBERS
@implementation MTRDeviceTypeRevision

- (nullable instancetype)initWithDeviceTypeID:(NSNumber *)deviceTypeID revision:(NSNumber *)revision
{
auto deviceTypeIDValue = deviceTypeID.unsignedLongLongValue;
if (!CanCastTo<DeviceTypeId>(deviceTypeIDValue)) {
MTR_LOG_ERROR("MTRDeviceTypeRevision provided too-large device type ID: 0x%llx", deviceTypeIDValue);
return nil;
}

auto id = static_cast<DeviceTypeId>(deviceTypeIDValue);
if (!IsValidDeviceTypeId(id)) {
MTR_LOG_ERROR("MTRDeviceTypeRevision provided invalid device type ID: 0x%" PRIx32, id);
return nil;
}

auto revisionValue = revision.unsignedLongLongValue;
if (!CanCastTo<uint16_t>(revisionValue) || revisionValue < 1) {
MTR_LOG_ERROR("MTRDeviceTypeRevision provided invalid device type revision: 0x%llx", revisionValue);
return nil;
}

return [self initInternalWithDeviceTypeID:[deviceTypeID copy] revision:[revision copy]];
}

// initInternalWithDeviceTypeID:revision assumes that the device type ID and device
// revision have already been validated and, if needed, copied from the input.
- (instancetype)initInternalWithDeviceTypeID:(NSNumber *)deviceTypeID revision:(NSNumber *)revision
{
if (!(self = [super init])) {
return nil;
}

_deviceTypeID = deviceTypeID;
_deviceTypeRevision = revision;
return self;
}

- (id)copyWithZone:(NSZone *)zone
{
// We have no mutable state.
return self;
}

- (BOOL)isEqual:(id)object
{
if ([object class] != [self class]) {
return NO;
}

MTRDeviceTypeRevision * other = object;

return [_deviceTypeID isEqual:other.deviceTypeID] && [_deviceTypeRevision isEqual:other.deviceTypeRevision];
}

- (NSUInteger)hash
{
return _deviceTypeID.unsignedLongValue ^ _deviceTypeRevision.unsignedShortValue;
}

@end
Loading

0 comments on commit 058f199

Please sign in to comment.