@onlineapps/service-validator-core 1.0.2

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 (35) hide show
  1. package/README.md +127 -0
  2. package/coverage/clover.xml +468 -0
  3. package/coverage/coverage-final.json +8 -0
  4. package/coverage/lcov-report/base.css +224 -0
  5. package/coverage/lcov-report/block-navigation.js +87 -0
  6. package/coverage/lcov-report/favicon.png +0 -0
  7. package/coverage/lcov-report/index.html +146 -0
  8. package/coverage/lcov-report/prettify.css +1 -0
  9. package/coverage/lcov-report/prettify.js +2 -0
  10. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  11. package/coverage/lcov-report/sorter.js +210 -0
  12. package/coverage/lcov-report/src/index.html +116 -0
  13. package/coverage/lcov-report/src/index.js.html +643 -0
  14. package/coverage/lcov-report/src/security/certificateManager.js.html +799 -0
  15. package/coverage/lcov-report/src/security/index.html +131 -0
  16. package/coverage/lcov-report/src/security/tokenManager.js.html +622 -0
  17. package/coverage/lcov-report/src/validators/connectorValidator.js.html +787 -0
  18. package/coverage/lcov-report/src/validators/endpointValidator.js.html +577 -0
  19. package/coverage/lcov-report/src/validators/healthValidator.js.html +655 -0
  20. package/coverage/lcov-report/src/validators/index.html +161 -0
  21. package/coverage/lcov-report/src/validators/openApiValidator.js.html +517 -0
  22. package/coverage/lcov.info +982 -0
  23. package/jest.config.js +21 -0
  24. package/package.json +31 -0
  25. package/src/index.js +212 -0
  26. package/src/security/ValidationProofVerifier.js +178 -0
  27. package/src/security/certificateManager.js +239 -0
  28. package/src/security/tokenManager.js +194 -0
  29. package/src/validators/connectorValidator.js +235 -0
  30. package/src/validators/endpointValidator.js +165 -0
  31. package/src/validators/healthValidator.js +191 -0
  32. package/src/validators/openApiValidator.js +145 -0
  33. package/test/component/validation-flow.test.js +353 -0
  34. package/test/integration/real-validation.test.js +548 -0
  35. package/test/unit/ValidationCore.test.js +320 -0
package/jest.config.js ADDED
@@ -0,0 +1,21 @@
1
+ module.exports = {
2
+ testEnvironment: 'node',
3
+ coverageDirectory: 'coverage',
4
+ collectCoverageFrom: [
5
+ 'src/**/*.js',
6
+ '!src/**/*.test.js',
7
+ '!src/**/*.spec.js'
8
+ ],
9
+ testMatch: [
10
+ '<rootDir>/test/**/*.test.js'
11
+ ],
12
+ coverageThreshold: {
13
+ global: {
14
+ branches: 80,
15
+ functions: 80,
16
+ lines: 80,
17
+ statements: 80
18
+ }
19
+ },
20
+ testTimeout: 10000
21
+ };
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@onlineapps/service-validator-core",
3
+ "version": "1.0.2",
4
+ "description": "Core validation logic for microservices",
5
+ "main": "src/index.js",
6
+ "scripts": {
7
+ "test": "jest",
8
+ "test:coverage": "jest --coverage",
9
+ "lint": "eslint src/"
10
+ },
11
+ "keywords": [
12
+ "validation",
13
+ "microservices",
14
+ "openapi"
15
+ ],
16
+ "author": "OnlineApps",
17
+ "license": "MIT",
18
+ "dependencies": {
19
+ "ajv": "^8.12.0",
20
+ "jsonwebtoken": "^9.0.2",
21
+ "uuid": "^9.0.1"
22
+ },
23
+ "devDependencies": {
24
+ "jest": "^29.7.0",
25
+ "eslint": "^8.56.0"
26
+ },
27
+ "publishConfig": {
28
+ "registry": "https://registry.npmjs.org/",
29
+ "access": "public"
30
+ }
31
+ }
package/src/index.js ADDED
@@ -0,0 +1,212 @@
1
+ const OpenApiValidator = require('./validators/openApiValidator');
2
+ const EndpointValidator = require('./validators/endpointValidator');
3
+ const ConnectorValidator = require('./validators/connectorValidator');
4
+ const HealthValidator = require('./validators/healthValidator');
5
+ const TokenManager = require('./security/tokenManager');
6
+ const CertificateManager = require('./security/certificateManager');
7
+ const ValidationProofVerifier = require('./security/ValidationProofVerifier');
8
+
9
+ class ValidationCore {
10
+ constructor(config = {}) {
11
+ this.config = {
12
+ strictMode: config.strictMode || false,
13
+ requiredConnectors: config.requiredConnectors || [
14
+ 'connector-logger',
15
+ 'connector-storage',
16
+ 'connector-registry-client',
17
+ 'connector-mq-client',
18
+ 'connector-cookbook'
19
+ ],
20
+ ...config
21
+ };
22
+
23
+ this.validators = {
24
+ openApi: new OpenApiValidator(),
25
+ endpoints: new EndpointValidator(),
26
+ connectors: new ConnectorValidator(this.config.requiredConnectors),
27
+ health: new HealthValidator()
28
+ };
29
+
30
+ this.tokenManager = new TokenManager();
31
+ this.certificateManager = new CertificateManager();
32
+ this.proofVerifier = new ValidationProofVerifier({
33
+ maxProofAge: config.maxProofAge,
34
+ strictMode: this.config.strictMode
35
+ });
36
+ }
37
+
38
+ /**
39
+ * Validates service configuration
40
+ * @param {object} serviceData - Service data to validate
41
+ * @returns {Promise<object>} Validation results
42
+ */
43
+ async validate(serviceData) {
44
+ const results = {
45
+ success: true,
46
+ validated: false,
47
+ checks: {},
48
+ errors: [],
49
+ warnings: [],
50
+ timestamp: new Date().toISOString()
51
+ };
52
+
53
+ try {
54
+ // Run all validators
55
+ if (serviceData.openApiSpec) {
56
+ const openApiResult = await this.validators.openApi.validate(serviceData.openApiSpec);
57
+ results.checks.openApi = openApiResult;
58
+ if (!openApiResult.valid) {
59
+ results.errors.push(...openApiResult.errors);
60
+ }
61
+ }
62
+
63
+ if (serviceData.endpoints) {
64
+ const endpointsResult = await this.validators.endpoints.validate(serviceData.endpoints);
65
+ results.checks.endpoints = endpointsResult;
66
+ if (!endpointsResult.valid) {
67
+ results.errors.push(...endpointsResult.errors);
68
+ }
69
+ }
70
+
71
+ if (serviceData.metadata) {
72
+ const connectorsResult = await this.validators.connectors.validate(serviceData.metadata);
73
+ results.checks.connectors = connectorsResult;
74
+ if (!connectorsResult.valid) {
75
+ results.warnings.push(...connectorsResult.warnings);
76
+ if (this.config.strictMode) {
77
+ results.errors.push(...connectorsResult.warnings);
78
+ }
79
+ }
80
+ }
81
+
82
+ if (serviceData.health) {
83
+ const healthResult = await this.validators.health.validate(serviceData.health);
84
+ results.checks.health = healthResult;
85
+ if (!healthResult.valid) {
86
+ results.warnings.push(...healthResult.warnings);
87
+ }
88
+ }
89
+
90
+ results.validated = true;
91
+ results.success = results.errors.length === 0;
92
+
93
+ } catch (error) {
94
+ results.success = false;
95
+ results.errors.push(`Validation error: ${error.message}`);
96
+ }
97
+
98
+ return results;
99
+ }
100
+
101
+ /**
102
+ * Generate pre-validation token
103
+ * @param {object} validationResults - Results from validate()
104
+ * @param {string} serviceName - Name of the service
105
+ * @returns {Promise<object>} Token data object with token, secret, and tokenId
106
+ */
107
+ async generatePreValidationToken(validationResults, serviceName) {
108
+ if (!validationResults.success) {
109
+ throw new Error('Cannot generate token for failed validation');
110
+ }
111
+
112
+ const tokenData = await this.tokenManager.generateToken({
113
+ serviceName,
114
+ validationResults,
115
+ type: 'pre-validation'
116
+ });
117
+
118
+ // Store secret for later verification (in real implementation, store in secure storage)
119
+ this.tokenSecrets = this.tokenSecrets || new Map();
120
+ this.tokenSecrets.set(tokenData.token, tokenData.secret);
121
+
122
+ // Return full token data object (token, secret, tokenId)
123
+ return tokenData;
124
+ }
125
+
126
+ /**
127
+ * Verify pre-validation token
128
+ * @param {string} token - Token to verify
129
+ * @param {string} secret - Secret to verify with (optional)
130
+ * @returns {Promise<object>} Token payload
131
+ */
132
+ async verifyPreValidationToken(token, secret = null) {
133
+ // Use provided secret or lookup from stored secrets
134
+ const verifySecret = secret || (this.tokenSecrets && this.tokenSecrets.get(token)) || null;
135
+ return this.tokenManager.verifyToken(token, verifySecret);
136
+ }
137
+
138
+ /**
139
+ * Generate validation certificate
140
+ * @param {object} validationResults - Results from validate()
141
+ * @param {string} serviceName - Name of the service
142
+ * @param {string} version - Service version
143
+ * @returns {Promise<object>} Validation certificate
144
+ */
145
+ async generateCertificate(validationResults, serviceName, version) {
146
+ if (!validationResults.success) {
147
+ throw new Error('Cannot generate certificate for failed validation');
148
+ }
149
+
150
+ return this.certificateManager.generateCertificate({
151
+ serviceName,
152
+ version,
153
+ validationResults,
154
+ issuedAt: new Date().toISOString(),
155
+ expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString() // 30 days
156
+ });
157
+ }
158
+
159
+ /**
160
+ * Verify validation certificate
161
+ * @param {object} certificate - Certificate to verify
162
+ * @returns {Promise<boolean>} Verification result
163
+ */
164
+ async verifyCertificate(certificate) {
165
+ return this.certificateManager.verifyCertificate(certificate);
166
+ }
167
+
168
+ /**
169
+ * Verify validation proof (SHA256-based from pre-validation)
170
+ * @param {object} proof - Complete proof object from .validation-proof.json
171
+ * @returns {object} Verification result with {valid, reason, details}
172
+ */
173
+ verifyValidationProof(proof) {
174
+ return this.proofVerifier.verifyProof(proof);
175
+ }
176
+
177
+ /**
178
+ * Generate functional tests from OpenAPI spec
179
+ * @param {object} openApiSpec - OpenAPI specification
180
+ * @returns {Promise<array>} Generated tests
181
+ */
182
+ async generateFunctionalTests(openApiSpec) {
183
+ const tests = [];
184
+
185
+ if (!openApiSpec || !openApiSpec.paths) {
186
+ return tests;
187
+ }
188
+
189
+ for (const [pathName, pathDef] of Object.entries(openApiSpec.paths)) {
190
+ for (const [method, operation] of Object.entries(pathDef)) {
191
+ if (['get', 'post', 'put', 'patch', 'delete'].includes(method.toLowerCase())) {
192
+ tests.push({
193
+ name: operation.summary || `${method.toUpperCase()} ${pathName}`,
194
+ method: method.toUpperCase(),
195
+ path: pathName,
196
+ description: operation.description || '',
197
+ parameters: operation.parameters || [],
198
+ requestBody: operation.requestBody || null,
199
+ expectedResponses: Object.keys(operation.responses || {}),
200
+ expectedSuccessResponse: operation.responses?.['200'] ? '200' :
201
+ operation.responses?.['201'] ? '201' : null
202
+ });
203
+ }
204
+ }
205
+ }
206
+
207
+ return tests;
208
+ }
209
+ }
210
+
211
+ module.exports = ValidationCore;
212
+ module.exports.ValidationProofVerifier = ValidationProofVerifier;
@@ -0,0 +1,178 @@
1
+ const crypto = require('crypto');
2
+
3
+ /**
4
+ * ValidationProofVerifier - Verifies SHA256 validation proofs from pre-validation
5
+ *
6
+ * This class verifies validation proofs generated by ValidationProofGenerator
7
+ * from the conn-e2e-testing package. It validates:
8
+ * - Hash integrity (SHA256)
9
+ * - Proof structure and format
10
+ * - Test results (passed/failed counts)
11
+ * - Proof age (max 7 days)
12
+ * - Validator identity
13
+ */
14
+ class ValidationProofVerifier {
15
+ constructor(options = {}) {
16
+ this.maxProofAge = options.maxProofAge || 7 * 24 * 60 * 60 * 1000; // 7 days default
17
+ this.strictMode = options.strictMode || false;
18
+ }
19
+
20
+ /**
21
+ * Verify validation proof
22
+ * @param {object} proof - Complete proof object from .validation-proof.json
23
+ * @returns {object} Verification result with {valid, reason, details}
24
+ */
25
+ verifyProof(proof) {
26
+ console.log('[ValidationProofVerifier] Starting proof verification');
27
+ console.log('[ValidationProofVerifier] Proof hash:', proof.validationProof?.substring(0, 16) + '...');
28
+
29
+ // 1. Check proof structure
30
+ if (!proof || typeof proof !== 'object') {
31
+ console.error('[ValidationProofVerifier] ❌ Invalid proof structure: not an object');
32
+ return { valid: false, reason: 'INVALID_STRUCTURE', details: 'Proof must be an object' };
33
+ }
34
+
35
+ if (!proof.validationProof || typeof proof.validationProof !== 'string') {
36
+ console.error('[ValidationProofVerifier] ❌ Missing or invalid validationProof field');
37
+ return { valid: false, reason: 'MISSING_HASH', details: 'validationProof field is required' };
38
+ }
39
+
40
+ if (!proof.validationData || typeof proof.validationData !== 'object') {
41
+ console.error('[ValidationProofVerifier] ❌ Missing or invalid validationData field');
42
+ return { valid: false, reason: 'MISSING_DATA', details: 'validationData field is required' };
43
+ }
44
+
45
+ // 2. Check hash format (SHA256 = 64 hex characters)
46
+ if (!/^[a-f0-9]{64}$/i.test(proof.validationProof)) {
47
+ console.error('[ValidationProofVerifier] ❌ Invalid hash format:', proof.validationProof);
48
+ return { valid: false, reason: 'INVALID_HASH_FORMAT', details: 'Hash must be 64-character hex string (SHA256)' };
49
+ }
50
+ console.log('[ValidationProofVerifier] ✓ Hash format valid (SHA256)');
51
+
52
+ // 3. Verify hash integrity
53
+ const data = proof.validationData;
54
+ const recomputedHash = this._generateHash(data);
55
+
56
+ if (recomputedHash !== proof.validationProof) {
57
+ console.error('[ValidationProofVerifier] ❌ Hash mismatch');
58
+ console.error('[ValidationProofVerifier] Expected:', proof.validationProof);
59
+ console.error('[ValidationProofVerifier] Computed:', recomputedHash);
60
+ return {
61
+ valid: false,
62
+ reason: 'HASH_MISMATCH',
63
+ details: 'Validation data does not match hash (data may have been tampered with)'
64
+ };
65
+ }
66
+ console.log('[ValidationProofVerifier] ✓ Hash integrity verified');
67
+
68
+ // 4. Check required validationData fields
69
+ const requiredFields = ['serviceName', 'version', 'testsRun', 'testsPassed', 'testsFailed', 'validator', 'timestamp'];
70
+ for (const field of requiredFields) {
71
+ if (data[field] === undefined || data[field] === null) {
72
+ console.error(`[ValidationProofVerifier] ❌ Missing required field: ${field}`);
73
+ return {
74
+ valid: false,
75
+ reason: 'MISSING_FIELD',
76
+ details: `Required field missing: ${field}`
77
+ };
78
+ }
79
+ }
80
+ console.log('[ValidationProofVerifier] ✓ All required fields present');
81
+
82
+ // 5. Check proof age
83
+ const proofDate = new Date(data.timestamp);
84
+ const now = new Date();
85
+ const age = now - proofDate;
86
+
87
+ if (isNaN(proofDate.getTime())) {
88
+ console.error('[ValidationProofVerifier] ❌ Invalid timestamp:', data.timestamp);
89
+ return { valid: false, reason: 'INVALID_TIMESTAMP', details: 'Timestamp is not a valid date' };
90
+ }
91
+
92
+ if (age > this.maxProofAge) {
93
+ const maxDays = Math.floor(this.maxProofAge / (24 * 60 * 60 * 1000));
94
+ const ageDays = Math.floor(age / (24 * 60 * 60 * 1000));
95
+ console.error(`[ValidationProofVerifier] ❌ Proof too old: ${ageDays} days (max ${maxDays} days)`);
96
+ return {
97
+ valid: false,
98
+ reason: 'PROOF_EXPIRED',
99
+ details: `Proof is ${ageDays} days old (max ${maxDays} days)`
100
+ };
101
+ }
102
+ console.log(`[ValidationProofVerifier] ✓ Proof age valid: ${Math.floor(age / (60 * 60 * 1000))} hours old`);
103
+
104
+ // 6. Check test results
105
+ if (data.testsRun <= 0) {
106
+ console.error('[ValidationProofVerifier] ❌ No tests were run');
107
+ return { valid: false, reason: 'NO_TESTS', details: 'At least one test must be run' };
108
+ }
109
+
110
+ if (data.testsFailed > 0) {
111
+ console.error(`[ValidationProofVerifier] ❌ Some tests failed: ${data.testsFailed}/${data.testsRun}`);
112
+ return {
113
+ valid: false,
114
+ reason: 'TESTS_FAILED',
115
+ details: `${data.testsFailed} out of ${data.testsRun} tests failed`
116
+ };
117
+ }
118
+
119
+ if (data.testsPassed !== data.testsRun) {
120
+ console.error('[ValidationProofVerifier] ❌ Test count mismatch');
121
+ return {
122
+ valid: false,
123
+ reason: 'TEST_COUNT_MISMATCH',
124
+ details: `Passed (${data.testsPassed}) + Failed (${data.testsFailed}) ≠ Total (${data.testsRun})`
125
+ };
126
+ }
127
+ console.log(`[ValidationProofVerifier] ✓ All tests passed: ${data.testsPassed}/${data.testsRun}`);
128
+
129
+ // 7. Check validator identity
130
+ if (!data.validator || typeof data.validator !== 'string') {
131
+ console.error('[ValidationProofVerifier] ❌ Invalid validator identity');
132
+ return { valid: false, reason: 'INVALID_VALIDATOR', details: 'Validator identity is required' };
133
+ }
134
+ console.log('[ValidationProofVerifier] ✓ Validator:', data.validator);
135
+
136
+ // 8. All checks passed
137
+ console.log('[ValidationProofVerifier] ✅ Proof verification successful');
138
+ console.log(`[ValidationProofVerifier] Service: ${data.serviceName} v${data.version}`);
139
+ console.log(`[ValidationProofVerifier] Tests: ${data.testsPassed}/${data.testsRun} passed`);
140
+ console.log(`[ValidationProofVerifier] Validator: ${data.validator}`);
141
+ console.log(`[ValidationProofVerifier] Timestamp: ${data.timestamp}`);
142
+
143
+ return {
144
+ valid: true,
145
+ reason: 'VALID',
146
+ details: {
147
+ serviceName: data.serviceName,
148
+ version: data.version,
149
+ testsRun: data.testsRun,
150
+ testsPassed: data.testsPassed,
151
+ validator: data.validator,
152
+ timestamp: data.timestamp,
153
+ fingerprint: data.fingerprint
154
+ }
155
+ };
156
+ }
157
+
158
+ /**
159
+ * Generate SHA256 hash from validation data (must match ValidationProofGenerator)
160
+ * @private
161
+ */
162
+ _generateHash(data) {
163
+ const hashInput = JSON.stringify({
164
+ serviceName: data.serviceName,
165
+ version: data.version,
166
+ testsRun: data.testsRun,
167
+ testsPassed: data.testsPassed,
168
+ testsFailed: data.testsFailed,
169
+ validator: data.validator,
170
+ timestamp: data.timestamp,
171
+ fingerprint: data.fingerprint || ''
172
+ }, null, 0); // No whitespace for consistency
173
+
174
+ return crypto.createHash('sha256').update(hashInput).digest('hex');
175
+ }
176
+ }
177
+
178
+ module.exports = ValidationProofVerifier;
@@ -0,0 +1,239 @@
1
+ const crypto = require('crypto');
2
+
3
+ class CertificateManager {
4
+ constructor(config = {}) {
5
+ this.algorithm = config.algorithm || 'RSA-SHA256';
6
+ this.keyPair = config.keyPair || this.generateKeyPair();
7
+ this.issuer = config.issuer || 'validation-service';
8
+ this.validityDays = config.validityDays || 30;
9
+ }
10
+
11
+ /**
12
+ * Generate RSA key pair for signing certificates
13
+ * @returns {object} Key pair
14
+ */
15
+ generateKeyPair() {
16
+ const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', {
17
+ modulusLength: 2048,
18
+ publicKeyEncoding: {
19
+ type: 'spki',
20
+ format: 'pem'
21
+ },
22
+ privateKeyEncoding: {
23
+ type: 'pkcs8',
24
+ format: 'pem'
25
+ }
26
+ });
27
+
28
+ return { publicKey, privateKey };
29
+ }
30
+
31
+ /**
32
+ * Generate a validation certificate
33
+ * @param {object} data - Certificate data
34
+ * @returns {Promise<object>} Generated certificate
35
+ */
36
+ async generateCertificate(data) {
37
+ const certificateId = crypto.randomBytes(16).toString('hex');
38
+ const issuedAt = new Date();
39
+ const expiresAt = new Date(issuedAt);
40
+ expiresAt.setDate(expiresAt.getDate() + this.validityDays);
41
+
42
+ const certificate = {
43
+ id: certificateId,
44
+ version: '1.0',
45
+ issuer: this.issuer,
46
+ subject: {
47
+ serviceName: data.serviceName,
48
+ serviceVersion: data.version
49
+ },
50
+ issuedAt: issuedAt.toISOString(),
51
+ expiresAt: expiresAt.toISOString(),
52
+ validationResults: {
53
+ timestamp: data.validationResults.timestamp,
54
+ success: data.validationResults.success,
55
+ checks: data.validationResults.checks
56
+ },
57
+ fingerprint: this.generateFingerprint(data.validationResults),
58
+ extensions: {
59
+ allowedOperations: data.allowedOperations || ['register', 'heartbeat', 'workflow'],
60
+ restrictions: data.restrictions || [],
61
+ metadata: data.metadata || {}
62
+ }
63
+ };
64
+
65
+ // Sign the certificate
66
+ certificate.signature = this.signData(certificate);
67
+
68
+ return certificate;
69
+ }
70
+
71
+ /**
72
+ * Verify a validation certificate
73
+ * @param {object} certificate - Certificate to verify
74
+ * @returns {Promise<boolean>} Verification result
75
+ */
76
+ async verifyCertificate(certificate) {
77
+ try {
78
+ // Check expiration
79
+ const now = new Date();
80
+ const expiresAt = new Date(certificate.expiresAt);
81
+ if (now > expiresAt) {
82
+ throw new Error('Certificate has expired');
83
+ }
84
+
85
+ // Check issued date (not in future)
86
+ const issuedAt = new Date(certificate.issuedAt);
87
+ if (now < issuedAt) {
88
+ throw new Error('Certificate issued in the future');
89
+ }
90
+
91
+ // Verify signature
92
+ const certCopy = { ...certificate };
93
+ const providedSignature = certCopy.signature;
94
+ delete certCopy.signature;
95
+
96
+ const calculatedSignature = this.signData(certCopy);
97
+ if (providedSignature !== calculatedSignature) {
98
+ throw new Error('Invalid certificate signature');
99
+ }
100
+
101
+ // Verify fingerprint
102
+ const calculatedFingerprint = this.generateFingerprint(certificate.validationResults);
103
+ if (certificate.fingerprint !== calculatedFingerprint) {
104
+ throw new Error('Certificate data has been tampered with');
105
+ }
106
+
107
+ // Check issuer
108
+ if (certificate.issuer !== this.issuer) {
109
+ throw new Error(`Unknown certificate issuer: ${certificate.issuer}`);
110
+ }
111
+
112
+ return true;
113
+ } catch (error) {
114
+ console.error('Certificate verification failed:', error.message);
115
+ return false;
116
+ }
117
+ }
118
+
119
+ /**
120
+ * Sign data with private key
121
+ * @param {object} data - Data to sign
122
+ * @returns {string} Signature
123
+ */
124
+ signData(data) {
125
+ const sign = crypto.createSign(this.algorithm);
126
+ sign.update(JSON.stringify(data, Object.keys(data).sort()));
127
+ sign.end();
128
+ return sign.sign(this.keyPair.privateKey, 'hex');
129
+ }
130
+
131
+ /**
132
+ * Verify data signature with public key
133
+ * @param {object} data - Data to verify
134
+ * @param {string} signature - Signature to verify
135
+ * @returns {boolean} Verification result
136
+ */
137
+ verifySignature(data, signature) {
138
+ const verify = crypto.createVerify(this.algorithm);
139
+ verify.update(JSON.stringify(data, Object.keys(data).sort()));
140
+ verify.end();
141
+ return verify.verify(this.keyPair.publicKey, signature, 'hex');
142
+ }
143
+
144
+ /**
145
+ * Generate fingerprint for data
146
+ * @param {object} data - Data to fingerprint
147
+ * @returns {string} Fingerprint
148
+ */
149
+ generateFingerprint(data) {
150
+ const sortedData = JSON.stringify(data, Object.keys(data).sort());
151
+ return crypto.createHash('sha256').update(sortedData).digest('hex');
152
+ }
153
+
154
+ /**
155
+ * Revoke a certificate (would need persistent storage in production)
156
+ * @param {string} certificateId - Certificate ID to revoke
157
+ * @returns {Promise<void>}
158
+ */
159
+ async revokeCertificate(certificateId) {
160
+ // In production, this would update a revocation list in database
161
+ // For now, we'll just log it
162
+ console.log(`Certificate ${certificateId} has been revoked`);
163
+ }
164
+
165
+ /**
166
+ * Check if a certificate is revoked
167
+ * @param {string} certificateId - Certificate ID to check
168
+ * @returns {Promise<boolean>} True if revoked
169
+ */
170
+ async isCertificateRevoked(certificateId) {
171
+ // In production, this would check against a revocation list
172
+ // For now, always return false
173
+ return false;
174
+ }
175
+
176
+ /**
177
+ * Export public key for distribution
178
+ * @returns {string} Public key in PEM format
179
+ */
180
+ exportPublicKey() {
181
+ return this.keyPair.publicKey;
182
+ }
183
+
184
+ /**
185
+ * Generate a certificate chain (for future use with intermediate CAs)
186
+ * @param {object} certificate - Leaf certificate
187
+ * @param {Array} chain - Certificate chain
188
+ * @returns {object} Complete chain
189
+ */
190
+ generateCertificateChain(certificate, chain = []) {
191
+ return {
192
+ certificate,
193
+ chain,
194
+ rootCA: this.exportPublicKey()
195
+ };
196
+ }
197
+
198
+ /**
199
+ * Validate certificate permissions
200
+ * @param {object} certificate - Certificate to check
201
+ * @param {string} operation - Operation to validate
202
+ * @returns {boolean} True if operation is allowed
203
+ */
204
+ validatePermissions(certificate, operation) {
205
+ if (!certificate.extensions || !certificate.extensions.allowedOperations) {
206
+ return false;
207
+ }
208
+
209
+ return certificate.extensions.allowedOperations.includes(operation);
210
+ }
211
+
212
+ /**
213
+ * Generate a validation report from certificate
214
+ * @param {object} certificate - Certificate to analyze
215
+ * @returns {object} Validation report
216
+ */
217
+ generateValidationReport(certificate) {
218
+ const now = new Date();
219
+ const expiresAt = new Date(certificate.expiresAt);
220
+ const daysRemaining = Math.floor((expiresAt - now) / (1000 * 60 * 60 * 24));
221
+
222
+ return {
223
+ certificateId: certificate.id,
224
+ serviceName: certificate.subject.serviceName,
225
+ serviceVersion: certificate.subject.serviceVersion,
226
+ issuer: certificate.issuer,
227
+ issuedAt: certificate.issuedAt,
228
+ expiresAt: certificate.expiresAt,
229
+ daysRemaining,
230
+ isExpired: daysRemaining < 0,
231
+ validationPassed: certificate.validationResults.success,
232
+ checksPerformed: Object.keys(certificate.validationResults.checks || {}),
233
+ allowedOperations: certificate.extensions.allowedOperations,
234
+ restrictions: certificate.extensions.restrictions
235
+ };
236
+ }
237
+ }
238
+
239
+ module.exports = CertificateManager;