1616using System . Globalization ;
1717using System . Security . Cryptography ;
1818using System . Security . Cryptography . X509Certificates ;
19+ using System . Text ;
1920
2021namespace 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}
0 commit comments