diff --git a/Generator/ClusterGenerator.cs b/Generator/ClusterGenerator.cs index ccb0866..3d070f2 100644 --- a/Generator/ClusterGenerator.cs +++ b/Generator/ClusterGenerator.cs @@ -79,7 +79,10 @@ private static void WriteClass(Cluster cluster, StreamWriter writer) writer.WriteLine(" // Attributes"); foreach (var attribute in cluster.attributes) - WriteAttribute(attribute, writer); + { + if (attribute.type != null) + WriteAttribute(attribute, writer); + } writer.WriteLine(" }"); writer.Write("}"); @@ -93,10 +96,12 @@ private static void WriteStruct(clusterCommand command, bool toServer, StreamWri writer.WriteLine($" {(toServer ? "private record" : "public struct")} " + GeneratorUtil.SanitizeName(command.name) + (toServer ? "Payload : TLVPayload {" : " {")); foreach (clusterCommandField field in command.field) { + if (field.type == null) //Reserved/removed fields + continue; writer.Write(" public "); if (field.optionalConform == null) writer.Write("required "); - WriteType(field.type, null, writer); + WriteType(field.type, field.entry?.type, writer); if (field.optionalConform != null) writer.Write("?"); if (field.name == GeneratorUtil.SanitizeName(command.name)) @@ -208,9 +213,9 @@ private static void WriteStructType(bool optional, string type, byte id, string case "uint56": case "uint64": case "epoch-us": - case "posix-ms": + case "ref_DataTypePosixMs": case "systime-us": - case "systime-ms": + case "ref_DataTypeSystemTimeMs": case "fabric-id": case "node-id": case "EUI64": @@ -225,11 +230,11 @@ private static void WriteStructType(bool optional, string type, byte id, string writer.WriteLine($"{totalIndent}writer.WriteDouble({id}, {name});"); break; case "octstr": - case "ipadr": - case "ipv4adr": - case "ipv6adr": + case "ref_IpAdr": + case "ref_Ipv4Adr": + case "ref_Ipv6Adr": case "ipv6pre": - case "hwadr": + case "Hardware Address": writer.WriteLine($"{totalIndent}writer.WriteBytes({id}, {name});"); break; case "string": @@ -274,8 +279,12 @@ private static void WriteCommands(Cluster cluster, StreamWriter writer) { foreach (var field in cmd.field) { + if (field.type == null) + continue; writer.Write(", "); - WriteType(field.type, null, writer); + WriteType(field.type, field.entry?.type, writer); + if (field.optionalConform != null) + writer.Write('?'); writer.Write(" " + field.name); } } @@ -290,7 +299,7 @@ private static void WriteCommands(Cluster cluster, StreamWriter writer) } else { - if (cmd.response != "N") + if (cmd.response == "N") writer.WriteLine(" await InteractionManager.SendCommand(session, endPoint, CLUSTER_ID, " + cmd.id + ");"); else writer.WriteLine(" InvokeResponseIB resp = await InteractionManager.ExecCommand(session, endPoint, CLUSTER_ID, " + cmd.id + ");"); @@ -308,7 +317,7 @@ private static void WriteCommands(Cluster cluster, StreamWriter writer) foreach (var field in response.field) { writer.Write(" " + field.name + " = ("); - WriteType(field.type, null, writer); + WriteType(field.type, field.entry?.type, writer); if (field.optionalConform != null) writer.WriteLine($"?)GetOptionalField(resp, {field.id}),"); else @@ -439,9 +448,9 @@ private static void WriteType(string type, string? entryType, StreamWriter write case "uint56": case "uint64": case "epoch-us": - case "posix-ms": + case "ref_DataTypePosixMs": case "systime-us": - case "systime-ms": + case "ref_DataTypeSystemTimeMs": case "fabric-id": case "node-id": case "EUI64": @@ -479,11 +488,11 @@ private static void WriteType(string type, string? entryType, StreamWriter write writer.Write("float"); break; case "octstr": - case "ipadr": - case "ipv4adr": - case "ipv6adr": + case "ref_IpAdr": + case "ref_Ipv4Adr": + case "ref_Ipv6Adr": case "ipv6pre": - case "hwadr": + case "Hardware Address": writer.Write("byte[]"); break; case "bool": diff --git a/Generator/Clusters/DiagnosticsGeneral.xml b/Generator/Clusters/DiagnosticsGeneral.xml new file mode 100644 index 0000000..0fea7a0 --- /dev/null +++ b/Generator/Clusters/DiagnosticsGeneral.xml @@ -0,0 +1,373 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Generator/Clusters/Group-Key-Management-Cluster.xml b/Generator/Clusters/Group-Key-Management-Cluster.xml new file mode 100644 index 0000000..14c7685 --- /dev/null +++ b/Generator/Clusters/Group-Key-Management-Cluster.xml @@ -0,0 +1,235 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Generator/Schema/Cluster.cs b/Generator/Schema/Cluster.cs index b4c8038..4fbbe78 100644 --- a/Generator/Schema/Cluster.cs +++ b/Generator/Schema/Cluster.cs @@ -1663,6 +1663,20 @@ public partial class clusterCommandField private bool defaultFieldSpecified; + private clusterAttributeEntry? entryField; + + public clusterAttributeEntry? entry + { + get + { + return this.entryField; + } + set + { + this.entryField = value; + } + } + /// public object optionalConform { diff --git a/MatterDotNet/Clusters/GeneralCommissioningCluster.cs b/MatterDotNet/Clusters/GeneralCommissioningCluster.cs index 131cb74..28aafb6 100644 --- a/MatterDotNet/Clusters/GeneralCommissioningCluster.cs +++ b/MatterDotNet/Clusters/GeneralCommissioningCluster.cs @@ -12,7 +12,6 @@ // // WARNING: This file was auto-generated. Do not edit. - using MatterDotNet.Messages.InteractionModel; using MatterDotNet.Protocol; using MatterDotNet.Protocol.Parsers; @@ -65,7 +64,7 @@ public enum RegulatoryLocationTypeEnum { IndoorOutdoor = 2, } - public record BasicCommissioningInfoStruct : TLVPayload { + public record BasicCommissioningInfo : TLVPayload { public required ushort FailSafeExpiryLengthSeconds { get; set; } public required ushort MaxCumulativeFailsafeSeconds { get; set; } public override void Serialize(TLVWriter writer, long structNumber = -1) { @@ -88,8 +87,8 @@ public override void Serialize(TLVWriter writer, long structNumber = -1) { } public struct ArmFailSafeResponse { - public CommissioningErrorEnum ErrorCode { get; set; } - public string DebugText { get; set; } + public required CommissioningErrorEnum ErrorCode { get; set; } + public required string DebugText { get; set; } } private record SetRegulatoryConfigPayload : TLVPayload { @@ -107,7 +106,7 @@ public override void Serialize(TLVWriter writer, long structNumber = -1) { public struct SetRegulatoryConfigResponse { public required CommissioningErrorEnum ErrorCode { get; set; } - public required String DebugText { get; set; } + public required string DebugText { get; set; } } public struct CommissioningCompleteResponse { @@ -125,7 +124,6 @@ public struct CommissioningCompleteResponse { if (!validateResponse(resp)) return null; return new ArmFailSafeResponse() { - ErrorCode = (CommissioningErrorEnum)(byte)GetField(resp, 0), DebugText = (string)GetField(resp, 1), }; @@ -158,7 +156,7 @@ public struct CommissioningCompleteResponse { // Attributes public ulong Breadcrumb { get; set; } = 0; - public BasicCommissioningInfoStruct BasicCommissioningInfo { get; } + public BasicCommissioningInfo BasicCommissioningInfoField { get; } public RegulatoryLocationTypeEnum RegulatoryConfig { get; } = RegulatoryLocationTypeEnum.IndoorOutdoor; public RegulatoryLocationTypeEnum LocationCapability { get; } = RegulatoryLocationTypeEnum.IndoorOutdoor; public bool SupportsConcurrentConnection { get; } = true; diff --git a/MatterDotNet/Clusters/NodeOperationalCredentialsCluster.cs b/MatterDotNet/Clusters/NodeOperationalCredentialsCluster.cs index 3ff6706..009cf58 100644 --- a/MatterDotNet/Clusters/NodeOperationalCredentialsCluster.cs +++ b/MatterDotNet/Clusters/NodeOperationalCredentialsCluster.cs @@ -13,6 +13,7 @@ // WARNING: This file was auto-generated. Do not edit. using MatterDotNet.Messages.InteractionModel; +using MatterDotNet.PKI; using MatterDotNet.Protocol; using MatterDotNet.Protocol.Parsers; using MatterDotNet.Protocol.Payloads; @@ -250,7 +251,7 @@ public override void Serialize(TLVWriter writer, long structNumber = -1) { }; } - public async Task CSRRequest (SecureSession session, byte[] CSRNonce, bool IsForUpdateNOC) { + public async Task CSRRequest (SecureSession session, byte[] CSRNonce, bool? IsForUpdateNOC) { CSRRequestPayload requestFields = new CSRRequestPayload() { CSRNonce = CSRNonce, IsForUpdateNOC = IsForUpdateNOC, @@ -264,7 +265,7 @@ public override void Serialize(TLVWriter writer, long structNumber = -1) { }; } - public async Task AddNOC (SecureSession session, byte[] NOCValue, byte[] ICACValue, byte[] IPKValue, ulong CaseAdminSubject, ushort AdminVendorId) { + public async Task AddNOC (SecureSession session, byte[] NOCValue, byte[]? ICACValue, byte[] IPKValue, ulong CaseAdminSubject, ushort AdminVendorId) { AddNOCPayload requestFields = new AddNOCPayload() { NOCValue = NOCValue, ICACValue = ICACValue, @@ -276,13 +277,13 @@ public override void Serialize(TLVWriter writer, long structNumber = -1) { if (!validateResponse(resp)) return null; return new NOCResponse() { - StatusCode = (NodeOperationalCertStatusEnum)GetField(resp, 0), + StatusCode = (NodeOperationalCertStatusEnum)(byte)GetField(resp, 0), FabricIndex = (byte?)GetOptionalField(resp, 1), DebugText = (string?)GetOptionalField(resp, 2), }; } - public async Task UpdateNOC (SecureSession session, byte[] NOCValue, byte[] ICACValue) { + public async Task UpdateNOC (SecureSession session, byte[] NOCValue, byte[]? ICACValue) { UpdateNOCPayload requestFields = new UpdateNOCPayload() { NOCValue = NOCValue, ICACValue = ICACValue, @@ -325,6 +326,13 @@ public override void Serialize(TLVWriter writer, long structNumber = -1) { }; } + public Task AddTrustedRootCertificate(SecureSession session, OperationalCertificate RootCACertificate) + { + PayloadWriter payload = new PayloadWriter(600); + RootCACertificate.ToMatterCertificate().Serialize(payload); + return AddTrustedRootCertificate(session, payload.GetPayload().ToArray()); + } + public async Task AddTrustedRootCertificate (SecureSession session, byte[] RootCACertificate) { AddTrustedRootCertificatePayload requestFields = new AddTrustedRootCertificatePayload() { RootCACertificate = RootCACertificate, diff --git a/MatterDotNet/PKI/Fabric.cs b/MatterDotNet/PKI/Fabric.cs index 11ac190..b7ef6a0 100644 --- a/MatterDotNet/PKI/Fabric.cs +++ b/MatterDotNet/PKI/Fabric.cs @@ -88,7 +88,7 @@ public OperationalCertificate Sign(CertificateRequest nocsr) collection.Add(new Oid("1.3.6.1.5.5.7.3.2")); signingCSR.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(collection, true)); signingCSR.CertificateExtensions.Add(new X509SubjectKeyIdentifierExtension(nocsr.PublicKey, false)); - signingCSR.CertificateExtensions.Add(cert.Extensions.First(e => e is X509AuthorityKeyIdentifierExtension)); + signingCSR.CertificateExtensions.Add(X509AuthorityKeyIdentifierExtension.CreateFromCertificate(cert, true, false)); byte[] serial = new byte[20]; Random.Shared.NextBytes(serial); OperationalCertificate ret = new OperationalCertificate(signingCSR.Create(cert, DateTime.Now.Subtract(TimeSpan.FromSeconds(30)), DateTime.Now.AddYears(1), serial)); @@ -111,7 +111,7 @@ public OperationalCertificate Sign(byte[] publicKey, byte[] privateKey) collection.Add(new Oid("1.3.6.1.5.5.7.3.2")); signingCSR.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(collection, true)); signingCSR.CertificateExtensions.Add(new X509SubjectKeyIdentifierExtension(key.ExportSubjectPublicKeyInfo(), false)); - signingCSR.CertificateExtensions.Add(cert.Extensions.First(e => e is X509AuthorityKeyIdentifierExtension)); + signingCSR.CertificateExtensions.Add(X509AuthorityKeyIdentifierExtension.CreateFromCertificate(cert, true, false)); byte[] serial = new byte[20]; Random.Shared.NextBytes(serial); OperationalCertificate ret = new OperationalCertificate(signingCSR.Create(cert, DateTime.Now.Subtract(TimeSpan.FromSeconds(30)), DateTime.Now.AddYears(1), serial)); diff --git a/MatterDotNet/Protocol/InteractionManager.cs b/MatterDotNet/Protocol/InteractionManager.cs index e4ecdb5..f945521 100644 --- a/MatterDotNet/Protocol/InteractionManager.cs +++ b/MatterDotNet/Protocol/InteractionManager.cs @@ -88,6 +88,12 @@ public static async Task GetAttribute(SecureSession session, } } + public static Task SendCommand(SecureSession session, ushort endpoint, uint cluster, uint command, TLVPayload? payload = null) + { + using (Exchange exchange = session.CreateExchange()) + return SendCommand(exchange, endpoint, cluster, command, payload); + } + public static async Task SendCommand(Exchange exchange, ushort endpoint, uint cluster, uint command, TLVPayload? payload = null) { InvokeRequestMessage run = new InvokeRequestMessage() diff --git a/MatterDotNet/Protocol/TLV/TLVReader.cs b/MatterDotNet/Protocol/TLV/TLVReader.cs index 1b63c6b..f505a52 100644 --- a/MatterDotNet/Protocol/TLV/TLVReader.cs +++ b/MatterDotNet/Protocol/TLV/TLVReader.cs @@ -351,7 +351,11 @@ public void EndContainer() if (control == TLVControl.Anonymous) structure.Add(GetAny(-1)!); else - structure.Insert((int)this.tagNumber, GetAny(this.tagNumber)!); + { + while (this.tagNumber > structure.Count) + structure.Add(null!); + structure.Add(GetAny(this.tagNumber)!); + } } EndContainer(); return structure.ToArray(); diff --git a/README.md b/README.md index 738c160..25dbb0b 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,10 @@ * Framing & TLV Parsing / Generation * Sessions & Exchanges * QR Code / MDNS Payload Parsing +* Device Commissioning #### What's In Progress: -* Device Commissioning +* Cluster generation #### Coming Soon * Matter 1.3 Standard Implementation