@functionland/fula-client 0.2.23 → 0.2.25

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/fula_js.d.ts CHANGED
@@ -47,12 +47,21 @@ export function deleteBucket(client: EncryptedClient, name: string): Promise<voi
47
47
  export function deleteEncrypted(client: EncryptedClient, bucket: string, key: string): Promise<void>;
48
48
 
49
49
  /**
50
- * Derive a 32-byte key from context and input
50
+ * Derive a 32-byte key from context and input using Argon2id (memory-hard KDF)
51
51
  *
52
- * Use this to derive encryption keys from Google credentials:
52
+ * Use this to derive encryption keys from Google credentials with brute-force resistance:
53
53
  * ```javascript
54
- * const key = deriveKey('my-app-v1', new TextEncoder().encode(userId + email));
54
+ * const key = deriveKey('fula-files-v1', new TextEncoder().encode(`google:${userId}:${email}`));
55
55
  * ```
56
+ *
57
+ * Parameters:
58
+ * - Memory: 64 MiB
59
+ * - Iterations: 3
60
+ * - Parallelism: 1 (for cross-platform consistency)
61
+ *
62
+ * @param context - Context string used as salt (e.g., "fula-files-v1")
63
+ * @param input - Input bytes (e.g., UTF-8 encoded credentials)
64
+ * @returns 32-byte derived key
56
65
  */
57
66
  export function deriveKey(context: string, input: Uint8Array): Uint8Array;
58
67
 
@@ -180,6 +189,61 @@ export function putEncrypted(client: EncryptedClient, bucket: string, key: strin
180
189
  */
181
190
  export function putEncryptedWithType(client: EncryptedClient, bucket: string, key: string, data: Uint8Array, content_type: string): Promise<any>;
182
191
 
192
+ /**
193
+ * AES-256-GCM decrypt data with a DEK
194
+ *
195
+ * @param dekBytes - 32-byte DEK
196
+ * @param nonceBase64 - Base64 encoded 12-byte nonce
197
+ * @param ciphertextBase64 - Base64 encoded ciphertext
198
+ * @returns Decrypted plaintext
199
+ */
200
+ export function testAesGcmDecrypt(dek_bytes: Uint8Array, nonce_base64: string, ciphertext_base64: string): Uint8Array;
201
+
202
+ /**
203
+ * AES-256-GCM encrypt data with a DEK
204
+ *
205
+ * @param dekBytes - 32-byte DEK
206
+ * @param plaintext - Data to encrypt
207
+ * @returns JSON string with {nonce, ciphertext} (both base64 encoded)
208
+ */
209
+ export function testAesGcmEncrypt(dek_bytes: Uint8Array, plaintext: Uint8Array): string;
210
+
211
+ /**
212
+ * Full encryption round-trip test
213
+ *
214
+ * This tests the EXACT flow: derive key -> encrypt DEK with HPKE -> encrypt data with AES-GCM
215
+ * then decrypt in reverse order. If this works, the WASM crypto is correct.
216
+ *
217
+ * @param context - Key derivation context (e.g., "fula-files-v1")
218
+ * @param input - Key derivation input (e.g., credentials)
219
+ * @param plaintext - Data to encrypt and decrypt
220
+ * @returns Original plaintext if successful (proves round-trip works)
221
+ */
222
+ export function testFullEncryptionRoundtrip(context: string, input: Uint8Array, plaintext: Uint8Array): Uint8Array;
223
+
224
+ /**
225
+ * Decrypt a wrapped DEK using HPKE with the given secret key
226
+ *
227
+ * This simulates what WebUI WASM should do when decrypting a file.
228
+ *
229
+ * @param secretKeyBytes - 32-byte X25519 secret key
230
+ * @param wrappedDekJson - JSON string from testHpkeEncryptDek
231
+ * @returns 32-byte decrypted DEK
232
+ */
233
+ export function testHpkeDecryptDek(secret_key_bytes: Uint8Array, wrapped_dek_json: string): Uint8Array;
234
+
235
+ /**
236
+ * Encrypt a DEK using HPKE with the given public key
237
+ *
238
+ * This simulates what FxFiles does when encrypting a file.
239
+ * Returns JSON string containing the EncryptedData (wrapped DEK).
240
+ *
241
+ * @param publicKeyBytes - 32-byte X25519 public key
242
+ * @param dekBytes - 32-byte DEK to encrypt
243
+ * @returns JSON string with {version, encapsulated_key, ciphertext}
244
+ */
245
+ export function testHpkeEncryptDek(public_key_bytes: Uint8Array, dek_bytes: Uint8Array): string;
246
+
183
247
  export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
184
248
 
185
249
  export interface InitOutput {
@@ -208,12 +272,17 @@ export interface InitOutput {
208
272
  readonly listDirectory: (a: number, b: number, c: number, d: number, e: number) => any;
209
273
  readonly putEncrypted: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => any;
210
274
  readonly putEncryptedWithType: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number) => any;
275
+ readonly testAesGcmDecrypt: (a: number, b: number, c: number, d: number, e: number, f: number) => [number, number, number, number];
276
+ readonly testAesGcmEncrypt: (a: number, b: number, c: number, d: number) => [number, number, number, number];
277
+ readonly testFullEncryptionRoundtrip: (a: number, b: number, c: number, d: number, e: number, f: number) => [number, number, number, number];
278
+ readonly testHpkeDecryptDek: (a: number, b: number, c: number, d: number) => [number, number, number, number];
279
+ readonly testHpkeEncryptDek: (a: number, b: number, c: number, d: number) => [number, number, number, number];
211
280
  readonly init: () => void;
212
- readonly wasm_bindgen__convert__closures_____invoke__h137a7db9bfc1a698: (a: number, b: number) => void;
213
- readonly wasm_bindgen__closure__destroy__h175e7795724731f3: (a: number, b: number) => void;
214
- readonly wasm_bindgen__convert__closures_____invoke__hfca1d4c99c3f4fb2: (a: number, b: number, c: any) => void;
215
- readonly wasm_bindgen__closure__destroy__hf1f5673d74edd4c1: (a: number, b: number) => void;
216
- readonly wasm_bindgen__convert__closures_____invoke__h480ba7e91e420106: (a: number, b: number, c: any, d: any) => void;
281
+ readonly wasm_bindgen__convert__closures_____invoke__hb49647253b2911c5: (a: number, b: number) => void;
282
+ readonly wasm_bindgen__closure__destroy__hde34fe38f5ae6975: (a: number, b: number) => void;
283
+ readonly wasm_bindgen__convert__closures_____invoke__h7e10992c477d6050: (a: number, b: number, c: any) => void;
284
+ readonly wasm_bindgen__closure__destroy__h40a33609ceb56b6e: (a: number, b: number) => void;
285
+ readonly wasm_bindgen__convert__closures_____invoke__h4b8cebda0a70f497: (a: number, b: number, c: any, d: any) => void;
217
286
  readonly __wbindgen_malloc: (a: number, b: number) => number;
218
287
  readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
219
288
  readonly __wbindgen_exn_store: (a: number) => void;
package/fula_js.js CHANGED
@@ -227,16 +227,16 @@ if (!('encodeInto' in cachedTextEncoder)) {
227
227
 
228
228
  let WASM_VECTOR_LEN = 0;
229
229
 
230
- function wasm_bindgen__convert__closures_____invoke__h137a7db9bfc1a698(arg0, arg1) {
231
- wasm.wasm_bindgen__convert__closures_____invoke__h137a7db9bfc1a698(arg0, arg1);
230
+ function wasm_bindgen__convert__closures_____invoke__hb49647253b2911c5(arg0, arg1) {
231
+ wasm.wasm_bindgen__convert__closures_____invoke__hb49647253b2911c5(arg0, arg1);
232
232
  }
233
233
 
234
- function wasm_bindgen__convert__closures_____invoke__hfca1d4c99c3f4fb2(arg0, arg1, arg2) {
235
- wasm.wasm_bindgen__convert__closures_____invoke__hfca1d4c99c3f4fb2(arg0, arg1, arg2);
234
+ function wasm_bindgen__convert__closures_____invoke__h7e10992c477d6050(arg0, arg1, arg2) {
235
+ wasm.wasm_bindgen__convert__closures_____invoke__h7e10992c477d6050(arg0, arg1, arg2);
236
236
  }
237
237
 
238
- function wasm_bindgen__convert__closures_____invoke__h480ba7e91e420106(arg0, arg1, arg2, arg3) {
239
- wasm.wasm_bindgen__convert__closures_____invoke__h480ba7e91e420106(arg0, arg1, arg2, arg3);
238
+ function wasm_bindgen__convert__closures_____invoke__h4b8cebda0a70f497(arg0, arg1, arg2, arg3) {
239
+ wasm.wasm_bindgen__convert__closures_____invoke__h4b8cebda0a70f497(arg0, arg1, arg2, arg3);
240
240
  }
241
241
 
242
242
  const __wbindgen_enum_RequestCache = ["default", "no-store", "reload", "no-cache", "force-cache", "only-if-cached"];
@@ -380,12 +380,21 @@ export function deleteEncrypted(client, bucket, key) {
380
380
  }
381
381
 
382
382
  /**
383
- * Derive a 32-byte key from context and input
383
+ * Derive a 32-byte key from context and input using Argon2id (memory-hard KDF)
384
384
  *
385
- * Use this to derive encryption keys from Google credentials:
385
+ * Use this to derive encryption keys from Google credentials with brute-force resistance:
386
386
  * ```javascript
387
- * const key = deriveKey('my-app-v1', new TextEncoder().encode(userId + email));
387
+ * const key = deriveKey('fula-files-v1', new TextEncoder().encode(`google:${userId}:${email}`));
388
388
  * ```
389
+ *
390
+ * Parameters:
391
+ * - Memory: 64 MiB
392
+ * - Iterations: 3
393
+ * - Parallelism: 1 (for cross-platform consistency)
394
+ *
395
+ * @param context - Context string used as salt (e.g., "fula-files-v1")
396
+ * @param input - Input bytes (e.g., UTF-8 encoded credentials)
397
+ * @returns 32-byte derived key
389
398
  * @param {string} context
390
399
  * @param {Uint8Array} input
391
400
  * @returns {Uint8Array}
@@ -698,6 +707,160 @@ export function putEncryptedWithType(client, bucket, key, data, content_type) {
698
707
  return ret;
699
708
  }
700
709
 
710
+ /**
711
+ * AES-256-GCM decrypt data with a DEK
712
+ *
713
+ * @param dekBytes - 32-byte DEK
714
+ * @param nonceBase64 - Base64 encoded 12-byte nonce
715
+ * @param ciphertextBase64 - Base64 encoded ciphertext
716
+ * @returns Decrypted plaintext
717
+ * @param {Uint8Array} dek_bytes
718
+ * @param {string} nonce_base64
719
+ * @param {string} ciphertext_base64
720
+ * @returns {Uint8Array}
721
+ */
722
+ export function testAesGcmDecrypt(dek_bytes, nonce_base64, ciphertext_base64) {
723
+ const ptr0 = passArray8ToWasm0(dek_bytes, wasm.__wbindgen_malloc);
724
+ const len0 = WASM_VECTOR_LEN;
725
+ const ptr1 = passStringToWasm0(nonce_base64, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
726
+ const len1 = WASM_VECTOR_LEN;
727
+ const ptr2 = passStringToWasm0(ciphertext_base64, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
728
+ const len2 = WASM_VECTOR_LEN;
729
+ const ret = wasm.testAesGcmDecrypt(ptr0, len0, ptr1, len1, ptr2, len2);
730
+ if (ret[3]) {
731
+ throw takeFromExternrefTable0(ret[2]);
732
+ }
733
+ var v4 = getArrayU8FromWasm0(ret[0], ret[1]).slice();
734
+ wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);
735
+ return v4;
736
+ }
737
+
738
+ /**
739
+ * AES-256-GCM encrypt data with a DEK
740
+ *
741
+ * @param dekBytes - 32-byte DEK
742
+ * @param plaintext - Data to encrypt
743
+ * @returns JSON string with {nonce, ciphertext} (both base64 encoded)
744
+ * @param {Uint8Array} dek_bytes
745
+ * @param {Uint8Array} plaintext
746
+ * @returns {string}
747
+ */
748
+ export function testAesGcmEncrypt(dek_bytes, plaintext) {
749
+ let deferred4_0;
750
+ let deferred4_1;
751
+ try {
752
+ const ptr0 = passArray8ToWasm0(dek_bytes, wasm.__wbindgen_malloc);
753
+ const len0 = WASM_VECTOR_LEN;
754
+ const ptr1 = passArray8ToWasm0(plaintext, wasm.__wbindgen_malloc);
755
+ const len1 = WASM_VECTOR_LEN;
756
+ const ret = wasm.testAesGcmEncrypt(ptr0, len0, ptr1, len1);
757
+ var ptr3 = ret[0];
758
+ var len3 = ret[1];
759
+ if (ret[3]) {
760
+ ptr3 = 0; len3 = 0;
761
+ throw takeFromExternrefTable0(ret[2]);
762
+ }
763
+ deferred4_0 = ptr3;
764
+ deferred4_1 = len3;
765
+ return getStringFromWasm0(ptr3, len3);
766
+ } finally {
767
+ wasm.__wbindgen_free(deferred4_0, deferred4_1, 1);
768
+ }
769
+ }
770
+
771
+ /**
772
+ * Full encryption round-trip test
773
+ *
774
+ * This tests the EXACT flow: derive key -> encrypt DEK with HPKE -> encrypt data with AES-GCM
775
+ * then decrypt in reverse order. If this works, the WASM crypto is correct.
776
+ *
777
+ * @param context - Key derivation context (e.g., "fula-files-v1")
778
+ * @param input - Key derivation input (e.g., credentials)
779
+ * @param plaintext - Data to encrypt and decrypt
780
+ * @returns Original plaintext if successful (proves round-trip works)
781
+ * @param {string} context
782
+ * @param {Uint8Array} input
783
+ * @param {Uint8Array} plaintext
784
+ * @returns {Uint8Array}
785
+ */
786
+ export function testFullEncryptionRoundtrip(context, input, plaintext) {
787
+ const ptr0 = passStringToWasm0(context, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
788
+ const len0 = WASM_VECTOR_LEN;
789
+ const ptr1 = passArray8ToWasm0(input, wasm.__wbindgen_malloc);
790
+ const len1 = WASM_VECTOR_LEN;
791
+ const ptr2 = passArray8ToWasm0(plaintext, wasm.__wbindgen_malloc);
792
+ const len2 = WASM_VECTOR_LEN;
793
+ const ret = wasm.testFullEncryptionRoundtrip(ptr0, len0, ptr1, len1, ptr2, len2);
794
+ if (ret[3]) {
795
+ throw takeFromExternrefTable0(ret[2]);
796
+ }
797
+ var v4 = getArrayU8FromWasm0(ret[0], ret[1]).slice();
798
+ wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);
799
+ return v4;
800
+ }
801
+
802
+ /**
803
+ * Decrypt a wrapped DEK using HPKE with the given secret key
804
+ *
805
+ * This simulates what WebUI WASM should do when decrypting a file.
806
+ *
807
+ * @param secretKeyBytes - 32-byte X25519 secret key
808
+ * @param wrappedDekJson - JSON string from testHpkeEncryptDek
809
+ * @returns 32-byte decrypted DEK
810
+ * @param {Uint8Array} secret_key_bytes
811
+ * @param {string} wrapped_dek_json
812
+ * @returns {Uint8Array}
813
+ */
814
+ export function testHpkeDecryptDek(secret_key_bytes, wrapped_dek_json) {
815
+ const ptr0 = passArray8ToWasm0(secret_key_bytes, wasm.__wbindgen_malloc);
816
+ const len0 = WASM_VECTOR_LEN;
817
+ const ptr1 = passStringToWasm0(wrapped_dek_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
818
+ const len1 = WASM_VECTOR_LEN;
819
+ const ret = wasm.testHpkeDecryptDek(ptr0, len0, ptr1, len1);
820
+ if (ret[3]) {
821
+ throw takeFromExternrefTable0(ret[2]);
822
+ }
823
+ var v3 = getArrayU8FromWasm0(ret[0], ret[1]).slice();
824
+ wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);
825
+ return v3;
826
+ }
827
+
828
+ /**
829
+ * Encrypt a DEK using HPKE with the given public key
830
+ *
831
+ * This simulates what FxFiles does when encrypting a file.
832
+ * Returns JSON string containing the EncryptedData (wrapped DEK).
833
+ *
834
+ * @param publicKeyBytes - 32-byte X25519 public key
835
+ * @param dekBytes - 32-byte DEK to encrypt
836
+ * @returns JSON string with {version, encapsulated_key, ciphertext}
837
+ * @param {Uint8Array} public_key_bytes
838
+ * @param {Uint8Array} dek_bytes
839
+ * @returns {string}
840
+ */
841
+ export function testHpkeEncryptDek(public_key_bytes, dek_bytes) {
842
+ let deferred4_0;
843
+ let deferred4_1;
844
+ try {
845
+ const ptr0 = passArray8ToWasm0(public_key_bytes, wasm.__wbindgen_malloc);
846
+ const len0 = WASM_VECTOR_LEN;
847
+ const ptr1 = passArray8ToWasm0(dek_bytes, wasm.__wbindgen_malloc);
848
+ const len1 = WASM_VECTOR_LEN;
849
+ const ret = wasm.testHpkeEncryptDek(ptr0, len0, ptr1, len1);
850
+ var ptr3 = ret[0];
851
+ var len3 = ret[1];
852
+ if (ret[3]) {
853
+ ptr3 = 0; len3 = 0;
854
+ throw takeFromExternrefTable0(ret[2]);
855
+ }
856
+ deferred4_0 = ptr3;
857
+ deferred4_1 = len3;
858
+ return getStringFromWasm0(ptr3, len3);
859
+ } finally {
860
+ wasm.__wbindgen_free(deferred4_0, deferred4_1, 1);
861
+ }
862
+ }
863
+
701
864
  const EXPECTED_RESPONSE_TYPES = new Set(['basic', 'cors', 'default']);
702
865
 
703
866
  async function __wbg_load(module, imports) {
@@ -999,7 +1162,7 @@ function __wbg_get_imports() {
999
1162
  const a = state0.a;
1000
1163
  state0.a = 0;
1001
1164
  try {
1002
- return wasm_bindgen__convert__closures_____invoke__h480ba7e91e420106(a, state0.b, arg0, arg1);
1165
+ return wasm_bindgen__convert__closures_____invoke__h4b8cebda0a70f497(a, state0.b, arg0, arg1);
1003
1166
  } finally {
1004
1167
  state0.a = a;
1005
1168
  }
@@ -1169,9 +1332,9 @@ function __wbg_get_imports() {
1169
1332
  const ret = getStringFromWasm0(arg0, arg1);
1170
1333
  return ret;
1171
1334
  };
1172
- imports.wbg.__wbindgen_cast_293162629359ccc7 = function(arg0, arg1) {
1173
- // Cast intrinsic for `Closure(Closure { dtor_idx: 284, function: Function { arguments: [], shim_idx: 285, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
1174
- const ret = makeMutClosure(arg0, arg1, wasm.wasm_bindgen__closure__destroy__h175e7795724731f3, wasm_bindgen__convert__closures_____invoke__h137a7db9bfc1a698);
1335
+ imports.wbg.__wbindgen_cast_3ef92b33ccb01780 = function(arg0, arg1) {
1336
+ // Cast intrinsic for `Closure(Closure { dtor_idx: 323, function: Function { arguments: [Externref], shim_idx: 324, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
1337
+ const ret = makeMutClosure(arg0, arg1, wasm.wasm_bindgen__closure__destroy__h40a33609ceb56b6e, wasm_bindgen__convert__closures_____invoke__h7e10992c477d6050);
1175
1338
  return ret;
1176
1339
  };
1177
1340
  imports.wbg.__wbindgen_cast_4625c577ab2ec9ee = function(arg0) {
@@ -1179,9 +1342,9 @@ function __wbg_get_imports() {
1179
1342
  const ret = BigInt.asUintN(64, arg0);
1180
1343
  return ret;
1181
1344
  };
1182
- imports.wbg.__wbindgen_cast_7418e77c9d43951b = function(arg0, arg1) {
1183
- // Cast intrinsic for `Closure(Closure { dtor_idx: 319, function: Function { arguments: [Externref], shim_idx: 320, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
1184
- const ret = makeMutClosure(arg0, arg1, wasm.wasm_bindgen__closure__destroy__hf1f5673d74edd4c1, wasm_bindgen__convert__closures_____invoke__hfca1d4c99c3f4fb2);
1345
+ imports.wbg.__wbindgen_cast_6b8383c0ec1ae211 = function(arg0, arg1) {
1346
+ // Cast intrinsic for `Closure(Closure { dtor_idx: 289, function: Function { arguments: [], shim_idx: 290, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
1347
+ const ret = makeMutClosure(arg0, arg1, wasm.wasm_bindgen__closure__destroy__hde34fe38f5ae6975, wasm_bindgen__convert__closures_____invoke__hb49647253b2911c5);
1185
1348
  return ret;
1186
1349
  };
1187
1350
  imports.wbg.__wbindgen_cast_77bc3e92745e9a35 = function(arg0, arg1) {
package/fula_js_bg.wasm CHANGED
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@functionland/fula-client",
3
- "version": "0.2.23",
3
+ "version": "0.2.25",
4
4
  "type": "module",
5
5
  "description": "JavaScript/TypeScript SDK for Fula decentralized storage - client-side encryption with cross-platform key compatibility",
6
6
  "main": "fula_js.js",