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