spd-lib 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.
Files changed (3) hide show
  1. package/index.js +126 -0
  2. package/package.json +30 -0
  3. package/readme.md +46 -0
package/index.js ADDED
@@ -0,0 +1,126 @@
1
+ const fs = require('fs');
2
+ const zlib = require('zlib');
3
+ const sodium = require('libsodium-wrappers');
4
+ const crypto = require('crypto');
5
+
6
+ class SPD {
7
+ constructor(userKey) {
8
+ this.data = [];
9
+ this.keyPair = sodium.crypto_box_keypair(); // Generate a key pair for encryption/decryption
10
+ this.userKey = userKey; // Symmetric key provided by the user
11
+ }
12
+
13
+ async addData(name, data) {
14
+ if (!name || typeof name !== 'string' || !name.trim() || !data || typeof data !== 'string' || !data.trim()) {
15
+ throw new Error('Invalid name or data. Both must be non-empty strings.');
16
+ }
17
+
18
+ await sodium.ready;
19
+ const dat = Buffer.from(data);
20
+ const compressedData = zlib.deflateSync(dat);
21
+ const nonce = sodium.randombytes_buf(sodium.crypto_box_NONCEBYTES);
22
+ const encryptedData = sodium.crypto_secretbox_easy(compressedData, nonce, this.userKey);
23
+ const hash = crypto.createHash('sha256').update(encryptedData).digest('hex');
24
+ this.data.push({ dataName: name, nonce: Array.from(nonce), data: Array.from(encryptedData), hash });
25
+ }
26
+
27
+ saveToFile(outputPath, salt) {
28
+ if (!outputPath || typeof outputPath !== 'string' || !outputPath.trim() || !salt || !(salt instanceof Uint8Array) || salt.length !== 16) {
29
+ throw new Error('Invalid output path or salt.');
30
+ }
31
+
32
+ const keyPair = {
33
+ publicKey: Array.from(this.keyPair.publicKey),
34
+ privateKey: Array.from(this.keyPair.privateKey)
35
+ };
36
+ const spdData = JSON.stringify({ keyPair, data: this.data, salt: Array.from(salt) });
37
+ const compressedSpdData = zlib.deflateSync(spdData);
38
+ fs.writeFileSync(outputPath, compressedSpdData, { mode: 0o600 });
39
+ }
40
+
41
+
42
+ static async loadFromFile(spdPath, passcode) {
43
+ if (!spdPath || typeof spdPath !== 'string' || !spdPath.trim() || !passcode || typeof passcode !== 'string' || !passcode.trim()) {
44
+ throw new Error('Invalid SPD path or passcode.');
45
+ }
46
+
47
+ await sodium.ready;
48
+ const compressedSpdData = fs.readFileSync(spdPath);
49
+ const spdData = zlib.inflateSync(compressedSpdData).toString('utf8');
50
+ const { keyPair, data, salt } = JSON.parse(spdData);
51
+
52
+ const { pqcKey } = await new SPD().convertPasscodeToPQCKeySalted(passcode, new Uint8Array(salt));
53
+ const pbk = pqcKey.publicKey;
54
+ const spd = new SPD(pbk);
55
+ spd.keyPair = {
56
+ publicKey: Buffer.from(keyPair.publicKey),
57
+ privateKey: Buffer.from(keyPair.privateKey)
58
+ };
59
+ spd.data = data.map(dat => ({
60
+ dataName: dat.dataName,
61
+ nonce: Buffer.from(dat.nonce),
62
+ data: Buffer.from(dat.data),
63
+ hash: dat.hash
64
+ }));
65
+ spd.data.forEach(dat => {
66
+ const calculatedHash = crypto.createHash('sha256').update(Buffer.from(dat.data)).digest('hex');
67
+ if (calculatedHash !== dat.hash) {
68
+ throw new Error(`Data integrity check failed for ${dat.dataName}`);
69
+ }
70
+ });
71
+ return spd;
72
+ }
73
+
74
+ async extractFiles() {
75
+ await sodium.ready;
76
+ let extractedFiles = {};
77
+ this.data.forEach(dat => {
78
+ const decryptedData = sodium.crypto_secretbox_open_easy(dat.data, dat.nonce, this.userKey);
79
+ const decompressedData = zlib.inflateSync(decryptedData);
80
+ extractedFiles[dat.dataName] = decompressedData.toString('utf8');
81
+ });
82
+ return extractedFiles;
83
+ }
84
+
85
+
86
+
87
+ static async derivePBK(passcode, salt) {
88
+ if (!passcode || typeof passcode !== 'string' || !passcode.trim() || !salt || !(salt instanceof Uint8Array) || salt.length !== 16) {
89
+ throw new Error('Invalid passcode or salt.');
90
+ }
91
+
92
+ return new Promise((resolve, reject) => {
93
+ crypto.pbkdf2(passcode, salt, 100000, 32, 'sha256', (err, derivedKey) => {
94
+ if (err) {
95
+ reject(err);
96
+ } else {
97
+ resolve({ pbk: derivedKey, salt: salt });
98
+ }
99
+ });
100
+ });
101
+ }
102
+
103
+ async convertPasscodeToPQCKeySalted(passcode, salt) {
104
+ if (!passcode || typeof passcode !== 'string' || !passcode.trim() || passcode.length < 8 || !salt || !(salt instanceof Uint8Array) || salt.length !== 16) {
105
+ throw new Error('Invalid passcode or salt.');
106
+ }
107
+
108
+ const { pbk } = await SPD.derivePBK(passcode, salt);
109
+ await sodium.ready;
110
+ const keyPair = sodium.crypto_kx_seed_keypair(pbk.slice(0, sodium.crypto_kx_SEEDBYTES));
111
+ return { pqcKey: { publicKey: keyPair.publicKey, privateKey: keyPair.privateKey }, salt };
112
+ }
113
+
114
+ async convertPasscodeToPQCKey(passcode) {
115
+ if (!passcode || typeof passcode !== 'string' || !passcode.trim() || passcode.length < 8) {
116
+ throw new Error('Invalid passcode.');
117
+ }
118
+
119
+ const { pbk, salt } = await SPD.derivePBK(passcode, crypto.getRandomValues(new Uint8Array(16)));
120
+ await sodium.ready;
121
+ const keyPair = sodium.crypto_kx_seed_keypair(pbk.slice(0, sodium.crypto_kx_SEEDBYTES));
122
+ return { pqcKey: { publicKey: keyPair.publicKey, privateKey: keyPair.privateKey }, salt };
123
+ }
124
+ }
125
+
126
+ export default SPD;
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "spd-lib",
3
+ "version": "1.0.0",
4
+ "description": "SPD or Secure Packaged Data is a compress PQC protected file format to sotre sensitive data localy",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "keywords": [
10
+ "security",
11
+ "encryption",
12
+ "data-storage",
13
+ "persistent",
14
+ "crypto",
15
+ "compression",
16
+ "file",
17
+ "nodejs",
18
+ "libsodium",
19
+ "zlib"
20
+ ],
21
+ "type": "module",
22
+ "author": "ALS-OPSS",
23
+ "license": "ISC",
24
+ "dependencies": {
25
+ "crypto": "^1.0.1",
26
+ "fs": "^0.0.1-security",
27
+ "libsodium-wrappers": "^0.7.13"
28
+ },
29
+ "devDependencies": {}
30
+ }
package/readme.md ADDED
@@ -0,0 +1,46 @@
1
+ ```markdown
2
+ # Secure Packaged Data (SPD)
3
+
4
+ Secure Packaged Data (SPD) is a Node.js package for securely storing and retrieving sensitive data persistently. It utilizes encryption, hashing, and compression techniques to ensure the confidentiality, integrity, and efficiency of data storage.
5
+
6
+ ## Installation
7
+
8
+ You can install SPD via npm:
9
+
10
+ ```bash
11
+ npm install spd-packager
12
+ ```
13
+
14
+ ## Usage
15
+
16
+ ```javascript
17
+ const SPD = require('spd-packager');
18
+
19
+ // Example usage
20
+ (async () => {
21
+ await sodium.ready;
22
+
23
+ const passcode = 'your-secure-passcode';
24
+ const { pqcKey, salt } = await new SPD().convertPasscodeToPQCKey(passcode);
25
+ const userKey = pqcKey.publicKey; // For this example, use the public key as the symmetric key
26
+ const spd = new SPD(userKey); // Create a new SPD object
27
+ await spd.addData('settings', '{"theme":"dark"}'); // Add a file to the SPD object
28
+ await spd.addData('tabs', '[{"title":"Home","url":"https://example.com"},{"title":"About","url":"https://example.com/about"}]'); // Add another file to the SPD object
29
+ spd.saveToFile('output.spd', salt); // Save SPD file to disk with the salt
30
+ const loadedSpd = await SPD.loadFromFile('output.spd', passcode); // Load SPD file with the passcode
31
+ const extractedFiles = await loadedSpd.extractFiles(); // Extract files to memory
32
+ console.log(extractedFiles); // Print extracted files to console
33
+ })();
34
+ ```
35
+
36
+ ## Features
37
+
38
+ - **Encryption**: Data is encrypted using a symmetric key derived from a user-provided passcode.
39
+ - **Compression**: Data is compressed before encryption to reduce storage size.
40
+ - **Data Integrity**: Hashing techniques ensure the integrity of stored data.
41
+ - **Asynchronous Operations**: Utilizes asynchronous programming for file I/O and cryptographic operations.
42
+
43
+ ## License
44
+
45
+ This project is licensed under the [MIT License](https://opensource.org/licenses/MIT).
46
+ ```