express-jcrypt 1.0.1
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 +0 -0
- package/index.js +18 -0
- package/package.json +36 -0
- package/src/authtag.js +15 -0
- package/src/base64url.js +28 -0
- package/src/cipher.js +46 -0
- package/src/iv.js +25 -0
- package/src/key.js +78 -0
- package/src/protectedHeader.js +17 -0
package/README.md
ADDED
|
File without changes
|
package/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const keyPair = require('../keyPair');
|
|
2
|
+
const cipher = require('../cipher');
|
|
3
|
+
const authTag = require('../authtag');
|
|
4
|
+
const generateIV = require('../iv');
|
|
5
|
+
const { encode, decode } = require('../base64url');
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
module.exports = {
|
|
9
|
+
keyPair,
|
|
10
|
+
cipher,
|
|
11
|
+
authTag,
|
|
12
|
+
generateIV,
|
|
13
|
+
encode,
|
|
14
|
+
decode,
|
|
15
|
+
undecipher,
|
|
16
|
+
encryptCEK,
|
|
17
|
+
decryptCEK,
|
|
18
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "express-jcrypt",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "jwe",
|
|
5
|
+
"keywords": [],
|
|
6
|
+
"homepage": "https://github.com/pdotexe/Jcrypt#readme",
|
|
7
|
+
"bugs": {
|
|
8
|
+
"url": "https://github.com/pdotexe/Jcrypt/issues"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/pdotexe/Jcrypt.git"
|
|
13
|
+
},
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"author": "prestigiouss",
|
|
16
|
+
"type": "module",
|
|
17
|
+
"main": "index.js",
|
|
18
|
+
"scripts": {
|
|
19
|
+
"start": "node index.js",
|
|
20
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"acorn": "^8.16.0",
|
|
24
|
+
"acorn-walk": "^8.3.5",
|
|
25
|
+
"arg": "^4.1.3",
|
|
26
|
+
"create-require": "^1.1.1",
|
|
27
|
+
"diff": "^4.0.4",
|
|
28
|
+
"make-error": "^1.3.6",
|
|
29
|
+
"ts-node": "^10.9.2",
|
|
30
|
+
"typescript": "^5.9.3",
|
|
31
|
+
"undici-types": "^6.21.0",
|
|
32
|
+
"v8-compile-cache-lib": "^3.0.1",
|
|
33
|
+
"yn": "^3.1.1"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {}
|
|
36
|
+
}
|
package/src/authtag.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const crypto = require('crypto');
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
*
|
|
7
|
+
* @param {*} cipher
|
|
8
|
+
* @returns - Authentication tag of whichever cipher used to ensure integrity of data.
|
|
9
|
+
*/
|
|
10
|
+
const authTag = (cipher) => {
|
|
11
|
+
if (!cipher || typeof cipher.getAuthTag !== 'function') throw new Error(" Invalid cipher object");
|
|
12
|
+
return cipher.getAuthTag();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
module.exports = authTag;
|
package/src/base64url.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// concise function call for base64url encoding and decoding
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @description - Converts buffer to URL safe string
|
|
6
|
+
* @param {Buffer | string} buffer - The input buffer to be encoded.
|
|
7
|
+
* @returns {string | null} - Encoded buffer string if buffer exists.
|
|
8
|
+
*/
|
|
9
|
+
const encode = (buffer) => {
|
|
10
|
+
if (!buffer) return null;
|
|
11
|
+
|
|
12
|
+
return Buffer.from(buffer).toString('base64url');
|
|
13
|
+
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @param {Buffer | string} encoded - Input string to decode
|
|
18
|
+
* @returns {Buffer | null} - Decoded buffer if input exists.
|
|
19
|
+
*/
|
|
20
|
+
const decode = (encoded) => {
|
|
21
|
+
if (!encoded) return null;
|
|
22
|
+
|
|
23
|
+
return Buffer.from(encoded, 'base64url');
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
module.exports = { encode, decode };
|
package/src/cipher.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import generateIV from './iv.js';
|
|
2
|
+
const crypto = require('crypto');
|
|
3
|
+
const authTag = require('./authtag.js');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @description - literal encryption function that takes plaintext and public key, generates random IV, CEK, and encrypts using AES-256-GCM.
|
|
7
|
+
* @param {string} plaintext - The data to be encrypted
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const generateCEK = () => crypto.randomBytes(32);
|
|
11
|
+
|
|
12
|
+
const encryptCEK = (contentEncKey, publicKey) => {
|
|
13
|
+
const cekEncrypt = crypto.publicEncrypt(publicKey, contentEncKey);
|
|
14
|
+
return cekEncrypt;
|
|
15
|
+
};
|
|
16
|
+
const decryptCEK = (encryptedCEK, privateKey, secretPassphrase) => {
|
|
17
|
+
const cekDecrypt = crypto.privateDecrypt(privateKey, encryptedCEK);
|
|
18
|
+
return cekDecrypt;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const cipher = (plaintext) => {
|
|
22
|
+
const cek = generateCEK();
|
|
23
|
+
|
|
24
|
+
const iv = generateIV(); // initialization vector
|
|
25
|
+
const enc = crypto.createCipheriv('aes-256-gcm', cek, iv); // actual encryption
|
|
26
|
+
const ciphertext = Buffer.concat([enc.update(plaintext, 'utf8'), enc.final()]); // convert to utf8 and concatenate
|
|
27
|
+
const tag = authTag(enc);
|
|
28
|
+
|
|
29
|
+
return { ciphertext, iv, tag, cek };
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const decipher = (ciphertext, iv, tag, cek) => {
|
|
35
|
+
const undecipher = crypto.createDecipheriv('aes-256-gcm', cek, iv);
|
|
36
|
+
const deciphertag = authTag(undecipher);
|
|
37
|
+
if (deciphertag.equals(tag)) {
|
|
38
|
+
const decrypted = Buffer.concat([undecipher.update(ciphertext), undecipher.final()]);
|
|
39
|
+
return decrypted.toString('utf8');
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
throw new Error('Invalid authentication tag');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
module.exports = { cipher, encryptCEK, decryptCEK, decipher };
|
package/src/iv.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import crypto from 'crypto';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @description - Generates random initialization vector for AES-256-GCM to add unique encryption values each time.
|
|
11
|
+
* @property {number} iv - IV value
|
|
12
|
+
* @returns {Buffer} - Randomly generated IV buffer
|
|
13
|
+
*/
|
|
14
|
+
const generateIV = () => {
|
|
15
|
+
|
|
16
|
+
const length = 12; // AES256GCM nonce size
|
|
17
|
+
|
|
18
|
+
const iv = crypto.randomBytes(length);
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
return iv;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
module.exports = generateIV;
|
package/src/key.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
|
|
2
|
+
import { generateKeyPair } from 'crypto';
|
|
3
|
+
import { writeFileSync } from 'fs';
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @typedef {Object} KeyPairOptions
|
|
8
|
+
* @property {number} modulusLength - RSA key length in bits
|
|
9
|
+
* @property {'spki'} publicKeyType - Public key encoding type
|
|
10
|
+
* @property {'pkcs8'} privateKeyType - Private key encoding type
|
|
11
|
+
* @property {string} [passphrase] - Optional passphrase for private key
|
|
12
|
+
* @property {Function} generateKeys - Instant key generation upon object instantiation
|
|
13
|
+
*/
|
|
14
|
+
class keyPair {
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @param {KeyPairOptions} options
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
constructor(options) {
|
|
25
|
+
if(!options || typeof options !== 'object') throw new Error();
|
|
26
|
+
this.modulusLength = Number(options.modulusLength || 2048);
|
|
27
|
+
if(this.modulusLength < 2048) throw new Error("Modulus length must be at least 2048 bits");
|
|
28
|
+
this.publicKeyType = 'spki';
|
|
29
|
+
this.privateKeyType = String(options.privateKeyType || 'pkcs8');
|
|
30
|
+
this.passphrase = options.passphrase ? String(options.passphrase) : undefined;
|
|
31
|
+
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* @description - Generates RSA key pair using crypto module's generateKeyPair function
|
|
35
|
+
* @throws Error if key generation fails
|
|
36
|
+
* @returns {Promise<{publicKey, privateKey}>} - Resolves generated key pair
|
|
37
|
+
*/
|
|
38
|
+
generateKeys() {
|
|
39
|
+
return new Promise((resolve, reject) => {
|
|
40
|
+
const privateKeyEncoding = {
|
|
41
|
+
type: this.privateKeyType,
|
|
42
|
+
format: 'pem',
|
|
43
|
+
}
|
|
44
|
+
if (this.passphrase){
|
|
45
|
+
privateKeyEncoding.cipher = 'aes-256-cbc';
|
|
46
|
+
privateKeyEncoding.passphrase = this.passphrase;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const publicKeyEncoding = {
|
|
50
|
+
type: this.publicKeyType,
|
|
51
|
+
format: 'pem',
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
generateKeyPair(
|
|
55
|
+
'rsa',
|
|
56
|
+
{
|
|
57
|
+
modulusLength: this.modulusLength,
|
|
58
|
+
publicKeyEncoding: publicKeyEncoding,
|
|
59
|
+
publicExponent: 0x10001,
|
|
60
|
+
privateKeyEncoding: privateKeyEncoding,
|
|
61
|
+
|
|
62
|
+
}, (err, publicKey, privateKey) => {
|
|
63
|
+
if (err) return reject(err);
|
|
64
|
+
/*
|
|
65
|
+
@description: Logs keys to console for readability and verification
|
|
66
|
+
*/
|
|
67
|
+
resolve({publicKey, privateKey});
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
module.exports = keyPair;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* @description - metadata containing algorithm and encryption method for JWE header.
|
|
4
|
+
* @returns {Buffer} - Buffer with JSON stringified protected JWE header.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const protectedHeader = (alg = "RSA-OAEP-256", enc = "AES-256-GCM", type = "JWE", kid) => {
|
|
8
|
+
const header = {alg, enc};
|
|
9
|
+
if (type) header.typ = type;
|
|
10
|
+
if (kid) header.kid = kid;
|
|
11
|
+
|
|
12
|
+
stringifyHeader = JSON.stringify(header);
|
|
13
|
+
return Buffer.from(stringifyHeader);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
module.exports = protectedHeader;
|