@matter/protocol 0.13.1-alpha.0-20250506-f9ad9c3d8 → 0.13.1-alpha.0-20250508-047aa0277

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.
Files changed (109) hide show
  1. package/dist/cjs/certificate/AttestationCertificateManager.d.ts +7 -13
  2. package/dist/cjs/certificate/AttestationCertificateManager.d.ts.map +1 -1
  3. package/dist/cjs/certificate/AttestationCertificateManager.js +37 -29
  4. package/dist/cjs/certificate/AttestationCertificateManager.js.map +1 -1
  5. package/dist/cjs/certificate/CertificateAuthority.d.ts +1 -6
  6. package/dist/cjs/certificate/CertificateAuthority.d.ts.map +1 -1
  7. package/dist/cjs/certificate/CertificateAuthority.js +56 -38
  8. package/dist/cjs/certificate/CertificateAuthority.js.map +1 -1
  9. package/dist/cjs/certificate/CertificateManager.d.ts +8 -8
  10. package/dist/cjs/certificate/CertificateManager.d.ts.map +1 -1
  11. package/dist/cjs/certificate/CertificateManager.js +20 -16
  12. package/dist/cjs/certificate/CertificateManager.js.map +1 -1
  13. package/dist/cjs/certificate/DeviceCertification.d.ts +1 -1
  14. package/dist/cjs/certificate/DeviceCertification.d.ts.map +1 -1
  15. package/dist/cjs/certificate/DeviceCertification.js +24 -26
  16. package/dist/cjs/certificate/DeviceCertification.js.map +2 -2
  17. package/dist/cjs/common/FailsafeContext.d.ts +2 -2
  18. package/dist/cjs/common/FailsafeContext.d.ts.map +1 -1
  19. package/dist/cjs/common/FailsafeContext.js +20 -13
  20. package/dist/cjs/common/FailsafeContext.js.map +1 -1
  21. package/dist/cjs/fabric/Fabric.d.ts +8 -6
  22. package/dist/cjs/fabric/Fabric.d.ts.map +1 -1
  23. package/dist/cjs/fabric/Fabric.js +15 -9
  24. package/dist/cjs/fabric/Fabric.js.map +1 -1
  25. package/dist/cjs/fabric/FabricAuthority.d.ts.map +1 -1
  26. package/dist/cjs/fabric/FabricAuthority.js +5 -3
  27. package/dist/cjs/fabric/FabricAuthority.js.map +1 -1
  28. package/dist/cjs/fabric/FabricManager.d.ts +1 -1
  29. package/dist/cjs/fabric/FabricManager.d.ts.map +1 -1
  30. package/dist/cjs/fabric/FabricManager.js +2 -2
  31. package/dist/cjs/fabric/FabricManager.js.map +1 -1
  32. package/dist/cjs/peer/ControllerCommissioningFlow.js +2 -2
  33. package/dist/cjs/peer/ControllerCommissioningFlow.js.map +1 -1
  34. package/dist/cjs/session/SessionManager.d.ts +4 -0
  35. package/dist/cjs/session/SessionManager.d.ts.map +1 -1
  36. package/dist/cjs/session/SessionManager.js +11 -2
  37. package/dist/cjs/session/SessionManager.js.map +1 -1
  38. package/dist/cjs/session/case/CaseClient.d.ts.map +1 -1
  39. package/dist/cjs/session/case/CaseClient.js +13 -10
  40. package/dist/cjs/session/case/CaseClient.js.map +1 -1
  41. package/dist/cjs/session/case/CaseServer.d.ts.map +1 -1
  42. package/dist/cjs/session/case/CaseServer.js +11 -8
  43. package/dist/cjs/session/case/CaseServer.js.map +1 -1
  44. package/dist/cjs/session/pase/PaseClient.js +1 -1
  45. package/dist/cjs/session/pase/PaseClient.js.map +1 -1
  46. package/dist/cjs/session/pase/PaseServer.js +1 -1
  47. package/dist/cjs/session/pase/PaseServer.js.map +1 -1
  48. package/dist/esm/certificate/AttestationCertificateManager.d.ts +7 -13
  49. package/dist/esm/certificate/AttestationCertificateManager.d.ts.map +1 -1
  50. package/dist/esm/certificate/AttestationCertificateManager.js +37 -29
  51. package/dist/esm/certificate/AttestationCertificateManager.js.map +1 -1
  52. package/dist/esm/certificate/CertificateAuthority.d.ts +1 -6
  53. package/dist/esm/certificate/CertificateAuthority.d.ts.map +1 -1
  54. package/dist/esm/certificate/CertificateAuthority.js +57 -38
  55. package/dist/esm/certificate/CertificateAuthority.js.map +1 -1
  56. package/dist/esm/certificate/CertificateManager.d.ts +8 -8
  57. package/dist/esm/certificate/CertificateManager.d.ts.map +1 -1
  58. package/dist/esm/certificate/CertificateManager.js +20 -16
  59. package/dist/esm/certificate/CertificateManager.js.map +1 -1
  60. package/dist/esm/certificate/DeviceCertification.d.ts +1 -1
  61. package/dist/esm/certificate/DeviceCertification.d.ts.map +1 -1
  62. package/dist/esm/certificate/DeviceCertification.js +24 -26
  63. package/dist/esm/certificate/DeviceCertification.js.map +2 -2
  64. package/dist/esm/common/FailsafeContext.d.ts +2 -2
  65. package/dist/esm/common/FailsafeContext.d.ts.map +1 -1
  66. package/dist/esm/common/FailsafeContext.js +28 -14
  67. package/dist/esm/common/FailsafeContext.js.map +1 -1
  68. package/dist/esm/fabric/Fabric.d.ts +8 -6
  69. package/dist/esm/fabric/Fabric.d.ts.map +1 -1
  70. package/dist/esm/fabric/Fabric.js +15 -9
  71. package/dist/esm/fabric/Fabric.js.map +1 -1
  72. package/dist/esm/fabric/FabricAuthority.d.ts.map +1 -1
  73. package/dist/esm/fabric/FabricAuthority.js +5 -3
  74. package/dist/esm/fabric/FabricAuthority.js.map +1 -1
  75. package/dist/esm/fabric/FabricManager.d.ts +1 -1
  76. package/dist/esm/fabric/FabricManager.d.ts.map +1 -1
  77. package/dist/esm/fabric/FabricManager.js +2 -2
  78. package/dist/esm/fabric/FabricManager.js.map +1 -1
  79. package/dist/esm/peer/ControllerCommissioningFlow.js +2 -2
  80. package/dist/esm/peer/ControllerCommissioningFlow.js.map +1 -1
  81. package/dist/esm/session/SessionManager.d.ts +4 -0
  82. package/dist/esm/session/SessionManager.d.ts.map +1 -1
  83. package/dist/esm/session/SessionManager.js +11 -2
  84. package/dist/esm/session/SessionManager.js.map +1 -1
  85. package/dist/esm/session/case/CaseClient.d.ts.map +1 -1
  86. package/dist/esm/session/case/CaseClient.js +13 -10
  87. package/dist/esm/session/case/CaseClient.js.map +1 -1
  88. package/dist/esm/session/case/CaseServer.d.ts.map +1 -1
  89. package/dist/esm/session/case/CaseServer.js +11 -8
  90. package/dist/esm/session/case/CaseServer.js.map +1 -1
  91. package/dist/esm/session/pase/PaseClient.js +1 -1
  92. package/dist/esm/session/pase/PaseClient.js.map +1 -1
  93. package/dist/esm/session/pase/PaseServer.js +1 -1
  94. package/dist/esm/session/pase/PaseServer.js.map +1 -1
  95. package/package.json +6 -6
  96. package/src/certificate/AttestationCertificateManager.ts +37 -27
  97. package/src/certificate/CertificateAuthority.ts +60 -38
  98. package/src/certificate/CertificateManager.ts +20 -16
  99. package/src/certificate/DeviceCertification.ts +28 -32
  100. package/src/common/FailsafeContext.ts +29 -14
  101. package/src/fabric/Fabric.ts +17 -9
  102. package/src/fabric/FabricAuthority.ts +5 -4
  103. package/src/fabric/FabricManager.ts +2 -2
  104. package/src/peer/ControllerCommissioningFlow.ts +2 -2
  105. package/src/session/SessionManager.ts +13 -2
  106. package/src/session/case/CaseClient.ts +13 -10
  107. package/src/session/case/CaseServer.ts +11 -8
  108. package/src/session/pase/PaseClient.ts +1 -1
  109. package/src/session/pase/PaseServer.ts +1 -1
@@ -33,29 +33,39 @@ export class AttestationCertificateManager {
33
33
 
34
34
  // We use the official PAA cert for now because else pairing with Chip tool do not work because
35
35
  // only this one is the Certificate store
36
- private readonly paaKeyPair = PrivateKey(TestCert_PAA_NoVID_PrivateKey, {
36
+ readonly #paaKeyPair = PrivateKey(TestCert_PAA_NoVID_PrivateKey, {
37
37
  publicKey: TestCert_PAA_NoVID_PublicKey,
38
38
  });
39
- private readonly paaKeyIdentifier = TestCert_PAA_NoVID_SKID;
40
- private readonly paiCertId = BigInt(1);
41
- private readonly paiKeyPair = Crypto.createKeyPair();
42
- private readonly paiKeyIdentifier = Crypto.hash(this.paiKeyPair.publicKey).slice(0, 20);
43
- private readonly paiCertBytes;
44
- private nextCertificateId = 2;
39
+ readonly #vendorId: VendorId;
40
+ readonly #paiKeyPair: PrivateKey;
41
+ readonly #paiKeyIdentifier: Uint8Array;
42
+ readonly #paaKeyIdentifier = TestCert_PAA_NoVID_SKID;
43
+ readonly #paiCertId = BigInt(1);
44
+ readonly #paiCertBytes;
45
+ #nextCertificateId = 2;
45
46
 
46
- constructor(private readonly vendorId: VendorId) {
47
- this.paiCertBytes = this.generatePAICert(vendorId);
47
+ constructor(vendorId: VendorId, paiKeyPair: PrivateKey, paiKeyIdentifier: Uint8Array) {
48
+ this.#vendorId = vendorId;
49
+ this.#paiKeyPair = paiKeyPair;
50
+ this.#paiKeyIdentifier = paiKeyIdentifier;
51
+ this.#paiCertBytes = this.generatePAICert(vendorId);
52
+ }
53
+
54
+ static async create(vendorId: VendorId) {
55
+ const key = await Crypto.createKeyPair();
56
+ const identifier = await Crypto.hash(key.publicKey);
57
+ return new AttestationCertificateManager(vendorId, key, identifier.slice(0, 20));
48
58
  }
49
59
 
50
60
  getPAICert() {
51
- return this.paiCertBytes;
61
+ return this.#paiCertBytes;
52
62
  }
53
63
 
54
- getDACert(productId: number) {
55
- const dacKeyPair = Crypto.createKeyPair();
64
+ async getDACert(productId: number) {
65
+ const dacKeyPair = await Crypto.createKeyPair();
56
66
  return {
57
67
  keyPair: dacKeyPair,
58
- dac: this.generateDaCert(dacKeyPair.publicKey, this.vendorId, productId),
68
+ dac: await this.generateDaCert(dacKeyPair.publicKey, this.#vendorId, productId),
59
69
  };
60
70
  }
61
71
 
@@ -79,7 +89,7 @@ export class AttestationCertificateManager {
79
89
  commonName: getPaaCommonName(),
80
90
  vendorId: vendorId,
81
91
  },
82
- ellipticCurvePublicKey: this.paaKeyPair.publicKey,
92
+ ellipticCurvePublicKey: this.#paaKeyPair.publicKey,
83
93
  extensions: {
84
94
  basicConstraints: {
85
95
  isCa: true,
@@ -89,17 +99,17 @@ export class AttestationCertificateManager {
89
99
  keyCertSign: true,
90
100
  cRLSign: true,
91
101
  },
92
- subjectKeyIdentifier: this.paaKeyIdentifier,
93
- authorityKeyIdentifier: this.paaKeyIdentifier,
102
+ subjectKeyIdentifier: this.#paaKeyIdentifier,
103
+ authorityKeyIdentifier: this.#paaKeyIdentifier,
94
104
  },
95
105
  };
96
- return CertificateManager.productAttestationAuthorityCertToAsn1(unsignedCertificate, this.paaKeyPair);
106
+ return CertificateManager.productAttestationAuthorityCertToAsn1(unsignedCertificate, this.#paaKeyPair);
97
107
  }
98
108
 
99
109
  private generatePAICert(vendorId: VendorId, productId?: number) {
100
110
  const now = Time.get().now();
101
111
  const unsignedCertificate = {
102
- serialNumber: Bytes.fromHex(toHex(this.paiCertId)),
112
+ serialNumber: Bytes.fromHex(toHex(this.#paiCertId)),
103
113
  signatureAlgorithm: 1 /* EcdsaWithSHA256 */,
104
114
  publicKeyAlgorithm: 1 /* EC */,
105
115
  ellipticCurveIdentifier: 1 /* P256v1 */,
@@ -113,7 +123,7 @@ export class AttestationCertificateManager {
113
123
  vendorId: vendorId,
114
124
  productId: productId,
115
125
  },
116
- ellipticCurvePublicKey: this.paiKeyPair.publicKey,
126
+ ellipticCurvePublicKey: this.#paiKeyPair.publicKey,
117
127
  extensions: {
118
128
  basicConstraints: {
119
129
  isCa: true,
@@ -123,16 +133,16 @@ export class AttestationCertificateManager {
123
133
  keyCertSign: true,
124
134
  cRLSign: true,
125
135
  },
126
- subjectKeyIdentifier: this.paiKeyIdentifier,
127
- authorityKeyIdentifier: this.paaKeyIdentifier,
136
+ subjectKeyIdentifier: this.#paiKeyIdentifier,
137
+ authorityKeyIdentifier: this.#paaKeyIdentifier,
128
138
  },
129
139
  };
130
- return CertificateManager.productAttestationIntermediateCertToAsn1(unsignedCertificate, this.paaKeyPair);
140
+ return CertificateManager.productAttestationIntermediateCertToAsn1(unsignedCertificate, this.#paaKeyPair);
131
141
  }
132
142
 
133
- generateDaCert(publicKey: Uint8Array, vendorId: VendorId, productId: number) {
143
+ async generateDaCert(publicKey: Uint8Array, vendorId: VendorId, productId: number) {
134
144
  const now = Time.get().now();
135
- const certId = this.nextCertificateId++;
145
+ const certId = this.#nextCertificateId++;
136
146
  const unsignedCertificate = {
137
147
  serialNumber: Bytes.fromHex(toHex(certId)),
138
148
  signatureAlgorithm: 1 /* EcdsaWithSHA256 */,
@@ -157,10 +167,10 @@ export class AttestationCertificateManager {
157
167
  keyUsage: {
158
168
  digitalSignature: true,
159
169
  },
160
- subjectKeyIdentifier: Crypto.hash(publicKey).slice(0, 20),
161
- authorityKeyIdentifier: this.paiKeyIdentifier,
170
+ subjectKeyIdentifier: (await Crypto.hash(publicKey)).slice(0, 20),
171
+ authorityKeyIdentifier: this.#paiKeyIdentifier,
162
172
  },
163
173
  };
164
- return CertificateManager.deviceAttestationCertToAsn1(unsignedCertificate, this.paiKeyPair);
174
+ return CertificateManager.deviceAttestationCertToAsn1(unsignedCertificate, this.#paiKeyPair);
165
175
  }
166
176
  }
@@ -11,6 +11,7 @@ import {
11
11
  Crypto,
12
12
  Environment,
13
13
  Environmental,
14
+ InternalError,
14
15
  Logger,
15
16
  PrivateKey,
16
17
  StorageContext,
@@ -37,11 +38,11 @@ const logger = Logger.get("CertificateAuthority");
37
38
  * TODO: Add support for (optional) ICACs
38
39
  */
39
40
  export class CertificateAuthority {
40
- private rootCertId = BigInt(0);
41
- private rootKeyPair = Crypto.createKeyPair();
42
- private rootKeyIdentifier: Uint8Array<ArrayBufferLike> = Crypto.hash(this.rootKeyPair.publicKey).slice(0, 20);
43
- private rootCertBytes: Uint8Array<ArrayBufferLike> = this.#generateRootCert();
44
- private nextCertificateId = BigInt(1);
41
+ #rootCertId = BigInt(0);
42
+ #rootKeyPair?: PrivateKey;
43
+ #rootKeyIdentifier?: Uint8Array<ArrayBufferLike>;
44
+ #rootCertBytes?: Uint8Array<ArrayBufferLike>;
45
+ #nextCertificateId = BigInt(1);
45
46
  #construction: Construction<CertificateAuthority>;
46
47
 
47
48
  get construction() {
@@ -57,6 +58,10 @@ export class CertificateAuthority {
57
58
  // Use provided CA config or read from storage, otherwise initialize and store
58
59
  const certValues = options instanceof StorageContext ? await options.values() : options;
59
60
 
61
+ this.#rootKeyPair = await Crypto.createKeyPair();
62
+ this.#rootKeyIdentifier = (await Crypto.hash(this.#rootKeyPair.publicKey)).slice(0, 20);
63
+ this.#rootCertBytes = await this.#generateRootCert();
64
+
60
65
  if (
61
66
  (typeof certValues.rootCertId === "number" || typeof certValues.rootCertId === "bigint") &&
62
67
  (ArrayBuffer.isView(certValues.rootKeyPair) || typeof certValues.rootKeyPair === "object") &&
@@ -64,24 +69,24 @@ export class CertificateAuthority {
64
69
  ArrayBuffer.isView(certValues.rootCertBytes) &&
65
70
  (typeof certValues.nextCertificateId === "number" || typeof certValues.nextCertificateId === "bigint")
66
71
  ) {
67
- this.rootCertId = BigInt(certValues.rootCertId);
68
- this.rootKeyPair = PrivateKey(certValues.rootKeyPair as BinaryKeyPair);
69
- this.rootKeyIdentifier = certValues.rootKeyIdentifier;
70
- this.rootCertBytes = certValues.rootCertBytes;
71
- this.nextCertificateId = BigInt(certValues.nextCertificateId);
72
- logger.info(`Loaded stored credentials with ID ${this.rootCertId}`);
72
+ this.#rootCertId = BigInt(certValues.rootCertId);
73
+ this.#rootKeyPair = PrivateKey(certValues.rootKeyPair as BinaryKeyPair);
74
+ this.#rootKeyIdentifier = certValues.rootKeyIdentifier;
75
+ this.#rootCertBytes = certValues.rootCertBytes;
76
+ this.#nextCertificateId = BigInt(certValues.nextCertificateId);
77
+ logger.info(`Loaded stored credentials with ID ${this.#rootCertId}`);
73
78
  return;
74
79
  }
75
80
 
76
- logger.info(`Created new credentials with ID ${this.rootCertId}`);
81
+ logger.info(`Created new credentials with ID ${this.#rootCertId}`);
77
82
 
78
83
  if (options instanceof StorageContext) {
79
84
  await options.set({
80
- rootCertId: this.rootCertId,
81
- rootKeyPair: this.rootKeyPair.keyPair,
82
- rootKeyIdentifier: this.rootKeyIdentifier,
83
- rootCertBytes: this.rootCertBytes,
84
- nextCertificateId: this.nextCertificateId,
85
+ rootCertId: this.#rootCertId,
86
+ rootKeyPair: this.#rootKeyPair.keyPair,
87
+ rootKeyIdentifier: this.#rootKeyIdentifier,
88
+ rootCertBytes: this.#rootCertBytes,
89
+ nextCertificateId: this.#nextCertificateId,
85
90
  });
86
91
  }
87
92
  });
@@ -95,59 +100,62 @@ export class CertificateAuthority {
95
100
  }
96
101
 
97
102
  get rootCert() {
98
- return this.rootCertBytes;
103
+ return this.#construction.assert("root cert", this.#rootCertBytes);
99
104
  }
100
105
 
101
106
  get config(): CertificateAuthority.Configuration {
102
107
  return {
103
- rootCertId: this.rootCertId,
104
- rootKeyPair: this.rootKeyPair.keyPair,
105
- rootKeyIdentifier: this.rootKeyIdentifier,
106
- rootCertBytes: this.rootCertBytes,
107
- nextCertificateId: this.nextCertificateId,
108
+ rootCertId: this.#rootCertId,
109
+ rootKeyPair: this.construction.assert("root key pair", this.#rootKeyPair).keyPair,
110
+ rootKeyIdentifier: this.construction.assert("root key identifier", this.#rootKeyIdentifier),
111
+ rootCertBytes: this.construction.assert("root cert bytes", this.#rootCertBytes),
112
+ nextCertificateId: this.#nextCertificateId,
108
113
  };
109
114
  }
110
115
 
111
- #generateRootCert() {
116
+ async #generateRootCert() {
112
117
  const now = Time.get().now();
113
118
  const unsignedCertificate: Unsigned<RootCertificate> = {
114
- serialNumber: Bytes.fromHex(toHex(this.rootCertId)),
119
+ serialNumber: Bytes.fromHex(toHex(this.#rootCertId)),
115
120
  signatureAlgorithm: 1 /* EcdsaWithSHA256 */,
116
121
  publicKeyAlgorithm: 1 /* EC */,
117
122
  ellipticCurveIdentifier: 1 /* P256v1 */,
118
- issuer: { rcacId: this.rootCertId },
123
+ issuer: { rcacId: this.#rootCertId },
119
124
  notBefore: jsToMatterDate(now, -1),
120
125
  notAfter: jsToMatterDate(now, 10),
121
- subject: { rcacId: this.rootCertId },
122
- ellipticCurvePublicKey: this.rootKeyPair.publicKey,
126
+ subject: { rcacId: this.#rootCertId },
127
+ ellipticCurvePublicKey: this.#initializedRootKeyPair.publicKey,
123
128
  extensions: {
124
129
  basicConstraints: { isCa: true },
125
130
  keyUsage: {
126
131
  keyCertSign: true,
127
132
  cRLSign: true,
128
133
  },
129
- subjectKeyIdentifier: this.rootKeyIdentifier,
130
- authorityKeyIdentifier: this.rootKeyIdentifier,
134
+ subjectKeyIdentifier: this.#initializedRootKeyIdentifier,
135
+ authorityKeyIdentifier: this.#initializedRootKeyIdentifier,
131
136
  },
132
137
  };
133
- const signature = Crypto.sign(this.rootKeyPair, CertificateManager.rootCertToAsn1(unsignedCertificate));
138
+ const signature = await Crypto.sign(
139
+ this.#initializedRootKeyPair,
140
+ CertificateManager.rootCertToAsn1(unsignedCertificate),
141
+ );
134
142
  return TlvRootCertificate.encode({ ...unsignedCertificate, signature });
135
143
  }
136
144
 
137
- generateNoc(
145
+ async generateNoc(
138
146
  publicKey: Uint8Array,
139
147
  fabricId: FabricId,
140
148
  nodeId: NodeId,
141
149
  caseAuthenticatedTags?: CaseAuthenticatedTag[],
142
150
  ) {
143
151
  const now = Time.get().now();
144
- const certId = this.nextCertificateId++;
152
+ const certId = this.#nextCertificateId++;
145
153
  const unsignedCertificate: Unsigned<OperationalCertificate> = {
146
154
  serialNumber: Bytes.fromHex(toHex(certId)),
147
155
  signatureAlgorithm: 1 /* EcdsaWithSHA256 */,
148
156
  publicKeyAlgorithm: 1 /* EC */,
149
157
  ellipticCurveIdentifier: 1 /* P256v1 */,
150
- issuer: { rcacId: this.rootCertId },
158
+ issuer: { rcacId: this.#rootCertId },
151
159
  notBefore: jsToMatterDate(now, -1),
152
160
  notAfter: jsToMatterDate(now, 10),
153
161
  subject: { fabricId, nodeId, caseAuthenticatedTags },
@@ -158,18 +166,32 @@ export class CertificateAuthority {
158
166
  digitalSignature: true,
159
167
  },
160
168
  extendedKeyUsage: [2, 1],
161
- subjectKeyIdentifier: Crypto.hash(publicKey).slice(0, 20),
162
- authorityKeyIdentifier: this.rootKeyIdentifier,
169
+ subjectKeyIdentifier: (await Crypto.hash(publicKey)).slice(0, 20),
170
+ authorityKeyIdentifier: this.#initializedRootKeyIdentifier,
163
171
  },
164
172
  };
165
173
 
166
- const signature = Crypto.sign(
167
- this.rootKeyPair,
174
+ const signature = await Crypto.sign(
175
+ this.#initializedRootKeyPair,
168
176
  CertificateManager.nodeOperationalCertToAsn1(unsignedCertificate),
169
177
  );
170
178
 
171
179
  return TlvOperationalCertificate.encode({ ...unsignedCertificate, signature });
172
180
  }
181
+
182
+ get #initializedRootKeyPair() {
183
+ if (this.#rootKeyPair === undefined) {
184
+ throw new InternalError("CA private key is not installed");
185
+ }
186
+ return this.#rootKeyPair;
187
+ }
188
+
189
+ get #initializedRootKeyIdentifier() {
190
+ if (this.#rootKeyIdentifier === undefined) {
191
+ throw new InternalError("CA key identifier is not installed");
192
+ }
193
+ return this.#rootKeyIdentifier;
194
+ }
173
195
  }
174
196
 
175
197
  export namespace CertificateAuthority {
@@ -664,18 +664,18 @@ export namespace CertificateManager {
664
664
  return genericCertToAsn1(cert);
665
665
  }
666
666
 
667
- export function deviceAttestationCertToAsn1(cert: Unsigned<DeviceAttestationCertificate>, key: Key) {
667
+ export async function deviceAttestationCertToAsn1(cert: Unsigned<DeviceAttestationCertificate>, key: Key) {
668
668
  const certificate = genericBuildAsn1Structure(cert);
669
669
  const certBytes = DerCodec.encode({
670
670
  certificate,
671
671
  signAlgorithm: X962.EcdsaWithSHA256,
672
- signature: BitByteArray(Crypto.sign(key, DerCodec.encode(certificate), "der")),
672
+ signature: BitByteArray(await Crypto.sign(key, DerCodec.encode(certificate), "der")),
673
673
  });
674
674
  assertCertificateDerSize(certBytes);
675
675
  return certBytes;
676
676
  }
677
677
 
678
- export function productAttestationIntermediateCertToAsn1(
678
+ export async function productAttestationIntermediateCertToAsn1(
679
679
  cert: Unsigned<ProductAttestationIntermediateCertificate>,
680
680
  key: Key,
681
681
  ) {
@@ -683,13 +683,13 @@ export namespace CertificateManager {
683
683
  const certBytes = DerCodec.encode({
684
684
  certificate,
685
685
  signAlgorithm: X962.EcdsaWithSHA256,
686
- signature: BitByteArray(Crypto.sign(key, DerCodec.encode(certificate), "der")),
686
+ signature: BitByteArray(await Crypto.sign(key, DerCodec.encode(certificate), "der")),
687
687
  });
688
688
  assertCertificateDerSize(certBytes);
689
689
  return certBytes;
690
690
  }
691
691
 
692
- export function productAttestationAuthorityCertToAsn1(
692
+ export async function productAttestationAuthorityCertToAsn1(
693
693
  cert: Unsigned<ProductAttestationAuthorityCertificate>,
694
694
  key: Key,
695
695
  ) {
@@ -697,7 +697,7 @@ export namespace CertificateManager {
697
697
  const certBytes = DerCodec.encode({
698
698
  certificate,
699
699
  signAlgorithm: X962.EcdsaWithSHA256,
700
- signature: BitByteArray(Crypto.sign(key, DerCodec.encode(certificate), "der")),
700
+ signature: BitByteArray(await Crypto.sign(key, DerCodec.encode(certificate), "der")),
701
701
  });
702
702
  assertCertificateDerSize(certBytes);
703
703
  return certBytes;
@@ -777,7 +777,7 @@ export namespace CertificateManager {
777
777
  * Verify requirements a Matter Root certificate must fulfill.
778
778
  * Rules for this are listed in @see {@link MatterSpecification.v12.Core} §6.5.x
779
779
  */
780
- export function verifyRootCertificate(rootCert: RootCertificate) {
780
+ export async function verifyRootCertificate(rootCert: RootCertificate) {
781
781
  CertificateManager.validateGeneralCertificateFields(rootCert);
782
782
 
783
783
  // The subject DN SHALL NOT encode any matter-node-id attribute.
@@ -861,14 +861,14 @@ export namespace CertificateManager {
861
861
  );
862
862
  }
863
863
 
864
- Crypto.verify(PublicKey(rootCert.ellipticCurvePublicKey), rootCertToAsn1(rootCert), rootCert.signature);
864
+ await Crypto.verify(PublicKey(rootCert.ellipticCurvePublicKey), rootCertToAsn1(rootCert), rootCert.signature);
865
865
  }
866
866
 
867
867
  /**
868
868
  * Verify requirements a Matter Node Operational certificate must fulfill.
869
869
  * Rules for this are listed in @see {@link MatterSpecification.v12.Core} §6.5.x
870
870
  */
871
- export function verifyNodeOperationalCertificate(
871
+ export async function verifyNodeOperationalCertificate(
872
872
  nocCert: OperationalCertificate,
873
873
  rootCert: RootCertificate,
874
874
  icaCert?: IntermediateCertificate,
@@ -985,7 +985,7 @@ export namespace CertificateManager {
985
985
  );
986
986
  }
987
987
 
988
- Crypto.verify(
988
+ await Crypto.verify(
989
989
  PublicKey((icaCert ?? rootCert).ellipticCurvePublicKey),
990
990
  nodeOperationalCertToAsn1(nocCert),
991
991
  nocCert.signature,
@@ -996,7 +996,7 @@ export namespace CertificateManager {
996
996
  * Verify requirements a Matter Intermediate CA certificate must fulfill.
997
997
  * Rules for this are listed in @see {@link MatterSpecification.v12.Core} §6.5.x
998
998
  */
999
- export function verifyIntermediateCaCertificate(rootCert: RootCertificate, icaCert: IntermediateCertificate) {
999
+ export async function verifyIntermediateCaCertificate(rootCert: RootCertificate, icaCert: IntermediateCertificate) {
1000
1000
  CertificateManager.validateGeneralCertificateFields(icaCert);
1001
1001
 
1002
1002
  // The subject DN SHALL NOT encode any matter-node-id attribute.
@@ -1103,10 +1103,14 @@ export namespace CertificateManager {
1103
1103
  );
1104
1104
  }
1105
1105
 
1106
- Crypto.verify(PublicKey(rootCert.ellipticCurvePublicKey), intermediateCaCertToAsn1(icaCert), icaCert.signature);
1106
+ await Crypto.verify(
1107
+ PublicKey(rootCert.ellipticCurvePublicKey),
1108
+ intermediateCaCertToAsn1(icaCert),
1109
+ icaCert.signature,
1110
+ );
1107
1111
  }
1108
1112
 
1109
- export function createCertificateSigningRequest(key: Key) {
1113
+ export async function createCertificateSigningRequest(key: Key) {
1110
1114
  const request = {
1111
1115
  version: 0,
1112
1116
  subject: { organization: X520.OrganisationName("CSR") },
@@ -1117,11 +1121,11 @@ export namespace CertificateManager {
1117
1121
  return DerCodec.encode({
1118
1122
  request,
1119
1123
  signAlgorithm: X962.EcdsaWithSHA256,
1120
- signature: BitByteArray(Crypto.sign(key, DerCodec.encode(request), "der")),
1124
+ signature: BitByteArray(await Crypto.sign(key, DerCodec.encode(request), "der")),
1121
1125
  });
1122
1126
  }
1123
1127
 
1124
- export function getPublicKeyFromCsr(csr: Uint8Array) {
1128
+ export async function getPublicKeyFromCsr(csr: Uint8Array) {
1125
1129
  const { [DerKey.Elements]: rootElements } = DerCodec.decode(csr);
1126
1130
  if (rootElements?.length !== 3) throw new CertificateError("Invalid CSR data");
1127
1131
  const [requestNode, signAlgorithmNode, signatureNode] = rootElements;
@@ -1149,7 +1153,7 @@ export namespace CertificateManager {
1149
1153
  )
1150
1154
  )
1151
1155
  throw new CertificateError("Unsupported signature type");
1152
- Crypto.verify(PublicKey(publicKey), DerCodec.encode(requestNode), signatureNode[DerKey.Bytes], "der");
1156
+ await Crypto.verify(PublicKey(publicKey), DerCodec.encode(requestNode), signatureNode[DerKey.Bytes], "der");
1153
1157
 
1154
1158
  return publicKey;
1155
1159
  }
@@ -37,42 +37,38 @@ export class DeviceCertification {
37
37
  }
38
38
 
39
39
  constructor(config?: DeviceCertification.Definition, product?: ProductDescription) {
40
- // Certification Provider function is used to request the certificates delayed
40
+ let configProvider;
41
41
  if (typeof config === "function") {
42
- const configProvider = config;
43
- this.#construction = Construction(this, async () => {
44
- this.#initializeFromConfig(await configProvider());
45
- });
46
- return;
47
- }
48
-
49
- // We need a dummy construction to avoid errors
50
- this.#construction = Construction(this, () => {});
51
-
52
- // With a directly provided config or without we can initialize directly
53
- if (config === undefined) {
54
- if (product === undefined) {
55
- throw new ImplementationError(`Cannot generate device certification without product information`);
56
- }
57
-
58
- const paa = new AttestationCertificateManager(product.vendorId);
59
- const { keyPair: dacKeyPair, dac } = paa.getDACert(product.productId);
60
-
61
- config = {
62
- privateKey: PrivateKey(dacKeyPair.privateKey),
63
- certificate: dac,
64
- intermediateCertificate: paa.getPAICert(),
65
- declaration: CertificationDeclarationManager.generate(product.vendorId, product.productId),
42
+ configProvider = config;
43
+ } else if (config) {
44
+ configProvider = () => config;
45
+ } else {
46
+ configProvider = async () => {
47
+ if (product === undefined) {
48
+ throw new ImplementationError(`Cannot generate device certification without product information`);
49
+ }
50
+
51
+ const paa = await AttestationCertificateManager.create(product.vendorId);
52
+ const { keyPair: dacKeyPair, dac } = await paa.getDACert(product.productId);
53
+
54
+ return {
55
+ privateKey: PrivateKey(dacKeyPair.privateKey),
56
+ certificate: dac,
57
+ intermediateCertificate: await paa.getPAICert(),
58
+ declaration: CertificationDeclarationManager.generate(product.vendorId, product.productId),
59
+ };
66
60
  };
67
61
  }
68
- this.#initializeFromConfig(config);
69
- }
70
62
 
71
- #initializeFromConfig(config: DeviceCertification.Configuration) {
72
- this.#privateKey = config.privateKey instanceof Uint8Array ? PrivateKey(config.privateKey) : config.privateKey;
73
- this.#certificate = config.certificate;
74
- this.#intermediateCertificate = config.intermediateCertificate;
75
- this.#declaration = config.declaration;
63
+ this.#construction = Construction(this, async () => {
64
+ const config = await configProvider();
65
+
66
+ this.#privateKey =
67
+ config.privateKey instanceof Uint8Array ? PrivateKey(config.privateKey) : config.privateKey;
68
+ this.#certificate = config.certificate;
69
+ this.#intermediateCertificate = config.intermediateCertificate;
70
+ this.#declaration = config.declaration;
71
+ });
76
72
  }
77
73
 
78
74
  sign(session: SecureSession, data: Uint8Array) {
@@ -4,7 +4,14 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
 
7
- import { AsyncObservable, Construction, Logger, MatterFlowError, UnexpectedDataError } from "#general";
7
+ import {
8
+ AsyncObservable,
9
+ Construction,
10
+ Logger,
11
+ MatterFlowError,
12
+ UnexpectedDataError,
13
+ UninitializedDependencyError,
14
+ } from "#general";
8
15
  import { CaseAuthenticatedTag, NodeId, ValidationError, VendorId } from "#types";
9
16
  import { Fabric, FabricBuilder } from "../fabric/Fabric.js";
10
17
  import { FabricManager } from "../fabric/FabricManager.js";
@@ -32,7 +39,7 @@ export abstract class FailsafeContext {
32
39
  #associatedFabric?: Fabric;
33
40
  #csrSessionId?: number;
34
41
  #forUpdateNoc?: boolean;
35
- #fabricBuilder = new FabricBuilder();
42
+ #fabricBuilder?: FabricBuilder;
36
43
  #rootCertSet = false;
37
44
 
38
45
  #commissioned = AsyncObservable<[], void>();
@@ -45,6 +52,7 @@ export abstract class FailsafeContext {
45
52
  this.#associatedFabric = options.associatedFabric;
46
53
 
47
54
  this.#construction = Construction(this, async () => {
55
+ this.#fabricBuilder = await FabricBuilder.create();
48
56
  // Ensure derived class construction is complete
49
57
  await Promise.resolve();
50
58
 
@@ -71,7 +79,7 @@ export abstract class FailsafeContext {
71
79
  }
72
80
 
73
81
  get fabricIndex() {
74
- return this.#fabricBuilder.fabricIndex;
82
+ return this.#builder.fabricIndex;
75
83
  }
76
84
 
77
85
  get construction() {
@@ -99,11 +107,11 @@ export abstract class FailsafeContext {
99
107
  }
100
108
 
101
109
  get hasRootCert() {
102
- return this.#fabricBuilder.rootCert !== undefined;
110
+ return this.#builder.rootCert !== undefined;
103
111
  }
104
112
 
105
113
  get rootCert() {
106
- return this.#fabricBuilder.rootCert;
114
+ return this.#builder.rootCert;
107
115
  }
108
116
 
109
117
  async completeCommission() {
@@ -157,11 +165,11 @@ export abstract class FailsafeContext {
157
165
  * validity checks.
158
166
  */
159
167
  createCertificateSigningRequest(isForUpdateNoc: boolean, sessionId: number) {
160
- if (this.#fabrics.findByKeypair(this.#fabricBuilder.keyPair)) {
168
+ if (this.#fabrics.findByKeypair(this.#builder.keyPair)) {
161
169
  throw new MatterFlowError("Key pair already exists."); // becomes Failure as StatusResponse
162
170
  }
163
171
 
164
- const result = this.#fabricBuilder.createCertificateSigningRequest();
172
+ const result = this.#builder.createCertificateSigningRequest();
165
173
  this.#csrSessionId = sessionId;
166
174
  this.#forUpdateNoc = isForUpdateNoc;
167
175
  return result;
@@ -186,8 +194,8 @@ export abstract class FailsafeContext {
186
194
  }
187
195
 
188
196
  /** Handles adding a trusted root certificate from Operational Credentials cluster. */
189
- setRootCert(rootCert: Uint8Array) {
190
- this.#fabricBuilder.setRootCert(rootCert);
197
+ async setRootCert(rootCert: Uint8Array) {
198
+ await this.#builder.setRootCert(rootCert);
191
199
  this.#rootCertSet = true;
192
200
  }
193
201
 
@@ -199,9 +207,9 @@ export abstract class FailsafeContext {
199
207
  if (this.associatedFabric === undefined) {
200
208
  throw new MatterFlowError("No fabric associated with failsafe context, but we prepare an Fabric update.");
201
209
  }
202
- this.#fabricBuilder.initializeFromFabricForUpdate(this.associatedFabric);
203
- this.#fabricBuilder.setOperationalCert(nocValue, icacValue);
204
- return await this.#fabricBuilder.build(this.associatedFabric.fabricIndex);
210
+ this.#builder.initializeFromFabricForUpdate(this.associatedFabric);
211
+ await this.#builder.setOperationalCert(nocValue, icacValue);
212
+ return await this.#builder.build(this.associatedFabric.fabricIndex);
205
213
  }
206
214
 
207
215
  /** Build a new Fabric object for a new fabric for the "AddNoc" case of the Operational Credentials cluster. */
@@ -212,7 +220,7 @@ export abstract class FailsafeContext {
212
220
  ipkValue: Uint8Array;
213
221
  caseAdminSubject: NodeId;
214
222
  }) {
215
- const builder = this.#fabricBuilder;
223
+ const builder = this.#builder;
216
224
 
217
225
  const { nocValue, icacValue, adminVendorId, ipkValue, caseAdminSubject } = nocData;
218
226
 
@@ -232,7 +240,7 @@ export abstract class FailsafeContext {
232
240
  }
233
241
  }
234
242
 
235
- builder.setOperationalCert(nocValue, icacValue);
243
+ await builder.setOperationalCert(nocValue, icacValue);
236
244
  const fabricAlreadyExisting = this.#fabrics.find(fabric => builder.matchesToFabric(fabric));
237
245
 
238
246
  if (fabricAlreadyExisting) {
@@ -320,6 +328,13 @@ export abstract class FailsafeContext {
320
328
  abstract revokeFabric(fabric: Fabric): Promise<void>;
321
329
 
322
330
  abstract restoreBreadcrumb(): Promise<void>;
331
+
332
+ get #builder() {
333
+ if (this.#fabricBuilder === undefined) {
334
+ throw new UninitializedDependencyError("FailsafeContext", "Fabric builder has not been initialized");
335
+ }
336
+ return this.#fabricBuilder;
337
+ }
323
338
  }
324
339
 
325
340
  export namespace FailsafeContext {