secure-comms-hybrid 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 +17 -0
- package/lib/backend/HybridCrypto.js +175 -0
- package/lib/backend/index.js +23 -0
- package/lib/backend/middleware.js +214 -0
- package/package.json +76 -0
package/README.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# ๐ Secure Comms Hybrid
|
|
2
|
+
|
|
3
|
+
Hybrid encryption (RSA + AES-GCM) for secure client-server communications.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- โ
**Hybrid Encryption**: RSA-2048 for key exchange + AES-GCM-256 for data
|
|
8
|
+
- โ
**Perfect Forward Secrecy**: Unique session keys
|
|
9
|
+
- โ
**Zero Configuration**: Easy setup for Express.js
|
|
10
|
+
- โ
**Browser & Node.js**: Universal support
|
|
11
|
+
- โ
**TypeScript**: Full type definitions
|
|
12
|
+
- โ
**Middleware**: Drop-in encryption for existing APIs
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install secure-comms-hybrid
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
const crypto = require('crypto');
|
|
2
|
+
const {
|
|
3
|
+
CRYPTO_CONFIG,
|
|
4
|
+
ERRORS
|
|
5
|
+
} = require('../shared/constants');
|
|
6
|
+
class HybridCrypto {
|
|
7
|
+
constructor(options = {}) {
|
|
8
|
+
this.options = {
|
|
9
|
+
rsaKeySize: options.rsaKeySize || CRYPTO_CONFIG.RSA.MODULUS_LENGTH,
|
|
10
|
+
sessionExpiry: options.sessionExpiry || CRYPTO_CONFIG.SESSION.EXPIRY_MS,
|
|
11
|
+
...options
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
// Generate or use provided RSA keys
|
|
15
|
+
if (options.rsaPrivateKey && options.rsaPublicKey) {
|
|
16
|
+
this.rsaPrivateKey = options.rsaPrivateKey;
|
|
17
|
+
this.rsaPublicKey = options.rsaPublicKey;
|
|
18
|
+
} else {
|
|
19
|
+
this.generateRSAKeys();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Session storage
|
|
23
|
+
this.sessionKeys = new Map();
|
|
24
|
+
this.sessionCleanupInterval = setInterval(() => this.cleanupExpiredSessions(), CRYPTO_CONFIG.SESSION.CLEANUP_INTERVAL);
|
|
25
|
+
console.log('๐ HybridCrypto initialized');
|
|
26
|
+
}
|
|
27
|
+
generateRSAKeys() {
|
|
28
|
+
const {
|
|
29
|
+
publicKey,
|
|
30
|
+
privateKey
|
|
31
|
+
} = crypto.generateKeyPairSync('rsa', {
|
|
32
|
+
modulusLength: this.options.rsaKeySize,
|
|
33
|
+
publicKeyEncoding: CRYPTO_CONFIG.RSA.PUBLIC_KEY_ENCODING,
|
|
34
|
+
privateKeyEncoding: CRYPTO_CONFIG.RSA.PRIVATE_KEY_ENCODING
|
|
35
|
+
});
|
|
36
|
+
this.rsaPublicKey = publicKey;
|
|
37
|
+
this.rsaPrivateKey = privateKey;
|
|
38
|
+
}
|
|
39
|
+
getPublicKey() {
|
|
40
|
+
return {
|
|
41
|
+
publicKey: this.rsaPublicKey,
|
|
42
|
+
algorithm: 'RSA-OAEP',
|
|
43
|
+
hash: CRYPTO_CONFIG.RSA.HASH,
|
|
44
|
+
keySize: this.options.rsaKeySize,
|
|
45
|
+
timestamp: new Date().toISOString()
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
decryptAESKey(encryptedAESKey) {
|
|
49
|
+
try {
|
|
50
|
+
const decrypted = crypto.privateDecrypt({
|
|
51
|
+
key: this.rsaPrivateKey,
|
|
52
|
+
padding: crypto.constants[CRYPTO_CONFIG.RSA.PADDING],
|
|
53
|
+
oaepHash: 'sha256'
|
|
54
|
+
}, Buffer.from(encryptedAESKey, 'base64'));
|
|
55
|
+
return decrypted;
|
|
56
|
+
} catch (error) {
|
|
57
|
+
throw new Error(`${ERRORS.DECRYPTION_FAILED}: ${error.message}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
storeSessionKey(clientId, aesKey, customExpiry = null) {
|
|
61
|
+
const expiresAt = Date.now() + (customExpiry || this.options.sessionExpiry);
|
|
62
|
+
this.sessionKeys.set(clientId, {
|
|
63
|
+
aesKey,
|
|
64
|
+
expiresAt,
|
|
65
|
+
createdAt: Date.now()
|
|
66
|
+
});
|
|
67
|
+
return expiresAt;
|
|
68
|
+
}
|
|
69
|
+
getSessionKey(clientId) {
|
|
70
|
+
const session = this.sessionKeys.get(clientId);
|
|
71
|
+
if (!session) {
|
|
72
|
+
throw new Error(ERRORS.SESSION_EXPIRED);
|
|
73
|
+
}
|
|
74
|
+
if (Date.now() > session.expiresAt) {
|
|
75
|
+
this.sessionKeys.delete(clientId);
|
|
76
|
+
throw new Error(ERRORS.SESSION_EXPIRED);
|
|
77
|
+
}
|
|
78
|
+
return session.aesKey;
|
|
79
|
+
}
|
|
80
|
+
cleanupExpiredSessions() {
|
|
81
|
+
const now = Date.now();
|
|
82
|
+
let cleaned = 0;
|
|
83
|
+
for (const [clientId, session] of this.sessionKeys.entries()) {
|
|
84
|
+
if (now > session.expiresAt) {
|
|
85
|
+
this.sessionKeys.delete(clientId);
|
|
86
|
+
cleaned++;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (cleaned > 0) {
|
|
90
|
+
console.log(`๐งน Cleaned ${cleaned} expired sessions`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
async decryptData(ciphertextBase64, ivBase64, authTagBase64, clientId) {
|
|
94
|
+
try {
|
|
95
|
+
const aesKey = this.getSessionKey(clientId);
|
|
96
|
+
const ciphertext = Buffer.from(ciphertextBase64, 'base64');
|
|
97
|
+
const iv = Buffer.from(ivBase64, 'base64');
|
|
98
|
+
const authTag = Buffer.from(authTagBase64, 'base64');
|
|
99
|
+
const decipher = crypto.createDecipheriv(CRYPTO_CONFIG.AES.ALGORITHM.toLowerCase(), aesKey, iv);
|
|
100
|
+
decipher.setAuthTag(authTag);
|
|
101
|
+
let decrypted = decipher.update(ciphertext, null, 'utf8');
|
|
102
|
+
decrypted += decipher.final('utf8');
|
|
103
|
+
return {
|
|
104
|
+
success: true,
|
|
105
|
+
data: JSON.parse(decrypted),
|
|
106
|
+
raw: decrypted,
|
|
107
|
+
timestamp: new Date().toISOString()
|
|
108
|
+
};
|
|
109
|
+
} catch (error) {
|
|
110
|
+
return {
|
|
111
|
+
success: false,
|
|
112
|
+
error: error.message,
|
|
113
|
+
timestamp: new Date().toISOString()
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
async encryptData(data, clientId) {
|
|
118
|
+
try {
|
|
119
|
+
const aesKey = this.getSessionKey(clientId);
|
|
120
|
+
const iv = crypto.randomBytes(CRYPTO_CONFIG.AES.IV_LENGTH);
|
|
121
|
+
const cipher = crypto.createCipheriv(CRYPTO_CONFIG.AES.ALGORITHM.toLowerCase(), aesKey, iv);
|
|
122
|
+
let encrypted = cipher.update(JSON.stringify(data), 'utf8', 'base64');
|
|
123
|
+
encrypted += cipher.final('base64');
|
|
124
|
+
const authTag = cipher.getAuthTag();
|
|
125
|
+
return {
|
|
126
|
+
success: true,
|
|
127
|
+
ciphertext: encrypted,
|
|
128
|
+
iv: iv.toString('base64'),
|
|
129
|
+
authTag: authTag.toString('base64'),
|
|
130
|
+
algorithm: `${CRYPTO_CONFIG.AES.ALGORITHM}-${CRYPTO_CONFIG.AES.KEY_LENGTH}`,
|
|
131
|
+
timestamp: new Date().toISOString()
|
|
132
|
+
};
|
|
133
|
+
} catch (error) {
|
|
134
|
+
return {
|
|
135
|
+
success: false,
|
|
136
|
+
error: error.message,
|
|
137
|
+
timestamp: new Date().toISOString()
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
async processHandshake(handshakeData, clientId, customExpiry = null) {
|
|
142
|
+
try {
|
|
143
|
+
const aesKey = this.decryptAESKey(handshakeData.encryptedAESKey);
|
|
144
|
+
const expiresAt = this.storeSessionKey(clientId, aesKey, customExpiry);
|
|
145
|
+
const clientIV = crypto.randomBytes(CRYPTO_CONFIG.AES.IV_LENGTH);
|
|
146
|
+
return {
|
|
147
|
+
success: true,
|
|
148
|
+
message: 'Handshake successful',
|
|
149
|
+
clientIV: clientIV.toString('base64'),
|
|
150
|
+
sessionExpiry: expiresAt - Date.now(),
|
|
151
|
+
expiresAt,
|
|
152
|
+
timestamp: new Date().toISOString()
|
|
153
|
+
};
|
|
154
|
+
} catch (error) {
|
|
155
|
+
return {
|
|
156
|
+
success: false,
|
|
157
|
+
error: error.message,
|
|
158
|
+
timestamp: new Date().toISOString()
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
destroy() {
|
|
163
|
+
clearInterval(this.sessionCleanupInterval);
|
|
164
|
+
this.sessionKeys.clear();
|
|
165
|
+
}
|
|
166
|
+
getStats() {
|
|
167
|
+
return {
|
|
168
|
+
activeSessions: this.sessionKeys.size,
|
|
169
|
+
rsaKeySize: this.options.rsaKeySize,
|
|
170
|
+
sessionExpiry: this.options.sessionExpiry,
|
|
171
|
+
uptime: process.uptime()
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
module.exports = HybridCrypto;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const HybridCrypto = require('./HybridCrypto');
|
|
2
|
+
const middleware = require('./middleware');
|
|
3
|
+
module.exports = {
|
|
4
|
+
HybridCrypto,
|
|
5
|
+
...middleware,
|
|
6
|
+
// Quick setup function
|
|
7
|
+
createHybridCrypto: (options = {}) => {
|
|
8
|
+
const crypto = new HybridCrypto(options);
|
|
9
|
+
const {
|
|
10
|
+
createExpressMiddleware
|
|
11
|
+
} = middleware;
|
|
12
|
+
return {
|
|
13
|
+
crypto,
|
|
14
|
+
expressMiddleware: createExpressMiddleware(crypto, options.middlewareOptions),
|
|
15
|
+
getPublicKey: () => crypto.getPublicKey(),
|
|
16
|
+
getStats: () => crypto.getStats(),
|
|
17
|
+
destroy: () => crypto.destroy()
|
|
18
|
+
};
|
|
19
|
+
},
|
|
20
|
+
// Version info
|
|
21
|
+
version: '1.0.0',
|
|
22
|
+
description: 'Hybrid encryption for secure communications'
|
|
23
|
+
};
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
const {
|
|
2
|
+
CRYPTO_CONFIG,
|
|
3
|
+
ERRORS
|
|
4
|
+
} = require('../shared/constants');
|
|
5
|
+
function createEncryptionMiddleware(hybridCrypto, options = {}) {
|
|
6
|
+
const {
|
|
7
|
+
excludedPaths = ['/health', '/'],
|
|
8
|
+
requireEncryption = false,
|
|
9
|
+
logLevel = 'info'
|
|
10
|
+
} = options;
|
|
11
|
+
|
|
12
|
+
// Decryption middleware
|
|
13
|
+
const decryptRequest = (req, res, next) => {
|
|
14
|
+
// Check if path is excluded
|
|
15
|
+
if (excludedPaths.includes(req.path)) {
|
|
16
|
+
return next();
|
|
17
|
+
}
|
|
18
|
+
const isEncrypted = req.headers[CRYPTO_CONFIG.HEADERS.ENCRYPTED] === 'true';
|
|
19
|
+
const clientId = req.headers[CRYPTO_CONFIG.HEADERS.CLIENT_ID];
|
|
20
|
+
if (!isEncrypted) {
|
|
21
|
+
if (requireEncryption) {
|
|
22
|
+
return res.status(400).json({
|
|
23
|
+
success: false,
|
|
24
|
+
error: 'Encryption required',
|
|
25
|
+
timestamp: new Date().toISOString()
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
return next();
|
|
29
|
+
}
|
|
30
|
+
if (!clientId) {
|
|
31
|
+
return res.status(400).json({
|
|
32
|
+
success: false,
|
|
33
|
+
error: ERRORS.MISSING_CLIENT_ID,
|
|
34
|
+
timestamp: new Date().toISOString()
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
let rawBody = '';
|
|
38
|
+
req.on('data', chunk => {
|
|
39
|
+
rawBody += chunk.toString();
|
|
40
|
+
});
|
|
41
|
+
req.on('end', async () => {
|
|
42
|
+
try {
|
|
43
|
+
if (!rawBody) {
|
|
44
|
+
return res.status(400).json({
|
|
45
|
+
success: false,
|
|
46
|
+
error: 'Empty request body',
|
|
47
|
+
timestamp: new Date().toISOString()
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
let parsedBody;
|
|
51
|
+
try {
|
|
52
|
+
parsedBody = JSON.parse(rawBody);
|
|
53
|
+
} catch (error) {
|
|
54
|
+
return res.status(400).json({
|
|
55
|
+
success: false,
|
|
56
|
+
error: 'Invalid JSON',
|
|
57
|
+
timestamp: new Date().toISOString()
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
if (!parsedBody.ciphertext || !parsedBody.iv || !parsedBody.authTag) {
|
|
61
|
+
return res.status(400).json({
|
|
62
|
+
success: false,
|
|
63
|
+
error: ERRORS.INVALID_REQUEST,
|
|
64
|
+
required: ['ciphertext', 'iv', 'authTag'],
|
|
65
|
+
timestamp: new Date().toISOString()
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
const decrypted = await hybridCrypto.decryptData(parsedBody.ciphertext, parsedBody.iv, parsedBody.authTag, clientId);
|
|
69
|
+
if (!decrypted.success) {
|
|
70
|
+
return res.status(400).json({
|
|
71
|
+
success: false,
|
|
72
|
+
error: ERRORS.DECRYPTION_FAILED,
|
|
73
|
+
details: decrypted.error,
|
|
74
|
+
timestamp: new Date().toISOString()
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
req.originalEncryptedBody = parsedBody;
|
|
78
|
+
req.body = decrypted.data;
|
|
79
|
+
req.encryptionMetadata = {
|
|
80
|
+
type: 'hybrid',
|
|
81
|
+
clientId,
|
|
82
|
+
decryptedAt: decrypted.timestamp,
|
|
83
|
+
algorithm: 'RSA-AES-GCM'
|
|
84
|
+
};
|
|
85
|
+
next();
|
|
86
|
+
} catch (error) {
|
|
87
|
+
console.error('โ Decryption middleware error:', error);
|
|
88
|
+
return res.status(500).json({
|
|
89
|
+
success: false,
|
|
90
|
+
error: 'Internal server error',
|
|
91
|
+
timestamp: new Date().toISOString()
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// Encryption middleware
|
|
98
|
+
const encryptResponse = (req, res, next) => {
|
|
99
|
+
const wantsEncryption = req.headers[CRYPTO_CONFIG.HEADERS.ENCRYPTED] === 'true';
|
|
100
|
+
const clientId = req.headers[CRYPTO_CONFIG.HEADERS.CLIENT_ID];
|
|
101
|
+
if (!wantsEncryption || !clientId || excludedPaths.includes(req.path)) {
|
|
102
|
+
return next();
|
|
103
|
+
}
|
|
104
|
+
const originalJson = res.json;
|
|
105
|
+
res.json = async function (data) {
|
|
106
|
+
try {
|
|
107
|
+
if (res.statusCode >= 400) {
|
|
108
|
+
return originalJson.call(this, data);
|
|
109
|
+
}
|
|
110
|
+
const encrypted = await hybridCrypto.encryptData(data, clientId);
|
|
111
|
+
if (!encrypted.success) {
|
|
112
|
+
throw new Error(`${ERRORS.ENCRYPTION_FAILED}: ${encrypted.error}`);
|
|
113
|
+
}
|
|
114
|
+
const encryptedResponse = {
|
|
115
|
+
success: true,
|
|
116
|
+
encrypted: true,
|
|
117
|
+
algorithm: encrypted.algorithm,
|
|
118
|
+
ciphertext: encrypted.ciphertext,
|
|
119
|
+
iv: encrypted.iv,
|
|
120
|
+
authTag: encrypted.authTag,
|
|
121
|
+
timestamp: encrypted.timestamp,
|
|
122
|
+
metadata: {
|
|
123
|
+
clientId,
|
|
124
|
+
originalDataType: typeof data
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
res.setHeader(CRYPTO_CONFIG.HEADERS.ENCRYPTED, 'true');
|
|
128
|
+
res.setHeader(CRYPTO_CONFIG.HEADERS.ENCRYPTION_ALGORITHM, 'RSA-AES-GCM');
|
|
129
|
+
res.setHeader(CRYPTO_CONFIG.HEADERS.CLIENT_ID, clientId);
|
|
130
|
+
return originalJson.call(this, encryptedResponse);
|
|
131
|
+
} catch (error) {
|
|
132
|
+
console.error('โ Response encryption failed:', error);
|
|
133
|
+
res.removeHeader(CRYPTO_CONFIG.HEADERS.ENCRYPTED);
|
|
134
|
+
res.removeHeader(CRYPTO_CONFIG.HEADERS.ENCRYPTION_ALGORITHM);
|
|
135
|
+
return originalJson.call(this, {
|
|
136
|
+
success: false,
|
|
137
|
+
error: ERRORS.ENCRYPTION_FAILED,
|
|
138
|
+
message: error.message,
|
|
139
|
+
fallbackData: data,
|
|
140
|
+
encrypted: false,
|
|
141
|
+
timestamp: new Date().toISOString()
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
next();
|
|
146
|
+
};
|
|
147
|
+
return {
|
|
148
|
+
decryptRequest,
|
|
149
|
+
encryptResponse,
|
|
150
|
+
middleware: [decryptRequest, encryptResponse]
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Express.js specific middleware factory
|
|
155
|
+
function createExpressMiddleware(hybridCrypto, options = {}) {
|
|
156
|
+
const {
|
|
157
|
+
decryptRequest,
|
|
158
|
+
encryptResponse
|
|
159
|
+
} = createEncryptionMiddleware(hybridCrypto, options);
|
|
160
|
+
return function (req, res, next) {
|
|
161
|
+
// Apply decryption
|
|
162
|
+
decryptRequest(req, res, err => {
|
|
163
|
+
if (err) return next(err);
|
|
164
|
+
// Apply encryption
|
|
165
|
+
encryptResponse(req, res, next);
|
|
166
|
+
});
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Koa.js middleware
|
|
171
|
+
function createKoaMiddleware(hybridCrypto, options = {}) {
|
|
172
|
+
const {
|
|
173
|
+
decryptRequest,
|
|
174
|
+
encryptResponse
|
|
175
|
+
} = createEncryptionMiddleware(hybridCrypto, options);
|
|
176
|
+
return async (ctx, next) => {
|
|
177
|
+
// Convert Koa context to Express-like req/res
|
|
178
|
+
const req = ctx.request;
|
|
179
|
+
const res = {
|
|
180
|
+
json: data => {
|
|
181
|
+
ctx.body = data;
|
|
182
|
+
ctx.type = 'application/json';
|
|
183
|
+
},
|
|
184
|
+
status: code => {
|
|
185
|
+
ctx.status = code;
|
|
186
|
+
return res;
|
|
187
|
+
},
|
|
188
|
+
setHeader: (name, value) => {
|
|
189
|
+
ctx.set(name, value);
|
|
190
|
+
},
|
|
191
|
+
removeHeader: name => {
|
|
192
|
+
ctx.remove(name);
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
await new Promise(resolve => {
|
|
196
|
+
decryptRequest(req, res, err => {
|
|
197
|
+
if (err) throw err;
|
|
198
|
+
resolve();
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
await next();
|
|
202
|
+
if (ctx.headers[CRYPTO_CONFIG.HEADERS.ENCRYPTED] === 'true') {
|
|
203
|
+
await new Promise(resolve => {
|
|
204
|
+
encryptResponse(req, res, () => resolve());
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
module.exports = {
|
|
210
|
+
createEncryptionMiddleware,
|
|
211
|
+
createExpressMiddleware,
|
|
212
|
+
createKoaMiddleware,
|
|
213
|
+
HybridCrypto: require('./HybridCrypto')
|
|
214
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "secure-comms-hybrid",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Hybrid encryption (RSA + AES) for secure client-server communications",
|
|
5
|
+
"main": "lib/backend/index.js",
|
|
6
|
+
"browser": "dist/frontend/index.js",
|
|
7
|
+
"module": "src/frontend/index.js",
|
|
8
|
+
"types": "types/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"node": "./lib/backend/index.js",
|
|
12
|
+
"browser": "./dist/frontend/index.js",
|
|
13
|
+
"import": "./lib/frontend/index.js",
|
|
14
|
+
"require": "./lib/backend/index.js"
|
|
15
|
+
},
|
|
16
|
+
"./backend": "./lib/backend/index.js",
|
|
17
|
+
"./frontend": "./dist/frontend/index.js",
|
|
18
|
+
"./express": "./lib/backend/middleware.js"
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "npm run build:backend",
|
|
22
|
+
"build:backend": "babel src/backend --out-dir lib/backend",
|
|
23
|
+
"build:types": "tsc --declaration --emitDeclarationOnly",
|
|
24
|
+
"dev": "npm run build:backend && nodemon --exec babel-node src/backend/test.js",
|
|
25
|
+
"test": "jest --passWithNoTests",
|
|
26
|
+
"test:backend": "jest backend.test.js --passWithNoTests",
|
|
27
|
+
"lint": "eslint src/**/*.js",
|
|
28
|
+
"prepublishOnly": "npm run build && npm test",
|
|
29
|
+
"example:backend": "node examples/backend/express-example.js"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"encryption",
|
|
33
|
+
"security",
|
|
34
|
+
"crypto",
|
|
35
|
+
"aes",
|
|
36
|
+
"rsa",
|
|
37
|
+
"hybrid",
|
|
38
|
+
"secure-communications",
|
|
39
|
+
"end-to-end-encryption"
|
|
40
|
+
],
|
|
41
|
+
"author": "Abhay Singh Kathayat",
|
|
42
|
+
"license": "MIT",
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"crypto": "^1.0.1"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@babel/cli": "^7.23.4",
|
|
48
|
+
"@babel/core": "^7.23.5",
|
|
49
|
+
"@babel/preset-env": "^7.23.5",
|
|
50
|
+
"@types/crypto-js": "^4.2.1",
|
|
51
|
+
"@types/jest": "^29.5.11",
|
|
52
|
+
"@types/node": "^20.10.5",
|
|
53
|
+
"babel-jest": "^29.7.0",
|
|
54
|
+
"eslint": "^8.55.0",
|
|
55
|
+
"express": "^4.18.2",
|
|
56
|
+
"jest": "^29.7.0",
|
|
57
|
+
"nodemon": "^3.0.2",
|
|
58
|
+
"typescript": "^5.3.3",
|
|
59
|
+
"webpack": "^5.89.0",
|
|
60
|
+
"webpack-cli": "^5.1.4"
|
|
61
|
+
},
|
|
62
|
+
"peerDependencies": {
|
|
63
|
+
"express": "^4.0.0"
|
|
64
|
+
},
|
|
65
|
+
"engines": {
|
|
66
|
+
"node": ">=14.0.0",
|
|
67
|
+
"npm": ">=6.0.0"
|
|
68
|
+
},
|
|
69
|
+
"files": [
|
|
70
|
+
"lib",
|
|
71
|
+
"dist",
|
|
72
|
+
"types",
|
|
73
|
+
"README.md",
|
|
74
|
+
"LICENSE"
|
|
75
|
+
]
|
|
76
|
+
}
|