spd-lib 1.1.6 → 1.1.7
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 +80 -13
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -11,11 +11,27 @@ class SPD {
|
|
|
11
11
|
this.keyPair; // Generate a key pair for encryption/decryption
|
|
12
12
|
this.userKey;
|
|
13
13
|
this.salt;
|
|
14
|
+
this.signingKeyPair;
|
|
14
15
|
this.init();
|
|
15
16
|
}
|
|
16
17
|
async init() {
|
|
17
18
|
await sodium.ready;
|
|
18
19
|
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 };
|
|
19
35
|
}
|
|
20
36
|
async setPassKey(passcode){
|
|
21
37
|
await sodium.ready;
|
|
@@ -24,6 +40,8 @@ class SPD {
|
|
|
24
40
|
const userKey = pqcKey.publicKey;
|
|
25
41
|
this.userKey = userKey;
|
|
26
42
|
this.salt = salt;
|
|
43
|
+
const derivedKeys = await SPD.deriveSigningKeys(passcode, salt);
|
|
44
|
+
this.signingKeyPair = derivedKeys.signingKeyPair;
|
|
27
45
|
}
|
|
28
46
|
|
|
29
47
|
async addData(name, data) {
|
|
@@ -36,7 +54,8 @@ this.salt = salt;
|
|
|
36
54
|
const nonce = sodium.randombytes_buf(sodium.crypto_box_NONCEBYTES);
|
|
37
55
|
const encryptedData = sodium.crypto_secretbox_easy(compressedData, nonce, this.userKey);
|
|
38
56
|
const hash = crypto.createHash('sha256').update(encryptedData).digest('hex');
|
|
39
|
-
|
|
57
|
+
const signature = sodium.crypto_sign_detached(encryptedData, this.signingKeyPair.privateKey);
|
|
58
|
+
this.data.push({ dataName: name, nonce: Array.from(nonce), data: Array.from(encryptedData),signature:Array.from(signature), hash, dataType: dmap[1] });
|
|
40
59
|
}
|
|
41
60
|
|
|
42
61
|
saveToFile(outputPath) {
|
|
@@ -44,7 +63,7 @@ this.salt = salt;
|
|
|
44
63
|
throw new Error('Invalid output path or salt.');
|
|
45
64
|
}
|
|
46
65
|
|
|
47
|
-
const spdData = JSON.stringify({ data: this.data, salt: Array.from(this.salt) });
|
|
66
|
+
const spdData = JSON.stringify({ data: this.data, salt: Array.from(this.salt),signingPublicKey:Array.from(this.signingKeyPair.publicKey) });
|
|
48
67
|
const compressedSpdData = zlib.deflateSync(spdData);
|
|
49
68
|
fs.writeFileSync(outputPath, compressedSpdData, { mode: 0o600 });
|
|
50
69
|
}
|
|
@@ -60,7 +79,7 @@ this.salt = salt;
|
|
|
60
79
|
await sodium.ready;
|
|
61
80
|
const compressedSpdData = fs.readFileSync(spdPath);
|
|
62
81
|
const spdData = zlib.inflateSync(compressedSpdData).toString('utf8');
|
|
63
|
-
const { data, salt } = JSON.parse(spdData);
|
|
82
|
+
const { data, salt, signingPublicKey } = JSON.parse(spdData);
|
|
64
83
|
|
|
65
84
|
const { pqcKey } = await new SPD().convertPasscodeToPQCKeySalted(passcode, new Uint8Array(salt));
|
|
66
85
|
const pbk = pqcKey.publicKey;
|
|
@@ -69,19 +88,34 @@ this.salt = salt;
|
|
|
69
88
|
spd.keyPair = {
|
|
70
89
|
publicKey: pbk.publicKey
|
|
71
90
|
};
|
|
91
|
+
|
|
92
|
+
spd.signingKeyPair = { publicKey: Uint8Array.from(signingPublicKey) };
|
|
72
93
|
spd.data = data.map(dat => ({
|
|
73
94
|
dataName: dat.dataName,
|
|
74
95
|
nonce: Buffer.from(dat.nonce),
|
|
75
96
|
data: Buffer.from(dat.data),
|
|
97
|
+
signature: Buffer.from(dat.signature),
|
|
76
98
|
hash: dat.hash,
|
|
77
99
|
dataType: dat.dataType
|
|
78
100
|
}));
|
|
79
101
|
spd.data.forEach(dat => {
|
|
80
|
-
const
|
|
102
|
+
const encryptedBuffer = Buffer.from(dat.data);
|
|
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');
|
|
81
113
|
if (calculatedHash !== dat.hash) {
|
|
82
114
|
rej(new Error(`Data integrity check failed for ${dat.dataName}`));
|
|
115
|
+
return;
|
|
83
116
|
}
|
|
84
117
|
});
|
|
118
|
+
|
|
85
119
|
res(spd);
|
|
86
120
|
}catch{
|
|
87
121
|
rej()
|
|
@@ -96,10 +130,30 @@ this.salt = salt;
|
|
|
96
130
|
let extractedFiles = {};
|
|
97
131
|
this.data.forEach(async dat => {
|
|
98
132
|
try{
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
133
|
+
const calculatedHash = crypto.createHash('sha256').update(dat.data).digest('hex');
|
|
134
|
+
if (calculatedHash !== dat.hash) {
|
|
135
|
+
throw new Error(`Hash mismatch for ${dat.dataName}`);
|
|
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
157
|
}catch{
|
|
104
158
|
rej()
|
|
105
159
|
}
|
|
@@ -145,14 +199,16 @@ this.salt = salt;
|
|
|
145
199
|
await sodium.ready;
|
|
146
200
|
const spdDataBuffer = Buffer.from(spdData, 'base64');
|
|
147
201
|
const spdData2 = zlib.inflateSync(spdDataBuffer).toString('utf8');
|
|
148
|
-
const { data, salt } = JSON.parse(spdData2);
|
|
202
|
+
const { data, salt, signingPublicKey } = JSON.parse(spdData2);
|
|
149
203
|
const { pqcKey } = await new SPD().convertPasscodeToPQCKeySalted(passcode, new Uint8Array(salt));
|
|
204
|
+
|
|
150
205
|
const pbk = pqcKey.publicKey;
|
|
151
206
|
const spd = new SPD();
|
|
152
207
|
spd.userKey = pbk;
|
|
153
208
|
spd.keyPair = {
|
|
154
209
|
publicKey: pbk.publicKey
|
|
155
210
|
};
|
|
211
|
+
spd.signingKeyPair = { publicKey: Uint8Array.from(signingPublicKey) };
|
|
156
212
|
spd.data = data.map(dat => ({
|
|
157
213
|
dataName: dat.dataName,
|
|
158
214
|
nonce: Buffer.from(dat.nonce),
|
|
@@ -161,10 +217,21 @@ this.salt = salt;
|
|
|
161
217
|
dataType: dat.dataType
|
|
162
218
|
}));
|
|
163
219
|
spd.data.forEach(dat => {
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
220
|
+
const encryptedBuffer = Buffer.from(dat.data);
|
|
221
|
+
const signatureBuffer = Buffer.from(dat.signature);
|
|
222
|
+
const signingPublicKeyBuffer = Uint8Array.from(signingPublicKey);
|
|
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
235
|
});
|
|
169
236
|
res(spd);
|
|
170
237
|
}catch{
|