spd-lib 1.1.7 → 1.1.9
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/index.js +22 -85
- package/package.json +2 -2
package/index.js
CHANGED
|
@@ -11,27 +11,11 @@ class SPD {
|
|
|
11
11
|
this.keyPair; // Generate a key pair for encryption/decryption
|
|
12
12
|
this.userKey;
|
|
13
13
|
this.salt;
|
|
14
|
-
this.signingKeyPair;
|
|
15
14
|
this.init();
|
|
16
15
|
}
|
|
17
16
|
async init() {
|
|
18
17
|
await sodium.ready;
|
|
19
18
|
this.keyPair = sodium.crypto_box_keypair()
|
|
20
|
-
this.signingKeyPair = sodium.crypto_sign_keypair();
|
|
21
|
-
}
|
|
22
|
-
static async deriveSigningKeys(passcode, salt) {
|
|
23
|
-
if (!passcode || typeof passcode !== 'string' || passcode.length < 8 ||
|
|
24
|
-
!salt || !(salt instanceof Buffer) || salt.length !== 16) {
|
|
25
|
-
throw new Error('Invalid passcode or salt.');
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const { pbk } = await SPD.derivePBK(passcode, salt);
|
|
29
|
-
await sodium.ready;
|
|
30
|
-
// Derive separate keys for signing to avoid key reuse
|
|
31
|
-
const signingSeed = pbk.slice(0, sodium.crypto_sign_SEEDBYTES);
|
|
32
|
-
const signingKeyPair = sodium.crypto_sign_seed_keypair(signingSeed);
|
|
33
|
-
|
|
34
|
-
return { signingKeyPair };
|
|
35
19
|
}
|
|
36
20
|
async setPassKey(passcode){
|
|
37
21
|
await sodium.ready;
|
|
@@ -40,8 +24,6 @@ class SPD {
|
|
|
40
24
|
const userKey = pqcKey.publicKey;
|
|
41
25
|
this.userKey = userKey;
|
|
42
26
|
this.salt = salt;
|
|
43
|
-
const derivedKeys = await SPD.deriveSigningKeys(passcode, salt);
|
|
44
|
-
this.signingKeyPair = derivedKeys.signingKeyPair;
|
|
45
27
|
}
|
|
46
28
|
|
|
47
29
|
async addData(name, data) {
|
|
@@ -50,12 +32,13 @@ const derivedKeys = await SPD.deriveSigningKeys(passcode, salt);
|
|
|
50
32
|
const dmap = await this.CITS(data)
|
|
51
33
|
await sodium.ready;
|
|
52
34
|
const dat = Buffer.from(dmap[0]);
|
|
53
|
-
const compressedData = zlib.deflateSync(dat
|
|
35
|
+
const compressedData = zlib.deflateSync(dat,{
|
|
36
|
+
level:9
|
|
37
|
+
});
|
|
54
38
|
const nonce = sodium.randombytes_buf(sodium.crypto_box_NONCEBYTES);
|
|
55
39
|
const encryptedData = sodium.crypto_secretbox_easy(compressedData, nonce, this.userKey);
|
|
56
40
|
const hash = crypto.createHash('sha256').update(encryptedData).digest('hex');
|
|
57
|
-
|
|
58
|
-
this.data.push({ dataName: name, nonce: Array.from(nonce), data: Array.from(encryptedData),signature:Array.from(signature), hash, dataType: dmap[1] });
|
|
41
|
+
this.data.push({ dataName: name, nonce: Array.from(nonce), data: Array.from(encryptedData), hash, dataType: dmap[1] });
|
|
59
42
|
}
|
|
60
43
|
|
|
61
44
|
saveToFile(outputPath) {
|
|
@@ -63,8 +46,10 @@ const derivedKeys = await SPD.deriveSigningKeys(passcode, salt);
|
|
|
63
46
|
throw new Error('Invalid output path or salt.');
|
|
64
47
|
}
|
|
65
48
|
|
|
66
|
-
const spdData = JSON.stringify({ data: this.data, salt: Array.from(this.salt)
|
|
67
|
-
const compressedSpdData = zlib.deflateSync(spdData
|
|
49
|
+
const spdData = JSON.stringify({ data: this.data, salt: Array.from(this.salt) });
|
|
50
|
+
const compressedSpdData = zlib.deflateSync(spdData,{
|
|
51
|
+
level:9
|
|
52
|
+
});
|
|
68
53
|
fs.writeFileSync(outputPath, compressedSpdData, { mode: 0o600 });
|
|
69
54
|
}
|
|
70
55
|
|
|
@@ -78,8 +63,8 @@ const derivedKeys = await SPD.deriveSigningKeys(passcode, salt);
|
|
|
78
63
|
|
|
79
64
|
await sodium.ready;
|
|
80
65
|
const compressedSpdData = fs.readFileSync(spdPath);
|
|
81
|
-
const spdData = zlib.inflateSync(compressedSpdData).toString('utf8');
|
|
82
|
-
const { data, salt
|
|
66
|
+
const spdData = zlib.inflateSync(compressedSpdData,{level:9}).toString('utf8');
|
|
67
|
+
const { data, salt } = JSON.parse(spdData);
|
|
83
68
|
|
|
84
69
|
const { pqcKey } = await new SPD().convertPasscodeToPQCKeySalted(passcode, new Uint8Array(salt));
|
|
85
70
|
const pbk = pqcKey.publicKey;
|
|
@@ -88,34 +73,19 @@ const derivedKeys = await SPD.deriveSigningKeys(passcode, salt);
|
|
|
88
73
|
spd.keyPair = {
|
|
89
74
|
publicKey: pbk.publicKey
|
|
90
75
|
};
|
|
91
|
-
|
|
92
|
-
spd.signingKeyPair = { publicKey: Uint8Array.from(signingPublicKey) };
|
|
93
76
|
spd.data = data.map(dat => ({
|
|
94
77
|
dataName: dat.dataName,
|
|
95
78
|
nonce: Buffer.from(dat.nonce),
|
|
96
79
|
data: Buffer.from(dat.data),
|
|
97
|
-
signature: Buffer.from(dat.signature),
|
|
98
80
|
hash: dat.hash,
|
|
99
81
|
dataType: dat.dataType
|
|
100
82
|
}));
|
|
101
83
|
spd.data.forEach(dat => {
|
|
102
|
-
const
|
|
103
|
-
const signatureBuffer = Buffer.from(dat.signature);
|
|
104
|
-
const signingPublicKeyBuffer = Uint8Array.from(signingPublicKey);
|
|
105
|
-
|
|
106
|
-
const isValid = sodium.crypto_sign_verify_detached(signatureBuffer, encryptedBuffer, signingPublicKeyBuffer);
|
|
107
|
-
if (!isValid) {
|
|
108
|
-
rej(new Error(`Signature verification failed for ${dat.dataName}`));
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const calculatedHash = crypto.createHash('sha256').update(encryptedBuffer).digest('hex');
|
|
84
|
+
const calculatedHash = crypto.createHash('sha256').update(Buffer.from(dat.data)).digest('hex');
|
|
113
85
|
if (calculatedHash !== dat.hash) {
|
|
114
86
|
rej(new Error(`Data integrity check failed for ${dat.dataName}`));
|
|
115
|
-
return;
|
|
116
87
|
}
|
|
117
88
|
});
|
|
118
|
-
|
|
119
89
|
res(spd);
|
|
120
90
|
}catch{
|
|
121
91
|
rej()
|
|
@@ -130,30 +100,10 @@ const derivedKeys = await SPD.deriveSigningKeys(passcode, salt);
|
|
|
130
100
|
let extractedFiles = {};
|
|
131
101
|
this.data.forEach(async dat => {
|
|
132
102
|
try{
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
// Verify signature
|
|
139
|
-
const isValid = sodium.crypto_sign_verify_detached(
|
|
140
|
-
Buffer.from(dat.signature),
|
|
141
|
-
Buffer.from(dat.data),
|
|
142
|
-
this.signingKeyPair.publicKey
|
|
143
|
-
);
|
|
144
|
-
if (!isValid) {
|
|
145
|
-
throw new Error(`Signature verification failed for ${dat.dataName}`);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Decrypt data
|
|
149
|
-
const decryptedData = sodium.crypto_secretbox_open_easy(
|
|
150
|
-
Buffer.from(dat.data),
|
|
151
|
-
Buffer.from(dat.nonce),
|
|
152
|
-
this.userKey
|
|
153
|
-
);
|
|
154
|
-
const decompressedData = zlib.inflateSync(decryptedData);
|
|
155
|
-
const dt = decompressedData.toString('utf8');
|
|
156
|
-
extractedFiles[dat.dataName] = await this.CSTI(dt, dat.dataType);
|
|
103
|
+
const decryptedData = sodium.crypto_secretbox_open_easy(dat.data, dat.nonce, this.userKey);
|
|
104
|
+
const decompressedData = zlib.inflateSync(decryptedData,{level:9});
|
|
105
|
+
const dt = decompressedData.toString('utf8');
|
|
106
|
+
extractedFiles[dat.dataName] = await this.CSTI(dt, dat.dataType);
|
|
157
107
|
}catch{
|
|
158
108
|
rej()
|
|
159
109
|
}
|
|
@@ -185,7 +135,7 @@ const derivedKeys = await SPD.deriveSigningKeys(passcode, salt);
|
|
|
185
135
|
|
|
186
136
|
saveData() {
|
|
187
137
|
const spdData = JSON.stringify({ data: this.data, salt: Array.from(this.salt) });
|
|
188
|
-
const compressedSpdData = zlib.deflateSync(spdData);
|
|
138
|
+
const compressedSpdData = zlib.deflateSync(spdData,{level:9});
|
|
189
139
|
return compressedSpdData;
|
|
190
140
|
}
|
|
191
141
|
|
|
@@ -198,17 +148,15 @@ const derivedKeys = await SPD.deriveSigningKeys(passcode, salt);
|
|
|
198
148
|
|
|
199
149
|
await sodium.ready;
|
|
200
150
|
const spdDataBuffer = Buffer.from(spdData, 'base64');
|
|
201
|
-
const spdData2 = zlib.inflateSync(spdDataBuffer).toString('utf8');
|
|
202
|
-
const { data, salt
|
|
151
|
+
const spdData2 = zlib.inflateSync(spdDataBuffer,{level:9}).toString('utf8');
|
|
152
|
+
const { data, salt } = JSON.parse(spdData2);
|
|
203
153
|
const { pqcKey } = await new SPD().convertPasscodeToPQCKeySalted(passcode, new Uint8Array(salt));
|
|
204
|
-
|
|
205
154
|
const pbk = pqcKey.publicKey;
|
|
206
155
|
const spd = new SPD();
|
|
207
156
|
spd.userKey = pbk;
|
|
208
157
|
spd.keyPair = {
|
|
209
158
|
publicKey: pbk.publicKey
|
|
210
159
|
};
|
|
211
|
-
spd.signingKeyPair = { publicKey: Uint8Array.from(signingPublicKey) };
|
|
212
160
|
spd.data = data.map(dat => ({
|
|
213
161
|
dataName: dat.dataName,
|
|
214
162
|
nonce: Buffer.from(dat.nonce),
|
|
@@ -217,21 +165,10 @@ const derivedKeys = await SPD.deriveSigningKeys(passcode, salt);
|
|
|
217
165
|
dataType: dat.dataType
|
|
218
166
|
}));
|
|
219
167
|
spd.data.forEach(dat => {
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
const isValid = sodium.crypto_sign_verify_detached(signatureBuffer, encryptedBuffer, signingPublicKeyBuffer);
|
|
225
|
-
if (!isValid) {
|
|
226
|
-
rej(new Error(`Signature verification failed for ${dat.dataName}`));
|
|
227
|
-
return;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
const calculatedHash = crypto.createHash('sha256').update(encryptedBuffer).digest('hex');
|
|
231
|
-
if (calculatedHash !== dat.hash) {
|
|
232
|
-
rej(new Error(`Data integrity check failed for ${dat.dataName}`));
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
168
|
+
const calculatedHash = crypto.createHash('sha256').update(Buffer.from(dat.data)).digest('hex');
|
|
169
|
+
if (calculatedHash !== dat.hash) {
|
|
170
|
+
rej(new Error(`Data integrity check failed for ${dat.dataName}`));
|
|
171
|
+
}
|
|
235
172
|
});
|
|
236
173
|
res(spd);
|
|
237
174
|
}catch{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spd-lib",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.9",
|
|
4
4
|
"description": "SPD or Secure Packaged Data is a compress PQC protected file format to store sensitive data localy",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -21,6 +21,6 @@
|
|
|
21
21
|
"author": "ALS-OPSS",
|
|
22
22
|
"license": "ISC",
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"libsodium-wrappers": "^0.7.
|
|
24
|
+
"libsodium-wrappers": "^0.7.15"
|
|
25
25
|
}
|
|
26
26
|
}
|