@project-chip/matter.js 0.10.4 → 0.10.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/protocol/interaction/AccessControlManager.d.ts.map +1 -1
- package/dist/cjs/protocol/interaction/AccessControlManager.js +6 -2
- package/dist/cjs/protocol/interaction/AccessControlManager.js.map +2 -2
- package/dist/cjs/session/case/CaseServer.d.ts.map +1 -1
- package/dist/cjs/session/case/CaseServer.js +6 -2
- package/dist/cjs/session/case/CaseServer.js.map +2 -2
- package/dist/esm/protocol/interaction/AccessControlManager.d.ts.map +1 -1
- package/dist/esm/protocol/interaction/AccessControlManager.js +6 -2
- package/dist/esm/protocol/interaction/AccessControlManager.js.map +2 -2
- package/dist/esm/session/case/CaseServer.d.ts.map +1 -1
- package/dist/esm/session/case/CaseServer.js +6 -2
- package/dist/esm/session/case/CaseServer.js.map +2 -2
- package/package.json +3 -3
- package/src/protocol/interaction/AccessControlManager.ts +5 -1
- package/src/session/case/CaseServer.ts +4 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AccessControlManager.d.ts","sourceRoot":"","sources":["../../../../src/protocol/interaction/AccessControlManager.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,mDAAmD,CAAC;AAGlF,OAAO,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAC5D,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qCAAqC,CAAC;AAGxE,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAE/D,OAAO,EAAc,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAIlE,MAAM,MAAM,QAAQ,GAAG,IAAI,CAAC,oBAAoB,CAAC,OAAO,aAAa,CAAC,qBAAqB,CAAC,EAAE,WAAW,CAAC,GAAG;IACzG,SAAS,EAAE,WAAW,CAAC;CAC1B,CAAC;AACF,MAAM,MAAM,OAAO,GAAG,QAAQ,EAAE,CAAC;AAEjC,MAAM,MAAM,iBAAiB,GAAG,oBAAoB,CAAC,OAAO,aAAa,CAAC,yBAAyB,CAAC,CAAC;AACrG,MAAM,MAAM,gBAAgB,GAAG,iBAAiB,EAAE,CAAC;AAUnD,aAAK,YAAY;IACb,IAAI,IAAI;CACX;AAED,MAAM,MAAM,yBAAyB,GAAG;IACpC,eAAe,EAAE,OAAO,CAAC;IACzB,QAAQ,EAAE,aAAa,CAAC,0BAA0B,GAAG,YAAY,CAAC;IAClE,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,WAAW,EAAE,WAAW,CAAC;CAC5B,CAAC;AAEF,qBAAa,iBAAkB,SAAQ,mBAAmB;gBAC1C,OAAO,CAAC,EAAE,MAAM;CAG/B;AAED;;GAEG;AACH,qBAAa,oBAAoB;;gBAWzB,OAAO,GAAE,aAAa,CAAC,kBAAkB,EAAO,EAChD,yBAAyB,CAAC,EAAE,CACxB,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,QAAQ,EAClB,WAAW,EAAE,yBAAyB,EACtC,QAAQ,EAAE,iBAAiB,EAC3B,SAAS,EAAE,SAAS,KACnB,OAAO;IAQhB;;OAEG;IACH,uBAAuB,CAAC,OAAO,GAAE,aAAa,CAAC,kBAAkB,EAAO,GAAG,IAAI;IAyD/E;;OAEG;IACH,eAAe,CACX,OAAO,EAAE,aAAa,CAAC,GAAG,CAAC,EAC3B,QAAQ,EAAE,iBAAiB,EAC3B,SAAS,EAAE,SAAS,EACpB,SAAS,EAAE,WAAW,GACvB,OAAO;
|
|
1
|
+
{"version":3,"file":"AccessControlManager.d.ts","sourceRoot":"","sources":["../../../../src/protocol/interaction/AccessControlManager.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,mDAAmD,CAAC;AAGlF,OAAO,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAC5D,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qCAAqC,CAAC;AAGxE,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAE/D,OAAO,EAAc,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAIlE,MAAM,MAAM,QAAQ,GAAG,IAAI,CAAC,oBAAoB,CAAC,OAAO,aAAa,CAAC,qBAAqB,CAAC,EAAE,WAAW,CAAC,GAAG;IACzG,SAAS,EAAE,WAAW,CAAC;CAC1B,CAAC;AACF,MAAM,MAAM,OAAO,GAAG,QAAQ,EAAE,CAAC;AAEjC,MAAM,MAAM,iBAAiB,GAAG,oBAAoB,CAAC,OAAO,aAAa,CAAC,yBAAyB,CAAC,CAAC;AACrG,MAAM,MAAM,gBAAgB,GAAG,iBAAiB,EAAE,CAAC;AAUnD,aAAK,YAAY;IACb,IAAI,IAAI;CACX;AAED,MAAM,MAAM,yBAAyB,GAAG;IACpC,eAAe,EAAE,OAAO,CAAC;IACzB,QAAQ,EAAE,aAAa,CAAC,0BAA0B,GAAG,YAAY,CAAC;IAClE,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,WAAW,EAAE,WAAW,CAAC;CAC5B,CAAC;AAEF,qBAAa,iBAAkB,SAAQ,mBAAmB;gBAC1C,OAAO,CAAC,EAAE,MAAM;CAG/B;AAED;;GAEG;AACH,qBAAa,oBAAoB;;gBAWzB,OAAO,GAAE,aAAa,CAAC,kBAAkB,EAAO,EAChD,yBAAyB,CAAC,EAAE,CACxB,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,QAAQ,EAClB,WAAW,EAAE,yBAAyB,EACtC,QAAQ,EAAE,iBAAiB,EAC3B,SAAS,EAAE,SAAS,KACnB,OAAO;IAQhB;;OAEG;IACH,uBAAuB,CAAC,OAAO,GAAE,aAAa,CAAC,kBAAkB,EAAO,GAAG,IAAI;IAyD/E;;OAEG;IACH,eAAe,CACX,OAAO,EAAE,aAAa,CAAC,GAAG,CAAC,EAC3B,QAAQ,EAAE,iBAAiB,EAC3B,SAAS,EAAE,SAAS,EACpB,SAAS,EAAE,WAAW,GACvB,OAAO;IAoBV;;OAEG;IACH,oBAAoB,CAChB,OAAO,EAAE,aAAa,CAAC,GAAG,CAAC,EAC3B,QAAQ,EAAE,iBAAiB,EAC3B,SAAS,EAAE,SAAS,GACrB,WAAW,EAAE;CA8KnB"}
|
|
@@ -123,10 +123,14 @@ class AccessControlManager {
|
|
|
123
123
|
return true;
|
|
124
124
|
}
|
|
125
125
|
logger.notice(
|
|
126
|
-
`Failed access control check for ${endpoint.number}
|
|
126
|
+
`Failed access control check for ${endpoint.number}/0x${(0, import_Number.toHex)(clusterId)} and fabricIndex ${session.associatedFabric.fabricIndex}, acl=`,
|
|
127
127
|
this.#getAccessControlEntriesForFabric(session.associatedFabric),
|
|
128
|
+
"with ISD=",
|
|
129
|
+
this.#getIsdFromMessage(session),
|
|
128
130
|
"granted privileges=",
|
|
129
|
-
grantedPrivileges
|
|
131
|
+
grantedPrivileges,
|
|
132
|
+
"not contains",
|
|
133
|
+
privilege
|
|
130
134
|
);
|
|
131
135
|
return false;
|
|
132
136
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/protocol/interaction/AccessControlManager.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * @license\n * Copyright 2022-2023 Project CHIP Authors\n * SPDX-License-Identifier: Apache-2.0\n */\nimport { AccessLevel } from \"../../cluster/Cluster.js\";\nimport { AccessControl } from \"../../cluster/definitions/AccessControlCluster.js\";\nimport { MatterFlowError } from \"../../common/MatterError.js\";\nimport { CaseAuthenticatedTag } from \"../../datatype/CaseAuthenticatedTag.js\";\nimport { ClusterId } from \"../../datatype/ClusterId.js\";\nimport { FabricIndex } from \"../../datatype/FabricIndex.js\";\nimport { NodeId } from \"../../datatype/NodeId.js\";\nimport { EndpointInterface } from \"../../endpoint/EndpointInterface.js\";\nimport { Fabric } from \"../../fabric/Fabric.js\";\nimport { Logger } from \"../../log/Logger.js\";\nimport { TypeFromBitmapSchema } from \"../../schema/BitmapSchema.js\";\nimport { SecureSession } from \"../../session/SecureSession.js\";\nimport { toHex } from \"../../util/Number.js\";\nimport { StatusCode, StatusResponseError } from \"./StatusCode.js\";\n\nconst logger = Logger.get(\"AccessControlManager\");\n\nexport type AclEntry = Omit<TypeFromBitmapSchema<typeof AccessControl.TlvAccessControlEntry>, \"privilege\"> & {\n privilege: AccessLevel;\n};\nexport type AclList = AclEntry[];\n\nexport type AclExtensionEntry = TypeFromBitmapSchema<typeof AccessControl.TlvAccessControlExtension>;\nexport type AclExtensionList = AclExtensionEntry[];\n\nconst ImplicitDefaultPaseAclEntry: AclEntry = {\n fabricIndex: FabricIndex.NO_FABRIC, // not fabric-specific\n privilege: AccessLevel.Administer,\n authMode: AccessControl.AccessControlEntryAuthMode.Pase,\n subjects: [],\n targets: [], // entire node\n};\n\nenum AuthModeNone {\n None = 0,\n}\n\nexport type IncomingSubjectDescriptor = {\n isCommissioning: boolean;\n authMode: AccessControl.AccessControlEntryAuthMode | AuthModeNone;\n subjects: NodeId[];\n fabricIndex: FabricIndex;\n};\n\nexport class AccessDeniedError extends StatusResponseError {\n constructor(message?: string) {\n super(message ?? \"Unauthorized\", StatusCode.UnsupportedAccess);\n }\n}\n\n/**\n * Implements Access Control Logic as per Matter Specification @see {@link MatterSpecification.v12.Core} \u00A7 6.6.5.2.\n */\nexport class AccessControlManager {\n #aclList: AclList;\n #extensionEntryAccessCheck: (\n aclList: AclList,\n aclEntry: AclEntry,\n subjectDesc: IncomingSubjectDescriptor,\n endpoint: EndpointInterface,\n clusterId: ClusterId,\n ) => boolean = () => true;\n\n constructor(\n aclList: AccessControl.AccessControlEntry[] = [],\n extensionEntryAccessCheck?: (\n aclList: AclList,\n aclEntry: AclEntry,\n subjectDesc: IncomingSubjectDescriptor,\n endpoint: EndpointInterface,\n clusterId: ClusterId,\n ) => boolean,\n ) {\n this.#aclList = aclList as unknown as AclList; // It is the same structure we just use an internal type for privilege\n if (extensionEntryAccessCheck !== undefined) {\n this.#extensionEntryAccessCheck = extensionEntryAccessCheck;\n }\n }\n\n /**\n * Public method used to update the Access Control List on changes.\n */\n updateAccessControlList(aclList: AccessControl.AccessControlEntry[] = []): void {\n this.#aclList = [...aclList] as unknown as AclList; // It is the same structure we just use an internal type for privilege\n }\n\n /**\n * Get the Access Control List for a given fabric.\n */\n #getAccessControlEntriesForFabric(fabric: Fabric): AclList {\n return this.#aclList.filter(entry => entry.fabricIndex === fabric.fabricIndex);\n }\n\n /**\n * Subjects must match exactly, or both are CAT with matching CAT ID and acceptable CAT version\n */\n #subjectMatches(aclSubject: NodeId, isdSubject: NodeId): boolean {\n if (aclSubject === isdSubject) {\n return true;\n }\n if (!NodeId.isCaseAuthenticatedTag(aclSubject) || !NodeId.isCaseAuthenticatedTag(isdSubject)) {\n return false;\n }\n const aclSubjectCat = NodeId.extractAsCaseAuthenticatedTag(aclSubject);\n const isdSubjectCat = NodeId.extractAsCaseAuthenticatedTag(isdSubject);\n return (\n CaseAuthenticatedTag.getIdentifyValue(aclSubjectCat) ===\n CaseAuthenticatedTag.getIdentifyValue(isdSubjectCat) &&\n CaseAuthenticatedTag.getVersion(isdSubjectCat) >= CaseAuthenticatedTag.getVersion(aclSubjectCat)\n );\n }\n\n /**\n * Add the new privilege to the granted privileges set and also add any privileges subsumed by the new privilege.\n */\n #addGrantedPrivilege(grantedPrivileges: Set<AccessLevel>, privilege: AccessLevel): void {\n // Add the new privilege to the granted privileges set\n grantedPrivileges.add(privilege);\n // Also add any privileges subsumed by the new privilege\n switch (privilege) {\n case AccessLevel.ProxyView:\n grantedPrivileges.add(AccessLevel.View);\n break;\n case AccessLevel.Operate:\n grantedPrivileges.add(AccessLevel.View);\n break;\n case AccessLevel.Manage:\n grantedPrivileges.add(AccessLevel.Operate);\n grantedPrivileges.add(AccessLevel.View);\n break;\n case AccessLevel.Administer:\n grantedPrivileges.add(AccessLevel.Manage);\n grantedPrivileges.add(AccessLevel.Operate);\n grantedPrivileges.add(AccessLevel.ProxyView);\n grantedPrivileges.add(AccessLevel.View);\n break;\n }\n }\n\n /**\n * Check if the given ACL entry is allowed to be used for the given subject descriptor, endpoint, and cluster ID.\n */\n allowsPrivilege(\n session: SecureSession<any>,\n endpoint: EndpointInterface,\n clusterId: ClusterId,\n privilege: AccessLevel,\n ): boolean {\n const grantedPrivileges = this.getGrantedPrivileges(session, endpoint, clusterId);\n if (grantedPrivileges.includes(privilege)) {\n return true;\n }\n\n logger.notice(\n `Failed access control check for ${endpoint.number}/${toHex(clusterId)} and fabricIndex ${session.associatedFabric.fabricIndex}, acl=`,\n this.#getAccessControlEntriesForFabric(session.associatedFabric),\n \"granted privileges=\",\n grantedPrivileges,\n );\n\n return false;\n }\n\n /**\n * Determines the granted privileges for the given session, endpoint, and cluster ID and returns them.\n */\n getGrantedPrivileges(\n session: SecureSession<any>,\n endpoint: EndpointInterface,\n clusterId: ClusterId,\n ): AccessLevel[] {\n const endpointId = endpoint.number;\n const fabric = session.fabric;\n const subjectDesc = this.#getIsdFromMessage(session);\n const acl = fabric ? this.#getAccessControlEntriesForFabric(fabric) : [ImplicitDefaultPaseAclEntry];\n\n // Granted privileges set is initially empty\n const grantedPrivileges = new Set<AccessLevel>();\n\n // PASE commissioning channel implicitly grants administer privilege to commissioner\n if (subjectDesc.authMode === AccessControl.AccessControlEntryAuthMode.Pase && subjectDesc.isCommissioning) {\n this.#addGrantedPrivilege(grantedPrivileges, AccessLevel.Administer);\n }\n\n for (const aclEntry of acl) {\n if (grantedPrivileges.has(AccessLevel.Administer)) {\n // End checking if highest privilege is granted\n break;\n }\n\n // Fabric index must match, there are no valid entries with FabricIndex == 0\n // other than the implicit PASE entry, which we will not see explicitly in the\n // access control list\n if (aclEntry.fabricIndex === FabricIndex.NO_FABRIC || aclEntry.fabricIndex !== subjectDesc.fabricIndex) {\n logger.debug(\n \"Skipping ACL entry with mismatched fabric index\",\n aclEntry.fabricIndex,\n subjectDesc.fabricIndex,\n );\n continue;\n }\n\n // Auth mode must match\n if (aclEntry.authMode !== subjectDesc.authMode) {\n logger.debug(\"Skipping ACL entry with mismatched auth mode\", aclEntry.authMode, subjectDesc.authMode);\n continue;\n }\n\n // Subject must match, or be \"wildcard\"\n if (aclEntry.subjects === null || aclEntry.subjects.length === 0) {\n // Precondition: only CASE and Group auth can have empty subjects\n if (\n aclEntry.authMode !== AccessControl.AccessControlEntryAuthMode.Case &&\n aclEntry.authMode !== AccessControl.AccessControlEntryAuthMode.Group\n ) {\n throw new MatterFlowError(\"ACL error: only CASE and Group auth can have empty subjects\");\n }\n // ... Empty is wildcard, no match required\n } else {\n // Non-empty requires a match\n let matchedSubject = false;\n subjectLoop: for (const aclSubject of aclEntry.subjects) {\n for (const isdSubject of subjectDesc.subjects) {\n if (this.#subjectMatches(aclSubject, isdSubject)) {\n matchedSubject = true;\n break subjectLoop;\n }\n }\n }\n if (!matchedSubject) {\n continue;\n }\n }\n\n // Target must match, or be \"wildcard\"\n if (aclEntry.targets === null || aclEntry.targets.length === 0) {\n // Empty is wildcard, no match required\n } else {\n // Non-empty requires a match\n let matchedTarget = false;\n for (const {\n cluster: targetClusterId,\n endpoint: targetEndpointId,\n deviceType: targetDeviceType,\n } of aclEntry.targets) {\n // Precondition: target cannot be empty\n if (targetClusterId === null && targetEndpointId === null && targetDeviceType === null) {\n throw new MatterFlowError(\"ACL error: target cannot be empty\");\n }\n // Precondition: target cannot specify both endpoint and device type\n if (targetEndpointId !== null && targetDeviceType !== null) {\n throw new MatterFlowError(\"ACL error: target cannot specify both endpoint and device type\");\n }\n // Cluster must match, or be wildcard\n if (targetClusterId !== null && targetClusterId !== clusterId) {\n continue;\n }\n // Endpoint must match, or be wildcard\n if (targetEndpointId !== null && targetEndpointId !== endpointId) {\n continue;\n }\n // Endpoint may be specified indirectly via device type\n // TODO adjust to array check once we use multiple devicetypes\n if (targetDeviceType !== null && endpoint.deviceType !== targetDeviceType) {\n continue;\n }\n matchedTarget = true;\n break;\n }\n if (!matchedTarget) {\n continue;\n }\n }\n\n // Extensions processing must not fail\n if (!this.#extensionEntryAccessCheck(acl, aclEntry, subjectDesc, endpoint, clusterId)) {\n continue;\n }\n\n // All checks have passed, add privilege to granted privilege set\n this.#addGrantedPrivilege(grantedPrivileges, aclEntry.privilege);\n }\n // Should never grant Administer privilege to a Group.\n if (\n subjectDesc.authMode === AccessControl.AccessControlEntryAuthMode.Group &&\n grantedPrivileges.has(AccessLevel.Administer)\n ) {\n throw new MatterFlowError(\"ACL error: should never grant Administer privilege to a Group\");\n }\n\n return [...grantedPrivileges];\n }\n\n /**\n * Determines the Incoming Subject Descriptor (ISD) from the given session.\n */\n #getIsdFromMessage(session: SecureSession<any>) {\n const fabric = session.fabric;\n const isd: IncomingSubjectDescriptor = {\n isCommissioning: false,\n authMode: AuthModeNone.None,\n subjects: new Array<NodeId>(),\n fabricIndex: FabricIndex.NO_FABRIC,\n };\n\n if (session.isPase) {\n isd.authMode = AccessControl.AccessControlEntryAuthMode.Pase;\n isd.isCommissioning = true; // Or how \"commissioning channel\" is defined?\n isd.subjects.push(NodeId(0)); // Default Commissioning Passcode ID\n if (fabric) {\n isd.fabricIndex = fabric.fabricIndex;\n }\n } else {\n // TODO Add Group session handling when implementing groups\n // if (session instanceof SecureGroupSession) {\n // Groups\n // # Message is assumed to have been decrypted and matched properly prior to\n // # this procedure occurring.\n // group_id = message.get_dst_group_id()\n // group_key_id = sessions_metadata.get_group_key_id(message)\n // # Group membership must be verified against Group Key Management Cluster\n // if group_key_management_cluster.group_key_map_has_mapping(group_id, group_key_id):\n // isd.AuthMode = AuthModeEnum.Group\n // isd.Subjects.append(group_id)\n // isd.FabricIndex = sessions_metadata.get_fabric_index(message)\n // assert(isd.FabricIndex != 0) # cannot be zero\n //\n // isd.authMode = AccessControl.AccessControlEntryAuthMode.Group;\n // } else {\n\n // CASE session\n isd.authMode = AccessControl.AccessControlEntryAuthMode.Case;\n isd.subjects.push(session.peerNodeId);\n // Append CASE session CATs which also serve as subjects\n session.caseAuthenticatedTags.forEach(cat => isd.subjects.push(NodeId.fromCaseAuthenticatedTag(cat)));\n // }\n if (fabric === undefined) {\n throw new MatterFlowError(\"ACL error: fabric is undefined\");\n }\n isd.fabricIndex = fabric.fabricIndex;\n }\n\n return isd;\n }\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAKA,qBAA4B;AAC5B,kCAA8B;AAC9B,yBAAgC;AAChC,kCAAqC;AAErC,yBAA4B;AAC5B,oBAAuB;AAGvB,oBAAuB;AAGvB,oBAAsB;AACtB,wBAAgD;AAlBhD;AAAA;AAAA;AAAA;AAAA;AAoBA,MAAM,SAAS,qBAAO,IAAI,sBAAsB;AAUhD,MAAM,8BAAwC;AAAA,EAC1C,aAAa,+BAAY;AAAA;AAAA,EACzB,WAAW,2BAAY;AAAA,EACvB,UAAU,0CAAc,2BAA2B;AAAA,EACnD,UAAU,CAAC;AAAA,EACX,SAAS,CAAC;AAAA;AACd;AAEA,IAAK,eAAL,kBAAKA,kBAAL;AACI,EAAAA,4BAAA,UAAO,KAAP;AADC,SAAAA;AAAA,GAAA;AAWE,MAAM,0BAA0B,sCAAoB;AAAA,EACvD,YAAY,SAAkB;AAC1B,UAAM,WAAW,gBAAgB,6BAAW,iBAAiB;AAAA,EACjE;AACJ;AAKO,MAAM,qBAAqB;AAAA,EAC9B;AAAA,EACA,6BAMe,MAAM;AAAA,EAErB,YACI,UAA8C,CAAC,GAC/C,2BAOF;AACE,SAAK,WAAW;AAChB,QAAI,8BAA8B,QAAW;AACzC,WAAK,6BAA6B;AAAA,IACtC;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAKA,wBAAwB,UAA8C,CAAC,GAAS;AAC5E,SAAK,WAAW,CAAC,GAAG,OAAO;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA,EAKA,kCAAkC,QAAyB;AACvD,WAAO,KAAK,SAAS,OAAO,WAAS,MAAM,gBAAgB,OAAO,WAAW;AAAA,EACjF;AAAA;AAAA;AAAA;AAAA,EAKA,gBAAgB,YAAoB,YAA6B;AAC7D,QAAI,eAAe,YAAY;AAC3B,aAAO;AAAA,IACX;AACA,QAAI,CAAC,qBAAO,uBAAuB,UAAU,KAAK,CAAC,qBAAO,uBAAuB,UAAU,GAAG;AAC1F,aAAO;AAAA,IACX;AACA,UAAM,gBAAgB,qBAAO,8BAA8B,UAAU;AACrE,UAAM,gBAAgB,qBAAO,8BAA8B,UAAU;AACrE,WACI,iDAAqB,iBAAiB,aAAa,MAC/C,iDAAqB,iBAAiB,aAAa,KACvD,iDAAqB,WAAW,aAAa,KAAK,iDAAqB,WAAW,aAAa;AAAA,EAEvG;AAAA;AAAA;AAAA;AAAA,EAKA,qBAAqB,mBAAqC,WAA8B;AAEpF,sBAAkB,IAAI,SAAS;AAE/B,YAAQ,WAAW;AAAA,MACf,KAAK,2BAAY;AACb,0BAAkB,IAAI,2BAAY,IAAI;AACtC;AAAA,MACJ,KAAK,2BAAY;AACb,0BAAkB,IAAI,2BAAY,IAAI;AACtC;AAAA,MACJ,KAAK,2BAAY;AACb,0BAAkB,IAAI,2BAAY,OAAO;AACzC,0BAAkB,IAAI,2BAAY,IAAI;AACtC;AAAA,MACJ,KAAK,2BAAY;AACb,0BAAkB,IAAI,2BAAY,MAAM;AACxC,0BAAkB,IAAI,2BAAY,OAAO;AACzC,0BAAkB,IAAI,2BAAY,SAAS;AAC3C,0BAAkB,IAAI,2BAAY,IAAI;AACtC;AAAA,IACR;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAKA,gBACI,SACA,UACA,WACA,WACO;AACP,UAAM,oBAAoB,KAAK,qBAAqB,SAAS,UAAU,SAAS;AAChF,QAAI,kBAAkB,SAAS,SAAS,GAAG;AACvC,aAAO;AAAA,IACX;AAEA,WAAO;AAAA,MACH,mCAAmC,SAAS,MAAM,
|
|
4
|
+
"sourcesContent": ["/**\n * @license\n * Copyright 2022-2023 Project CHIP Authors\n * SPDX-License-Identifier: Apache-2.0\n */\nimport { AccessLevel } from \"../../cluster/Cluster.js\";\nimport { AccessControl } from \"../../cluster/definitions/AccessControlCluster.js\";\nimport { MatterFlowError } from \"../../common/MatterError.js\";\nimport { CaseAuthenticatedTag } from \"../../datatype/CaseAuthenticatedTag.js\";\nimport { ClusterId } from \"../../datatype/ClusterId.js\";\nimport { FabricIndex } from \"../../datatype/FabricIndex.js\";\nimport { NodeId } from \"../../datatype/NodeId.js\";\nimport { EndpointInterface } from \"../../endpoint/EndpointInterface.js\";\nimport { Fabric } from \"../../fabric/Fabric.js\";\nimport { Logger } from \"../../log/Logger.js\";\nimport { TypeFromBitmapSchema } from \"../../schema/BitmapSchema.js\";\nimport { SecureSession } from \"../../session/SecureSession.js\";\nimport { toHex } from \"../../util/Number.js\";\nimport { StatusCode, StatusResponseError } from \"./StatusCode.js\";\n\nconst logger = Logger.get(\"AccessControlManager\");\n\nexport type AclEntry = Omit<TypeFromBitmapSchema<typeof AccessControl.TlvAccessControlEntry>, \"privilege\"> & {\n privilege: AccessLevel;\n};\nexport type AclList = AclEntry[];\n\nexport type AclExtensionEntry = TypeFromBitmapSchema<typeof AccessControl.TlvAccessControlExtension>;\nexport type AclExtensionList = AclExtensionEntry[];\n\nconst ImplicitDefaultPaseAclEntry: AclEntry = {\n fabricIndex: FabricIndex.NO_FABRIC, // not fabric-specific\n privilege: AccessLevel.Administer,\n authMode: AccessControl.AccessControlEntryAuthMode.Pase,\n subjects: [],\n targets: [], // entire node\n};\n\nenum AuthModeNone {\n None = 0,\n}\n\nexport type IncomingSubjectDescriptor = {\n isCommissioning: boolean;\n authMode: AccessControl.AccessControlEntryAuthMode | AuthModeNone;\n subjects: NodeId[];\n fabricIndex: FabricIndex;\n};\n\nexport class AccessDeniedError extends StatusResponseError {\n constructor(message?: string) {\n super(message ?? \"Unauthorized\", StatusCode.UnsupportedAccess);\n }\n}\n\n/**\n * Implements Access Control Logic as per Matter Specification @see {@link MatterSpecification.v12.Core} \u00A7 6.6.5.2.\n */\nexport class AccessControlManager {\n #aclList: AclList;\n #extensionEntryAccessCheck: (\n aclList: AclList,\n aclEntry: AclEntry,\n subjectDesc: IncomingSubjectDescriptor,\n endpoint: EndpointInterface,\n clusterId: ClusterId,\n ) => boolean = () => true;\n\n constructor(\n aclList: AccessControl.AccessControlEntry[] = [],\n extensionEntryAccessCheck?: (\n aclList: AclList,\n aclEntry: AclEntry,\n subjectDesc: IncomingSubjectDescriptor,\n endpoint: EndpointInterface,\n clusterId: ClusterId,\n ) => boolean,\n ) {\n this.#aclList = aclList as unknown as AclList; // It is the same structure we just use an internal type for privilege\n if (extensionEntryAccessCheck !== undefined) {\n this.#extensionEntryAccessCheck = extensionEntryAccessCheck;\n }\n }\n\n /**\n * Public method used to update the Access Control List on changes.\n */\n updateAccessControlList(aclList: AccessControl.AccessControlEntry[] = []): void {\n this.#aclList = [...aclList] as unknown as AclList; // It is the same structure we just use an internal type for privilege\n }\n\n /**\n * Get the Access Control List for a given fabric.\n */\n #getAccessControlEntriesForFabric(fabric: Fabric): AclList {\n return this.#aclList.filter(entry => entry.fabricIndex === fabric.fabricIndex);\n }\n\n /**\n * Subjects must match exactly, or both are CAT with matching CAT ID and acceptable CAT version\n */\n #subjectMatches(aclSubject: NodeId, isdSubject: NodeId): boolean {\n if (aclSubject === isdSubject) {\n return true;\n }\n if (!NodeId.isCaseAuthenticatedTag(aclSubject) || !NodeId.isCaseAuthenticatedTag(isdSubject)) {\n return false;\n }\n const aclSubjectCat = NodeId.extractAsCaseAuthenticatedTag(aclSubject);\n const isdSubjectCat = NodeId.extractAsCaseAuthenticatedTag(isdSubject);\n return (\n CaseAuthenticatedTag.getIdentifyValue(aclSubjectCat) ===\n CaseAuthenticatedTag.getIdentifyValue(isdSubjectCat) &&\n CaseAuthenticatedTag.getVersion(isdSubjectCat) >= CaseAuthenticatedTag.getVersion(aclSubjectCat)\n );\n }\n\n /**\n * Add the new privilege to the granted privileges set and also add any privileges subsumed by the new privilege.\n */\n #addGrantedPrivilege(grantedPrivileges: Set<AccessLevel>, privilege: AccessLevel): void {\n // Add the new privilege to the granted privileges set\n grantedPrivileges.add(privilege);\n // Also add any privileges subsumed by the new privilege\n switch (privilege) {\n case AccessLevel.ProxyView:\n grantedPrivileges.add(AccessLevel.View);\n break;\n case AccessLevel.Operate:\n grantedPrivileges.add(AccessLevel.View);\n break;\n case AccessLevel.Manage:\n grantedPrivileges.add(AccessLevel.Operate);\n grantedPrivileges.add(AccessLevel.View);\n break;\n case AccessLevel.Administer:\n grantedPrivileges.add(AccessLevel.Manage);\n grantedPrivileges.add(AccessLevel.Operate);\n grantedPrivileges.add(AccessLevel.ProxyView);\n grantedPrivileges.add(AccessLevel.View);\n break;\n }\n }\n\n /**\n * Check if the given ACL entry is allowed to be used for the given subject descriptor, endpoint, and cluster ID.\n */\n allowsPrivilege(\n session: SecureSession<any>,\n endpoint: EndpointInterface,\n clusterId: ClusterId,\n privilege: AccessLevel,\n ): boolean {\n const grantedPrivileges = this.getGrantedPrivileges(session, endpoint, clusterId);\n if (grantedPrivileges.includes(privilege)) {\n return true;\n }\n\n logger.notice(\n `Failed access control check for ${endpoint.number}/0x${toHex(clusterId)} and fabricIndex ${session.associatedFabric.fabricIndex}, acl=`,\n this.#getAccessControlEntriesForFabric(session.associatedFabric),\n \"with ISD=\",\n this.#getIsdFromMessage(session),\n \"granted privileges=\",\n grantedPrivileges,\n \"not contains\",\n privilege,\n );\n\n return false;\n }\n\n /**\n * Determines the granted privileges for the given session, endpoint, and cluster ID and returns them.\n */\n getGrantedPrivileges(\n session: SecureSession<any>,\n endpoint: EndpointInterface,\n clusterId: ClusterId,\n ): AccessLevel[] {\n const endpointId = endpoint.number;\n const fabric = session.fabric;\n const subjectDesc = this.#getIsdFromMessage(session);\n const acl = fabric ? this.#getAccessControlEntriesForFabric(fabric) : [ImplicitDefaultPaseAclEntry];\n\n // Granted privileges set is initially empty\n const grantedPrivileges = new Set<AccessLevel>();\n\n // PASE commissioning channel implicitly grants administer privilege to commissioner\n if (subjectDesc.authMode === AccessControl.AccessControlEntryAuthMode.Pase && subjectDesc.isCommissioning) {\n this.#addGrantedPrivilege(grantedPrivileges, AccessLevel.Administer);\n }\n\n for (const aclEntry of acl) {\n if (grantedPrivileges.has(AccessLevel.Administer)) {\n // End checking if highest privilege is granted\n break;\n }\n\n // Fabric index must match, there are no valid entries with FabricIndex == 0\n // other than the implicit PASE entry, which we will not see explicitly in the\n // access control list\n if (aclEntry.fabricIndex === FabricIndex.NO_FABRIC || aclEntry.fabricIndex !== subjectDesc.fabricIndex) {\n logger.debug(\n \"Skipping ACL entry with mismatched fabric index\",\n aclEntry.fabricIndex,\n subjectDesc.fabricIndex,\n );\n continue;\n }\n\n // Auth mode must match\n if (aclEntry.authMode !== subjectDesc.authMode) {\n logger.debug(\"Skipping ACL entry with mismatched auth mode\", aclEntry.authMode, subjectDesc.authMode);\n continue;\n }\n\n // Subject must match, or be \"wildcard\"\n if (aclEntry.subjects === null || aclEntry.subjects.length === 0) {\n // Precondition: only CASE and Group auth can have empty subjects\n if (\n aclEntry.authMode !== AccessControl.AccessControlEntryAuthMode.Case &&\n aclEntry.authMode !== AccessControl.AccessControlEntryAuthMode.Group\n ) {\n throw new MatterFlowError(\"ACL error: only CASE and Group auth can have empty subjects\");\n }\n // ... Empty is wildcard, no match required\n } else {\n // Non-empty requires a match\n let matchedSubject = false;\n subjectLoop: for (const aclSubject of aclEntry.subjects) {\n for (const isdSubject of subjectDesc.subjects) {\n if (this.#subjectMatches(aclSubject, isdSubject)) {\n matchedSubject = true;\n break subjectLoop;\n }\n }\n }\n if (!matchedSubject) {\n continue;\n }\n }\n\n // Target must match, or be \"wildcard\"\n if (aclEntry.targets === null || aclEntry.targets.length === 0) {\n // Empty is wildcard, no match required\n } else {\n // Non-empty requires a match\n let matchedTarget = false;\n for (const {\n cluster: targetClusterId,\n endpoint: targetEndpointId,\n deviceType: targetDeviceType,\n } of aclEntry.targets) {\n // Precondition: target cannot be empty\n if (targetClusterId === null && targetEndpointId === null && targetDeviceType === null) {\n throw new MatterFlowError(\"ACL error: target cannot be empty\");\n }\n // Precondition: target cannot specify both endpoint and device type\n if (targetEndpointId !== null && targetDeviceType !== null) {\n throw new MatterFlowError(\"ACL error: target cannot specify both endpoint and device type\");\n }\n // Cluster must match, or be wildcard\n if (targetClusterId !== null && targetClusterId !== clusterId) {\n continue;\n }\n // Endpoint must match, or be wildcard\n if (targetEndpointId !== null && targetEndpointId !== endpointId) {\n continue;\n }\n // Endpoint may be specified indirectly via device type\n // TODO adjust to array check once we use multiple devicetypes\n if (targetDeviceType !== null && endpoint.deviceType !== targetDeviceType) {\n continue;\n }\n matchedTarget = true;\n break;\n }\n if (!matchedTarget) {\n continue;\n }\n }\n\n // Extensions processing must not fail\n if (!this.#extensionEntryAccessCheck(acl, aclEntry, subjectDesc, endpoint, clusterId)) {\n continue;\n }\n\n // All checks have passed, add privilege to granted privilege set\n this.#addGrantedPrivilege(grantedPrivileges, aclEntry.privilege);\n }\n // Should never grant Administer privilege to a Group.\n if (\n subjectDesc.authMode === AccessControl.AccessControlEntryAuthMode.Group &&\n grantedPrivileges.has(AccessLevel.Administer)\n ) {\n throw new MatterFlowError(\"ACL error: should never grant Administer privilege to a Group\");\n }\n\n return [...grantedPrivileges];\n }\n\n /**\n * Determines the Incoming Subject Descriptor (ISD) from the given session.\n */\n #getIsdFromMessage(session: SecureSession<any>) {\n const fabric = session.fabric;\n const isd: IncomingSubjectDescriptor = {\n isCommissioning: false,\n authMode: AuthModeNone.None,\n subjects: new Array<NodeId>(),\n fabricIndex: FabricIndex.NO_FABRIC,\n };\n\n if (session.isPase) {\n isd.authMode = AccessControl.AccessControlEntryAuthMode.Pase;\n isd.isCommissioning = true; // Or how \"commissioning channel\" is defined?\n isd.subjects.push(NodeId(0)); // Default Commissioning Passcode ID\n if (fabric) {\n isd.fabricIndex = fabric.fabricIndex;\n }\n } else {\n // TODO Add Group session handling when implementing groups\n // if (session instanceof SecureGroupSession) {\n // Groups\n // # Message is assumed to have been decrypted and matched properly prior to\n // # this procedure occurring.\n // group_id = message.get_dst_group_id()\n // group_key_id = sessions_metadata.get_group_key_id(message)\n // # Group membership must be verified against Group Key Management Cluster\n // if group_key_management_cluster.group_key_map_has_mapping(group_id, group_key_id):\n // isd.AuthMode = AuthModeEnum.Group\n // isd.Subjects.append(group_id)\n // isd.FabricIndex = sessions_metadata.get_fabric_index(message)\n // assert(isd.FabricIndex != 0) # cannot be zero\n //\n // isd.authMode = AccessControl.AccessControlEntryAuthMode.Group;\n // } else {\n\n // CASE session\n isd.authMode = AccessControl.AccessControlEntryAuthMode.Case;\n isd.subjects.push(session.peerNodeId);\n // Append CASE session CATs which also serve as subjects\n session.caseAuthenticatedTags.forEach(cat => isd.subjects.push(NodeId.fromCaseAuthenticatedTag(cat)));\n // }\n if (fabric === undefined) {\n throw new MatterFlowError(\"ACL error: fabric is undefined\");\n }\n isd.fabricIndex = fabric.fabricIndex;\n }\n\n return isd;\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["AuthModeNone"]
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CaseServer.d.ts","sourceRoot":"","sources":["../../../../src/session/case/CaseServer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAQrD,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAqBpE,qBAAa,UAAW,YAAW,eAAe,CAAC,YAAY,CAAC;IACtD,aAAa,CAAC,QAAQ,EAAE,eAAe,CAAC,YAAY,CAAC;IAoB3D,KAAK,IAAI,MAAM;YAID,YAAY;
|
|
1
|
+
{"version":3,"file":"CaseServer.d.ts","sourceRoot":"","sources":["../../../../src/session/case/CaseServer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAQrD,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAqBpE,qBAAa,UAAW,YAAW,eAAe,CAAC,YAAY,CAAC;IACtD,aAAa,CAAC,QAAQ,EAAE,eAAe,CAAC,YAAY,CAAC;IAoB3D,KAAK,IAAI,MAAM;YAID,YAAY;IA4MpB,KAAK;CAGd"}
|
|
@@ -113,7 +113,9 @@ class CaseServer {
|
|
|
113
113
|
logger.info(
|
|
114
114
|
`session ${secureSession.id} resumed with ${messenger.getChannelName()} for Fabric ${import_NodeId.NodeId.toHexString(
|
|
115
115
|
fabric.nodeId
|
|
116
|
-
)}(index ${fabric.fabricIndex}) and PeerNode ${import_NodeId.NodeId.toHexString(peerNodeId)}
|
|
116
|
+
)}(index ${fabric.fabricIndex}) and PeerNode ${import_NodeId.NodeId.toHexString(peerNodeId)}`,
|
|
117
|
+
"with CATs",
|
|
118
|
+
caseAuthenticatedTags
|
|
117
119
|
);
|
|
118
120
|
resumptionRecord.resumptionId = resumptionId;
|
|
119
121
|
await messenger.waitForSuccess("Success after CASE Sigma2Resume");
|
|
@@ -202,7 +204,9 @@ class CaseServer {
|
|
|
202
204
|
logger.info(
|
|
203
205
|
`session ${secureSession.id} created with ${messenger.getChannelName()} for Fabric ${import_NodeId.NodeId.toHexString(
|
|
204
206
|
fabric.nodeId
|
|
205
|
-
)}(index ${fabric.fabricIndex}) and PeerNode ${import_NodeId.NodeId.toHexString(peerNodeId)}
|
|
207
|
+
)}(index ${fabric.fabricIndex}) and PeerNode ${import_NodeId.NodeId.toHexString(peerNodeId)}`,
|
|
208
|
+
"with CATs",
|
|
209
|
+
caseAuthenticatedTags
|
|
206
210
|
);
|
|
207
211
|
await messenger.sendSuccess();
|
|
208
212
|
const resumptionRecord2 = {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/session/case/CaseServer.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * @license\n * Copyright 2022-2024 Matter.js Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { MatterDevice } from \"../../MatterDevice.js\";\nimport { TlvOperationalCertificate } from \"../../certificate/CertificateManager.js\";\nimport { UnexpectedDataError } from \"../../common/MatterError.js\";\nimport { Crypto } from \"../../crypto/Crypto.js\";\nimport { PublicKey } from \"../../crypto/Key.js\";\nimport { NodeId } from \"../../datatype/NodeId.js\";\nimport { FabricNotFoundError } from \"../../fabric/FabricManager.js\";\nimport { Logger } from \"../../log/Logger.js\";\nimport { MessageExchange } from \"../../protocol/MessageExchange.js\";\nimport { ProtocolHandler } from \"../../protocol/ProtocolHandler.js\";\nimport { ProtocolStatusCode, SECURE_CHANNEL_PROTOCOL_ID } from \"../../protocol/securechannel/SecureChannelMessages.js\";\nimport { ChannelStatusResponseError } from \"../../protocol/securechannel/SecureChannelMessenger.js\";\nimport { ByteArray } from \"../../util/ByteArray.js\";\nimport {\n KDFSR1_KEY_INFO,\n KDFSR2_INFO,\n KDFSR2_KEY_INFO,\n KDFSR3_INFO,\n RESUME1_MIC_NONCE,\n RESUME2_MIC_NONCE,\n TBE_DATA2_NONCE,\n TBE_DATA3_NONCE,\n TlvEncryptedDataSigma2,\n TlvEncryptedDataSigma3,\n TlvSignedData,\n} from \"./CaseMessages.js\";\nimport { CaseServerMessenger } from \"./CaseMessenger.js\";\n\nconst logger = Logger.get(\"CaseServer\");\n\nexport class CaseServer implements ProtocolHandler<MatterDevice> {\n async onNewExchange(exchange: MessageExchange<MatterDevice>) {\n const messenger = new CaseServerMessenger(exchange);\n try {\n await this.handleSigma1(exchange.session.context, messenger);\n } catch (error) {\n logger.error(\"An error occurred during the commissioning\", error);\n\n if (error instanceof FabricNotFoundError) {\n await messenger.sendError(ProtocolStatusCode.NoSharedTrustRoots);\n }\n // If we received a ChannelStatusResponseError we do not need to send one back, so just cancel pairing\n else if (!(error instanceof ChannelStatusResponseError)) {\n await messenger.sendError(ProtocolStatusCode.InvalidParam);\n }\n } finally {\n // Destroy the unsecure session used to establish the secure Case session\n await exchange.session.destroy();\n }\n }\n\n getId(): number {\n return SECURE_CHANNEL_PROTOCOL_ID;\n }\n\n private async handleSigma1(server: MatterDevice, messenger: CaseServerMessenger) {\n logger.info(`Received pairing request from ${messenger.getChannelName()}`);\n // Generate pairing info\n const responderRandom = Crypto.getRandom();\n\n // Read and process sigma 1\n const { sigma1Bytes, sigma1 } = await messenger.readSigma1();\n const {\n initiatorSessionId: peerSessionId,\n resumptionId: peerResumptionId,\n initiatorResumeMic: peerResumeMic,\n destinationId,\n initiatorRandom: peerRandom,\n initiatorEcdhPublicKey: peerEcdhPublicKey,\n initiatorSessionParams,\n } = sigma1;\n\n // Try to resume a previous session\n const resumptionId = Crypto.getRandomData(16);\n\n const resumptionRecord =\n peerResumptionId !== undefined && peerResumeMic !== undefined\n ? server.findResumptionRecordById(peerResumptionId)\n : undefined;\n // We try to resume the session\n if (peerResumptionId !== undefined && peerResumeMic !== undefined && resumptionRecord !== undefined) {\n const { sharedSecret, fabric, peerNodeId, caseAuthenticatedTags } = resumptionRecord;\n const peerResumeKey = await Crypto.hkdf(\n sharedSecret,\n ByteArray.concat(peerRandom, peerResumptionId),\n KDFSR1_KEY_INFO,\n );\n Crypto.decrypt(peerResumeKey, peerResumeMic, RESUME1_MIC_NONCE);\n\n // All good! Create secure session\n const responderSessionId = await server.getNextAvailableSessionId();\n const secureSessionSalt = ByteArray.concat(peerRandom, peerResumptionId);\n const secureSession = await server.sessionManager.createSecureSession({\n sessionId: responderSessionId,\n fabric,\n peerNodeId,\n peerSessionId,\n sharedSecret,\n salt: secureSessionSalt,\n isInitiator: false,\n isResumption: true,\n peerSessionParameters: initiatorSessionParams,\n caseAuthenticatedTags,\n });\n\n // Generate sigma 2 resume\n const resumeSalt = ByteArray.concat(peerRandom, resumptionId);\n const resumeKey = await Crypto.hkdf(sharedSecret, resumeSalt, KDFSR2_KEY_INFO);\n const resumeMic = Crypto.encrypt(resumeKey, new ByteArray(0), RESUME2_MIC_NONCE);\n try {\n await messenger.sendSigma2Resume({\n resumptionId,\n resumeMic,\n responderSessionId,\n responderSessionParams: server.sessionParameters, // responder session parameters\n });\n } catch (error) {\n // If we fail to send the resume, we destroy the session\n await secureSession.destroy(false);\n throw error;\n }\n\n logger.info(\n `session ${secureSession.id} resumed with ${messenger.getChannelName()} for Fabric ${NodeId.toHexString(\n fabric.nodeId,\n )}(index ${fabric.fabricIndex}) and PeerNode ${NodeId.toHexString(peerNodeId)}`,\n );\n resumptionRecord.resumptionId = resumptionId; /* Update the ID */\n\n // Wait for success on the peer side\n await messenger.waitForSuccess(\"Success after CASE Sigma2Resume\");\n\n await messenger.close();\n await server.saveResumptionRecord(resumptionRecord);\n } else if (\n (peerResumptionId === undefined && peerResumeMic === undefined) ||\n (peerResumptionId !== undefined && peerResumeMic !== undefined && resumptionRecord === undefined)\n ) {\n // Generate sigma 2\n // TODO: Pass through a group id?\n const fabric = server.findFabricFromDestinationId(destinationId, peerRandom);\n const { operationalCert: nodeOpCert, intermediateCACert, operationalIdentityProtectionKey } = fabric;\n const { publicKey: responderEcdhPublicKey, sharedSecret } =\n Crypto.ecdhGeneratePublicKeyAndSecret(peerEcdhPublicKey);\n const sigma2Salt = ByteArray.concat(\n operationalIdentityProtectionKey,\n responderRandom,\n responderEcdhPublicKey,\n Crypto.hash(sigma1Bytes),\n );\n const sigma2Key = await Crypto.hkdf(sharedSecret, sigma2Salt, KDFSR2_INFO);\n const signatureData = TlvSignedData.encode({\n nodeOpCert,\n intermediateCACert,\n ecdhPublicKey: responderEcdhPublicKey,\n peerEcdhPublicKey,\n });\n const signature = fabric.sign(signatureData);\n const encryptedData = TlvEncryptedDataSigma2.encode({\n nodeOpCert,\n intermediateCACert,\n signature,\n resumptionId,\n });\n const encrypted = Crypto.encrypt(sigma2Key, encryptedData, TBE_DATA2_NONCE);\n const responderSessionId = await server.getNextAvailableSessionId();\n const sigma2Bytes = await messenger.sendSigma2({\n responderRandom,\n responderSessionId,\n responderEcdhPublicKey,\n encrypted,\n responderSessionParams: server.sessionParameters, // responder session parameters\n });\n\n // Read and process sigma 3\n const {\n sigma3Bytes,\n sigma3: { encrypted: peerEncrypted },\n } = await messenger.readSigma3();\n const sigma3Salt = ByteArray.concat(\n operationalIdentityProtectionKey,\n Crypto.hash([sigma1Bytes, sigma2Bytes]),\n );\n const sigma3Key = await Crypto.hkdf(sharedSecret, sigma3Salt, KDFSR3_INFO);\n const peerDecryptedData = Crypto.decrypt(sigma3Key, peerEncrypted, TBE_DATA3_NONCE);\n const {\n nodeOpCert: peerNewOpCert,\n intermediateCACert: peerIntermediateCACert,\n signature: peerSignature,\n } = TlvEncryptedDataSigma3.decode(peerDecryptedData);\n\n fabric.verifyCredentials(peerNewOpCert, peerIntermediateCACert);\n\n const peerSignatureData = TlvSignedData.encode({\n nodeOpCert: peerNewOpCert,\n intermediateCACert: peerIntermediateCACert,\n ecdhPublicKey: peerEcdhPublicKey,\n peerEcdhPublicKey: responderEcdhPublicKey,\n });\n const {\n ellipticCurvePublicKey: peerPublicKey,\n subject: { fabricId: peerFabricId, nodeId: peerNodeId, caseAuthenticatedTags },\n } = TlvOperationalCertificate.decode(peerNewOpCert);\n\n if (fabric.fabricId !== peerFabricId) {\n throw new UnexpectedDataError(`Fabric ID mismatch: ${fabric.fabricId} !== ${peerFabricId}`);\n }\n\n Crypto.verify(PublicKey(peerPublicKey), peerSignatureData, peerSignature);\n\n // All good! Create secure session\n const secureSessionSalt = ByteArray.concat(\n operationalIdentityProtectionKey,\n Crypto.hash([sigma1Bytes, sigma2Bytes, sigma3Bytes]),\n );\n const secureSession = await server.sessionManager.createSecureSession({\n sessionId: responderSessionId,\n fabric,\n peerNodeId,\n peerSessionId,\n sharedSecret,\n salt: secureSessionSalt,\n isInitiator: false,\n isResumption: false,\n peerSessionParameters: initiatorSessionParams,\n caseAuthenticatedTags,\n });\n logger.info(\n `session ${secureSession.id} created with ${messenger.getChannelName()} for Fabric ${NodeId.toHexString(\n fabric.nodeId,\n )}(index ${fabric.fabricIndex}) and PeerNode ${NodeId.toHexString(peerNodeId)}`,\n );\n await messenger.sendSuccess();\n\n const resumptionRecord = {\n peerNodeId,\n fabric,\n sharedSecret,\n resumptionId,\n sessionParameters: secureSession.parameters,\n caseAuthenticatedTags,\n };\n\n await messenger.close();\n await server.saveResumptionRecord(resumptionRecord);\n } else {\n logger.info(\n `Invalid resumption ID or resume MIC received from ${messenger.getChannelName()}`,\n peerResumptionId,\n peerResumeMic,\n );\n throw new UnexpectedDataError(\"Invalid resumption ID or resume MIC.\");\n }\n }\n\n async close() {\n // Nothing to do\n }\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAOA,gCAA0C;AAC1C,yBAAoC;AACpC,oBAAuB;AACvB,iBAA0B;AAC1B,oBAAuB;AACvB,2BAAoC;AACpC,oBAAuB;AAGvB,mCAA+D;AAC/D,oCAA2C;AAC3C,uBAA0B;AAC1B,0BAYO;AACP,2BAAoC;AAhCpC;AAAA;AAAA;AAAA;AAAA;AAkCA,MAAM,SAAS,qBAAO,IAAI,YAAY;AAE/B,MAAM,WAAoD;AAAA,EAC7D,MAAM,cAAc,UAAyC;AACzD,UAAM,YAAY,IAAI,yCAAoB,QAAQ;AAClD,QAAI;AACA,YAAM,KAAK,aAAa,SAAS,QAAQ,SAAS,SAAS;AAAA,IAC/D,SAAS,OAAO;AACZ,aAAO,MAAM,8CAA8C,KAAK;AAEhE,UAAI,iBAAiB,0CAAqB;AACtC,cAAM,UAAU,UAAU,gDAAmB,kBAAkB;AAAA,MACnE,WAES,EAAE,iBAAiB,2DAA6B;AACrD,cAAM,UAAU,UAAU,gDAAmB,YAAY;AAAA,MAC7D;AAAA,IACJ,UAAE;AAEE,YAAM,SAAS,QAAQ,QAAQ;AAAA,IACnC;AAAA,EACJ;AAAA,EAEA,QAAgB;AACZ,WAAO;AAAA,EACX;AAAA,EAEA,MAAc,aAAa,QAAsB,WAAgC;AAC7E,WAAO,KAAK,iCAAiC,UAAU,eAAe,CAAC,EAAE;AAEzE,UAAM,kBAAkB,qBAAO,UAAU;AAGzC,UAAM,EAAE,aAAa,OAAO,IAAI,MAAM,UAAU,WAAW;AAC3D,UAAM;AAAA,MACF,oBAAoB;AAAA,MACpB,cAAc;AAAA,MACd,oBAAoB;AAAA,MACpB;AAAA,MACA,iBAAiB;AAAA,MACjB,wBAAwB;AAAA,MACxB;AAAA,IACJ,IAAI;AAGJ,UAAM,eAAe,qBAAO,cAAc,EAAE;AAE5C,UAAM,mBACF,qBAAqB,UAAa,kBAAkB,SAC9C,OAAO,yBAAyB,gBAAgB,IAChD;AAEV,QAAI,qBAAqB,UAAa,kBAAkB,UAAa,qBAAqB,QAAW;AACjG,YAAM,EAAE,cAAc,QAAQ,YAAY,sBAAsB,IAAI;AACpE,YAAM,gBAAgB,MAAM,qBAAO;AAAA,QAC/B;AAAA,QACA,2BAAU,OAAO,YAAY,gBAAgB;AAAA,QAC7C;AAAA,MACJ;AACA,2BAAO,QAAQ,eAAe,eAAe,qCAAiB;AAG9D,YAAM,qBAAqB,MAAM,OAAO,0BAA0B;AAClE,YAAM,oBAAoB,2BAAU,OAAO,YAAY,gBAAgB;AACvE,YAAM,gBAAgB,MAAM,OAAO,eAAe,oBAAoB;AAAA,QAClE,WAAW;AAAA,QACX;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,MAAM;AAAA,QACN,aAAa;AAAA,QACb,cAAc;AAAA,QACd,uBAAuB;AAAA,QACvB;AAAA,MACJ,CAAC;AAGD,YAAM,aAAa,2BAAU,OAAO,YAAY,YAAY;AAC5D,YAAM,YAAY,MAAM,qBAAO,KAAK,cAAc,YAAY,mCAAe;AAC7E,YAAM,YAAY,qBAAO,QAAQ,WAAW,IAAI,2BAAU,CAAC,GAAG,qCAAiB;AAC/E,UAAI;AACA,cAAM,UAAU,iBAAiB;AAAA,UAC7B;AAAA,UACA;AAAA,UACA;AAAA,UACA,wBAAwB,OAAO;AAAA;AAAA,QACnC,CAAC;AAAA,MACL,SAAS,OAAO;AAEZ,cAAM,cAAc,QAAQ,KAAK;AACjC,cAAM;AAAA,MACV;AAEA,aAAO;AAAA,QACH,WAAW,cAAc,EAAE,iBAAiB,UAAU,eAAe,CAAC,eAAe,qBAAO;AAAA,UACxF,OAAO;AAAA,QACX,CAAC,UAAU,OAAO,WAAW,kBAAkB,qBAAO,YAAY,UAAU,CAAC;AAAA,
|
|
4
|
+
"sourcesContent": ["/**\n * @license\n * Copyright 2022-2024 Matter.js Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { MatterDevice } from \"../../MatterDevice.js\";\nimport { TlvOperationalCertificate } from \"../../certificate/CertificateManager.js\";\nimport { UnexpectedDataError } from \"../../common/MatterError.js\";\nimport { Crypto } from \"../../crypto/Crypto.js\";\nimport { PublicKey } from \"../../crypto/Key.js\";\nimport { NodeId } from \"../../datatype/NodeId.js\";\nimport { FabricNotFoundError } from \"../../fabric/FabricManager.js\";\nimport { Logger } from \"../../log/Logger.js\";\nimport { MessageExchange } from \"../../protocol/MessageExchange.js\";\nimport { ProtocolHandler } from \"../../protocol/ProtocolHandler.js\";\nimport { ProtocolStatusCode, SECURE_CHANNEL_PROTOCOL_ID } from \"../../protocol/securechannel/SecureChannelMessages.js\";\nimport { ChannelStatusResponseError } from \"../../protocol/securechannel/SecureChannelMessenger.js\";\nimport { ByteArray } from \"../../util/ByteArray.js\";\nimport {\n KDFSR1_KEY_INFO,\n KDFSR2_INFO,\n KDFSR2_KEY_INFO,\n KDFSR3_INFO,\n RESUME1_MIC_NONCE,\n RESUME2_MIC_NONCE,\n TBE_DATA2_NONCE,\n TBE_DATA3_NONCE,\n TlvEncryptedDataSigma2,\n TlvEncryptedDataSigma3,\n TlvSignedData,\n} from \"./CaseMessages.js\";\nimport { CaseServerMessenger } from \"./CaseMessenger.js\";\n\nconst logger = Logger.get(\"CaseServer\");\n\nexport class CaseServer implements ProtocolHandler<MatterDevice> {\n async onNewExchange(exchange: MessageExchange<MatterDevice>) {\n const messenger = new CaseServerMessenger(exchange);\n try {\n await this.handleSigma1(exchange.session.context, messenger);\n } catch (error) {\n logger.error(\"An error occurred during the commissioning\", error);\n\n if (error instanceof FabricNotFoundError) {\n await messenger.sendError(ProtocolStatusCode.NoSharedTrustRoots);\n }\n // If we received a ChannelStatusResponseError we do not need to send one back, so just cancel pairing\n else if (!(error instanceof ChannelStatusResponseError)) {\n await messenger.sendError(ProtocolStatusCode.InvalidParam);\n }\n } finally {\n // Destroy the unsecure session used to establish the secure Case session\n await exchange.session.destroy();\n }\n }\n\n getId(): number {\n return SECURE_CHANNEL_PROTOCOL_ID;\n }\n\n private async handleSigma1(server: MatterDevice, messenger: CaseServerMessenger) {\n logger.info(`Received pairing request from ${messenger.getChannelName()}`);\n // Generate pairing info\n const responderRandom = Crypto.getRandom();\n\n // Read and process sigma 1\n const { sigma1Bytes, sigma1 } = await messenger.readSigma1();\n const {\n initiatorSessionId: peerSessionId,\n resumptionId: peerResumptionId,\n initiatorResumeMic: peerResumeMic,\n destinationId,\n initiatorRandom: peerRandom,\n initiatorEcdhPublicKey: peerEcdhPublicKey,\n initiatorSessionParams,\n } = sigma1;\n\n // Try to resume a previous session\n const resumptionId = Crypto.getRandomData(16);\n\n const resumptionRecord =\n peerResumptionId !== undefined && peerResumeMic !== undefined\n ? server.findResumptionRecordById(peerResumptionId)\n : undefined;\n // We try to resume the session\n if (peerResumptionId !== undefined && peerResumeMic !== undefined && resumptionRecord !== undefined) {\n const { sharedSecret, fabric, peerNodeId, caseAuthenticatedTags } = resumptionRecord;\n const peerResumeKey = await Crypto.hkdf(\n sharedSecret,\n ByteArray.concat(peerRandom, peerResumptionId),\n KDFSR1_KEY_INFO,\n );\n Crypto.decrypt(peerResumeKey, peerResumeMic, RESUME1_MIC_NONCE);\n\n // All good! Create secure session\n const responderSessionId = await server.getNextAvailableSessionId();\n const secureSessionSalt = ByteArray.concat(peerRandom, peerResumptionId);\n const secureSession = await server.sessionManager.createSecureSession({\n sessionId: responderSessionId,\n fabric,\n peerNodeId,\n peerSessionId,\n sharedSecret,\n salt: secureSessionSalt,\n isInitiator: false,\n isResumption: true,\n peerSessionParameters: initiatorSessionParams,\n caseAuthenticatedTags,\n });\n\n // Generate sigma 2 resume\n const resumeSalt = ByteArray.concat(peerRandom, resumptionId);\n const resumeKey = await Crypto.hkdf(sharedSecret, resumeSalt, KDFSR2_KEY_INFO);\n const resumeMic = Crypto.encrypt(resumeKey, new ByteArray(0), RESUME2_MIC_NONCE);\n try {\n await messenger.sendSigma2Resume({\n resumptionId,\n resumeMic,\n responderSessionId,\n responderSessionParams: server.sessionParameters, // responder session parameters\n });\n } catch (error) {\n // If we fail to send the resume, we destroy the session\n await secureSession.destroy(false);\n throw error;\n }\n\n logger.info(\n `session ${secureSession.id} resumed with ${messenger.getChannelName()} for Fabric ${NodeId.toHexString(\n fabric.nodeId,\n )}(index ${fabric.fabricIndex}) and PeerNode ${NodeId.toHexString(peerNodeId)}`,\n \"with CATs\",\n caseAuthenticatedTags,\n );\n resumptionRecord.resumptionId = resumptionId; /* Update the ID */\n\n // Wait for success on the peer side\n await messenger.waitForSuccess(\"Success after CASE Sigma2Resume\");\n\n await messenger.close();\n await server.saveResumptionRecord(resumptionRecord);\n } else if (\n (peerResumptionId === undefined && peerResumeMic === undefined) ||\n (peerResumptionId !== undefined && peerResumeMic !== undefined && resumptionRecord === undefined)\n ) {\n // Generate sigma 2\n // TODO: Pass through a group id?\n const fabric = server.findFabricFromDestinationId(destinationId, peerRandom);\n const { operationalCert: nodeOpCert, intermediateCACert, operationalIdentityProtectionKey } = fabric;\n const { publicKey: responderEcdhPublicKey, sharedSecret } =\n Crypto.ecdhGeneratePublicKeyAndSecret(peerEcdhPublicKey);\n const sigma2Salt = ByteArray.concat(\n operationalIdentityProtectionKey,\n responderRandom,\n responderEcdhPublicKey,\n Crypto.hash(sigma1Bytes),\n );\n const sigma2Key = await Crypto.hkdf(sharedSecret, sigma2Salt, KDFSR2_INFO);\n const signatureData = TlvSignedData.encode({\n nodeOpCert,\n intermediateCACert,\n ecdhPublicKey: responderEcdhPublicKey,\n peerEcdhPublicKey,\n });\n const signature = fabric.sign(signatureData);\n const encryptedData = TlvEncryptedDataSigma2.encode({\n nodeOpCert,\n intermediateCACert,\n signature,\n resumptionId,\n });\n const encrypted = Crypto.encrypt(sigma2Key, encryptedData, TBE_DATA2_NONCE);\n const responderSessionId = await server.getNextAvailableSessionId();\n const sigma2Bytes = await messenger.sendSigma2({\n responderRandom,\n responderSessionId,\n responderEcdhPublicKey,\n encrypted,\n responderSessionParams: server.sessionParameters, // responder session parameters\n });\n\n // Read and process sigma 3\n const {\n sigma3Bytes,\n sigma3: { encrypted: peerEncrypted },\n } = await messenger.readSigma3();\n const sigma3Salt = ByteArray.concat(\n operationalIdentityProtectionKey,\n Crypto.hash([sigma1Bytes, sigma2Bytes]),\n );\n const sigma3Key = await Crypto.hkdf(sharedSecret, sigma3Salt, KDFSR3_INFO);\n const peerDecryptedData = Crypto.decrypt(sigma3Key, peerEncrypted, TBE_DATA3_NONCE);\n const {\n nodeOpCert: peerNewOpCert,\n intermediateCACert: peerIntermediateCACert,\n signature: peerSignature,\n } = TlvEncryptedDataSigma3.decode(peerDecryptedData);\n\n fabric.verifyCredentials(peerNewOpCert, peerIntermediateCACert);\n\n const peerSignatureData = TlvSignedData.encode({\n nodeOpCert: peerNewOpCert,\n intermediateCACert: peerIntermediateCACert,\n ecdhPublicKey: peerEcdhPublicKey,\n peerEcdhPublicKey: responderEcdhPublicKey,\n });\n const {\n ellipticCurvePublicKey: peerPublicKey,\n subject: { fabricId: peerFabricId, nodeId: peerNodeId, caseAuthenticatedTags },\n } = TlvOperationalCertificate.decode(peerNewOpCert);\n\n if (fabric.fabricId !== peerFabricId) {\n throw new UnexpectedDataError(`Fabric ID mismatch: ${fabric.fabricId} !== ${peerFabricId}`);\n }\n\n Crypto.verify(PublicKey(peerPublicKey), peerSignatureData, peerSignature);\n\n // All good! Create secure session\n const secureSessionSalt = ByteArray.concat(\n operationalIdentityProtectionKey,\n Crypto.hash([sigma1Bytes, sigma2Bytes, sigma3Bytes]),\n );\n const secureSession = await server.sessionManager.createSecureSession({\n sessionId: responderSessionId,\n fabric,\n peerNodeId,\n peerSessionId,\n sharedSecret,\n salt: secureSessionSalt,\n isInitiator: false,\n isResumption: false,\n peerSessionParameters: initiatorSessionParams,\n caseAuthenticatedTags,\n });\n logger.info(\n `session ${secureSession.id} created with ${messenger.getChannelName()} for Fabric ${NodeId.toHexString(\n fabric.nodeId,\n )}(index ${fabric.fabricIndex}) and PeerNode ${NodeId.toHexString(peerNodeId)}`,\n \"with CATs\",\n caseAuthenticatedTags,\n );\n await messenger.sendSuccess();\n\n const resumptionRecord = {\n peerNodeId,\n fabric,\n sharedSecret,\n resumptionId,\n sessionParameters: secureSession.parameters,\n caseAuthenticatedTags,\n };\n\n await messenger.close();\n await server.saveResumptionRecord(resumptionRecord);\n } else {\n logger.info(\n `Invalid resumption ID or resume MIC received from ${messenger.getChannelName()}`,\n peerResumptionId,\n peerResumeMic,\n );\n throw new UnexpectedDataError(\"Invalid resumption ID or resume MIC.\");\n }\n }\n\n async close() {\n // Nothing to do\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["resumptionRecord"]
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AccessControlManager.d.ts","sourceRoot":"","sources":["../../../../src/protocol/interaction/AccessControlManager.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,mDAAmD,CAAC;AAGlF,OAAO,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAC5D,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qCAAqC,CAAC;AAGxE,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAE/D,OAAO,EAAc,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAIlE,MAAM,MAAM,QAAQ,GAAG,IAAI,CAAC,oBAAoB,CAAC,OAAO,aAAa,CAAC,qBAAqB,CAAC,EAAE,WAAW,CAAC,GAAG;IACzG,SAAS,EAAE,WAAW,CAAC;CAC1B,CAAC;AACF,MAAM,MAAM,OAAO,GAAG,QAAQ,EAAE,CAAC;AAEjC,MAAM,MAAM,iBAAiB,GAAG,oBAAoB,CAAC,OAAO,aAAa,CAAC,yBAAyB,CAAC,CAAC;AACrG,MAAM,MAAM,gBAAgB,GAAG,iBAAiB,EAAE,CAAC;AAUnD,aAAK,YAAY;IACb,IAAI,IAAI;CACX;AAED,MAAM,MAAM,yBAAyB,GAAG;IACpC,eAAe,EAAE,OAAO,CAAC;IACzB,QAAQ,EAAE,aAAa,CAAC,0BAA0B,GAAG,YAAY,CAAC;IAClE,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,WAAW,EAAE,WAAW,CAAC;CAC5B,CAAC;AAEF,qBAAa,iBAAkB,SAAQ,mBAAmB;gBAC1C,OAAO,CAAC,EAAE,MAAM;CAG/B;AAED;;GAEG;AACH,qBAAa,oBAAoB;;gBAWzB,OAAO,GAAE,aAAa,CAAC,kBAAkB,EAAO,EAChD,yBAAyB,CAAC,EAAE,CACxB,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,QAAQ,EAClB,WAAW,EAAE,yBAAyB,EACtC,QAAQ,EAAE,iBAAiB,EAC3B,SAAS,EAAE,SAAS,KACnB,OAAO;IAQhB;;OAEG;IACH,uBAAuB,CAAC,OAAO,GAAE,aAAa,CAAC,kBAAkB,EAAO,GAAG,IAAI;IAyD/E;;OAEG;IACH,eAAe,CACX,OAAO,EAAE,aAAa,CAAC,GAAG,CAAC,EAC3B,QAAQ,EAAE,iBAAiB,EAC3B,SAAS,EAAE,SAAS,EACpB,SAAS,EAAE,WAAW,GACvB,OAAO;
|
|
1
|
+
{"version":3,"file":"AccessControlManager.d.ts","sourceRoot":"","sources":["../../../../src/protocol/interaction/AccessControlManager.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,mDAAmD,CAAC;AAGlF,OAAO,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAC5D,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qCAAqC,CAAC;AAGxE,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAE/D,OAAO,EAAc,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAIlE,MAAM,MAAM,QAAQ,GAAG,IAAI,CAAC,oBAAoB,CAAC,OAAO,aAAa,CAAC,qBAAqB,CAAC,EAAE,WAAW,CAAC,GAAG;IACzG,SAAS,EAAE,WAAW,CAAC;CAC1B,CAAC;AACF,MAAM,MAAM,OAAO,GAAG,QAAQ,EAAE,CAAC;AAEjC,MAAM,MAAM,iBAAiB,GAAG,oBAAoB,CAAC,OAAO,aAAa,CAAC,yBAAyB,CAAC,CAAC;AACrG,MAAM,MAAM,gBAAgB,GAAG,iBAAiB,EAAE,CAAC;AAUnD,aAAK,YAAY;IACb,IAAI,IAAI;CACX;AAED,MAAM,MAAM,yBAAyB,GAAG;IACpC,eAAe,EAAE,OAAO,CAAC;IACzB,QAAQ,EAAE,aAAa,CAAC,0BAA0B,GAAG,YAAY,CAAC;IAClE,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,WAAW,EAAE,WAAW,CAAC;CAC5B,CAAC;AAEF,qBAAa,iBAAkB,SAAQ,mBAAmB;gBAC1C,OAAO,CAAC,EAAE,MAAM;CAG/B;AAED;;GAEG;AACH,qBAAa,oBAAoB;;gBAWzB,OAAO,GAAE,aAAa,CAAC,kBAAkB,EAAO,EAChD,yBAAyB,CAAC,EAAE,CACxB,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,QAAQ,EAClB,WAAW,EAAE,yBAAyB,EACtC,QAAQ,EAAE,iBAAiB,EAC3B,SAAS,EAAE,SAAS,KACnB,OAAO;IAQhB;;OAEG;IACH,uBAAuB,CAAC,OAAO,GAAE,aAAa,CAAC,kBAAkB,EAAO,GAAG,IAAI;IAyD/E;;OAEG;IACH,eAAe,CACX,OAAO,EAAE,aAAa,CAAC,GAAG,CAAC,EAC3B,QAAQ,EAAE,iBAAiB,EAC3B,SAAS,EAAE,SAAS,EACpB,SAAS,EAAE,WAAW,GACvB,OAAO;IAoBV;;OAEG;IACH,oBAAoB,CAChB,OAAO,EAAE,aAAa,CAAC,GAAG,CAAC,EAC3B,QAAQ,EAAE,iBAAiB,EAC3B,SAAS,EAAE,SAAS,GACrB,WAAW,EAAE;CA8KnB"}
|
|
@@ -99,10 +99,14 @@ class AccessControlManager {
|
|
|
99
99
|
return true;
|
|
100
100
|
}
|
|
101
101
|
logger.notice(
|
|
102
|
-
`Failed access control check for ${endpoint.number}
|
|
102
|
+
`Failed access control check for ${endpoint.number}/0x${toHex(clusterId)} and fabricIndex ${session.associatedFabric.fabricIndex}, acl=`,
|
|
103
103
|
this.#getAccessControlEntriesForFabric(session.associatedFabric),
|
|
104
|
+
"with ISD=",
|
|
105
|
+
this.#getIsdFromMessage(session),
|
|
104
106
|
"granted privileges=",
|
|
105
|
-
grantedPrivileges
|
|
107
|
+
grantedPrivileges,
|
|
108
|
+
"not contains",
|
|
109
|
+
privilege
|
|
106
110
|
);
|
|
107
111
|
return false;
|
|
108
112
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/protocol/interaction/AccessControlManager.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * @license\n * Copyright 2022-2023 Project CHIP Authors\n * SPDX-License-Identifier: Apache-2.0\n */\nimport { AccessLevel } from \"../../cluster/Cluster.js\";\nimport { AccessControl } from \"../../cluster/definitions/AccessControlCluster.js\";\nimport { MatterFlowError } from \"../../common/MatterError.js\";\nimport { CaseAuthenticatedTag } from \"../../datatype/CaseAuthenticatedTag.js\";\nimport { ClusterId } from \"../../datatype/ClusterId.js\";\nimport { FabricIndex } from \"../../datatype/FabricIndex.js\";\nimport { NodeId } from \"../../datatype/NodeId.js\";\nimport { EndpointInterface } from \"../../endpoint/EndpointInterface.js\";\nimport { Fabric } from \"../../fabric/Fabric.js\";\nimport { Logger } from \"../../log/Logger.js\";\nimport { TypeFromBitmapSchema } from \"../../schema/BitmapSchema.js\";\nimport { SecureSession } from \"../../session/SecureSession.js\";\nimport { toHex } from \"../../util/Number.js\";\nimport { StatusCode, StatusResponseError } from \"./StatusCode.js\";\n\nconst logger = Logger.get(\"AccessControlManager\");\n\nexport type AclEntry = Omit<TypeFromBitmapSchema<typeof AccessControl.TlvAccessControlEntry>, \"privilege\"> & {\n privilege: AccessLevel;\n};\nexport type AclList = AclEntry[];\n\nexport type AclExtensionEntry = TypeFromBitmapSchema<typeof AccessControl.TlvAccessControlExtension>;\nexport type AclExtensionList = AclExtensionEntry[];\n\nconst ImplicitDefaultPaseAclEntry: AclEntry = {\n fabricIndex: FabricIndex.NO_FABRIC, // not fabric-specific\n privilege: AccessLevel.Administer,\n authMode: AccessControl.AccessControlEntryAuthMode.Pase,\n subjects: [],\n targets: [], // entire node\n};\n\nenum AuthModeNone {\n None = 0,\n}\n\nexport type IncomingSubjectDescriptor = {\n isCommissioning: boolean;\n authMode: AccessControl.AccessControlEntryAuthMode | AuthModeNone;\n subjects: NodeId[];\n fabricIndex: FabricIndex;\n};\n\nexport class AccessDeniedError extends StatusResponseError {\n constructor(message?: string) {\n super(message ?? \"Unauthorized\", StatusCode.UnsupportedAccess);\n }\n}\n\n/**\n * Implements Access Control Logic as per Matter Specification @see {@link MatterSpecification.v12.Core} \u00A7 6.6.5.2.\n */\nexport class AccessControlManager {\n #aclList: AclList;\n #extensionEntryAccessCheck: (\n aclList: AclList,\n aclEntry: AclEntry,\n subjectDesc: IncomingSubjectDescriptor,\n endpoint: EndpointInterface,\n clusterId: ClusterId,\n ) => boolean = () => true;\n\n constructor(\n aclList: AccessControl.AccessControlEntry[] = [],\n extensionEntryAccessCheck?: (\n aclList: AclList,\n aclEntry: AclEntry,\n subjectDesc: IncomingSubjectDescriptor,\n endpoint: EndpointInterface,\n clusterId: ClusterId,\n ) => boolean,\n ) {\n this.#aclList = aclList as unknown as AclList; // It is the same structure we just use an internal type for privilege\n if (extensionEntryAccessCheck !== undefined) {\n this.#extensionEntryAccessCheck = extensionEntryAccessCheck;\n }\n }\n\n /**\n * Public method used to update the Access Control List on changes.\n */\n updateAccessControlList(aclList: AccessControl.AccessControlEntry[] = []): void {\n this.#aclList = [...aclList] as unknown as AclList; // It is the same structure we just use an internal type for privilege\n }\n\n /**\n * Get the Access Control List for a given fabric.\n */\n #getAccessControlEntriesForFabric(fabric: Fabric): AclList {\n return this.#aclList.filter(entry => entry.fabricIndex === fabric.fabricIndex);\n }\n\n /**\n * Subjects must match exactly, or both are CAT with matching CAT ID and acceptable CAT version\n */\n #subjectMatches(aclSubject: NodeId, isdSubject: NodeId): boolean {\n if (aclSubject === isdSubject) {\n return true;\n }\n if (!NodeId.isCaseAuthenticatedTag(aclSubject) || !NodeId.isCaseAuthenticatedTag(isdSubject)) {\n return false;\n }\n const aclSubjectCat = NodeId.extractAsCaseAuthenticatedTag(aclSubject);\n const isdSubjectCat = NodeId.extractAsCaseAuthenticatedTag(isdSubject);\n return (\n CaseAuthenticatedTag.getIdentifyValue(aclSubjectCat) ===\n CaseAuthenticatedTag.getIdentifyValue(isdSubjectCat) &&\n CaseAuthenticatedTag.getVersion(isdSubjectCat) >= CaseAuthenticatedTag.getVersion(aclSubjectCat)\n );\n }\n\n /**\n * Add the new privilege to the granted privileges set and also add any privileges subsumed by the new privilege.\n */\n #addGrantedPrivilege(grantedPrivileges: Set<AccessLevel>, privilege: AccessLevel): void {\n // Add the new privilege to the granted privileges set\n grantedPrivileges.add(privilege);\n // Also add any privileges subsumed by the new privilege\n switch (privilege) {\n case AccessLevel.ProxyView:\n grantedPrivileges.add(AccessLevel.View);\n break;\n case AccessLevel.Operate:\n grantedPrivileges.add(AccessLevel.View);\n break;\n case AccessLevel.Manage:\n grantedPrivileges.add(AccessLevel.Operate);\n grantedPrivileges.add(AccessLevel.View);\n break;\n case AccessLevel.Administer:\n grantedPrivileges.add(AccessLevel.Manage);\n grantedPrivileges.add(AccessLevel.Operate);\n grantedPrivileges.add(AccessLevel.ProxyView);\n grantedPrivileges.add(AccessLevel.View);\n break;\n }\n }\n\n /**\n * Check if the given ACL entry is allowed to be used for the given subject descriptor, endpoint, and cluster ID.\n */\n allowsPrivilege(\n session: SecureSession<any>,\n endpoint: EndpointInterface,\n clusterId: ClusterId,\n privilege: AccessLevel,\n ): boolean {\n const grantedPrivileges = this.getGrantedPrivileges(session, endpoint, clusterId);\n if (grantedPrivileges.includes(privilege)) {\n return true;\n }\n\n logger.notice(\n `Failed access control check for ${endpoint.number}/${toHex(clusterId)} and fabricIndex ${session.associatedFabric.fabricIndex}, acl=`,\n this.#getAccessControlEntriesForFabric(session.associatedFabric),\n \"granted privileges=\",\n grantedPrivileges,\n );\n\n return false;\n }\n\n /**\n * Determines the granted privileges for the given session, endpoint, and cluster ID and returns them.\n */\n getGrantedPrivileges(\n session: SecureSession<any>,\n endpoint: EndpointInterface,\n clusterId: ClusterId,\n ): AccessLevel[] {\n const endpointId = endpoint.number;\n const fabric = session.fabric;\n const subjectDesc = this.#getIsdFromMessage(session);\n const acl = fabric ? this.#getAccessControlEntriesForFabric(fabric) : [ImplicitDefaultPaseAclEntry];\n\n // Granted privileges set is initially empty\n const grantedPrivileges = new Set<AccessLevel>();\n\n // PASE commissioning channel implicitly grants administer privilege to commissioner\n if (subjectDesc.authMode === AccessControl.AccessControlEntryAuthMode.Pase && subjectDesc.isCommissioning) {\n this.#addGrantedPrivilege(grantedPrivileges, AccessLevel.Administer);\n }\n\n for (const aclEntry of acl) {\n if (grantedPrivileges.has(AccessLevel.Administer)) {\n // End checking if highest privilege is granted\n break;\n }\n\n // Fabric index must match, there are no valid entries with FabricIndex == 0\n // other than the implicit PASE entry, which we will not see explicitly in the\n // access control list\n if (aclEntry.fabricIndex === FabricIndex.NO_FABRIC || aclEntry.fabricIndex !== subjectDesc.fabricIndex) {\n logger.debug(\n \"Skipping ACL entry with mismatched fabric index\",\n aclEntry.fabricIndex,\n subjectDesc.fabricIndex,\n );\n continue;\n }\n\n // Auth mode must match\n if (aclEntry.authMode !== subjectDesc.authMode) {\n logger.debug(\"Skipping ACL entry with mismatched auth mode\", aclEntry.authMode, subjectDesc.authMode);\n continue;\n }\n\n // Subject must match, or be \"wildcard\"\n if (aclEntry.subjects === null || aclEntry.subjects.length === 0) {\n // Precondition: only CASE and Group auth can have empty subjects\n if (\n aclEntry.authMode !== AccessControl.AccessControlEntryAuthMode.Case &&\n aclEntry.authMode !== AccessControl.AccessControlEntryAuthMode.Group\n ) {\n throw new MatterFlowError(\"ACL error: only CASE and Group auth can have empty subjects\");\n }\n // ... Empty is wildcard, no match required\n } else {\n // Non-empty requires a match\n let matchedSubject = false;\n subjectLoop: for (const aclSubject of aclEntry.subjects) {\n for (const isdSubject of subjectDesc.subjects) {\n if (this.#subjectMatches(aclSubject, isdSubject)) {\n matchedSubject = true;\n break subjectLoop;\n }\n }\n }\n if (!matchedSubject) {\n continue;\n }\n }\n\n // Target must match, or be \"wildcard\"\n if (aclEntry.targets === null || aclEntry.targets.length === 0) {\n // Empty is wildcard, no match required\n } else {\n // Non-empty requires a match\n let matchedTarget = false;\n for (const {\n cluster: targetClusterId,\n endpoint: targetEndpointId,\n deviceType: targetDeviceType,\n } of aclEntry.targets) {\n // Precondition: target cannot be empty\n if (targetClusterId === null && targetEndpointId === null && targetDeviceType === null) {\n throw new MatterFlowError(\"ACL error: target cannot be empty\");\n }\n // Precondition: target cannot specify both endpoint and device type\n if (targetEndpointId !== null && targetDeviceType !== null) {\n throw new MatterFlowError(\"ACL error: target cannot specify both endpoint and device type\");\n }\n // Cluster must match, or be wildcard\n if (targetClusterId !== null && targetClusterId !== clusterId) {\n continue;\n }\n // Endpoint must match, or be wildcard\n if (targetEndpointId !== null && targetEndpointId !== endpointId) {\n continue;\n }\n // Endpoint may be specified indirectly via device type\n // TODO adjust to array check once we use multiple devicetypes\n if (targetDeviceType !== null && endpoint.deviceType !== targetDeviceType) {\n continue;\n }\n matchedTarget = true;\n break;\n }\n if (!matchedTarget) {\n continue;\n }\n }\n\n // Extensions processing must not fail\n if (!this.#extensionEntryAccessCheck(acl, aclEntry, subjectDesc, endpoint, clusterId)) {\n continue;\n }\n\n // All checks have passed, add privilege to granted privilege set\n this.#addGrantedPrivilege(grantedPrivileges, aclEntry.privilege);\n }\n // Should never grant Administer privilege to a Group.\n if (\n subjectDesc.authMode === AccessControl.AccessControlEntryAuthMode.Group &&\n grantedPrivileges.has(AccessLevel.Administer)\n ) {\n throw new MatterFlowError(\"ACL error: should never grant Administer privilege to a Group\");\n }\n\n return [...grantedPrivileges];\n }\n\n /**\n * Determines the Incoming Subject Descriptor (ISD) from the given session.\n */\n #getIsdFromMessage(session: SecureSession<any>) {\n const fabric = session.fabric;\n const isd: IncomingSubjectDescriptor = {\n isCommissioning: false,\n authMode: AuthModeNone.None,\n subjects: new Array<NodeId>(),\n fabricIndex: FabricIndex.NO_FABRIC,\n };\n\n if (session.isPase) {\n isd.authMode = AccessControl.AccessControlEntryAuthMode.Pase;\n isd.isCommissioning = true; // Or how \"commissioning channel\" is defined?\n isd.subjects.push(NodeId(0)); // Default Commissioning Passcode ID\n if (fabric) {\n isd.fabricIndex = fabric.fabricIndex;\n }\n } else {\n // TODO Add Group session handling when implementing groups\n // if (session instanceof SecureGroupSession) {\n // Groups\n // # Message is assumed to have been decrypted and matched properly prior to\n // # this procedure occurring.\n // group_id = message.get_dst_group_id()\n // group_key_id = sessions_metadata.get_group_key_id(message)\n // # Group membership must be verified against Group Key Management Cluster\n // if group_key_management_cluster.group_key_map_has_mapping(group_id, group_key_id):\n // isd.AuthMode = AuthModeEnum.Group\n // isd.Subjects.append(group_id)\n // isd.FabricIndex = sessions_metadata.get_fabric_index(message)\n // assert(isd.FabricIndex != 0) # cannot be zero\n //\n // isd.authMode = AccessControl.AccessControlEntryAuthMode.Group;\n // } else {\n\n // CASE session\n isd.authMode = AccessControl.AccessControlEntryAuthMode.Case;\n isd.subjects.push(session.peerNodeId);\n // Append CASE session CATs which also serve as subjects\n session.caseAuthenticatedTags.forEach(cat => isd.subjects.push(NodeId.fromCaseAuthenticatedTag(cat)));\n // }\n if (fabric === undefined) {\n throw new MatterFlowError(\"ACL error: fabric is undefined\");\n }\n isd.fabricIndex = fabric.fabricIndex;\n }\n\n return isd;\n }\n}\n"],
|
|
5
|
-
"mappings": "AAAA;AAAA;AAAA;AAAA;AAAA;AAKA,SAAS,mBAAmB;AAC5B,SAAS,qBAAqB;AAC9B,SAAS,uBAAuB;AAChC,SAAS,4BAA4B;AAErC,SAAS,mBAAmB;AAC5B,SAAS,cAAc;AAGvB,SAAS,cAAc;AAGvB,SAAS,aAAa;AACtB,SAAS,YAAY,2BAA2B;AAEhD,MAAM,SAAS,OAAO,IAAI,sBAAsB;AAUhD,MAAM,8BAAwC;AAAA,EAC1C,aAAa,YAAY;AAAA;AAAA,EACzB,WAAW,YAAY;AAAA,EACvB,UAAU,cAAc,2BAA2B;AAAA,EACnD,UAAU,CAAC;AAAA,EACX,SAAS,CAAC;AAAA;AACd;AAEA,IAAK,eAAL,kBAAKA,kBAAL;AACI,EAAAA,4BAAA,UAAO,KAAP;AADC,SAAAA;AAAA,GAAA;AAWE,MAAM,0BAA0B,oBAAoB;AAAA,EACvD,YAAY,SAAkB;AAC1B,UAAM,WAAW,gBAAgB,WAAW,iBAAiB;AAAA,EACjE;AACJ;AAKO,MAAM,qBAAqB;AAAA,EAC9B;AAAA,EACA,6BAMe,MAAM;AAAA,EAErB,YACI,UAA8C,CAAC,GAC/C,2BAOF;AACE,SAAK,WAAW;AAChB,QAAI,8BAA8B,QAAW;AACzC,WAAK,6BAA6B;AAAA,IACtC;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAKA,wBAAwB,UAA8C,CAAC,GAAS;AAC5E,SAAK,WAAW,CAAC,GAAG,OAAO;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA,EAKA,kCAAkC,QAAyB;AACvD,WAAO,KAAK,SAAS,OAAO,WAAS,MAAM,gBAAgB,OAAO,WAAW;AAAA,EACjF;AAAA;AAAA;AAAA;AAAA,EAKA,gBAAgB,YAAoB,YAA6B;AAC7D,QAAI,eAAe,YAAY;AAC3B,aAAO;AAAA,IACX;AACA,QAAI,CAAC,OAAO,uBAAuB,UAAU,KAAK,CAAC,OAAO,uBAAuB,UAAU,GAAG;AAC1F,aAAO;AAAA,IACX;AACA,UAAM,gBAAgB,OAAO,8BAA8B,UAAU;AACrE,UAAM,gBAAgB,OAAO,8BAA8B,UAAU;AACrE,WACI,qBAAqB,iBAAiB,aAAa,MAC/C,qBAAqB,iBAAiB,aAAa,KACvD,qBAAqB,WAAW,aAAa,KAAK,qBAAqB,WAAW,aAAa;AAAA,EAEvG;AAAA;AAAA;AAAA;AAAA,EAKA,qBAAqB,mBAAqC,WAA8B;AAEpF,sBAAkB,IAAI,SAAS;AAE/B,YAAQ,WAAW;AAAA,MACf,KAAK,YAAY;AACb,0BAAkB,IAAI,YAAY,IAAI;AACtC;AAAA,MACJ,KAAK,YAAY;AACb,0BAAkB,IAAI,YAAY,IAAI;AACtC;AAAA,MACJ,KAAK,YAAY;AACb,0BAAkB,IAAI,YAAY,OAAO;AACzC,0BAAkB,IAAI,YAAY,IAAI;AACtC;AAAA,MACJ,KAAK,YAAY;AACb,0BAAkB,IAAI,YAAY,MAAM;AACxC,0BAAkB,IAAI,YAAY,OAAO;AACzC,0BAAkB,IAAI,YAAY,SAAS;AAC3C,0BAAkB,IAAI,YAAY,IAAI;AACtC;AAAA,IACR;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAKA,gBACI,SACA,UACA,WACA,WACO;AACP,UAAM,oBAAoB,KAAK,qBAAqB,SAAS,UAAU,SAAS;AAChF,QAAI,kBAAkB,SAAS,SAAS,GAAG;AACvC,aAAO;AAAA,IACX;AAEA,WAAO;AAAA,MACH,mCAAmC,SAAS,MAAM,
|
|
4
|
+
"sourcesContent": ["/**\n * @license\n * Copyright 2022-2023 Project CHIP Authors\n * SPDX-License-Identifier: Apache-2.0\n */\nimport { AccessLevel } from \"../../cluster/Cluster.js\";\nimport { AccessControl } from \"../../cluster/definitions/AccessControlCluster.js\";\nimport { MatterFlowError } from \"../../common/MatterError.js\";\nimport { CaseAuthenticatedTag } from \"../../datatype/CaseAuthenticatedTag.js\";\nimport { ClusterId } from \"../../datatype/ClusterId.js\";\nimport { FabricIndex } from \"../../datatype/FabricIndex.js\";\nimport { NodeId } from \"../../datatype/NodeId.js\";\nimport { EndpointInterface } from \"../../endpoint/EndpointInterface.js\";\nimport { Fabric } from \"../../fabric/Fabric.js\";\nimport { Logger } from \"../../log/Logger.js\";\nimport { TypeFromBitmapSchema } from \"../../schema/BitmapSchema.js\";\nimport { SecureSession } from \"../../session/SecureSession.js\";\nimport { toHex } from \"../../util/Number.js\";\nimport { StatusCode, StatusResponseError } from \"./StatusCode.js\";\n\nconst logger = Logger.get(\"AccessControlManager\");\n\nexport type AclEntry = Omit<TypeFromBitmapSchema<typeof AccessControl.TlvAccessControlEntry>, \"privilege\"> & {\n privilege: AccessLevel;\n};\nexport type AclList = AclEntry[];\n\nexport type AclExtensionEntry = TypeFromBitmapSchema<typeof AccessControl.TlvAccessControlExtension>;\nexport type AclExtensionList = AclExtensionEntry[];\n\nconst ImplicitDefaultPaseAclEntry: AclEntry = {\n fabricIndex: FabricIndex.NO_FABRIC, // not fabric-specific\n privilege: AccessLevel.Administer,\n authMode: AccessControl.AccessControlEntryAuthMode.Pase,\n subjects: [],\n targets: [], // entire node\n};\n\nenum AuthModeNone {\n None = 0,\n}\n\nexport type IncomingSubjectDescriptor = {\n isCommissioning: boolean;\n authMode: AccessControl.AccessControlEntryAuthMode | AuthModeNone;\n subjects: NodeId[];\n fabricIndex: FabricIndex;\n};\n\nexport class AccessDeniedError extends StatusResponseError {\n constructor(message?: string) {\n super(message ?? \"Unauthorized\", StatusCode.UnsupportedAccess);\n }\n}\n\n/**\n * Implements Access Control Logic as per Matter Specification @see {@link MatterSpecification.v12.Core} \u00A7 6.6.5.2.\n */\nexport class AccessControlManager {\n #aclList: AclList;\n #extensionEntryAccessCheck: (\n aclList: AclList,\n aclEntry: AclEntry,\n subjectDesc: IncomingSubjectDescriptor,\n endpoint: EndpointInterface,\n clusterId: ClusterId,\n ) => boolean = () => true;\n\n constructor(\n aclList: AccessControl.AccessControlEntry[] = [],\n extensionEntryAccessCheck?: (\n aclList: AclList,\n aclEntry: AclEntry,\n subjectDesc: IncomingSubjectDescriptor,\n endpoint: EndpointInterface,\n clusterId: ClusterId,\n ) => boolean,\n ) {\n this.#aclList = aclList as unknown as AclList; // It is the same structure we just use an internal type for privilege\n if (extensionEntryAccessCheck !== undefined) {\n this.#extensionEntryAccessCheck = extensionEntryAccessCheck;\n }\n }\n\n /**\n * Public method used to update the Access Control List on changes.\n */\n updateAccessControlList(aclList: AccessControl.AccessControlEntry[] = []): void {\n this.#aclList = [...aclList] as unknown as AclList; // It is the same structure we just use an internal type for privilege\n }\n\n /**\n * Get the Access Control List for a given fabric.\n */\n #getAccessControlEntriesForFabric(fabric: Fabric): AclList {\n return this.#aclList.filter(entry => entry.fabricIndex === fabric.fabricIndex);\n }\n\n /**\n * Subjects must match exactly, or both are CAT with matching CAT ID and acceptable CAT version\n */\n #subjectMatches(aclSubject: NodeId, isdSubject: NodeId): boolean {\n if (aclSubject === isdSubject) {\n return true;\n }\n if (!NodeId.isCaseAuthenticatedTag(aclSubject) || !NodeId.isCaseAuthenticatedTag(isdSubject)) {\n return false;\n }\n const aclSubjectCat = NodeId.extractAsCaseAuthenticatedTag(aclSubject);\n const isdSubjectCat = NodeId.extractAsCaseAuthenticatedTag(isdSubject);\n return (\n CaseAuthenticatedTag.getIdentifyValue(aclSubjectCat) ===\n CaseAuthenticatedTag.getIdentifyValue(isdSubjectCat) &&\n CaseAuthenticatedTag.getVersion(isdSubjectCat) >= CaseAuthenticatedTag.getVersion(aclSubjectCat)\n );\n }\n\n /**\n * Add the new privilege to the granted privileges set and also add any privileges subsumed by the new privilege.\n */\n #addGrantedPrivilege(grantedPrivileges: Set<AccessLevel>, privilege: AccessLevel): void {\n // Add the new privilege to the granted privileges set\n grantedPrivileges.add(privilege);\n // Also add any privileges subsumed by the new privilege\n switch (privilege) {\n case AccessLevel.ProxyView:\n grantedPrivileges.add(AccessLevel.View);\n break;\n case AccessLevel.Operate:\n grantedPrivileges.add(AccessLevel.View);\n break;\n case AccessLevel.Manage:\n grantedPrivileges.add(AccessLevel.Operate);\n grantedPrivileges.add(AccessLevel.View);\n break;\n case AccessLevel.Administer:\n grantedPrivileges.add(AccessLevel.Manage);\n grantedPrivileges.add(AccessLevel.Operate);\n grantedPrivileges.add(AccessLevel.ProxyView);\n grantedPrivileges.add(AccessLevel.View);\n break;\n }\n }\n\n /**\n * Check if the given ACL entry is allowed to be used for the given subject descriptor, endpoint, and cluster ID.\n */\n allowsPrivilege(\n session: SecureSession<any>,\n endpoint: EndpointInterface,\n clusterId: ClusterId,\n privilege: AccessLevel,\n ): boolean {\n const grantedPrivileges = this.getGrantedPrivileges(session, endpoint, clusterId);\n if (grantedPrivileges.includes(privilege)) {\n return true;\n }\n\n logger.notice(\n `Failed access control check for ${endpoint.number}/0x${toHex(clusterId)} and fabricIndex ${session.associatedFabric.fabricIndex}, acl=`,\n this.#getAccessControlEntriesForFabric(session.associatedFabric),\n \"with ISD=\",\n this.#getIsdFromMessage(session),\n \"granted privileges=\",\n grantedPrivileges,\n \"not contains\",\n privilege,\n );\n\n return false;\n }\n\n /**\n * Determines the granted privileges for the given session, endpoint, and cluster ID and returns them.\n */\n getGrantedPrivileges(\n session: SecureSession<any>,\n endpoint: EndpointInterface,\n clusterId: ClusterId,\n ): AccessLevel[] {\n const endpointId = endpoint.number;\n const fabric = session.fabric;\n const subjectDesc = this.#getIsdFromMessage(session);\n const acl = fabric ? this.#getAccessControlEntriesForFabric(fabric) : [ImplicitDefaultPaseAclEntry];\n\n // Granted privileges set is initially empty\n const grantedPrivileges = new Set<AccessLevel>();\n\n // PASE commissioning channel implicitly grants administer privilege to commissioner\n if (subjectDesc.authMode === AccessControl.AccessControlEntryAuthMode.Pase && subjectDesc.isCommissioning) {\n this.#addGrantedPrivilege(grantedPrivileges, AccessLevel.Administer);\n }\n\n for (const aclEntry of acl) {\n if (grantedPrivileges.has(AccessLevel.Administer)) {\n // End checking if highest privilege is granted\n break;\n }\n\n // Fabric index must match, there are no valid entries with FabricIndex == 0\n // other than the implicit PASE entry, which we will not see explicitly in the\n // access control list\n if (aclEntry.fabricIndex === FabricIndex.NO_FABRIC || aclEntry.fabricIndex !== subjectDesc.fabricIndex) {\n logger.debug(\n \"Skipping ACL entry with mismatched fabric index\",\n aclEntry.fabricIndex,\n subjectDesc.fabricIndex,\n );\n continue;\n }\n\n // Auth mode must match\n if (aclEntry.authMode !== subjectDesc.authMode) {\n logger.debug(\"Skipping ACL entry with mismatched auth mode\", aclEntry.authMode, subjectDesc.authMode);\n continue;\n }\n\n // Subject must match, or be \"wildcard\"\n if (aclEntry.subjects === null || aclEntry.subjects.length === 0) {\n // Precondition: only CASE and Group auth can have empty subjects\n if (\n aclEntry.authMode !== AccessControl.AccessControlEntryAuthMode.Case &&\n aclEntry.authMode !== AccessControl.AccessControlEntryAuthMode.Group\n ) {\n throw new MatterFlowError(\"ACL error: only CASE and Group auth can have empty subjects\");\n }\n // ... Empty is wildcard, no match required\n } else {\n // Non-empty requires a match\n let matchedSubject = false;\n subjectLoop: for (const aclSubject of aclEntry.subjects) {\n for (const isdSubject of subjectDesc.subjects) {\n if (this.#subjectMatches(aclSubject, isdSubject)) {\n matchedSubject = true;\n break subjectLoop;\n }\n }\n }\n if (!matchedSubject) {\n continue;\n }\n }\n\n // Target must match, or be \"wildcard\"\n if (aclEntry.targets === null || aclEntry.targets.length === 0) {\n // Empty is wildcard, no match required\n } else {\n // Non-empty requires a match\n let matchedTarget = false;\n for (const {\n cluster: targetClusterId,\n endpoint: targetEndpointId,\n deviceType: targetDeviceType,\n } of aclEntry.targets) {\n // Precondition: target cannot be empty\n if (targetClusterId === null && targetEndpointId === null && targetDeviceType === null) {\n throw new MatterFlowError(\"ACL error: target cannot be empty\");\n }\n // Precondition: target cannot specify both endpoint and device type\n if (targetEndpointId !== null && targetDeviceType !== null) {\n throw new MatterFlowError(\"ACL error: target cannot specify both endpoint and device type\");\n }\n // Cluster must match, or be wildcard\n if (targetClusterId !== null && targetClusterId !== clusterId) {\n continue;\n }\n // Endpoint must match, or be wildcard\n if (targetEndpointId !== null && targetEndpointId !== endpointId) {\n continue;\n }\n // Endpoint may be specified indirectly via device type\n // TODO adjust to array check once we use multiple devicetypes\n if (targetDeviceType !== null && endpoint.deviceType !== targetDeviceType) {\n continue;\n }\n matchedTarget = true;\n break;\n }\n if (!matchedTarget) {\n continue;\n }\n }\n\n // Extensions processing must not fail\n if (!this.#extensionEntryAccessCheck(acl, aclEntry, subjectDesc, endpoint, clusterId)) {\n continue;\n }\n\n // All checks have passed, add privilege to granted privilege set\n this.#addGrantedPrivilege(grantedPrivileges, aclEntry.privilege);\n }\n // Should never grant Administer privilege to a Group.\n if (\n subjectDesc.authMode === AccessControl.AccessControlEntryAuthMode.Group &&\n grantedPrivileges.has(AccessLevel.Administer)\n ) {\n throw new MatterFlowError(\"ACL error: should never grant Administer privilege to a Group\");\n }\n\n return [...grantedPrivileges];\n }\n\n /**\n * Determines the Incoming Subject Descriptor (ISD) from the given session.\n */\n #getIsdFromMessage(session: SecureSession<any>) {\n const fabric = session.fabric;\n const isd: IncomingSubjectDescriptor = {\n isCommissioning: false,\n authMode: AuthModeNone.None,\n subjects: new Array<NodeId>(),\n fabricIndex: FabricIndex.NO_FABRIC,\n };\n\n if (session.isPase) {\n isd.authMode = AccessControl.AccessControlEntryAuthMode.Pase;\n isd.isCommissioning = true; // Or how \"commissioning channel\" is defined?\n isd.subjects.push(NodeId(0)); // Default Commissioning Passcode ID\n if (fabric) {\n isd.fabricIndex = fabric.fabricIndex;\n }\n } else {\n // TODO Add Group session handling when implementing groups\n // if (session instanceof SecureGroupSession) {\n // Groups\n // # Message is assumed to have been decrypted and matched properly prior to\n // # this procedure occurring.\n // group_id = message.get_dst_group_id()\n // group_key_id = sessions_metadata.get_group_key_id(message)\n // # Group membership must be verified against Group Key Management Cluster\n // if group_key_management_cluster.group_key_map_has_mapping(group_id, group_key_id):\n // isd.AuthMode = AuthModeEnum.Group\n // isd.Subjects.append(group_id)\n // isd.FabricIndex = sessions_metadata.get_fabric_index(message)\n // assert(isd.FabricIndex != 0) # cannot be zero\n //\n // isd.authMode = AccessControl.AccessControlEntryAuthMode.Group;\n // } else {\n\n // CASE session\n isd.authMode = AccessControl.AccessControlEntryAuthMode.Case;\n isd.subjects.push(session.peerNodeId);\n // Append CASE session CATs which also serve as subjects\n session.caseAuthenticatedTags.forEach(cat => isd.subjects.push(NodeId.fromCaseAuthenticatedTag(cat)));\n // }\n if (fabric === undefined) {\n throw new MatterFlowError(\"ACL error: fabric is undefined\");\n }\n isd.fabricIndex = fabric.fabricIndex;\n }\n\n return isd;\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["AuthModeNone"]
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CaseServer.d.ts","sourceRoot":"","sources":["../../../../src/session/case/CaseServer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAQrD,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAqBpE,qBAAa,UAAW,YAAW,eAAe,CAAC,YAAY,CAAC;IACtD,aAAa,CAAC,QAAQ,EAAE,eAAe,CAAC,YAAY,CAAC;IAoB3D,KAAK,IAAI,MAAM;YAID,YAAY;
|
|
1
|
+
{"version":3,"file":"CaseServer.d.ts","sourceRoot":"","sources":["../../../../src/session/case/CaseServer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAQrD,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAqBpE,qBAAa,UAAW,YAAW,eAAe,CAAC,YAAY,CAAC;IACtD,aAAa,CAAC,QAAQ,EAAE,eAAe,CAAC,YAAY,CAAC;IAoB3D,KAAK,IAAI,MAAM;YAID,YAAY;IA4MpB,KAAK;CAGd"}
|
|
@@ -102,7 +102,9 @@ class CaseServer {
|
|
|
102
102
|
logger.info(
|
|
103
103
|
`session ${secureSession.id} resumed with ${messenger.getChannelName()} for Fabric ${NodeId.toHexString(
|
|
104
104
|
fabric.nodeId
|
|
105
|
-
)}(index ${fabric.fabricIndex}) and PeerNode ${NodeId.toHexString(peerNodeId)}
|
|
105
|
+
)}(index ${fabric.fabricIndex}) and PeerNode ${NodeId.toHexString(peerNodeId)}`,
|
|
106
|
+
"with CATs",
|
|
107
|
+
caseAuthenticatedTags
|
|
106
108
|
);
|
|
107
109
|
resumptionRecord.resumptionId = resumptionId;
|
|
108
110
|
await messenger.waitForSuccess("Success after CASE Sigma2Resume");
|
|
@@ -191,7 +193,9 @@ class CaseServer {
|
|
|
191
193
|
logger.info(
|
|
192
194
|
`session ${secureSession.id} created with ${messenger.getChannelName()} for Fabric ${NodeId.toHexString(
|
|
193
195
|
fabric.nodeId
|
|
194
|
-
)}(index ${fabric.fabricIndex}) and PeerNode ${NodeId.toHexString(peerNodeId)}
|
|
196
|
+
)}(index ${fabric.fabricIndex}) and PeerNode ${NodeId.toHexString(peerNodeId)}`,
|
|
197
|
+
"with CATs",
|
|
198
|
+
caseAuthenticatedTags
|
|
195
199
|
);
|
|
196
200
|
await messenger.sendSuccess();
|
|
197
201
|
const resumptionRecord2 = {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/session/case/CaseServer.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * @license\n * Copyright 2022-2024 Matter.js Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { MatterDevice } from \"../../MatterDevice.js\";\nimport { TlvOperationalCertificate } from \"../../certificate/CertificateManager.js\";\nimport { UnexpectedDataError } from \"../../common/MatterError.js\";\nimport { Crypto } from \"../../crypto/Crypto.js\";\nimport { PublicKey } from \"../../crypto/Key.js\";\nimport { NodeId } from \"../../datatype/NodeId.js\";\nimport { FabricNotFoundError } from \"../../fabric/FabricManager.js\";\nimport { Logger } from \"../../log/Logger.js\";\nimport { MessageExchange } from \"../../protocol/MessageExchange.js\";\nimport { ProtocolHandler } from \"../../protocol/ProtocolHandler.js\";\nimport { ProtocolStatusCode, SECURE_CHANNEL_PROTOCOL_ID } from \"../../protocol/securechannel/SecureChannelMessages.js\";\nimport { ChannelStatusResponseError } from \"../../protocol/securechannel/SecureChannelMessenger.js\";\nimport { ByteArray } from \"../../util/ByteArray.js\";\nimport {\n KDFSR1_KEY_INFO,\n KDFSR2_INFO,\n KDFSR2_KEY_INFO,\n KDFSR3_INFO,\n RESUME1_MIC_NONCE,\n RESUME2_MIC_NONCE,\n TBE_DATA2_NONCE,\n TBE_DATA3_NONCE,\n TlvEncryptedDataSigma2,\n TlvEncryptedDataSigma3,\n TlvSignedData,\n} from \"./CaseMessages.js\";\nimport { CaseServerMessenger } from \"./CaseMessenger.js\";\n\nconst logger = Logger.get(\"CaseServer\");\n\nexport class CaseServer implements ProtocolHandler<MatterDevice> {\n async onNewExchange(exchange: MessageExchange<MatterDevice>) {\n const messenger = new CaseServerMessenger(exchange);\n try {\n await this.handleSigma1(exchange.session.context, messenger);\n } catch (error) {\n logger.error(\"An error occurred during the commissioning\", error);\n\n if (error instanceof FabricNotFoundError) {\n await messenger.sendError(ProtocolStatusCode.NoSharedTrustRoots);\n }\n // If we received a ChannelStatusResponseError we do not need to send one back, so just cancel pairing\n else if (!(error instanceof ChannelStatusResponseError)) {\n await messenger.sendError(ProtocolStatusCode.InvalidParam);\n }\n } finally {\n // Destroy the unsecure session used to establish the secure Case session\n await exchange.session.destroy();\n }\n }\n\n getId(): number {\n return SECURE_CHANNEL_PROTOCOL_ID;\n }\n\n private async handleSigma1(server: MatterDevice, messenger: CaseServerMessenger) {\n logger.info(`Received pairing request from ${messenger.getChannelName()}`);\n // Generate pairing info\n const responderRandom = Crypto.getRandom();\n\n // Read and process sigma 1\n const { sigma1Bytes, sigma1 } = await messenger.readSigma1();\n const {\n initiatorSessionId: peerSessionId,\n resumptionId: peerResumptionId,\n initiatorResumeMic: peerResumeMic,\n destinationId,\n initiatorRandom: peerRandom,\n initiatorEcdhPublicKey: peerEcdhPublicKey,\n initiatorSessionParams,\n } = sigma1;\n\n // Try to resume a previous session\n const resumptionId = Crypto.getRandomData(16);\n\n const resumptionRecord =\n peerResumptionId !== undefined && peerResumeMic !== undefined\n ? server.findResumptionRecordById(peerResumptionId)\n : undefined;\n // We try to resume the session\n if (peerResumptionId !== undefined && peerResumeMic !== undefined && resumptionRecord !== undefined) {\n const { sharedSecret, fabric, peerNodeId, caseAuthenticatedTags } = resumptionRecord;\n const peerResumeKey = await Crypto.hkdf(\n sharedSecret,\n ByteArray.concat(peerRandom, peerResumptionId),\n KDFSR1_KEY_INFO,\n );\n Crypto.decrypt(peerResumeKey, peerResumeMic, RESUME1_MIC_NONCE);\n\n // All good! Create secure session\n const responderSessionId = await server.getNextAvailableSessionId();\n const secureSessionSalt = ByteArray.concat(peerRandom, peerResumptionId);\n const secureSession = await server.sessionManager.createSecureSession({\n sessionId: responderSessionId,\n fabric,\n peerNodeId,\n peerSessionId,\n sharedSecret,\n salt: secureSessionSalt,\n isInitiator: false,\n isResumption: true,\n peerSessionParameters: initiatorSessionParams,\n caseAuthenticatedTags,\n });\n\n // Generate sigma 2 resume\n const resumeSalt = ByteArray.concat(peerRandom, resumptionId);\n const resumeKey = await Crypto.hkdf(sharedSecret, resumeSalt, KDFSR2_KEY_INFO);\n const resumeMic = Crypto.encrypt(resumeKey, new ByteArray(0), RESUME2_MIC_NONCE);\n try {\n await messenger.sendSigma2Resume({\n resumptionId,\n resumeMic,\n responderSessionId,\n responderSessionParams: server.sessionParameters, // responder session parameters\n });\n } catch (error) {\n // If we fail to send the resume, we destroy the session\n await secureSession.destroy(false);\n throw error;\n }\n\n logger.info(\n `session ${secureSession.id} resumed with ${messenger.getChannelName()} for Fabric ${NodeId.toHexString(\n fabric.nodeId,\n )}(index ${fabric.fabricIndex}) and PeerNode ${NodeId.toHexString(peerNodeId)}`,\n );\n resumptionRecord.resumptionId = resumptionId; /* Update the ID */\n\n // Wait for success on the peer side\n await messenger.waitForSuccess(\"Success after CASE Sigma2Resume\");\n\n await messenger.close();\n await server.saveResumptionRecord(resumptionRecord);\n } else if (\n (peerResumptionId === undefined && peerResumeMic === undefined) ||\n (peerResumptionId !== undefined && peerResumeMic !== undefined && resumptionRecord === undefined)\n ) {\n // Generate sigma 2\n // TODO: Pass through a group id?\n const fabric = server.findFabricFromDestinationId(destinationId, peerRandom);\n const { operationalCert: nodeOpCert, intermediateCACert, operationalIdentityProtectionKey } = fabric;\n const { publicKey: responderEcdhPublicKey, sharedSecret } =\n Crypto.ecdhGeneratePublicKeyAndSecret(peerEcdhPublicKey);\n const sigma2Salt = ByteArray.concat(\n operationalIdentityProtectionKey,\n responderRandom,\n responderEcdhPublicKey,\n Crypto.hash(sigma1Bytes),\n );\n const sigma2Key = await Crypto.hkdf(sharedSecret, sigma2Salt, KDFSR2_INFO);\n const signatureData = TlvSignedData.encode({\n nodeOpCert,\n intermediateCACert,\n ecdhPublicKey: responderEcdhPublicKey,\n peerEcdhPublicKey,\n });\n const signature = fabric.sign(signatureData);\n const encryptedData = TlvEncryptedDataSigma2.encode({\n nodeOpCert,\n intermediateCACert,\n signature,\n resumptionId,\n });\n const encrypted = Crypto.encrypt(sigma2Key, encryptedData, TBE_DATA2_NONCE);\n const responderSessionId = await server.getNextAvailableSessionId();\n const sigma2Bytes = await messenger.sendSigma2({\n responderRandom,\n responderSessionId,\n responderEcdhPublicKey,\n encrypted,\n responderSessionParams: server.sessionParameters, // responder session parameters\n });\n\n // Read and process sigma 3\n const {\n sigma3Bytes,\n sigma3: { encrypted: peerEncrypted },\n } = await messenger.readSigma3();\n const sigma3Salt = ByteArray.concat(\n operationalIdentityProtectionKey,\n Crypto.hash([sigma1Bytes, sigma2Bytes]),\n );\n const sigma3Key = await Crypto.hkdf(sharedSecret, sigma3Salt, KDFSR3_INFO);\n const peerDecryptedData = Crypto.decrypt(sigma3Key, peerEncrypted, TBE_DATA3_NONCE);\n const {\n nodeOpCert: peerNewOpCert,\n intermediateCACert: peerIntermediateCACert,\n signature: peerSignature,\n } = TlvEncryptedDataSigma3.decode(peerDecryptedData);\n\n fabric.verifyCredentials(peerNewOpCert, peerIntermediateCACert);\n\n const peerSignatureData = TlvSignedData.encode({\n nodeOpCert: peerNewOpCert,\n intermediateCACert: peerIntermediateCACert,\n ecdhPublicKey: peerEcdhPublicKey,\n peerEcdhPublicKey: responderEcdhPublicKey,\n });\n const {\n ellipticCurvePublicKey: peerPublicKey,\n subject: { fabricId: peerFabricId, nodeId: peerNodeId, caseAuthenticatedTags },\n } = TlvOperationalCertificate.decode(peerNewOpCert);\n\n if (fabric.fabricId !== peerFabricId) {\n throw new UnexpectedDataError(`Fabric ID mismatch: ${fabric.fabricId} !== ${peerFabricId}`);\n }\n\n Crypto.verify(PublicKey(peerPublicKey), peerSignatureData, peerSignature);\n\n // All good! Create secure session\n const secureSessionSalt = ByteArray.concat(\n operationalIdentityProtectionKey,\n Crypto.hash([sigma1Bytes, sigma2Bytes, sigma3Bytes]),\n );\n const secureSession = await server.sessionManager.createSecureSession({\n sessionId: responderSessionId,\n fabric,\n peerNodeId,\n peerSessionId,\n sharedSecret,\n salt: secureSessionSalt,\n isInitiator: false,\n isResumption: false,\n peerSessionParameters: initiatorSessionParams,\n caseAuthenticatedTags,\n });\n logger.info(\n `session ${secureSession.id} created with ${messenger.getChannelName()} for Fabric ${NodeId.toHexString(\n fabric.nodeId,\n )}(index ${fabric.fabricIndex}) and PeerNode ${NodeId.toHexString(peerNodeId)}`,\n );\n await messenger.sendSuccess();\n\n const resumptionRecord = {\n peerNodeId,\n fabric,\n sharedSecret,\n resumptionId,\n sessionParameters: secureSession.parameters,\n caseAuthenticatedTags,\n };\n\n await messenger.close();\n await server.saveResumptionRecord(resumptionRecord);\n } else {\n logger.info(\n `Invalid resumption ID or resume MIC received from ${messenger.getChannelName()}`,\n peerResumptionId,\n peerResumeMic,\n );\n throw new UnexpectedDataError(\"Invalid resumption ID or resume MIC.\");\n }\n }\n\n async close() {\n // Nothing to do\n }\n}\n"],
|
|
5
|
-
"mappings": "AAAA;AAAA;AAAA;AAAA;AAAA;AAOA,SAAS,iCAAiC;AAC1C,SAAS,2BAA2B;AACpC,SAAS,cAAc;AACvB,SAAS,iBAAiB;AAC1B,SAAS,cAAc;AACvB,SAAS,2BAA2B;AACpC,SAAS,cAAc;AAGvB,SAAS,oBAAoB,kCAAkC;AAC/D,SAAS,kCAAkC;AAC3C,SAAS,iBAAiB;AAC1B;AAAA,EACI;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACG;AACP,SAAS,2BAA2B;AAEpC,MAAM,SAAS,OAAO,IAAI,YAAY;AAE/B,MAAM,WAAoD;AAAA,EAC7D,MAAM,cAAc,UAAyC;AACzD,UAAM,YAAY,IAAI,oBAAoB,QAAQ;AAClD,QAAI;AACA,YAAM,KAAK,aAAa,SAAS,QAAQ,SAAS,SAAS;AAAA,IAC/D,SAAS,OAAO;AACZ,aAAO,MAAM,8CAA8C,KAAK;AAEhE,UAAI,iBAAiB,qBAAqB;AACtC,cAAM,UAAU,UAAU,mBAAmB,kBAAkB;AAAA,MACnE,WAES,EAAE,iBAAiB,6BAA6B;AACrD,cAAM,UAAU,UAAU,mBAAmB,YAAY;AAAA,MAC7D;AAAA,IACJ,UAAE;AAEE,YAAM,SAAS,QAAQ,QAAQ;AAAA,IACnC;AAAA,EACJ;AAAA,EAEA,QAAgB;AACZ,WAAO;AAAA,EACX;AAAA,EAEA,MAAc,aAAa,QAAsB,WAAgC;AAC7E,WAAO,KAAK,iCAAiC,UAAU,eAAe,CAAC,EAAE;AAEzE,UAAM,kBAAkB,OAAO,UAAU;AAGzC,UAAM,EAAE,aAAa,OAAO,IAAI,MAAM,UAAU,WAAW;AAC3D,UAAM;AAAA,MACF,oBAAoB;AAAA,MACpB,cAAc;AAAA,MACd,oBAAoB;AAAA,MACpB;AAAA,MACA,iBAAiB;AAAA,MACjB,wBAAwB;AAAA,MACxB;AAAA,IACJ,IAAI;AAGJ,UAAM,eAAe,OAAO,cAAc,EAAE;AAE5C,UAAM,mBACF,qBAAqB,UAAa,kBAAkB,SAC9C,OAAO,yBAAyB,gBAAgB,IAChD;AAEV,QAAI,qBAAqB,UAAa,kBAAkB,UAAa,qBAAqB,QAAW;AACjG,YAAM,EAAE,cAAc,QAAQ,YAAY,sBAAsB,IAAI;AACpE,YAAM,gBAAgB,MAAM,OAAO;AAAA,QAC/B;AAAA,QACA,UAAU,OAAO,YAAY,gBAAgB;AAAA,QAC7C;AAAA,MACJ;AACA,aAAO,QAAQ,eAAe,eAAe,iBAAiB;AAG9D,YAAM,qBAAqB,MAAM,OAAO,0BAA0B;AAClE,YAAM,oBAAoB,UAAU,OAAO,YAAY,gBAAgB;AACvE,YAAM,gBAAgB,MAAM,OAAO,eAAe,oBAAoB;AAAA,QAClE,WAAW;AAAA,QACX;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,MAAM;AAAA,QACN,aAAa;AAAA,QACb,cAAc;AAAA,QACd,uBAAuB;AAAA,QACvB;AAAA,MACJ,CAAC;AAGD,YAAM,aAAa,UAAU,OAAO,YAAY,YAAY;AAC5D,YAAM,YAAY,MAAM,OAAO,KAAK,cAAc,YAAY,eAAe;AAC7E,YAAM,YAAY,OAAO,QAAQ,WAAW,IAAI,UAAU,CAAC,GAAG,iBAAiB;AAC/E,UAAI;AACA,cAAM,UAAU,iBAAiB;AAAA,UAC7B;AAAA,UACA;AAAA,UACA;AAAA,UACA,wBAAwB,OAAO;AAAA;AAAA,QACnC,CAAC;AAAA,MACL,SAAS,OAAO;AAEZ,cAAM,cAAc,QAAQ,KAAK;AACjC,cAAM;AAAA,MACV;AAEA,aAAO;AAAA,QACH,WAAW,cAAc,EAAE,iBAAiB,UAAU,eAAe,CAAC,eAAe,OAAO;AAAA,UACxF,OAAO;AAAA,QACX,CAAC,UAAU,OAAO,WAAW,kBAAkB,OAAO,YAAY,UAAU,CAAC;AAAA,
|
|
4
|
+
"sourcesContent": ["/**\n * @license\n * Copyright 2022-2024 Matter.js Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { MatterDevice } from \"../../MatterDevice.js\";\nimport { TlvOperationalCertificate } from \"../../certificate/CertificateManager.js\";\nimport { UnexpectedDataError } from \"../../common/MatterError.js\";\nimport { Crypto } from \"../../crypto/Crypto.js\";\nimport { PublicKey } from \"../../crypto/Key.js\";\nimport { NodeId } from \"../../datatype/NodeId.js\";\nimport { FabricNotFoundError } from \"../../fabric/FabricManager.js\";\nimport { Logger } from \"../../log/Logger.js\";\nimport { MessageExchange } from \"../../protocol/MessageExchange.js\";\nimport { ProtocolHandler } from \"../../protocol/ProtocolHandler.js\";\nimport { ProtocolStatusCode, SECURE_CHANNEL_PROTOCOL_ID } from \"../../protocol/securechannel/SecureChannelMessages.js\";\nimport { ChannelStatusResponseError } from \"../../protocol/securechannel/SecureChannelMessenger.js\";\nimport { ByteArray } from \"../../util/ByteArray.js\";\nimport {\n KDFSR1_KEY_INFO,\n KDFSR2_INFO,\n KDFSR2_KEY_INFO,\n KDFSR3_INFO,\n RESUME1_MIC_NONCE,\n RESUME2_MIC_NONCE,\n TBE_DATA2_NONCE,\n TBE_DATA3_NONCE,\n TlvEncryptedDataSigma2,\n TlvEncryptedDataSigma3,\n TlvSignedData,\n} from \"./CaseMessages.js\";\nimport { CaseServerMessenger } from \"./CaseMessenger.js\";\n\nconst logger = Logger.get(\"CaseServer\");\n\nexport class CaseServer implements ProtocolHandler<MatterDevice> {\n async onNewExchange(exchange: MessageExchange<MatterDevice>) {\n const messenger = new CaseServerMessenger(exchange);\n try {\n await this.handleSigma1(exchange.session.context, messenger);\n } catch (error) {\n logger.error(\"An error occurred during the commissioning\", error);\n\n if (error instanceof FabricNotFoundError) {\n await messenger.sendError(ProtocolStatusCode.NoSharedTrustRoots);\n }\n // If we received a ChannelStatusResponseError we do not need to send one back, so just cancel pairing\n else if (!(error instanceof ChannelStatusResponseError)) {\n await messenger.sendError(ProtocolStatusCode.InvalidParam);\n }\n } finally {\n // Destroy the unsecure session used to establish the secure Case session\n await exchange.session.destroy();\n }\n }\n\n getId(): number {\n return SECURE_CHANNEL_PROTOCOL_ID;\n }\n\n private async handleSigma1(server: MatterDevice, messenger: CaseServerMessenger) {\n logger.info(`Received pairing request from ${messenger.getChannelName()}`);\n // Generate pairing info\n const responderRandom = Crypto.getRandom();\n\n // Read and process sigma 1\n const { sigma1Bytes, sigma1 } = await messenger.readSigma1();\n const {\n initiatorSessionId: peerSessionId,\n resumptionId: peerResumptionId,\n initiatorResumeMic: peerResumeMic,\n destinationId,\n initiatorRandom: peerRandom,\n initiatorEcdhPublicKey: peerEcdhPublicKey,\n initiatorSessionParams,\n } = sigma1;\n\n // Try to resume a previous session\n const resumptionId = Crypto.getRandomData(16);\n\n const resumptionRecord =\n peerResumptionId !== undefined && peerResumeMic !== undefined\n ? server.findResumptionRecordById(peerResumptionId)\n : undefined;\n // We try to resume the session\n if (peerResumptionId !== undefined && peerResumeMic !== undefined && resumptionRecord !== undefined) {\n const { sharedSecret, fabric, peerNodeId, caseAuthenticatedTags } = resumptionRecord;\n const peerResumeKey = await Crypto.hkdf(\n sharedSecret,\n ByteArray.concat(peerRandom, peerResumptionId),\n KDFSR1_KEY_INFO,\n );\n Crypto.decrypt(peerResumeKey, peerResumeMic, RESUME1_MIC_NONCE);\n\n // All good! Create secure session\n const responderSessionId = await server.getNextAvailableSessionId();\n const secureSessionSalt = ByteArray.concat(peerRandom, peerResumptionId);\n const secureSession = await server.sessionManager.createSecureSession({\n sessionId: responderSessionId,\n fabric,\n peerNodeId,\n peerSessionId,\n sharedSecret,\n salt: secureSessionSalt,\n isInitiator: false,\n isResumption: true,\n peerSessionParameters: initiatorSessionParams,\n caseAuthenticatedTags,\n });\n\n // Generate sigma 2 resume\n const resumeSalt = ByteArray.concat(peerRandom, resumptionId);\n const resumeKey = await Crypto.hkdf(sharedSecret, resumeSalt, KDFSR2_KEY_INFO);\n const resumeMic = Crypto.encrypt(resumeKey, new ByteArray(0), RESUME2_MIC_NONCE);\n try {\n await messenger.sendSigma2Resume({\n resumptionId,\n resumeMic,\n responderSessionId,\n responderSessionParams: server.sessionParameters, // responder session parameters\n });\n } catch (error) {\n // If we fail to send the resume, we destroy the session\n await secureSession.destroy(false);\n throw error;\n }\n\n logger.info(\n `session ${secureSession.id} resumed with ${messenger.getChannelName()} for Fabric ${NodeId.toHexString(\n fabric.nodeId,\n )}(index ${fabric.fabricIndex}) and PeerNode ${NodeId.toHexString(peerNodeId)}`,\n \"with CATs\",\n caseAuthenticatedTags,\n );\n resumptionRecord.resumptionId = resumptionId; /* Update the ID */\n\n // Wait for success on the peer side\n await messenger.waitForSuccess(\"Success after CASE Sigma2Resume\");\n\n await messenger.close();\n await server.saveResumptionRecord(resumptionRecord);\n } else if (\n (peerResumptionId === undefined && peerResumeMic === undefined) ||\n (peerResumptionId !== undefined && peerResumeMic !== undefined && resumptionRecord === undefined)\n ) {\n // Generate sigma 2\n // TODO: Pass through a group id?\n const fabric = server.findFabricFromDestinationId(destinationId, peerRandom);\n const { operationalCert: nodeOpCert, intermediateCACert, operationalIdentityProtectionKey } = fabric;\n const { publicKey: responderEcdhPublicKey, sharedSecret } =\n Crypto.ecdhGeneratePublicKeyAndSecret(peerEcdhPublicKey);\n const sigma2Salt = ByteArray.concat(\n operationalIdentityProtectionKey,\n responderRandom,\n responderEcdhPublicKey,\n Crypto.hash(sigma1Bytes),\n );\n const sigma2Key = await Crypto.hkdf(sharedSecret, sigma2Salt, KDFSR2_INFO);\n const signatureData = TlvSignedData.encode({\n nodeOpCert,\n intermediateCACert,\n ecdhPublicKey: responderEcdhPublicKey,\n peerEcdhPublicKey,\n });\n const signature = fabric.sign(signatureData);\n const encryptedData = TlvEncryptedDataSigma2.encode({\n nodeOpCert,\n intermediateCACert,\n signature,\n resumptionId,\n });\n const encrypted = Crypto.encrypt(sigma2Key, encryptedData, TBE_DATA2_NONCE);\n const responderSessionId = await server.getNextAvailableSessionId();\n const sigma2Bytes = await messenger.sendSigma2({\n responderRandom,\n responderSessionId,\n responderEcdhPublicKey,\n encrypted,\n responderSessionParams: server.sessionParameters, // responder session parameters\n });\n\n // Read and process sigma 3\n const {\n sigma3Bytes,\n sigma3: { encrypted: peerEncrypted },\n } = await messenger.readSigma3();\n const sigma3Salt = ByteArray.concat(\n operationalIdentityProtectionKey,\n Crypto.hash([sigma1Bytes, sigma2Bytes]),\n );\n const sigma3Key = await Crypto.hkdf(sharedSecret, sigma3Salt, KDFSR3_INFO);\n const peerDecryptedData = Crypto.decrypt(sigma3Key, peerEncrypted, TBE_DATA3_NONCE);\n const {\n nodeOpCert: peerNewOpCert,\n intermediateCACert: peerIntermediateCACert,\n signature: peerSignature,\n } = TlvEncryptedDataSigma3.decode(peerDecryptedData);\n\n fabric.verifyCredentials(peerNewOpCert, peerIntermediateCACert);\n\n const peerSignatureData = TlvSignedData.encode({\n nodeOpCert: peerNewOpCert,\n intermediateCACert: peerIntermediateCACert,\n ecdhPublicKey: peerEcdhPublicKey,\n peerEcdhPublicKey: responderEcdhPublicKey,\n });\n const {\n ellipticCurvePublicKey: peerPublicKey,\n subject: { fabricId: peerFabricId, nodeId: peerNodeId, caseAuthenticatedTags },\n } = TlvOperationalCertificate.decode(peerNewOpCert);\n\n if (fabric.fabricId !== peerFabricId) {\n throw new UnexpectedDataError(`Fabric ID mismatch: ${fabric.fabricId} !== ${peerFabricId}`);\n }\n\n Crypto.verify(PublicKey(peerPublicKey), peerSignatureData, peerSignature);\n\n // All good! Create secure session\n const secureSessionSalt = ByteArray.concat(\n operationalIdentityProtectionKey,\n Crypto.hash([sigma1Bytes, sigma2Bytes, sigma3Bytes]),\n );\n const secureSession = await server.sessionManager.createSecureSession({\n sessionId: responderSessionId,\n fabric,\n peerNodeId,\n peerSessionId,\n sharedSecret,\n salt: secureSessionSalt,\n isInitiator: false,\n isResumption: false,\n peerSessionParameters: initiatorSessionParams,\n caseAuthenticatedTags,\n });\n logger.info(\n `session ${secureSession.id} created with ${messenger.getChannelName()} for Fabric ${NodeId.toHexString(\n fabric.nodeId,\n )}(index ${fabric.fabricIndex}) and PeerNode ${NodeId.toHexString(peerNodeId)}`,\n \"with CATs\",\n caseAuthenticatedTags,\n );\n await messenger.sendSuccess();\n\n const resumptionRecord = {\n peerNodeId,\n fabric,\n sharedSecret,\n resumptionId,\n sessionParameters: secureSession.parameters,\n caseAuthenticatedTags,\n };\n\n await messenger.close();\n await server.saveResumptionRecord(resumptionRecord);\n } else {\n logger.info(\n `Invalid resumption ID or resume MIC received from ${messenger.getChannelName()}`,\n peerResumptionId,\n peerResumeMic,\n );\n throw new UnexpectedDataError(\"Invalid resumption ID or resume MIC.\");\n }\n }\n\n async close() {\n // Nothing to do\n }\n}\n"],
|
|
5
|
+
"mappings": "AAAA;AAAA;AAAA;AAAA;AAAA;AAOA,SAAS,iCAAiC;AAC1C,SAAS,2BAA2B;AACpC,SAAS,cAAc;AACvB,SAAS,iBAAiB;AAC1B,SAAS,cAAc;AACvB,SAAS,2BAA2B;AACpC,SAAS,cAAc;AAGvB,SAAS,oBAAoB,kCAAkC;AAC/D,SAAS,kCAAkC;AAC3C,SAAS,iBAAiB;AAC1B;AAAA,EACI;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACG;AACP,SAAS,2BAA2B;AAEpC,MAAM,SAAS,OAAO,IAAI,YAAY;AAE/B,MAAM,WAAoD;AAAA,EAC7D,MAAM,cAAc,UAAyC;AACzD,UAAM,YAAY,IAAI,oBAAoB,QAAQ;AAClD,QAAI;AACA,YAAM,KAAK,aAAa,SAAS,QAAQ,SAAS,SAAS;AAAA,IAC/D,SAAS,OAAO;AACZ,aAAO,MAAM,8CAA8C,KAAK;AAEhE,UAAI,iBAAiB,qBAAqB;AACtC,cAAM,UAAU,UAAU,mBAAmB,kBAAkB;AAAA,MACnE,WAES,EAAE,iBAAiB,6BAA6B;AACrD,cAAM,UAAU,UAAU,mBAAmB,YAAY;AAAA,MAC7D;AAAA,IACJ,UAAE;AAEE,YAAM,SAAS,QAAQ,QAAQ;AAAA,IACnC;AAAA,EACJ;AAAA,EAEA,QAAgB;AACZ,WAAO;AAAA,EACX;AAAA,EAEA,MAAc,aAAa,QAAsB,WAAgC;AAC7E,WAAO,KAAK,iCAAiC,UAAU,eAAe,CAAC,EAAE;AAEzE,UAAM,kBAAkB,OAAO,UAAU;AAGzC,UAAM,EAAE,aAAa,OAAO,IAAI,MAAM,UAAU,WAAW;AAC3D,UAAM;AAAA,MACF,oBAAoB;AAAA,MACpB,cAAc;AAAA,MACd,oBAAoB;AAAA,MACpB;AAAA,MACA,iBAAiB;AAAA,MACjB,wBAAwB;AAAA,MACxB;AAAA,IACJ,IAAI;AAGJ,UAAM,eAAe,OAAO,cAAc,EAAE;AAE5C,UAAM,mBACF,qBAAqB,UAAa,kBAAkB,SAC9C,OAAO,yBAAyB,gBAAgB,IAChD;AAEV,QAAI,qBAAqB,UAAa,kBAAkB,UAAa,qBAAqB,QAAW;AACjG,YAAM,EAAE,cAAc,QAAQ,YAAY,sBAAsB,IAAI;AACpE,YAAM,gBAAgB,MAAM,OAAO;AAAA,QAC/B;AAAA,QACA,UAAU,OAAO,YAAY,gBAAgB;AAAA,QAC7C;AAAA,MACJ;AACA,aAAO,QAAQ,eAAe,eAAe,iBAAiB;AAG9D,YAAM,qBAAqB,MAAM,OAAO,0BAA0B;AAClE,YAAM,oBAAoB,UAAU,OAAO,YAAY,gBAAgB;AACvE,YAAM,gBAAgB,MAAM,OAAO,eAAe,oBAAoB;AAAA,QAClE,WAAW;AAAA,QACX;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,MAAM;AAAA,QACN,aAAa;AAAA,QACb,cAAc;AAAA,QACd,uBAAuB;AAAA,QACvB;AAAA,MACJ,CAAC;AAGD,YAAM,aAAa,UAAU,OAAO,YAAY,YAAY;AAC5D,YAAM,YAAY,MAAM,OAAO,KAAK,cAAc,YAAY,eAAe;AAC7E,YAAM,YAAY,OAAO,QAAQ,WAAW,IAAI,UAAU,CAAC,GAAG,iBAAiB;AAC/E,UAAI;AACA,cAAM,UAAU,iBAAiB;AAAA,UAC7B;AAAA,UACA;AAAA,UACA;AAAA,UACA,wBAAwB,OAAO;AAAA;AAAA,QACnC,CAAC;AAAA,MACL,SAAS,OAAO;AAEZ,cAAM,cAAc,QAAQ,KAAK;AACjC,cAAM;AAAA,MACV;AAEA,aAAO;AAAA,QACH,WAAW,cAAc,EAAE,iBAAiB,UAAU,eAAe,CAAC,eAAe,OAAO;AAAA,UACxF,OAAO;AAAA,QACX,CAAC,UAAU,OAAO,WAAW,kBAAkB,OAAO,YAAY,UAAU,CAAC;AAAA,QAC7E;AAAA,QACA;AAAA,MACJ;AACA,uBAAiB,eAAe;AAGhC,YAAM,UAAU,eAAe,iCAAiC;AAEhE,YAAM,UAAU,MAAM;AACtB,YAAM,OAAO,qBAAqB,gBAAgB;AAAA,IACtD,WACK,qBAAqB,UAAa,kBAAkB,UACpD,qBAAqB,UAAa,kBAAkB,UAAa,qBAAqB,QACzF;AAGE,YAAM,SAAS,OAAO,4BAA4B,eAAe,UAAU;AAC3E,YAAM,EAAE,iBAAiB,YAAY,oBAAoB,iCAAiC,IAAI;AAC9F,YAAM,EAAE,WAAW,wBAAwB,aAAa,IACpD,OAAO,+BAA+B,iBAAiB;AAC3D,YAAM,aAAa,UAAU;AAAA,QACzB;AAAA,QACA;AAAA,QACA;AAAA,QACA,OAAO,KAAK,WAAW;AAAA,MAC3B;AACA,YAAM,YAAY,MAAM,OAAO,KAAK,cAAc,YAAY,WAAW;AACzE,YAAM,gBAAgB,cAAc,OAAO;AAAA,QACvC;AAAA,QACA;AAAA,QACA,eAAe;AAAA,QACf;AAAA,MACJ,CAAC;AACD,YAAM,YAAY,OAAO,KAAK,aAAa;AAC3C,YAAM,gBAAgB,uBAAuB,OAAO;AAAA,QAChD;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACJ,CAAC;AACD,YAAM,YAAY,OAAO,QAAQ,WAAW,eAAe,eAAe;AAC1E,YAAM,qBAAqB,MAAM,OAAO,0BAA0B;AAClE,YAAM,cAAc,MAAM,UAAU,WAAW;AAAA,QAC3C;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,wBAAwB,OAAO;AAAA;AAAA,MACnC,CAAC;AAGD,YAAM;AAAA,QACF;AAAA,QACA,QAAQ,EAAE,WAAW,cAAc;AAAA,MACvC,IAAI,MAAM,UAAU,WAAW;AAC/B,YAAM,aAAa,UAAU;AAAA,QACzB;AAAA,QACA,OAAO,KAAK,CAAC,aAAa,WAAW,CAAC;AAAA,MAC1C;AACA,YAAM,YAAY,MAAM,OAAO,KAAK,cAAc,YAAY,WAAW;AACzE,YAAM,oBAAoB,OAAO,QAAQ,WAAW,eAAe,eAAe;AAClF,YAAM;AAAA,QACF,YAAY;AAAA,QACZ,oBAAoB;AAAA,QACpB,WAAW;AAAA,MACf,IAAI,uBAAuB,OAAO,iBAAiB;AAEnD,aAAO,kBAAkB,eAAe,sBAAsB;AAE9D,YAAM,oBAAoB,cAAc,OAAO;AAAA,QAC3C,YAAY;AAAA,QACZ,oBAAoB;AAAA,QACpB,eAAe;AAAA,QACf,mBAAmB;AAAA,MACvB,CAAC;AACD,YAAM;AAAA,QACF,wBAAwB;AAAA,QACxB,SAAS,EAAE,UAAU,cAAc,QAAQ,YAAY,sBAAsB;AAAA,MACjF,IAAI,0BAA0B,OAAO,aAAa;AAElD,UAAI,OAAO,aAAa,cAAc;AAClC,cAAM,IAAI,oBAAoB,uBAAuB,OAAO,QAAQ,QAAQ,YAAY,EAAE;AAAA,MAC9F;AAEA,aAAO,OAAO,UAAU,aAAa,GAAG,mBAAmB,aAAa;AAGxE,YAAM,oBAAoB,UAAU;AAAA,QAChC;AAAA,QACA,OAAO,KAAK,CAAC,aAAa,aAAa,WAAW,CAAC;AAAA,MACvD;AACA,YAAM,gBAAgB,MAAM,OAAO,eAAe,oBAAoB;AAAA,QAClE,WAAW;AAAA,QACX;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,MAAM;AAAA,QACN,aAAa;AAAA,QACb,cAAc;AAAA,QACd,uBAAuB;AAAA,QACvB;AAAA,MACJ,CAAC;AACD,aAAO;AAAA,QACH,WAAW,cAAc,EAAE,iBAAiB,UAAU,eAAe,CAAC,eAAe,OAAO;AAAA,UACxF,OAAO;AAAA,QACX,CAAC,UAAU,OAAO,WAAW,kBAAkB,OAAO,YAAY,UAAU,CAAC;AAAA,QAC7E;AAAA,QACA;AAAA,MACJ;AACA,YAAM,UAAU,YAAY;AAE5B,YAAMA,oBAAmB;AAAA,QACrB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,mBAAmB,cAAc;AAAA,QACjC;AAAA,MACJ;AAEA,YAAM,UAAU,MAAM;AACtB,YAAM,OAAO,qBAAqBA,iBAAgB;AAAA,IACtD,OAAO;AACH,aAAO;AAAA,QACH,qDAAqD,UAAU,eAAe,CAAC;AAAA,QAC/E;AAAA,QACA;AAAA,MACJ;AACA,YAAM,IAAI,oBAAoB,sCAAsC;AAAA,IACxE;AAAA,EACJ;AAAA,EAEA,MAAM,QAAQ;AAAA,EAEd;AACJ;",
|
|
6
6
|
"names": ["resumptionRecord"]
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@project-chip/matter.js",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.5",
|
|
4
4
|
"description": "Matter protocol in pure js",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"iot",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"@noble/curves": "^1.5.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@project-chip/matter.js-tools": "0.10.
|
|
39
|
+
"@project-chip/matter.js-tools": "0.10.5",
|
|
40
40
|
"@types/chai": "^4.3.16",
|
|
41
41
|
"@types/mocha": "^10.0.7",
|
|
42
42
|
"@types/wtfnode": "^0.7.3",
|
|
@@ -199,5 +199,5 @@
|
|
|
199
199
|
"publishConfig": {
|
|
200
200
|
"access": "public"
|
|
201
201
|
},
|
|
202
|
-
"gitHead": "
|
|
202
|
+
"gitHead": "083d4c719a91c25db071a90ca55903be6af722ef"
|
|
203
203
|
}
|
|
@@ -157,10 +157,14 @@ export class AccessControlManager {
|
|
|
157
157
|
}
|
|
158
158
|
|
|
159
159
|
logger.notice(
|
|
160
|
-
`Failed access control check for ${endpoint.number}
|
|
160
|
+
`Failed access control check for ${endpoint.number}/0x${toHex(clusterId)} and fabricIndex ${session.associatedFabric.fabricIndex}, acl=`,
|
|
161
161
|
this.#getAccessControlEntriesForFabric(session.associatedFabric),
|
|
162
|
+
"with ISD=",
|
|
163
|
+
this.#getIsdFromMessage(session),
|
|
162
164
|
"granted privileges=",
|
|
163
165
|
grantedPrivileges,
|
|
166
|
+
"not contains",
|
|
167
|
+
privilege,
|
|
164
168
|
);
|
|
165
169
|
|
|
166
170
|
return false;
|
|
@@ -130,6 +130,8 @@ export class CaseServer implements ProtocolHandler<MatterDevice> {
|
|
|
130
130
|
`session ${secureSession.id} resumed with ${messenger.getChannelName()} for Fabric ${NodeId.toHexString(
|
|
131
131
|
fabric.nodeId,
|
|
132
132
|
)}(index ${fabric.fabricIndex}) and PeerNode ${NodeId.toHexString(peerNodeId)}`,
|
|
133
|
+
"with CATs",
|
|
134
|
+
caseAuthenticatedTags,
|
|
133
135
|
);
|
|
134
136
|
resumptionRecord.resumptionId = resumptionId; /* Update the ID */
|
|
135
137
|
|
|
@@ -235,6 +237,8 @@ export class CaseServer implements ProtocolHandler<MatterDevice> {
|
|
|
235
237
|
`session ${secureSession.id} created with ${messenger.getChannelName()} for Fabric ${NodeId.toHexString(
|
|
236
238
|
fabric.nodeId,
|
|
237
239
|
)}(index ${fabric.fabricIndex}) and PeerNode ${NodeId.toHexString(peerNodeId)}`,
|
|
240
|
+
"with CATs",
|
|
241
|
+
caseAuthenticatedTags,
|
|
238
242
|
);
|
|
239
243
|
await messenger.sendSuccess();
|
|
240
244
|
|