secure-gateway-sdk 1.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/README.md ADDED
@@ -0,0 +1,70 @@
1
+ # Security Gateway SDK - Proxy Library
2
+
3
+ This package is a drop-in API Gateway library designed to secure and route traffic to a microservice ecosystem containing Auth, Enclave, and Audit endpoints.
4
+
5
+ It acts exclusively as an Express Middleware router.
6
+
7
+ ## How to use this Library
8
+
9
+ As a third-party developer (Client), you will include this library inside your own backend express server to instantly spin up the Security Gateway shield over your logic.
10
+
11
+ ### 1. Installation
12
+ Ensure you have the gateway folder available in your workspace (or published via NPM if deployed).
13
+
14
+ ### 2. Integration Example (Your main app)
15
+ ```javascript
16
+ const express = require('express');
17
+ const { SecurityGateway } = require('security-gateway-sdk/proxy');
18
+
19
+ const app = express();
20
+
21
+ // Mount the Security Gateway to any route string (e.g. /api)
22
+ app.use('/api', SecurityGateway({
23
+ jwtSecret: 'process.env.MY_SECRET', // Required: For verifying tokens
24
+ authTarget: 'http://localhost:7001', // Your custom configured Auth backend
25
+ enclaveTarget: 'http://localhost:4000', // Your Enclave
26
+ auditTarget: 'http://localhost:5000'
27
+ }));
28
+
29
+ app.listen(3000, () => {
30
+ console.log('Main Application running on port 3000');
31
+ });
32
+ ```
33
+
34
+ ### Routing Rules Under the Hood
35
+ Once mounted (for example at `/api`), the library automatically protects traffic:
36
+ * `POST /api/auth/register` -> Publicly proxies to `authTarget`
37
+ * `GET /api/enclave/data` -> Instantly intercepted! Extracts JWT, validates against `jwtSecret`, and passes to `enclaveTarget` only if valid.
38
+
39
+ ---
40
+
41
+ ## Testing (cURL Examples)
42
+
43
+ Since we mounted the `SecurityGateway` directly into the Auth service, the Gateway is currently running actively on `http://localhost:7001/api`. You can use these commands to test the integration:
44
+
45
+ ### 1. Register a new user (Public Route)
46
+ ```bash
47
+ curl -X POST http://localhost:7001/api/auth/register \
48
+ -H "Content-Type: application/json" \
49
+ -d '{"username": "test_user", "password": "password123", "role": "admin"}'
50
+ ```
51
+
52
+ ### 2. Login to get a JWT Token (Public Route)
53
+ ```bash
54
+ curl -X POST http://localhost:7001/api/auth/login \
55
+ -H "Content-Type: application/json" \
56
+ -d '{"username": "test_user", "password": "password123"}'
57
+ ```
58
+
59
+ ### 3. Access the Enclave (Protected Route - Success)
60
+ Replace `<YOUR_TOKEN>` with the token string received from the login command.
61
+ ```bash
62
+ curl -X GET http://localhost:7001/api/enclave \
63
+ -H "Authorization: Bearer <YOUR_TOKEN>"
64
+ ```
65
+
66
+ ### 4. Access the Enclave (Protected Route - Failure)
67
+ If you try to access the enclave without attaching the token header, the SDK will proactively block you.
68
+ ```bash
69
+ curl -X GET http://localhost:7001/api/enclave
70
+ ```
package/index.js ADDED
@@ -0,0 +1,107 @@
1
+ const express = require('express');
2
+ const { createProxyMiddleware } = require('http-proxy-middleware');
3
+ const { createSecurityMiddleware } = require('./middleware/security');
4
+ const { createMLSMiddleware } = require('./middleware/mls'); // <--- Import MLS Engine
5
+
6
+ /**
7
+ * Universal Security Gateway Library (Publishable SDK)
8
+ * Dynamic configuration for securing any microservice architecture.
9
+ *
10
+ * @param {Object} options Configuration Object
11
+ * @param {string} options.jwtSecret The verification secret for Tokens
12
+ * @param {Array} options.routes Array defining paths, targets, and protection logic
13
+ */
14
+ function SecurityGateway(options) {
15
+ const router = express.Router();
16
+
17
+ if (!options || !options.jwtSecret) {
18
+ throw new Error('[SecurityGateway] Fatal: "jwtSecret" must be provided in configuration options.');
19
+ }
20
+
21
+ const verifyToken = createSecurityMiddleware(options.jwtSecret);
22
+
23
+ if (options.routes && Array.isArray(options.routes)) {
24
+ options.routes.forEach(route => {
25
+ const proxyConfig = {
26
+ target: route.target,
27
+ changeOrigin: true
28
+ };
29
+
30
+ // Custom rewrite rules provided by the implementing developer
31
+ if (route.pathRewrite) {
32
+ proxyConfig.pathRewrite = route.pathRewrite;
33
+ }
34
+
35
+ // Chain middlewares dynamically depending on if route is protected
36
+ const middlewares = [];
37
+ if (route.protected) {
38
+ // Step 1: Extract and verify JWT
39
+ middlewares.push(verifyToken);
40
+
41
+ // Step 2: Inject Bell-LaPadula rules if clearance is specified for the route!
42
+ if (route.requiredClearance) {
43
+ middlewares.push(createMLSMiddleware(route.requiredClearance));
44
+ // middlewares.push('S');
45
+ }
46
+ }
47
+
48
+ // Step 3: Physically Forward the Request
49
+ if (route.useSecureChannel) {
50
+ const EnclaveClient = require('./lib/enclave-client');
51
+ const enclaveClient = new EnclaveClient(route.target, options.enclave?.mrenclave);
52
+ const BLSAuditSim = require('./lib/bls');
53
+ const blsAudit = new BLSAuditSim(options.bls?.privateKey);
54
+
55
+ // Express json parser for the proxy to read the body before encryption
56
+ middlewares.push(express.json());
57
+ middlewares.push(async (req, res) => {
58
+ try {
59
+ const payload = req.body && Object.keys(req.body).length > 0 ? req.body : { action: "read_data" };
60
+ const token = req.headers['authorization']?.split(' ')[1];
61
+
62
+ // Execute over ECDH secure channel
63
+ const result = await enclaveClient.execute(payload, token);
64
+
65
+ // Generate Audit Log
66
+ const logEntry = {
67
+ timestamp: new Date().toISOString(),
68
+ user: req.user.username,
69
+ clearance: req.user.clearance,
70
+ action: 'enclave_execution',
71
+ status: 'SUCCESS'
72
+ };
73
+ const sigData = blsAudit.signAndAggregate(logEntry);
74
+ Object.assign(logEntry, sigData);
75
+
76
+ // Send log asynchronously
77
+ if (options.auditTarget) {
78
+ fetch(options.auditTarget + '/log', {
79
+ method: 'POST',
80
+ headers: { 'Content-Type': 'application/json' },
81
+ body: JSON.stringify(logEntry)
82
+ }).catch(e => console.error('[Proxy] Failed to send audit log'));
83
+ }
84
+
85
+ res.json(result);
86
+ } catch (error) {
87
+ res.status(500).json({ error: error.message });
88
+ }
89
+ });
90
+ } else {
91
+ middlewares.push(createProxyMiddleware(proxyConfig));
92
+ }
93
+
94
+ router.use(route.path, ...middlewares);
95
+
96
+ console.log(`[SecurityGateway] Shield mounted: ${route.path} -> ${route.target} (Protected: ${!!route.protected}, MLS: ${route.requiredClearance || 'None'})`);
97
+ });
98
+ }
99
+
100
+ return router;
101
+ }
102
+
103
+ module.exports = {
104
+ SecurityGateway,
105
+ createSecurityMiddleware,
106
+ createMLSMiddleware
107
+ };
package/lib/bls.js ADDED
@@ -0,0 +1,43 @@
1
+ const crypto = require('crypto');
2
+
3
+ /**
4
+ * Simulates BLS12-381 Aggregate Signatures for the Audit Logger.
5
+ * Real pairing-based crypto requires large WASM libraries (like @noble/bls12-381).
6
+ * We simulate the "aggregation" property here using chained SHA-256 hashes.
7
+ *
8
+ * Property simulated: N events produce a single constant-size proof.
9
+ */
10
+ class BLSAuditSim {
11
+ constructor(privateKey) {
12
+ // Use injected private key instead of hardcoded config
13
+ this.privateKey = privateKey || 'default_bls_key';
14
+ this.aggregateSignature = crypto.createHash('sha256').update('GENESIS').digest('hex');
15
+ this.sigCount = 0;
16
+ }
17
+
18
+ /**
19
+ * Signs a single log entry and aggregates it into the session proof.
20
+ */
21
+ signAndAggregate(eventPayload) {
22
+ // 1. Sign the individual event
23
+ const hmac = crypto.createHmac('sha256', this.privateKey);
24
+ hmac.update(JSON.stringify(eventPayload));
25
+ const eventSig = hmac.digest('hex');
26
+
27
+ // 2. Aggregate: Combine the new signature with the existing aggregate signature
28
+ // In real BLS this is scalar point addition. Here we simulate it by hashing them together.
29
+ const aggregator = crypto.createHash('sha256');
30
+ aggregator.update(this.aggregateSignature + eventSig);
31
+ this.aggregateSignature = aggregator.digest('hex');
32
+
33
+ this.sigCount++;
34
+
35
+ return {
36
+ eventSig,
37
+ aggregateSignature: this.aggregateSignature,
38
+ sigIndex: this.sigCount
39
+ };
40
+ }
41
+ }
42
+
43
+ module.exports = BLSAuditSim;
package/lib/crypto.js ADDED
@@ -0,0 +1,95 @@
1
+ const crypto = require('crypto');
2
+
3
+ /**
4
+ * SecureChannel implements the Elliptic-Curve Diffie-Hellman (ECDH) key exchange
5
+ * and AES-256-GCM authenticated encryption for secure communication.
6
+ *
7
+ * This fulfills the Confidentiality and Integrity requirements of the CIA triad.
8
+ */
9
+ class SecureChannel {
10
+ constructor() {
11
+ // Using secp256k1, the standard curve used in many high-security applications
12
+ this.ecdh = crypto.createECDH('secp256k1');
13
+ this.ecdh.generateKeys();
14
+ this.aesKey = null;
15
+ }
16
+
17
+ /**
18
+ * Gets the public key to share with the other untrusted party.
19
+ * @returns {string} Hexadecimal representation of the public key
20
+ */
21
+ getPublicKey() {
22
+ return this.ecdh.getPublicKey('hex');
23
+ }
24
+
25
+ /**
26
+ * Computes the shared symmetric secret using the other party's public key.
27
+ * @param {string} otherPublicKeyHex
28
+ */
29
+ computeSharedSecret(otherPublicKeyHex) {
30
+ // ECDH math: Your Private Key + Their Public Key = Shared Secret
31
+ const sharedSecret = this.ecdh.computeSecret(otherPublicKeyHex, 'hex');
32
+
33
+ // We hash the shared secret with SHA-256 to ensure we get a perfect 32-byte key for AES-256
34
+ this.aesKey = crypto.createHash('sha256').update(sharedSecret).digest();
35
+ return true;
36
+ }
37
+
38
+ /**
39
+ * Encrypts a payload using AES-256-GCM.
40
+ * @param {Object|string} payload The data to encrypt
41
+ * @returns {Object} The encryption envelope containing ciphertext, iv, and auth tag
42
+ */
43
+ encrypt(payload) {
44
+ if (!this.aesKey) throw new Error("Security Error: Shared secret not computed yet.");
45
+
46
+ // GCM standard requires a unique 12-byte (96-bit) Initialization Vector per encryption
47
+ const iv = crypto.randomBytes(12);
48
+
49
+ const cipher = crypto.createCipheriv('aes-256-gcm', this.aesKey, iv);
50
+
51
+ const stringifiedPayload = typeof payload === 'string' ? payload : JSON.stringify(payload);
52
+ let ciphertext = cipher.update(stringifiedPayload, 'utf8', 'hex');
53
+ ciphertext += cipher.final('hex');
54
+
55
+ // The Authentication Tag provides Integrity - it proves the ciphertext hasn't been tampered with
56
+ const authTag = cipher.getAuthTag().toString('hex');
57
+
58
+ return {
59
+ ciphertext,
60
+ iv: iv.toString('hex'),
61
+ authTag
62
+ };
63
+ }
64
+
65
+ /**
66
+ * Decrypts an AES-256-GCM encrypted envelope.
67
+ * @param {Object} envelope The envelope containing ciphertext, iv, and authTag
68
+ * @returns {Object|string} The decrypted plaintext
69
+ */
70
+ decrypt({ ciphertext, iv, authTag }) {
71
+ if (!this.aesKey) throw new Error("Security Error: Shared secret not computed yet.");
72
+
73
+ const decipher = crypto.createDecipheriv(
74
+ 'aes-256-gcm',
75
+ this.aesKey,
76
+ Buffer.from(iv, 'hex')
77
+ );
78
+
79
+ // Set the auth tag to verify integrity before decryption
80
+ decipher.setAuthTag(Buffer.from(authTag, 'hex'));
81
+
82
+ let decrypted = decipher.update(ciphertext, 'hex', 'utf8');
83
+ decrypted += decipher.final('utf8'); // Will throw error if tampered!
84
+
85
+ try {
86
+ return JSON.parse(decrypted);
87
+ } catch (e) {
88
+ return decrypted; // Fallback if plaintext was just a string
89
+ }
90
+ }
91
+ }
92
+
93
+ module.exports = {
94
+ SecureChannel
95
+ };
@@ -0,0 +1,79 @@
1
+ const { SecureChannel } = require('./crypto');
2
+ const crypto = require('crypto');
3
+
4
+ /**
5
+ * EnclaveClient acts as the secure intermediary between the Proxy and the TEE.
6
+ * It enforces Enclave Attestation and establishes an ECDH secure channel.
7
+ */
8
+ class EnclaveClient {
9
+ constructor(enclaveUrl, expectedMrenclave) {
10
+ this.enclaveUrl = enclaveUrl;
11
+ this.expectedMrenclave = expectedMrenclave;
12
+ this.channel = new SecureChannel();
13
+ this.isAttested = false;
14
+ }
15
+
16
+ async attestAndHandshake() {
17
+ const nonce = crypto.randomBytes(16).toString('hex');
18
+ const clientPubKey = this.channel.getPublicKey();
19
+
20
+ // 1. Send our public key and a nonce to the Enclave
21
+ const response = await fetch(`${this.enclaveUrl}/attest`, {
22
+ method: 'POST',
23
+ headers: { 'Content-Type': 'application/json' },
24
+ body: JSON.stringify({ nonce, clientPublicKey: clientPubKey })
25
+ });
26
+
27
+ const data = await response.json();
28
+
29
+ // 2. Verify mrenclave (Simulated hardware hash)
30
+ if (data.mrenclave !== this.expectedMrenclave) {
31
+ throw new Error('ATTESTATION FAILED: Unknown mrenclave hash! Server might be spoofed.');
32
+ }
33
+
34
+ // 3. Verify Quote (Simulated hardware ECDSA signature using HMAC)
35
+ const hmac = crypto.createHmac('sha256', 'simulated_hardware_key');
36
+ hmac.update(data.mrenclave + nonce + data.publicKey);
37
+ const expectedQuote = hmac.digest('hex');
38
+
39
+ if (data.quote !== expectedQuote) {
40
+ throw new Error('ATTESTATION FAILED: Invalid hardware quote! Key exchange compromised.');
41
+ }
42
+
43
+ // 4. Compute shared secret with the verified enclave public key
44
+ this.channel.computeSharedSecret(data.publicKey);
45
+ this.isAttested = true;
46
+ console.log('[Proxy->Enclave] Attestation successful. ECDH secure channel established.');
47
+ }
48
+
49
+ async execute(payload, token) {
50
+ // If we haven't verified the enclave yet, do it now
51
+ if (!this.isAttested) {
52
+ await this.attestAndHandshake();
53
+ }
54
+
55
+ // Encrypt the payload using AES-256-GCM
56
+ const encrypted = this.channel.encrypt(payload);
57
+
58
+ // Send to Enclave
59
+ const response = await fetch(`${this.enclaveUrl}/execute`, {
60
+ method: 'POST',
61
+ headers: {
62
+ 'Content-Type': 'application/json',
63
+ 'Authorization': `Bearer ${token}` // Pass the user's JWT to prove they passed proxy auth
64
+ },
65
+ body: JSON.stringify(encrypted)
66
+ });
67
+
68
+ const data = await response.json();
69
+
70
+ if (!response.ok) {
71
+ throw new Error(data.error || 'Execution failed');
72
+ }
73
+
74
+ // Decrypt the response
75
+ return this.channel.decrypt(data);
76
+ }
77
+ }
78
+
79
+ module.exports = EnclaveClient;
@@ -0,0 +1,69 @@
1
+ /**
2
+ * MLS Engine (Multi-Level Security)
3
+ * Implements Bell-LaPadula Lattice logic for the SDK.
4
+ */
5
+
6
+ const LATTICE = {
7
+ 'U': 10, // Unclassified
8
+ 'C': 20, // Confidential
9
+ 'S': 30, // Secret
10
+ 'TS': 40 // Top Secret
11
+ };
12
+
13
+ function createMLSMiddleware(routeRequiredClearance) {
14
+ const routeLevel = LATTICE[routeRequiredClearance?.toUpperCase()];
15
+
16
+ if (!routeLevel) {
17
+ throw new Error(`[MLS Engine] Fatal: Invalid clearance level provided: ${routeRequiredClearance}. Must be U, C, S, or TS.`);
18
+ }
19
+
20
+ return (req, res, next) => {
21
+ // verifyToken MUST run before this middleware so req.user exists
22
+ if (!req.user || !req.user.clearance) {
23
+ console.error('[MLS Engine] Missing clearance from JWT Payload!');
24
+ return res.status(403).json({ error: 'MLS Blocked: User clearance level is not defined.' });
25
+ }
26
+
27
+ const userLevel = LATTICE[req.user.clearance.toUpperCase()];
28
+ if (!userLevel) {
29
+ return res.status(403).json({ error: `MLS Blocked: Unknown user clearance header '${req.user.clearance}'` });
30
+ }
31
+
32
+ const isRead = ['GET', 'HEAD', 'OPTIONS'].includes(req.method);
33
+ const isWrite = ['POST', 'PUT', 'PATCH', 'DELETE'].includes(req.method);
34
+
35
+ // ----------------------------------------------------
36
+ // Rule 1: Simple Security Property (No-Read-Up)
37
+ // ----------------------------------------------------
38
+ // Users cannot read data at a classification level higher than their own.
39
+ if (isRead) {
40
+ if (userLevel < routeLevel) {
41
+ console.warn(`[MLS] BLocked READ-UP Attack: User(${req.user.clearance}) trying to read Route(${routeRequiredClearance})`);
42
+ return res.status(403).json({
43
+ error: `Bell-LaPadula Violation: Operation denied. Your clearance (${req.user.clearance}) is too low to read this resource (${routeRequiredClearance}).`
44
+ });
45
+ }
46
+ }
47
+
48
+ // ----------------------------------------------------
49
+ // Rule 2: *-Property (No-Write-Down)
50
+ // ----------------------------------------------------
51
+ // Users at a high classification cannot accidentally or maliciously write high-level data to a low-level target.
52
+ if (isWrite) {
53
+ if (userLevel > routeLevel) {
54
+ console.warn(`[MLS] Blocked WRITE-DOWN Attack: User(${req.user.clearance}) trying to write to Route(${routeRequiredClearance})`);
55
+ return res.status(403).json({
56
+ error: `Bell-LaPadula Violation: Data spillage risk denied. You cannot write sensitive data (${req.user.clearance}) to a lower clearance partition (${routeRequiredClearance}).`
57
+ });
58
+ }
59
+ }
60
+
61
+ // Passed lattice checks!
62
+ console.log(`[MLS Engine] Passed Bell-LaPadula checks for user ${req.user.sub}`);
63
+ next();
64
+ };
65
+ }
66
+
67
+ module.exports = {
68
+ createMLSMiddleware
69
+ };
@@ -0,0 +1,44 @@
1
+ const jwt = require('jsonwebtoken');
2
+
3
+ /**
4
+ * Creates the security verification middleware using an injected secret.
5
+ * @param {string} secret The secret key used to verify the JWT signature.
6
+ */
7
+ const createSecurityMiddleware = (secret) => {
8
+ if (!secret) {
9
+ throw new Error('[SecurityGateway] A JWT secret is required to initialize security middleware.');
10
+ }
11
+
12
+ return (req, res, next) => {
13
+ const authHeader = req.headers['authorization'];
14
+
15
+ if (!authHeader || !authHeader.startsWith('Bearer ')) {
16
+ console.warn('[SecurityGateway] Blocked request: Missing or invalid Authorization header');
17
+ return res.status(401).json({ error: 'Unauthorized: Missing token' });
18
+ }
19
+
20
+ const token = authHeader.split(' ')[1];
21
+
22
+ try {
23
+ const decoded = jwt.verify(token, secret);
24
+
25
+ // Attach the fully decoded token metadata to the request
26
+ req.user = decoded;
27
+
28
+ // Inject into headers so downstream client microservices know who the user is
29
+ req.headers['x-user-id'] = decoded.sub;
30
+ req.headers['x-user-role'] = decoded.role;
31
+ req.headers['x-user-clearance'] = decoded.clearance;
32
+
33
+ console.log(`[SecurityGateway] Authorized request for user ${decoded.username} (${decoded.role})`);
34
+ next();
35
+ } catch (error) {
36
+ console.warn(`[SecurityGateway] Blocked request: Invalid token - ${error.message}`);
37
+ return res.status(403).json({ error: 'Forbidden: Invalid or expired token' });
38
+ }
39
+ };
40
+ };
41
+
42
+ module.exports = {
43
+ createSecurityMiddleware
44
+ };
package/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "secure-gateway-sdk",
3
+ "version": "1.0.0",
4
+ "scripts": {
5
+ "dev": "echo 'Proxy is an SDK library and cannot be run directly. Import it into your own server script.' && exit 0"
6
+ },
7
+ "dependencies": {
8
+ "cors": "^2.8.6",
9
+ "dotenv": "^16.6.1",
10
+ "express": "^5.2.1",
11
+ "http-proxy-middleware": "^3.0.5",
12
+ "jsonwebtoken": "^9.0.3"
13
+ },
14
+ "description": "This package is a drop-in API Gateway library designed to secure and route traffic to a microservice ecosystem containing Auth, Enclave, and Audit endpoints.",
15
+ "main": "index.js",
16
+ "directories": {
17
+ "lib": "lib"
18
+ },
19
+ "keywords": [],
20
+ "author": "",
21
+ "license": "ISC",
22
+ "type": "commonjs"
23
+ }