Skip to content

Commit b9d51c5

Browse files
committed
feat: add CryptoDriver class
1 parent 540f8c3 commit b9d51c5

File tree

2 files changed

+217
-0
lines changed

2 files changed

+217
-0
lines changed

index.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* @author Victor Giovanni Beltrán Rodríguez
3+
* @file Manages main entry point.
4+
*/
5+
6+
// ━━ TYPE DEFINITIONS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
7+
/**
8+
* The CryptoDriver class contain methods to encrypt and decrypt data.
9+
*
10+
* This class uses the AES-256-GCM encryption algorithm with a
11+
* 256-bit key, which is considered one of the most secure symmetric encryption
12+
* algorithms available.
13+
*
14+
* @version 1.0.0
15+
* @author Victor Giovanni Beltrán Rodríguez
16+
* @module CryptoDriver
17+
*/
18+
const CryptoDriver = require('./src');
19+
20+
// ━━ EXPORT MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
21+
module.exports = CryptoDriver;

src/index.js

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
/**
2+
* @author Victor Giovanni Beltrán Rodríguez
3+
* @file Manages `CryptoDriver` class related to cipher and decipher of data.
4+
*/
5+
6+
// ━━ IMPORT MODULES ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
7+
// » IMPORT NATIVE NODE MODULES
8+
const crypto = require('crypto');
9+
10+
// ━━ TYPE DEFINITIONS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
11+
/**
12+
* Type definition for the cipher options object.
13+
*
14+
* @private
15+
* @typedef {crypto.CipherGCMOptions} CipherGCMOptions
16+
*/
17+
18+
/**
19+
* Defines the structure of an object that holds error messages thrown by the
20+
* `CryptoDriver` class.
21+
*
22+
* @private
23+
* @typedef {object} CryptoDriverErrors
24+
* @property {string} LENGTH_KEY - Error message thrown when the `key` value is not 32 characters long.
25+
* @property {string} UNDEFINED_KEY - Error message thrown when the `key` value is not provided.
26+
* @property {string} TYPE_KEY - Error message thrown when the `key` value is not a string.
27+
* @property {string} UNDEFINED_DATA - Error message thrown when the `data` value is not provided.
28+
* @property {string} TYPE_DATA - Error message thrown when the `data` value is not a string.
29+
* @property {string} UNDEFINED_ENCRYPTED - Error message thrown when the `encrypted` value is not provided.
30+
* @property {string} TYPE_ENCRYPTED - Error message thrown when the `encrypted` value is not a string.
31+
*/
32+
33+
// ━━ CONSTANTS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
34+
/**
35+
* Algorithm used for encryption and decryption.
36+
*
37+
* @private
38+
* @constant ALGORITHM
39+
* @type {string}
40+
*/
41+
const ALGORITHM = 'aes-256-gcm';
42+
43+
/**
44+
* Length in bytes of the GCM auth tag used for encryption and decryption.
45+
*
46+
* @private
47+
* @constant TAG_BYTE_LENGTH
48+
* @type {number}
49+
*/
50+
const TAG_BYTE_LENGTH = 16;
51+
52+
/**
53+
* Length in bytes of the initialization vector (IV) used for encryption and
54+
* decryption.
55+
*
56+
* @private
57+
* @constant IV_BYTE_LENGTH
58+
* @type {number}
59+
*/
60+
const IV_BYTE_LENGTH = 12;
61+
62+
/**
63+
* Options object for the GCM auth tag length.
64+
*
65+
* @private
66+
* @constant IV_OPTIONS
67+
* @type {CipherGCMOptions}
68+
*/
69+
const IV_OPTIONS = {
70+
authTagLength: TAG_BYTE_LENGTH,
71+
};
72+
73+
/**
74+
* Holds error messages thrown by the CryptoDriver class.
75+
*
76+
* @private
77+
* @constant ERRORS
78+
* @type {CryptoDriverErrors}
79+
*/
80+
const ERRORS = {
81+
LENGTH_KEY: 'The "Key" value must be 32 characters (256 bits).',
82+
UNDEFINED_KEY: 'The "key" value must be provided and must be a string.',
83+
TYPE_KEY: 'The "key" value must be of type string',
84+
UNDEFINED_DATA: 'The "data" value must be provided and must be a string.',
85+
TYPE_DATA: 'The "data" value must be of type string',
86+
UNDEFINED_ENCRYPTED: 'The "encrypted" value must be provided and must be a string.',
87+
TYPE_ENCRYPTED: 'The "encrypted" value must be of type string',
88+
};
89+
90+
// ━━ MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
91+
/**
92+
* The CryptoDriver class contain methods to encrypt and decrypt data.
93+
*
94+
* @class
95+
* @classdesc This class uses the AES-256-GCM encryption algorithm with a
96+
* 256-bit key, which is considered one of the most secure symmetric encryption
97+
* algorithms available.
98+
* @see {@link https://en.wikipedia.org/wiki/Advanced_Encryption_Standard Advanced Encryption Standard}
99+
* @see {@link https://en.wikipedia.org/wiki/Galois/Counter_Mode Galois/Counter Mode}
100+
*
101+
* @author Victor Giovanni Beltrán Rodríguez
102+
* @version 1.0.0
103+
*/
104+
class CryptoDriver {
105+
/**
106+
* Creates an instance of CryptoDriver, the `contructor` method require
107+
* the param `key` must be a string of 32 characters.
108+
*
109+
* @param {string} key - The key used for encryption and decryption.
110+
* @memberof CryptoDriver
111+
* @throws {ReferenceError} If `key` value is `undefined`.
112+
* @throws {TypeError} If `key` type is other than string.
113+
* @throws {RangeError} If `key` type length is other than 32.
114+
* @example
115+
* ```js
116+
* const crypto = new CryptoDriver('d6F3Efeqd6F3Efeqd6F3Efeqd6F3Efeq');
117+
*```
118+
*/
119+
constructor(key) {
120+
if (key === undefined) throw new ReferenceError(ERRORS.UNDEFINED_KEY);
121+
if (typeof key !== 'string') throw new TypeError(ERRORS.TYPE_KEY);
122+
if (key.length !== 32) throw new RangeError(ERRORS.LENGTH_KEY);
123+
/**
124+
* Key used for encryption and decryption methods, must be of type string
125+
* with 32 characters.
126+
*
127+
* @name key
128+
* @memberof CryptoDriver#
129+
* @type {string}
130+
* @readonly
131+
*/
132+
Object.defineProperty(this, 'key', {
133+
value: key,
134+
writable: false,
135+
enumerable: false,
136+
configurable: false,
137+
});
138+
}
139+
140+
/**
141+
* Methods to encrypt data, encrypts a string using the AES-256-GCM
142+
* encryption algorithm with the key specified in the constructor.
143+
*
144+
* @param {string} data - Data to encrypt.
145+
* @returns {string} The encrypted data.
146+
* @memberof CryptoDriver
147+
* @throws {ReferenceError} If `data` value is `undefined`.
148+
* @throws {TypeError} If `data` type is other than string.
149+
* @example
150+
* ```js
151+
* const crypto = new CryptoDriver('d6F3Efeqd6F3Efeqd6F3Efeqd6F3Efeq');
152+
* const encrypted = crypto.encrypt('Hello world');
153+
* // Output like a e5be8c02e8d478b0ab9...8c28e6828010a5789a446e781f36d0
154+
*```
155+
*/
156+
encrypt(data) {
157+
if (data === undefined) throw new ReferenceError(ERRORS.UNDEFINED_DATA);
158+
if (typeof data !== 'string') throw new TypeError(ERRORS.TYPE_DATA);
159+
const iv = crypto.randomBytes(IV_BYTE_LENGTH);
160+
const cipher = crypto.createCipheriv(ALGORITHM, this.key, iv, IV_OPTIONS);
161+
const raw = Buffer.from(data, 'utf-8');
162+
const encrypted = Buffer.concat([iv, cipher.update(raw), cipher.final(), cipher.getAuthTag()]);
163+
return encrypted.toString('hex');
164+
}
165+
166+
/**
167+
* Methods to decrypt data.
168+
*
169+
* @param {string} encrypted - Encrypted data.
170+
* @returns {string} Decrypted data.
171+
* @memberof CryptoDriver
172+
* @throws {ReferenceError} If `encrypted` value is `undefined`.
173+
* @throws {TypeError} If `encrypted` type is other than string.
174+
* @example
175+
* ```js
176+
* // Output like a e5be8c02e8d478b0ab9...8c28e6828010a5789a446e781f36d0
177+
* const encrypted = cryptoDriver.encrypt('Hello world');
178+
* const decrypted = cryptoDriver.decrypt(encrypted); // Hello world
179+
*```
180+
*/
181+
decrypt(encrypted) {
182+
if (encrypted === undefined) throw new ReferenceError(ERRORS.UNDEFINED_ENCRYPTED);
183+
if (typeof encrypted !== 'string') throw new TypeError(ERRORS.TYPE_ENCRYPTED);
184+
const cipher = Buffer.from(encrypted, 'hex');
185+
const iv = cipher.subarray(0, IV_BYTE_LENGTH);
186+
const data = cipher.subarray(IV_BYTE_LENGTH, cipher.length - TAG_BYTE_LENGTH);
187+
const authTag = cipher.subarray(data.length + IV_BYTE_LENGTH);
188+
const decipher = crypto.createDecipheriv(ALGORITHM, this.key, iv, IV_OPTIONS);
189+
decipher.setAuthTag(authTag);
190+
const decrypted = Buffer.concat([decipher.update(data), decipher.final()]);
191+
return decrypted.toString('utf-8');
192+
}
193+
}
194+
195+
// ━━ EXPORT MODULES ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
196+
module.exports = CryptoDriver;

0 commit comments

Comments
 (0)