@superhero/eventflow-certificates 4.0.0

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/LICENCE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Erik Landvall
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,218 @@
1
+ # @superhero/eventflow-certificates
2
+
3
+ **Version:** 4.0.0
4
+
5
+ Eventflow Certificates is a TLS certificates management library designed for use within the Eventflow ecosystem. It handles the creation, management, and lifecycle of root, intermediate, and leaf certificates with encryption and secure storage capabilities.
6
+
7
+ ---
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ npm install @superhero/eventflow-certificates
13
+ ```
14
+
15
+ ---
16
+
17
+ ## Features
18
+
19
+ - Root, intermediate, and leaf certificate generation
20
+ - Secure encryption and storage of private keys and passwords
21
+ - Automatic certificate renewal upon expiration
22
+ - Configurable encryption algorithms and certificate parameters
23
+ - Lazy-loading with caching to minimize resource usage
24
+ - Easy integration with Eventflow's database layer
25
+
26
+ ---
27
+
28
+ ## Dependencies
29
+
30
+ - [@superhero/eventflow-db](https://npmjs.com/package/@superhero/eventflow-db)
31
+ - [@superhero/log](https://npmjs.com/package/@superhero/log)
32
+ - [@superhero/openssl](https://npmjs.com/package/@superhero/openssl)
33
+ - [@superhero/deep](https://npmjs.com/package/@superhero/deep)
34
+
35
+ ---
36
+
37
+ ## Usage
38
+
39
+ ### Example
40
+
41
+ ```javascript
42
+ import Certificates from '@superhero/eventflow-certificates';
43
+ import Locator from '@superhero/locator';
44
+ import Config from '@superhero/config';
45
+
46
+ const locator = new Locator();
47
+ const config = new Config();
48
+ await config.add('@superhero/eventflow-db');
49
+ locator.set('@superhero/config', config);
50
+
51
+ const db = await locator.lazyload('@superhero/eventflow-db');
52
+
53
+ const configData =
54
+ {
55
+ CERT_PASS_ENCRYPTION_KEY : 'encryptionKey123',
56
+ CERT_ROOT_DAYS : 365,
57
+ CERT_INTERMEDIATE_DAYS : 30,
58
+ CERT_LEAF_DAYS : 7,
59
+ CERT_ALGORITHM : 'EdDSA:Ed25519',
60
+ CERT_HASH : 'sha256',
61
+ };
62
+
63
+ const intermediateUID = 'INTERMEDIATE-CERT-ID';
64
+ const leafUID = 'LEAF-CERT-ID';
65
+ const certificates = new Certificates(intermediateUID, leafUID, configData, db);
66
+ const rootCert = await certificates.root;
67
+ const intermediateCert = await certificates.intermediate;
68
+ const leafCert = await certificates.leaf;
69
+ ```
70
+
71
+ ---
72
+
73
+ ## Configuration
74
+
75
+ The `Certificates` class accepts a configuration object with the following properties:
76
+
77
+ | Property | Type | Description |
78
+ |---------------------------|----------|-----------------------------------------------------------------------------|
79
+ | `CERT_PASS_ENCRYPTION_KEY`| `string` | Encryption key used to secure certificate passwords and private keys. |
80
+ | `CERT_ROOT_DAYS` | `number` | Validity period of the root certificate in days. |
81
+ | `CERT_INTERMEDIATE_DAYS` | `number` | Validity period of the intermediate certificate in days. |
82
+ | `CERT_LEAF_DAYS` | `number` | Validity period of the leaf certificate in days. |
83
+ | `CERT_ALGORITHM` | `string` | Algorithm used for certificate generation (e.g., `rsa`, `ecdsa`). |
84
+ | `CERT_HASH` | `string` | Hash function for certificate signing (e.g., `sha256`, `sha512`). |
85
+
86
+ ---
87
+
88
+ ## Methods
89
+
90
+ ### Constructor
91
+
92
+ ```javascript
93
+ constructor(intermediateUID, leafUID, config, db)
94
+ ```
95
+
96
+ - **Parameters:**
97
+ - `intermediateUID`: A unique identifier for the intermediate certificate.
98
+ - `leafUID`: A unique identifier for the leaf certificate.
99
+ - `config`: Configuration object.
100
+ - `db`: Database instance from Eventflow's database layer.
101
+
102
+ ### clearCache
103
+
104
+ Clears the cached certificates.
105
+
106
+ ```javascript
107
+ clearCache();
108
+ ```
109
+
110
+ ### persist
111
+
112
+ Stores a certificate in the database.
113
+
114
+ ```javascript
115
+ persist(id, validity, cert, key, pass);
116
+ ```
117
+
118
+ - **Parameters:**
119
+ - `id`: Certificate identifier.
120
+ - `validity`: Expiration date of the certificate.
121
+ - `cert`: Certificate content.
122
+ - `key`: Private key of the certificate.
123
+ - `pass`: Password for the private key.
124
+
125
+ ### revoke
126
+
127
+ Revokes a certificate by its ID.
128
+
129
+ ```javascript
130
+ revoke(id);
131
+ ```
132
+
133
+ - **Parameters:**
134
+ - `id`: Certificate identifier.
135
+
136
+ ### root
137
+
138
+ Retrieves the root certificate.
139
+
140
+ ```javascript
141
+ const rootCertificate = await certificates.root;
142
+ ```
143
+
144
+ ### intermediate
145
+
146
+ Retrieves the intermediate certificate.
147
+
148
+ ```javascript
149
+ const intermediateCertificate = await certificates.intermediate;
150
+ ```
151
+
152
+ ### leaf
153
+
154
+ Retrieves the leaf certificate.
155
+
156
+ ```javascript
157
+ const leafCertificate = await certificates.leaf;
158
+ ```
159
+
160
+ ---
161
+
162
+ ## Testing
163
+
164
+ Run the test suite with the following command:
165
+
166
+ ```bash
167
+ npm run test-build
168
+ npm test
169
+ ```
170
+
171
+ The test suite includes comprehensive cases to validate functionality, such as:
172
+
173
+ - Certificate creation and retrieval
174
+ - Cache clearing and lazy loading
175
+ - Certificate expiration handling
176
+ - Error handling for missing configuration
177
+
178
+ ### Test Coverage
179
+
180
+ ```
181
+ ▶ @superhero/eventflow-certificates
182
+ ✔ Throw error if CERT_PASS_ENCRYPTION_KEY is missing in config (2.592666ms)
183
+
184
+ ▶ Get root certificate
185
+ ✔ Get same root certificate each time lazyloading it (0.443311ms)
186
+
187
+ ▶ Get intermediate certificate
188
+ ▶ Get leaf certificate
189
+ ✔ Clear cache and still get the same certificates (5666.373892ms)
190
+ ✔ Revoke certificate and regenerate when expired (11.303099ms)
191
+ ✔ Get leaf certificate (7800.137773ms)
192
+ ✔ Get intermediate certificate (9664.033522ms)
193
+ ✔ Get root certificate (11692.320218ms)
194
+ ✔ @superhero/eventflow-certificates (11699.129313ms)
195
+
196
+ tests 7
197
+ suites 1
198
+ pass 7
199
+
200
+ ----------------------------------------------------------------------------------------
201
+ file | line % | branch % | funcs % | uncovered lines
202
+ ----------------------------------------------------------------------------------------
203
+ index.js | 85.91 | 87.50 | 88.24 | 137-140 154-158 198-204 208-217 220-235
204
+ index.test.js | 100.00 | 100.00 | 100.00 |
205
+ ----------------------------------------------------------------------------------------
206
+ all files | 89.83 | 91.67 | 92.86 |
207
+ ----------------------------------------------------------------------------------------
208
+ ```
209
+
210
+ ---
211
+
212
+ ## License
213
+
214
+ This project is licensed under the MIT License.
215
+
216
+ ## Contributing
217
+
218
+ Feel free to submit issues or pull requests for improvements or additional features.
package/index.js ADDED
@@ -0,0 +1,298 @@
1
+ import crypto from 'node:crypto'
2
+ import Log from '@superhero/log'
3
+ import OpenSSL from '@superhero/openssl'
4
+ import deepassign from '@superhero/deep/assign'
5
+
6
+ /**
7
+ * @memberof Eventflow
8
+ */
9
+ export default class Certificates
10
+ {
11
+ #db
12
+ log = new Log({ label: '[EVENTFLOW:CERTIFICATES]' })
13
+ #map = new Map()
14
+ openSSL = new OpenSSL()
15
+ config =
16
+ {
17
+ CERT_ALGORITHM : OpenSSL.ALGO.EdDSAEd448,
18
+ CERT_HASH : OpenSSL.HASH.SHA512,
19
+ CERT_ROOT_DAYS : 365,
20
+ CERT_INTERMEDIATE_DAYS : 30,
21
+ CERT_LEAF_DAYS : 7,
22
+ CERT_PASS_CIPHER : 'aes-256-gcm',
23
+ CERT_PASS_PBKDF2_HASH : 'sha512',
24
+ CERT_PASS_PBKDF2_BYTES : 32,
25
+ CERT_PASS_PBKDF2_ITERATIONS : 1e6
26
+ }
27
+
28
+ constructor(intermediateUID, leafUID, config, db)
29
+ {
30
+ this.intermediateUID = intermediateUID
31
+ this.leafUID = leafUID
32
+ this.#db = db
33
+
34
+ deepassign(this.config, config)
35
+
36
+ if(false === !!this.config.CERT_PASS_ENCRYPTION_KEY)
37
+ {
38
+ const error = new Error('missing configured certification password encryption key')
39
+ error.code = 'E_EVENTFLOW_CERTIFICATES_MISSING_CONFIGURATION'
40
+ error.cause = 'The encryption key is required to encrypt and decrypt the certificates password and private key'
41
+ throw error
42
+ }
43
+ }
44
+
45
+ clearCache()
46
+ {
47
+ this.#map.clear()
48
+ }
49
+
50
+ /**
51
+ * Persist a specific certificate by ID in the database.
52
+ *
53
+ * @param {string} id the certificate identifier
54
+ * @param {string} validity the validity expiration date of the certificate
55
+ * @param {string} cert the certificate
56
+ * @param {string} key the certificate private key
57
+ * @param {string} pass the certificate private key password
58
+ *
59
+ * @returns {Promise<boolean>} true if the certificate was persisted, false if it already exists
60
+ */
61
+ perist(id, validity, cert, key, pass)
62
+ {
63
+ const
64
+ encryptionKey = this.config.CERT_PASS_ENCRYPTION_KEY,
65
+ encryptedKey = this.#encrypt(encryptionKey, key),
66
+ encryptedPass = this.#encrypt(encryptionKey, pass)
67
+
68
+ return this.#db.persistCertificate(
69
+ {
70
+ id,
71
+ validity,
72
+ cert,
73
+ // Encrypted Certificate Private Key
74
+ key : encryptedKey.encrypted,
75
+ key_salt : encryptedKey.salt,
76
+ key_iv : encryptedKey.iv,
77
+ key_tag : encryptedKey.tag,
78
+ // Encrypted Certificate Private Key Password
79
+ pass : encryptedPass.encrypted,
80
+ pass_salt : encryptedPass.salt,
81
+ pass_iv : encryptedPass.iv,
82
+ pass_tag : encryptedPass.tag
83
+ })
84
+ }
85
+
86
+ /**
87
+ * Revoke a specific certificate by ID in the database.
88
+ * @param {string} id the certificate identifier
89
+ * @returns {Promise<boolean>} true if the certificate was revoked, false if it does not exist
90
+ */
91
+ revoke(id)
92
+ {
93
+ return this.#db.revokeCertificate(id)
94
+ }
95
+
96
+ /**
97
+ * The root certificate authority (CA).
98
+ * @returns {Promise<{cert:string, key:string, pass:string}>}
99
+ */
100
+ get root()
101
+ {
102
+ return this.#lazyload('EVENTFLOW-ROOT-CA', this.#createRoot.bind(this))
103
+ }
104
+
105
+ /**
106
+ * The intermediate certificate authority (ICA).
107
+ * @returns {Promise<{cert:string, key:string, pass:string}>}
108
+ */
109
+ get intermediate()
110
+ {
111
+ return this.#lazyload(this.intermediateUID, this.#createIntermediate.bind(this))
112
+ }
113
+
114
+ /**
115
+ * The leaf end-entity certificate.
116
+ * @returns {Promise<{cert:string, key:string, pass:string}>}
117
+ */
118
+ get leaf()
119
+ {
120
+ return this.#lazyload(this.leafUID, this.#createLeaf.bind(this))
121
+ }
122
+
123
+ async #lazyload(id, factory)
124
+ {
125
+ if(false === this.#map.has(id))
126
+ {
127
+ try
128
+ {
129
+ this.#map.set(id, await this.#readCertificate(id))
130
+ }
131
+ catch(error)
132
+ {
133
+ if('E_EVENTFLOW_DB_CERTIFICATE_NOT_FOUND' === error.code)
134
+ {
135
+ this.#map.set(id, await this.#eagerload(id, factory))
136
+ }
137
+ else
138
+ {
139
+ throw error
140
+ }
141
+ }
142
+
143
+ this.log.info`certificate loaded ${id}`
144
+ }
145
+
146
+ const crt = this.#map.get(id)
147
+
148
+ // If the certificate is expired, then revoke it and create a new one.
149
+ // 10 minutes before expiration, the certificate is considered expired
150
+ // to prevent the certificate from being validated during a borderline
151
+ // expiration.
152
+ if(Date.now() + 6e5 > crt.validity)
153
+ {
154
+ this.log.warn`certificate expired ${id}`
155
+ this.#map.delete(id)
156
+ await this.#db.revokeCertificate(id)
157
+ return this.#lazyload(id, factory)
158
+ }
159
+ else
160
+ {
161
+ return crt
162
+ }
163
+ }
164
+
165
+ async #readCertificate(id)
166
+ {
167
+ const { cert, key, key_iv, key_salt, key_tag,
168
+ validity, pass, pass_iv, pass_salt, pass_tag } = await this.#db.readCertificate(id)
169
+ const
170
+ encryptionKey = this.config.CERT_PASS_ENCRYPTION_KEY,
171
+ encryptedPass = { encrypted:pass, salt:pass_salt, iv:pass_iv, tag:pass_tag },
172
+ encryptedKey = { encrypted:key, salt:key_salt, iv:key_iv, tag:key_tag },
173
+ decryptedPass = this.#decrypt(encryptionKey, encryptedPass),
174
+ decryptedKey = this.#decrypt(encryptionKey, encryptedKey)
175
+
176
+ return { validity, cert, key:decryptedKey, pass:decryptedPass }
177
+ }
178
+
179
+ async #eagerload(id, factory)
180
+ {
181
+ const
182
+ keyPass = this.#generateRandomPassword(),
183
+ certificate = await factory(id, keyPass)
184
+
185
+ const { cert, key } = certificate
186
+ const
187
+ x509 = new crypto.X509Certificate(certificate.cert),
188
+ validity = x509.validToDate,
189
+ isPersisted = await this.perist(id, validity, cert, key, keyPass)
190
+
191
+ // If multiple replicas tries at the same time to creater the certificate,
192
+ // then only the first one to persist is valid, the rest should throw away
193
+ // the work they done and instead read what is persisted in the database.
194
+ if(isPersisted)
195
+ {
196
+ return { validity, cert, key, pass:keyPass }
197
+ }
198
+ else
199
+ {
200
+ // If the certificate was not persisted, then it was already created
201
+ // by another replica. We should then read the persisted certificate
202
+ // from the database, and return it instead of what has been generated.
203
+ return await this.#readCertificate(id)
204
+ }
205
+ }
206
+
207
+ #createRoot(UID, password)
208
+ {
209
+ return this.openSSL.root(
210
+ {
211
+ days : this.config.CERT_ROOT_DAYS,
212
+ algorithm : this.config.CERT_ALGORITHM,
213
+ hash : this.config.CERT_HASH,
214
+ subject : { UID },
215
+ password
216
+ })
217
+ }
218
+
219
+ async #createIntermediate(UID, password)
220
+ {
221
+ const root = await this.root
222
+ return await this.openSSL.intermediate(root,
223
+ {
224
+ days : this.config.CERT_INTERMEDIATE_DAYS,
225
+ algorithm : this.config.CERT_ALGORITHM,
226
+ hash : this.config.CERT_HASH,
227
+ dns : [ '.' + UID ],
228
+ subject : { UID },
229
+ password :
230
+ {
231
+ input : root.pass,
232
+ output : password
233
+ }
234
+ })
235
+ }
236
+
237
+ async #createLeaf(UID, password)
238
+ {
239
+ const ica = await this.intermediate
240
+ return await this.openSSL.leaf(ica,
241
+ {
242
+ days : this.config.CERT_LEAF_DAYS,
243
+ algorithm : this.config.CERT_ALGORITHM,
244
+ hash : this.config.CERT_HASH,
245
+ dns : [ this.leafUID ],
246
+ subject : { UID },
247
+ password :
248
+ {
249
+ input : ica.pass,
250
+ output : password
251
+ }
252
+ })
253
+ }
254
+
255
+ #encrypt(password, decrypted)
256
+ {
257
+ const
258
+ cipher = this.config.CERT_PASS_CIPHER,
259
+ hash = this.config.CERT_PASS_PBKDF2_HASH,
260
+ bytes = this.config.CERT_PASS_PBKDF2_BYTES,
261
+ iterations = this.config.CERT_PASS_PBKDF2_ITERATIONS,
262
+ salt = crypto.randomBytes(16),
263
+ iv = crypto.randomBytes(16),
264
+ key = crypto.pbkdf2Sync(password, salt, iterations, bytes, hash), // derive key
265
+ cipheriv = crypto.createCipheriv(cipher, key, iv),
266
+ encrypted = Buffer.concat([ cipheriv.update(decrypted, 'utf8'), cipheriv.final() ]),
267
+ tag = cipheriv.getAuthTag() // helps identify corruption
268
+
269
+ return { encrypted, salt, iv, tag }
270
+ }
271
+
272
+ #decrypt(password, { encrypted, salt, iv, tag })
273
+ {
274
+ const
275
+ cipher = this.config.CERT_PASS_CIPHER,
276
+ hash = this.config.CERT_PASS_PBKDF2_HASH,
277
+ bytes = this.config.CERT_PASS_PBKDF2_BYTES,
278
+ iterations = this.config.CERT_PASS_PBKDF2_ITERATIONS,
279
+ key = crypto.pbkdf2Sync(password, salt, iterations, bytes, hash),
280
+ decipher = crypto.createDecipheriv(cipher, key, iv)
281
+
282
+ decipher.setAuthTag(tag)
283
+
284
+ const decrypted = Buffer.concat([ decipher.update(encrypted), decipher.final() ])
285
+ return decrypted.toString('utf8')
286
+ }
287
+
288
+ #generateRandomPassword()
289
+ {
290
+ const
291
+ length = crypto.randomInt(64, 128),
292
+ random = crypto.randomBytes(length),
293
+ ascii = random.toString('latin1'),
294
+ nonNull = ascii.replaceAll('\x00', '').replaceAll('$', '')
295
+
296
+ return nonNull
297
+ }
298
+ }
package/index.test.js ADDED
@@ -0,0 +1,115 @@
1
+ import Config from '@superhero/config'
2
+ import Locator from '@superhero/locator'
3
+ import Certificates from '@superhero/eventflow-certificates'
4
+ import assert from 'node:assert/strict'
5
+ import { X509Certificate } from 'node:crypto'
6
+ import { suite, test, before, after } from 'node:test'
7
+
8
+ suite('@superhero/eventflow-certificates', async () =>
9
+ {
10
+ const
11
+ config = new Config(),
12
+ locator = new Locator()
13
+
14
+ await config.add('@superhero/eventflow-db')
15
+ locator.set('@superhero/config', config)
16
+ const db = await locator.lazyload('@superhero/eventflow-db')
17
+
18
+ let conf, manager, icaUID = 'INTERMEDIATE-CERT-ID', leafUID = 'LEAF-CERT-ID'
19
+
20
+ before(() =>
21
+ {
22
+ conf =
23
+ {
24
+ CERT_PASS_ENCRYPTION_KEY : 'encryptionKey123',
25
+ CERT_ROOT_DAYS : 365,
26
+ CERT_INTERMEDIATE_DAYS : 30,
27
+ CERT_LEAF_DAYS : 7,
28
+ CERT_ALGORITHM : 'rsa',
29
+ CERT_HASH : 'sha256'
30
+ }
31
+
32
+ manager = new Certificates(icaUID, leafUID, conf, db)
33
+ manager.log.config.mute = true
34
+ })
35
+
36
+ after(() => db.close())
37
+
38
+ test('Throw error if CERT_PASS_ENCRYPTION_KEY is missing in config', () =>
39
+ {
40
+ delete conf.CERT_PASS_ENCRYPTION_KEY
41
+ assert.throws(
42
+ () => new Certificates(icaUID, leafUID, conf, db),
43
+ { code: 'E_EVENTFLOW_CERTIFICATES_MISSING_CONFIGURATION' })
44
+ })
45
+
46
+ test('Get root certificate', async (sub) =>
47
+ {
48
+ const root = await manager.root
49
+
50
+ assert.ok(root.validity > Date.now())
51
+ assert.ok(root.cert)
52
+ assert.ok(root.key)
53
+ assert.ok(root.pass)
54
+
55
+ const rootX509 = new X509Certificate(root.cert)
56
+ assert.ok(rootX509.checkIssued(rootX509))
57
+
58
+ await sub.test('Get same root certificate each time lazyloading it', async () =>
59
+ {
60
+ const root2 = await manager.root
61
+ assert.deepEqual(root, root2)
62
+ })
63
+
64
+ await sub.test('Get intermediate certificate', async (sub) =>
65
+ {
66
+ const ica = await manager.intermediate
67
+
68
+ assert.ok(ica.validity > Date.now())
69
+ assert.ok(ica.cert)
70
+ assert.ok(ica.key)
71
+ assert.ok(ica.pass)
72
+
73
+ const icaX509 = new X509Certificate(ica.cert)
74
+ assert.ok(icaX509.checkIssued(rootX509))
75
+
76
+ await sub.test('Get leaf certificate', async (sub) =>
77
+ {
78
+ const leaf = await manager.leaf
79
+
80
+ assert.ok(leaf.validity > Date.now())
81
+ assert.ok(leaf.cert)
82
+ assert.ok(leaf.key)
83
+ assert.ok(leaf.pass)
84
+
85
+ const leafX509 = new X509Certificate(leaf.cert)
86
+ assert.ok(leafX509.checkIssued(icaX509))
87
+
88
+ await sub.test('Clear cache and still get the same certificates', async (sub) =>
89
+ {
90
+ manager.clearCache()
91
+
92
+ const
93
+ root2 = await manager.root,
94
+ ica2 = await manager.intermediate,
95
+ leaf2 = await manager.leaf
96
+
97
+ assert.deepEqual(root, root2)
98
+ assert.deepEqual(ica, ica2)
99
+ assert.deepEqual(leaf, leaf2)
100
+ })
101
+
102
+ await sub.test('Revoke certificate and regenerate when expired', async () =>
103
+ {
104
+ await assert.doesNotReject(manager.revoke(leafUID))
105
+
106
+ const
107
+ leaf = await manager.leaf,
108
+ leafX509 = new X509Certificate(leaf.cert)
109
+
110
+ assert.ok(leafX509.checkIssued(icaX509))
111
+ })
112
+ })
113
+ })
114
+ })
115
+ })
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@superhero/eventflow-certificates",
3
+ "version": "4.0.0",
4
+ "description": "Eventflow certificates is common tls certificates logic used in the eventflow ecosystem.",
5
+ "keywords": [
6
+ "eventflow"
7
+ ],
8
+ "main": "index.js",
9
+ "license": "MIT",
10
+ "type": "module",
11
+ "exports": {
12
+ ".": "./index.js"
13
+ },
14
+ "dependencies": {
15
+ "@superhero/eventflow-db": "^4.1.0",
16
+ "@superhero/log": "^4.0.0",
17
+ "@superhero/openssl": "^4.0.2",
18
+ "@superhero/deep": "^4.1.0"
19
+ },
20
+ "devDependencies": {
21
+ "@superhero/config": "^4.1.2",
22
+ "@superhero/locator": "^4.2.0"
23
+ },
24
+ "scripts": {
25
+ "test-build": "npm explore @superhero/eventflow-db -- npm run test-build",
26
+ "test-only": "node --test-only --trace-warnings --test --experimental-test-coverage",
27
+ "test": "node --test --experimental-test-coverage"
28
+ },
29
+ "author": {
30
+ "name": "Erik Landvall",
31
+ "email": "erik@landvall.se"
32
+ },
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "git+https://github.com/superhero/eventflow-certificates.git"
36
+ }
37
+ }