wolfronix-sdk 1.3.0 → 1.3.2

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/README.md CHANGED
@@ -127,8 +127,49 @@ async function main() {
127
127
  }
128
128
 
129
129
  main();
130
+ // Decrypt and save
131
+ const decrypted = await wfx.decryptToBuffer(file_id);
132
+ fs.writeFileSync('decrypted.pdf', Buffer.from(decrypted));
133
+ }
134
+
135
+ main();
136
+ ```
137
+
138
+ ### 💬 E2E Encrypted Chat Integration
139
+
140
+ Turn any chat app into a secure, end-to-end encrypted messenger in minutes.
141
+
142
+ **Sender (Alice):**
143
+ ```typescript
144
+ // 1. Get Bob's Public Key & Encrypt Message
145
+ const securePacket = await wfx.encryptMessage("Secret details at 5 PM", "bob_user_id");
146
+
147
+ // 2. Send 'securePacket' string via your normal chat API (Socket.io, Firebase, etc.)
148
+ chatSocket.emit('message', {
149
+ to: 'bob',
150
+ text: securePacket // Valid JSON string
151
+ });
130
152
  ```
131
153
 
154
+ **Recipient (Bob):**
155
+ ```typescript
156
+ // 1. Receive message from chat server
157
+ chatSocket.on('message', async (msg) => {
158
+ try {
159
+ // 2. Decrypt locally with Bob's Private Key
160
+ const plainText = await wfx.decryptMessage(msg.text);
161
+ console.log("Decrypted:", plainText);
162
+ } catch (err) {
163
+ console.error("Could not decrypt message");
164
+ }
165
+ });
166
+ ```
167
+
168
+ **Features:**
169
+ - **Hybrid Encryption:** Uses AES-256 for messages + RSA-2048 for key exchange (Fast & Secure).
170
+ - **Zero-Knowledge:** Your chat server only sees encrypted packets.
171
+ - **Universal:** Works with any backend (Socket.io, Firebase, PostgreSQL, etc).
172
+
132
173
  ## API Reference
133
174
 
134
175
  ### Constructor
@@ -166,6 +207,14 @@ new Wolfronix(config: WolfronixConfig | string)
166
207
  | `listFiles()` | List user's encrypted files |
167
208
  | `deleteFile(fileId)` | Delete encrypted file |
168
209
 
210
+ ### E2E Chat Encryption
211
+
212
+ | Method | Description |
213
+ |--------|-------------|
214
+ | `getPublicKey(userId)` | Fetch a user's RSA public key |
215
+ | `encryptMessage(text, recipientId)` | Encrypt text for a recipient (returns packet string) |
216
+ | `decryptMessage(packetString)` | Decrypt a received message packet |
217
+
169
218
  ### Utility
170
219
 
171
220
  | Method | Description |
package/dist/index.d.mts CHANGED
@@ -51,6 +51,11 @@ interface MetricsResponse {
51
51
  total_bytes_encrypted: number;
52
52
  total_bytes_decrypted: number;
53
53
  }
54
+ interface EncryptMessagePacket {
55
+ key: string;
56
+ iv: string;
57
+ msg: string;
58
+ }
54
59
  declare class WolfronixError extends Error {
55
60
  readonly code: string;
56
61
  readonly statusCode?: number;
@@ -188,6 +193,25 @@ declare class Wolfronix {
188
193
  * ```
189
194
  */
190
195
  deleteFile(fileId: string): Promise<DeleteResponse>;
196
+ /**
197
+ * Get another user's public key (for E2E encryption)
198
+ * @param userId The ID of the recipient
199
+ */
200
+ getPublicKey(userId: string): Promise<string>;
201
+ /**
202
+ * Encrypt a short text message for a recipient (Hybrid Encryption: RSA + AES)
203
+ * Returns a secure JSON string (packet) to send via chat
204
+ *
205
+ * @param text The plain text message
206
+ * @param recipientId The recipient's user ID
207
+ */
208
+ encryptMessage(text: string, recipientId: string): Promise<string>;
209
+ /**
210
+ * Decrypt a message packet received from chat
211
+ *
212
+ * @param packetJson The secure JSON string packet
213
+ */
214
+ decryptMessage(packetJson: string): Promise<string>;
191
215
  /**
192
216
  * Get encryption/decryption metrics
193
217
  *
@@ -218,4 +242,4 @@ declare class Wolfronix {
218
242
  */
219
243
  declare function createClient(config: WolfronixConfig | string): Wolfronix;
220
244
 
221
- export { type AuthResponse, AuthenticationError, type DeleteResponse, type EncryptResponse, type FileInfo, FileNotFoundError, type ListFilesResponse, type MetricsResponse, NetworkError, PermissionDeniedError, ValidationError, Wolfronix, type WolfronixConfig, WolfronixError, createClient, Wolfronix as default };
245
+ export { type AuthResponse, AuthenticationError, type DeleteResponse, type EncryptMessagePacket, type EncryptResponse, type FileInfo, FileNotFoundError, type ListFilesResponse, type MetricsResponse, NetworkError, PermissionDeniedError, ValidationError, Wolfronix, type WolfronixConfig, WolfronixError, createClient, Wolfronix as default };
package/dist/index.d.ts CHANGED
@@ -51,6 +51,11 @@ interface MetricsResponse {
51
51
  total_bytes_encrypted: number;
52
52
  total_bytes_decrypted: number;
53
53
  }
54
+ interface EncryptMessagePacket {
55
+ key: string;
56
+ iv: string;
57
+ msg: string;
58
+ }
54
59
  declare class WolfronixError extends Error {
55
60
  readonly code: string;
56
61
  readonly statusCode?: number;
@@ -188,6 +193,25 @@ declare class Wolfronix {
188
193
  * ```
189
194
  */
190
195
  deleteFile(fileId: string): Promise<DeleteResponse>;
196
+ /**
197
+ * Get another user's public key (for E2E encryption)
198
+ * @param userId The ID of the recipient
199
+ */
200
+ getPublicKey(userId: string): Promise<string>;
201
+ /**
202
+ * Encrypt a short text message for a recipient (Hybrid Encryption: RSA + AES)
203
+ * Returns a secure JSON string (packet) to send via chat
204
+ *
205
+ * @param text The plain text message
206
+ * @param recipientId The recipient's user ID
207
+ */
208
+ encryptMessage(text: string, recipientId: string): Promise<string>;
209
+ /**
210
+ * Decrypt a message packet received from chat
211
+ *
212
+ * @param packetJson The secure JSON string packet
213
+ */
214
+ decryptMessage(packetJson: string): Promise<string>;
191
215
  /**
192
216
  * Get encryption/decryption metrics
193
217
  *
@@ -218,4 +242,4 @@ declare class Wolfronix {
218
242
  */
219
243
  declare function createClient(config: WolfronixConfig | string): Wolfronix;
220
244
 
221
- export { type AuthResponse, AuthenticationError, type DeleteResponse, type EncryptResponse, type FileInfo, FileNotFoundError, type ListFilesResponse, type MetricsResponse, NetworkError, PermissionDeniedError, ValidationError, Wolfronix, type WolfronixConfig, WolfronixError, createClient, Wolfronix as default };
245
+ export { type AuthResponse, AuthenticationError, type DeleteResponse, type EncryptMessagePacket, type EncryptResponse, type FileInfo, FileNotFoundError, type ListFilesResponse, type MetricsResponse, NetworkError, PermissionDeniedError, ValidationError, Wolfronix, type WolfronixConfig, WolfronixError, createClient, Wolfronix as default };
package/dist/index.js CHANGED
@@ -48,6 +48,7 @@ var RSA_ALG = {
48
48
  hash: "SHA-256"
49
49
  };
50
50
  var WRAP_ALG = "AES-GCM";
51
+ var SESSION_ALG = "AES-GCM";
51
52
  var PBKDF2_ITERATIONS = 1e5;
52
53
  async function generateKeyPair() {
53
54
  return await getCrypto().subtle.generateKey(
@@ -150,6 +151,80 @@ async function unwrapPrivateKey(encryptedKeyBase64, password, saltHex) {
150
151
  ["decrypt", "unwrapKey"]
151
152
  );
152
153
  }
154
+ async function generateSessionKey() {
155
+ return await getCrypto().subtle.generateKey(
156
+ {
157
+ name: SESSION_ALG,
158
+ length: 256
159
+ },
160
+ true,
161
+ ["encrypt", "decrypt"]
162
+ );
163
+ }
164
+ async function encryptData(data, key) {
165
+ const enc = new TextEncoder();
166
+ const encodedData = enc.encode(data);
167
+ const iv = getCrypto().getRandomValues(new Uint8Array(12));
168
+ const encryptedContent = await getCrypto().subtle.encrypt(
169
+ {
170
+ name: SESSION_ALG,
171
+ iv
172
+ },
173
+ key,
174
+ encodedData
175
+ );
176
+ return {
177
+ encrypted: arrayBufferToBase64(encryptedContent),
178
+ iv: arrayBufferToBase64(iv.buffer)
179
+ };
180
+ }
181
+ async function decryptData(encryptedBase64, ivBase64, key) {
182
+ const encryptedData = base64ToArrayBuffer(encryptedBase64);
183
+ const iv = base64ToArrayBuffer(ivBase64);
184
+ const decryptedContent = await getCrypto().subtle.decrypt(
185
+ {
186
+ name: SESSION_ALG,
187
+ iv
188
+ },
189
+ key,
190
+ encryptedData
191
+ );
192
+ const dec = new TextDecoder();
193
+ return dec.decode(decryptedContent);
194
+ }
195
+ async function rsaEncrypt(data, publicKey) {
196
+ const encrypted = await getCrypto().subtle.encrypt(
197
+ {
198
+ name: "RSA-OAEP"
199
+ },
200
+ publicKey,
201
+ data
202
+ );
203
+ return arrayBufferToBase64(encrypted);
204
+ }
205
+ async function rsaDecrypt(encryptedBase64, privateKey) {
206
+ const data = base64ToArrayBuffer(encryptedBase64);
207
+ const decrypted = await getCrypto().subtle.decrypt(
208
+ {
209
+ name: "RSA-OAEP"
210
+ },
211
+ privateKey,
212
+ data
213
+ );
214
+ return decrypted;
215
+ }
216
+ async function exportSessionKey(key) {
217
+ return await getCrypto().subtle.exportKey("raw", key);
218
+ }
219
+ async function importSessionKey(raw) {
220
+ return await getCrypto().subtle.importKey(
221
+ "raw",
222
+ raw,
223
+ SESSION_ALG,
224
+ true,
225
+ ["encrypt", "decrypt"]
226
+ );
227
+ }
153
228
  function arrayBufferToBase64(buffer) {
154
229
  const bytes = new Uint8Array(buffer);
155
230
  if (typeof Buffer !== "undefined") {
@@ -607,6 +682,68 @@ var Wolfronix = class {
607
682
  }
608
683
  return this.request("DELETE", `/api/v1/files/${fileId}`);
609
684
  }
685
+ // ============================================================================
686
+ // E2E Chat Encryption Methods
687
+ // ============================================================================
688
+ /**
689
+ * Get another user's public key (for E2E encryption)
690
+ * @param userId The ID of the recipient
691
+ */
692
+ async getPublicKey(userId) {
693
+ this.ensureAuthenticated();
694
+ const result = await this.request("GET", `/api/v1/keys/${userId}`);
695
+ return result.public_key;
696
+ }
697
+ /**
698
+ * Encrypt a short text message for a recipient (Hybrid Encryption: RSA + AES)
699
+ * Returns a secure JSON string (packet) to send via chat
700
+ *
701
+ * @param text The plain text message
702
+ * @param recipientId The recipient's user ID
703
+ */
704
+ async encryptMessage(text, recipientId) {
705
+ this.ensureAuthenticated();
706
+ const recipientPubKeyPEM = await this.getPublicKey(recipientId);
707
+ const recipientPubKey = await importKeyFromPEM(recipientPubKeyPEM, "public");
708
+ const sessionKey = await generateSessionKey();
709
+ const { encrypted: encryptedMsg, iv } = await encryptData(text, sessionKey);
710
+ const rawSessionKey = await exportSessionKey(sessionKey);
711
+ const encryptedSessionKey = await rsaEncrypt(rawSessionKey, recipientPubKey);
712
+ const packet = {
713
+ key: encryptedSessionKey,
714
+ iv,
715
+ msg: encryptedMsg
716
+ };
717
+ return JSON.stringify(packet);
718
+ }
719
+ /**
720
+ * Decrypt a message packet received from chat
721
+ *
722
+ * @param packetJson The secure JSON string packet
723
+ */
724
+ async decryptMessage(packetJson) {
725
+ this.ensureAuthenticated();
726
+ if (!this.privateKey) {
727
+ throw new Error("Private key not available. Is user logged in?");
728
+ }
729
+ let packet;
730
+ try {
731
+ packet = JSON.parse(packetJson);
732
+ } catch (e) {
733
+ throw new ValidationError("Invalid message packet format");
734
+ }
735
+ if (!packet.key || !packet.iv || !packet.msg) {
736
+ throw new ValidationError("Invalid message packet structure");
737
+ }
738
+ try {
739
+ const rawSessionKey = await rsaDecrypt(packet.key, this.privateKey);
740
+ const sessionKey = await importSessionKey(rawSessionKey);
741
+ const plainText = await decryptData(packet.msg, packet.iv, sessionKey);
742
+ return plainText;
743
+ } catch (error) {
744
+ throw new Error("Decryption failed. You may not be the intended recipient.");
745
+ }
746
+ }
610
747
  // ==========================================================================
611
748
  // Metrics & Status
612
749
  // ==========================================================================
package/dist/index.mjs CHANGED
@@ -14,6 +14,7 @@ var RSA_ALG = {
14
14
  hash: "SHA-256"
15
15
  };
16
16
  var WRAP_ALG = "AES-GCM";
17
+ var SESSION_ALG = "AES-GCM";
17
18
  var PBKDF2_ITERATIONS = 1e5;
18
19
  async function generateKeyPair() {
19
20
  return await getCrypto().subtle.generateKey(
@@ -116,6 +117,80 @@ async function unwrapPrivateKey(encryptedKeyBase64, password, saltHex) {
116
117
  ["decrypt", "unwrapKey"]
117
118
  );
118
119
  }
120
+ async function generateSessionKey() {
121
+ return await getCrypto().subtle.generateKey(
122
+ {
123
+ name: SESSION_ALG,
124
+ length: 256
125
+ },
126
+ true,
127
+ ["encrypt", "decrypt"]
128
+ );
129
+ }
130
+ async function encryptData(data, key) {
131
+ const enc = new TextEncoder();
132
+ const encodedData = enc.encode(data);
133
+ const iv = getCrypto().getRandomValues(new Uint8Array(12));
134
+ const encryptedContent = await getCrypto().subtle.encrypt(
135
+ {
136
+ name: SESSION_ALG,
137
+ iv
138
+ },
139
+ key,
140
+ encodedData
141
+ );
142
+ return {
143
+ encrypted: arrayBufferToBase64(encryptedContent),
144
+ iv: arrayBufferToBase64(iv.buffer)
145
+ };
146
+ }
147
+ async function decryptData(encryptedBase64, ivBase64, key) {
148
+ const encryptedData = base64ToArrayBuffer(encryptedBase64);
149
+ const iv = base64ToArrayBuffer(ivBase64);
150
+ const decryptedContent = await getCrypto().subtle.decrypt(
151
+ {
152
+ name: SESSION_ALG,
153
+ iv
154
+ },
155
+ key,
156
+ encryptedData
157
+ );
158
+ const dec = new TextDecoder();
159
+ return dec.decode(decryptedContent);
160
+ }
161
+ async function rsaEncrypt(data, publicKey) {
162
+ const encrypted = await getCrypto().subtle.encrypt(
163
+ {
164
+ name: "RSA-OAEP"
165
+ },
166
+ publicKey,
167
+ data
168
+ );
169
+ return arrayBufferToBase64(encrypted);
170
+ }
171
+ async function rsaDecrypt(encryptedBase64, privateKey) {
172
+ const data = base64ToArrayBuffer(encryptedBase64);
173
+ const decrypted = await getCrypto().subtle.decrypt(
174
+ {
175
+ name: "RSA-OAEP"
176
+ },
177
+ privateKey,
178
+ data
179
+ );
180
+ return decrypted;
181
+ }
182
+ async function exportSessionKey(key) {
183
+ return await getCrypto().subtle.exportKey("raw", key);
184
+ }
185
+ async function importSessionKey(raw) {
186
+ return await getCrypto().subtle.importKey(
187
+ "raw",
188
+ raw,
189
+ SESSION_ALG,
190
+ true,
191
+ ["encrypt", "decrypt"]
192
+ );
193
+ }
119
194
  function arrayBufferToBase64(buffer) {
120
195
  const bytes = new Uint8Array(buffer);
121
196
  if (typeof Buffer !== "undefined") {
@@ -573,6 +648,68 @@ var Wolfronix = class {
573
648
  }
574
649
  return this.request("DELETE", `/api/v1/files/${fileId}`);
575
650
  }
651
+ // ============================================================================
652
+ // E2E Chat Encryption Methods
653
+ // ============================================================================
654
+ /**
655
+ * Get another user's public key (for E2E encryption)
656
+ * @param userId The ID of the recipient
657
+ */
658
+ async getPublicKey(userId) {
659
+ this.ensureAuthenticated();
660
+ const result = await this.request("GET", `/api/v1/keys/${userId}`);
661
+ return result.public_key;
662
+ }
663
+ /**
664
+ * Encrypt a short text message for a recipient (Hybrid Encryption: RSA + AES)
665
+ * Returns a secure JSON string (packet) to send via chat
666
+ *
667
+ * @param text The plain text message
668
+ * @param recipientId The recipient's user ID
669
+ */
670
+ async encryptMessage(text, recipientId) {
671
+ this.ensureAuthenticated();
672
+ const recipientPubKeyPEM = await this.getPublicKey(recipientId);
673
+ const recipientPubKey = await importKeyFromPEM(recipientPubKeyPEM, "public");
674
+ const sessionKey = await generateSessionKey();
675
+ const { encrypted: encryptedMsg, iv } = await encryptData(text, sessionKey);
676
+ const rawSessionKey = await exportSessionKey(sessionKey);
677
+ const encryptedSessionKey = await rsaEncrypt(rawSessionKey, recipientPubKey);
678
+ const packet = {
679
+ key: encryptedSessionKey,
680
+ iv,
681
+ msg: encryptedMsg
682
+ };
683
+ return JSON.stringify(packet);
684
+ }
685
+ /**
686
+ * Decrypt a message packet received from chat
687
+ *
688
+ * @param packetJson The secure JSON string packet
689
+ */
690
+ async decryptMessage(packetJson) {
691
+ this.ensureAuthenticated();
692
+ if (!this.privateKey) {
693
+ throw new Error("Private key not available. Is user logged in?");
694
+ }
695
+ let packet;
696
+ try {
697
+ packet = JSON.parse(packetJson);
698
+ } catch (e) {
699
+ throw new ValidationError("Invalid message packet format");
700
+ }
701
+ if (!packet.key || !packet.iv || !packet.msg) {
702
+ throw new ValidationError("Invalid message packet structure");
703
+ }
704
+ try {
705
+ const rawSessionKey = await rsaDecrypt(packet.key, this.privateKey);
706
+ const sessionKey = await importSessionKey(rawSessionKey);
707
+ const plainText = await decryptData(packet.msg, packet.iv, sessionKey);
708
+ return plainText;
709
+ } catch (error) {
710
+ throw new Error("Decryption failed. You may not be the intended recipient.");
711
+ }
712
+ }
576
713
  // ==========================================================================
577
714
  // Metrics & Status
578
715
  // ==========================================================================
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wolfronix-sdk",
3
- "version": "1.3.0",
3
+ "version": "1.3.2",
4
4
  "description": "Official Wolfronix SDK for JavaScript/TypeScript - Zero-knowledge encryption made simple",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -62,4 +62,4 @@
62
62
  "optional": true
63
63
  }
64
64
  }
65
- }
65
+ }