Skip to content

Commit 21dd08b

Browse files
committed
First CASE implementation (WIP)
1 parent 3b7d88b commit 21dd08b

File tree

4 files changed

+222
-29
lines changed

4 files changed

+222
-29
lines changed

MatterDotNet/PKI/Fabric.cs

Lines changed: 51 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,24 @@
1616
using System.Globalization;
1717
using System.Security.Cryptography;
1818
using System.Security.Cryptography.X509Certificates;
19+
using System.Text;
1920

2021
namespace MatterDotNet.PKI
2122
{
2223
public class Fabric : OperationalCertificate
2324
{
2425
private static readonly byte[] COMPRESSED_FABRIC_INFO = new byte[] {0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x46, 0x61, 0x62, 0x72, 0x69, 0x63};
25-
Dictionary<ulong, OperationalCertificate> nodes = new Dictionary<ulong, OperationalCertificate>();
2626

27-
public Fabric(ulong rcac, ulong fabricId) : base()
27+
private Dictionary<ulong, OperationalCertificate> nodes = new Dictionary<ulong, OperationalCertificate>();
28+
private byte[] epochIPK;
29+
30+
public Fabric(ulong rcac, ulong fabricId, byte[] ipk) : base()
2831
{
2932
if (fabricId == 0)
3033
throw new ArgumentException("Invalid Fabric ID");
3134
this.RCAC = rcac;
3235
this.FabricID = fabricId;
36+
epochIPK = ipk;
3337
X500DistinguishedNameBuilder builder = new X500DistinguishedNameBuilder();
3438
builder.Add(OID_RCAC, $"{RCAC:X16}", UniversalTagNumber.UTF8String);
3539
builder.Add(OID_FabricID, $"{FabricID:X16}", UniversalTagNumber.UTF8String);
@@ -41,14 +45,16 @@ public Fabric(ulong rcac, ulong fabricId) : base()
4145
req.CertificateExtensions.Add(subjectKeyIdentifier);
4246
req.CertificateExtensions.Add(X509AuthorityKeyIdentifierExtension.CreateFromSubjectKeyIdentifier(subjectKeyIdentifier));
4347
this.cert = req.CreateSelfSigned(DateTime.Now.Subtract(TimeSpan.FromSeconds(30)), DateTime.Now.AddYears(10));
44-
byte[] fabricBytes = new byte[8];
45-
BinaryPrimitives.WriteUInt64BigEndian(fabricBytes, FabricID);
46-
CompressedFabricID = Crypto.KDF(PublicKey.AsSpan(1), fabricBytes, COMPRESSED_FABRIC_INFO, 64);
48+
byte[] fabricIDBytes = new byte[8];
49+
BinaryPrimitives.WriteUInt64BigEndian(fabricIDBytes, FabricID);
50+
CompressedFabricID = Crypto.KDF(PublicKey.AsSpan(1), fabricIDBytes, COMPRESSED_FABRIC_INFO, 64);
51+
OperationalIdentityProtectionKey = Crypto.KDF(ipk, CompressedFabricID, Encoding.ASCII.GetBytes("GroupKey v1.0"), Crypto.SYMMETRIC_KEY_LENGTH_BITS);
4752
}
4853

4954
protected Fabric(X509Certificate2 cert, ECDsa key)
5055
{
5156
this.cert = cert;
57+
epochIPK = []; //TODO
5258
foreach (X500RelativeDistinguishedName dn in cert.SubjectName.EnumerateRelativeDistinguishedNames(false))
5359
{
5460
switch (dn.GetSingleElementType().Value)
@@ -64,9 +70,9 @@ protected Fabric(X509Certificate2 cert, ECDsa key)
6470
}
6571
}
6672
PrivateKey = key;
67-
byte[] fabricBytes = new byte[8];
68-
BinaryPrimitives.WriteUInt64BigEndian(fabricBytes, FabricID);
69-
CompressedFabricID = Crypto.KDF(PublicKey.AsSpan(1), fabricBytes, COMPRESSED_FABRIC_INFO, 64);
73+
byte[] fabricIDBytes = new byte[8];
74+
BinaryPrimitives.WriteUInt64BigEndian(fabricIDBytes, FabricID);
75+
CompressedFabricID = Crypto.KDF(PublicKey.AsSpan(1), fabricIDBytes, COMPRESSED_FABRIC_INFO, 64);
7076
}
7177

7278
public OperationalCertificate Sign(CertificateRequest nocsr)
@@ -84,14 +90,14 @@ public OperationalCertificate Sign(CertificateRequest nocsr)
8490
signingCSR.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(collection, true));
8591
signingCSR.CertificateExtensions.Add(new X509SubjectKeyIdentifierExtension(nocsr.PublicKey, false));
8692
signingCSR.CertificateExtensions.Add(X509AuthorityKeyIdentifierExtension.CreateFromCertificate(cert, true, false));
87-
byte[] serial = new byte[20];
93+
byte[] serial = new byte[19];
8894
Random.Shared.NextBytes(serial);
8995
OperationalCertificate ret = new OperationalCertificate(signingCSR.Create(cert, DateTime.Now.Subtract(TimeSpan.FromSeconds(30)), DateTime.Now.AddYears(1), serial));
9096
nodes.Add(ret.NodeID, ret);
9197
return ret;
9298
}
9399

94-
public OperationalCertificate Sign(byte[] publicKey, byte[] privateKey)
100+
public OperationalCertificate CreateCommissioner(byte[] publicKey, byte[] privateKey)
95101
{
96102
ulong nodeId = (ulong)(0xbaddeed2 + nodes.Count);
97103
ECDsa key = ECDsa.Create(new ECParameters() { Curve = ECCurve.NamedCurves.nistP256, D = privateKey, Q = new BigIntegerPoint(publicKey).ToECPoint()});
@@ -109,17 +115,23 @@ public OperationalCertificate Sign(byte[] publicKey, byte[] privateKey)
109115
signingCSR.CertificateExtensions.Add(X509AuthorityKeyIdentifierExtension.CreateFromCertificate(cert, true, false));
110116
byte[] serial = new byte[20];
111117
Random.Shared.NextBytes(serial);
112-
OperationalCertificate ret = new OperationalCertificate(signingCSR.Create(cert, DateTime.Now.Subtract(TimeSpan.FromSeconds(30)), DateTime.Now.AddYears(1), serial));
118+
OperationalCertificate ret = new OperationalCertificate(signingCSR.Create(cert, DateTime.Now.Subtract(TimeSpan.FromSeconds(30)), DateTime.Now.AddYears(1), serial).CopyWithPrivateKey(key));
113119
nodes.Add(ret.NodeID, ret);
120+
Commissioner = ret;
114121
return ret;
115122
}
116123

117124
public X509Certificate2Collection Export()
118125
{
119126
X509Certificate2Collection collection = new X509Certificate2Collection();
120127
collection.Add(cert);
128+
if (Commissioner != null)
129+
collection.Add(Commissioner.GetRaw());
121130
foreach (var node in nodes)
122-
collection.Add(node.Value.GetRaw());
131+
{
132+
if (node.Value != Commissioner)
133+
collection.Add(node.Value.GetRaw());
134+
}
123135
return collection;
124136
}
125137

@@ -138,15 +150,41 @@ public static Fabric Import(string certPath, string keyPath)
138150
ECDsa key = ECDsa.Create();
139151
key.ImportPkcs8PrivateKey(File.ReadAllBytes(keyPath), out _);
140152
Fabric fabric = new Fabric(collection[0], key);
141-
for (int i = 1; i < collection.Count; i++)
153+
for (int i = 1; i < collection.Count; i++)
142154
{
143155
OperationalCertificate noc = new OperationalCertificate(collection[i]);
156+
if (i == 1)
157+
fabric.Commissioner = noc;
144158
fabric.nodes.TryAdd(noc.NodeID, noc);
145159
}
146160
return fabric;
147161
}
148162

163+
public bool ContainsNOC(ulong nodeId)
164+
{
165+
return nodes.ContainsKey(nodeId);
166+
}
167+
168+
public OperationalCertificate? GetNOC(ulong nodeId)
169+
{
170+
if (nodes.ContainsKey(nodeId))
171+
return nodes[nodeId];
172+
return null;
173+
}
174+
175+
public byte[] ComputeDestinationID(byte[] random, ulong nodeId)
176+
{
177+
byte[] message = new byte[113];
178+
Array.Copy(random, message, 32);
179+
Array.Copy(PublicKey, 0, message, 32, Crypto.PUBLIC_KEY_SIZE_BYTES);
180+
BinaryPrimitives.WriteUInt64LittleEndian(message.AsSpan(Crypto.PUBLIC_KEY_SIZE_BYTES + 32), FabricID);
181+
BinaryPrimitives.WriteUInt64LittleEndian(message.AsSpan(Crypto.PUBLIC_KEY_SIZE_BYTES + 32 + 8), nodeId);
182+
return Crypto.HMAC(OperationalIdentityProtectionKey, message);
183+
}
184+
149185
public ECDsa PrivateKey { get; init; }
150186
public byte[] CompressedFabricID { get; init; }
187+
public OperationalCertificate? Commissioner { get; private set; }
188+
public byte[] OperationalIdentityProtectionKey { get; init; }
151189
}
152190
}

MatterDotNet/PKI/OperationalCertificate.cs

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
// along with this program. If not, see <http://www.gnu.org/licenses/>.
1212

1313
using MatterDotNet.Messages.Certificates;
14+
using MatterDotNet.Protocol.Payloads;
1415
using System.Formats.Asn1;
1516
using System.Globalization;
1617
using System.Numerics;
@@ -31,6 +32,8 @@ public class OperationalCertificate
3132
protected const string OID_RCAC = "1.3.6.1.4.1.37244.1.4";
3233
protected const string OID_FabricID = "1.3.6.1.4.1.37244.1.5";
3334
protected const string OID_NOCCat = "1.3.6.1.4.1.37244.1.6";
35+
protected const string OID_VendorID = "1.3.6.1.4.1.37244.2.1";
36+
protected const string OID_ProductID = "1.3.6.1.4.1.37244.2.2";
3437

3538
protected OperationalCertificate() { }
3639

@@ -83,11 +86,11 @@ private void ParseCert()
8386
if (uint.TryParse(dn.GetSingleElementValue()!, NumberStyles.HexNumber, null, out uint noc))
8487
NOCCat = noc;
8588
break;
86-
case "1.3.6.1.4.1.37244.2.1":
89+
case OID_VendorID:
8790
if (uint.TryParse(dn.GetSingleElementValue()!, NumberStyles.HexNumber, null, out uint vid))
8891
VendorID = vid;
8992
break;
90-
case "1.3.6.1.4.1.37244.2.2":
93+
case OID_ProductID:
9194
if (uint.TryParse(dn.GetSingleElementValue()!, NumberStyles.HexNumber, null, out uint pid))
9295
ProductID = pid;
9396
break;
@@ -263,7 +266,7 @@ public MatterCertificate ToMatterCertificate()
263266
PubKeyAlgo = 0x1,
264267
SigAlgo = 0x1,
265268
EcPubKey = cert.GetPublicKey(),
266-
SerialNum = cert.GetSerialNumber().Reverse().ToArray(),
269+
SerialNum = cert.SerialNumberBytes.ToArray(),
267270
NotBefore = (uint)((DateTimeOffset)cert.NotBefore - EPOCH).ToUnixTimeSeconds(),
268271
NotAfter = (uint)((DateTimeOffset)cert.NotAfter - EPOCH).ToUnixTimeSeconds(),
269272
Signature = GetSignature(),
@@ -273,11 +276,25 @@ public MatterCertificate ToMatterCertificate()
273276
};
274277
}
275278

279+
public byte[] GetMatterCertBytes()
280+
{
281+
PayloadWriter payload = new PayloadWriter(600);
282+
ToMatterCertificate().Serialize(payload);
283+
return payload.GetPayload().ToArray();
284+
}
285+
276286
internal X509Certificate2 GetRaw()
277287
{
278288
return cert;
279289
}
280290

291+
public byte[]? GetPrivateKey()
292+
{
293+
if (!cert.HasPrivateKey)
294+
return null;
295+
return cert.GetECDsaPrivateKey()?.ExportParameters(true).D;
296+
}
297+
281298
public string IssuerName { get; set; } = string.Empty;
282299

283300
public string CommonName { get; set; } = string.Empty;
@@ -297,6 +314,7 @@ internal X509Certificate2 GetRaw()
297314
public uint VendorID { get; set; }
298315

299316
public uint ProductID { get; set; }
317+
300318
public byte[] PublicKey { get { return cert.GetPublicKey(); } }
301319
}
302320
}

0 commit comments

Comments
 (0)