@f0rest8/metamorphic-crypto 0.3.6 → 0.4.0

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
@@ -134,6 +134,48 @@ const secret = recoveryKeyToSecret("ABCD-EFGH-...");
134
134
  const restored2 = decryptPrivateKeyWithRecovery(backup, secret);
135
135
  ```
136
136
 
137
+ ### Hashing (SHA-3 / SHA-2)
138
+
139
+ Public, one-shot digest functions — intended for **public** data only, such as
140
+ key fingerprints, safety numbers, and key-transparency-log entries. Each takes
141
+ **base64-encoded input** and returns the digest as **base64**. Decode with
142
+ `atob`, or re-encode to hex, if you need a hex fingerprint.
143
+
144
+ `sha3_512` is the recommended default (highest assurance, NIST Cat-5). The
145
+ others let you match an existing format.
146
+
147
+ ```js
148
+ import { sha3_512, sha3_256, sha256, sha512 } from "@f0rest8/metamorphic-crypto";
149
+
150
+ const dataBase64 = btoa("public key bytes");
151
+ const digestBase64 = sha3_512(dataBase64); // 64-byte digest, base64
152
+
153
+ // Need hex? decode the base64 digest:
154
+ const hex = [...atob(digestBase64)].map(c => c.charCodeAt(0).toString(16).padStart(2, "0")).join("");
155
+ ```
156
+
157
+ **Domain separation (recommended for fingerprints / transparency logs).** Use
158
+ `sha3_512WithContext` to bind a digest to a versioned context label, so the same
159
+ bytes hashed for different purposes can never collide or be reinterpreted across
160
+ contexts. It is exactly as strong as `sha3_512`:
161
+
162
+ ```js
163
+ import { sha3_512WithContext } from "@f0rest8/metamorphic-crypto";
164
+
165
+ const dataBase64 = btoa("public key bytes");
166
+ const fingerprint = sha3_512WithContext("mosslet/key-fingerprint/v1", dataBase64);
167
+ ```
168
+
169
+ Stable wire format (reproduce exactly to match native/Elixir):
170
+ `SHA3-512( u64_be(len(context_utf8)) || context_utf8 || data )`.
171
+
172
+ > **Do not hash secrets with these.** A bare hash makes no guarantees about its
173
+ > inputs, and the hashing path intentionally adds no zeroize/constant-time
174
+ > ceremony — wiping a transient copy of already-public data adds cost without
175
+ > protection. To process secret material (passwords, private keys), use the
176
+ > right construction instead: this package's Argon2id `deriveSessionKey` for
177
+ > password-based derivation, or a dedicated KDF/MAC.
178
+
137
179
  ## Security levels
138
180
 
139
181
  | Level | ML-KEM | NIST Category | Equivalent | Default |
@@ -115,6 +115,39 @@ export function sealForUser(plaintext_b64: string, public_key_b64: string, pq_pu
115
115
  */
116
116
  export function sealForUserWithLevel(plaintext_b64: string, public_key_b64: string, pq_public_key_b64: string | null | undefined, level: string): string;
117
117
 
118
+ /**
119
+ * SHA-256 (SHA-2) digest of base64-encoded `data`. Returns the 32-byte digest as base64.
120
+ */
121
+ export function sha256(data_b64: string): string;
122
+
123
+ /**
124
+ * SHA3-256 digest of base64-encoded `data`. Returns the 32-byte digest as base64.
125
+ */
126
+ export function sha3_256(data_b64: string): string;
127
+
128
+ /**
129
+ * SHA3-512 digest of base64-encoded `data`. Returns the 64-byte digest as base64.
130
+ *
131
+ * This is the recommended default hash (NIST Cat-5).
132
+ */
133
+ export function sha3_512(data_b64: string): string;
134
+
135
+ /**
136
+ * Domain-separated SHA3-512: binds the digest to a UTF-8 `context` label.
137
+ *
138
+ * `data` is base64; returns the 64-byte digest as base64. Prefer this over
139
+ * `sha3_512` for fingerprints / safety numbers / key-transparency entries.
140
+ *
141
+ * Wire format (reproduce exactly for parity with native/Elixir):
142
+ * `SHA3-512( u64_be(len(context_utf8)) || context_utf8 || data )`.
143
+ */
144
+ export function sha3_512WithContext(context: string, data_b64: string): string;
145
+
146
+ /**
147
+ * SHA-512 (SHA-2) digest of base64-encoded `data`. Returns the 64-byte digest as base64.
148
+ */
149
+ export function sha512(data_b64: string): string;
150
+
118
151
  /**
119
152
  * Unseal ciphertext using the user's keys. Auto-detects format.
120
153
  * Returns base64-encoded plaintext.
@@ -144,10 +177,15 @@ export interface InitOutput {
144
177
  readonly generateRecoveryKey: (a: number) => void;
145
178
  readonly recoveryKeyToSecret: (a: number, b: number, c: number) => void;
146
179
  readonly parseSaltFromKeyHash: (a: number, b: number, c: number) => void;
180
+ readonly sha3_512: (a: number, b: number, c: number) => void;
181
+ readonly sha3_512WithContext: (a: number, b: number, c: number, d: number, e: number) => void;
182
+ readonly sha3_256: (a: number, b: number, c: number) => void;
183
+ readonly sha256: (a: number, b: number, c: number) => void;
184
+ readonly sha512: (a: number, b: number, c: number) => void;
147
185
  readonly encryptSecretboxString: (a: number, b: number, c: number, d: number, e: number) => void;
148
186
  readonly encryptPrivateKeyForRecovery: (a: number, b: number, c: number, d: number, e: number) => void;
149
- readonly decryptSecretboxToString: (a: number, b: number, c: number, d: number, e: number) => void;
150
187
  readonly decryptPrivateKeyWithRecovery: (a: number, b: number, c: number, d: number, e: number) => void;
188
+ readonly decryptSecretboxToString: (a: number, b: number, c: number, d: number, e: number) => void;
151
189
  readonly __wbindgen_export: (a: number, b: number) => number;
152
190
  readonly __wbindgen_export2: (a: number, b: number, c: number, d: number) => number;
153
191
  readonly __wbindgen_export3: (a: number) => void;
@@ -636,6 +636,177 @@ export function sealForUserWithLevel(plaintext_b64, public_key_b64, pq_public_ke
636
636
  }
637
637
  }
638
638
 
639
+ /**
640
+ * SHA-256 (SHA-2) digest of base64-encoded `data`. Returns the 32-byte digest as base64.
641
+ * @param {string} data_b64
642
+ * @returns {string}
643
+ */
644
+ export function sha256(data_b64) {
645
+ let deferred3_0;
646
+ let deferred3_1;
647
+ try {
648
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
649
+ const ptr0 = passStringToWasm0(data_b64, wasm.__wbindgen_export, wasm.__wbindgen_export2);
650
+ const len0 = WASM_VECTOR_LEN;
651
+ wasm.sha256(retptr, ptr0, len0);
652
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
653
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
654
+ var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
655
+ var r3 = getDataViewMemory0().getInt32(retptr + 4 * 3, true);
656
+ var ptr2 = r0;
657
+ var len2 = r1;
658
+ if (r3) {
659
+ ptr2 = 0; len2 = 0;
660
+ throw takeObject(r2);
661
+ }
662
+ deferred3_0 = ptr2;
663
+ deferred3_1 = len2;
664
+ return getStringFromWasm0(ptr2, len2);
665
+ } finally {
666
+ wasm.__wbindgen_add_to_stack_pointer(16);
667
+ wasm.__wbindgen_export4(deferred3_0, deferred3_1, 1);
668
+ }
669
+ }
670
+
671
+ /**
672
+ * SHA3-256 digest of base64-encoded `data`. Returns the 32-byte digest as base64.
673
+ * @param {string} data_b64
674
+ * @returns {string}
675
+ */
676
+ export function sha3_256(data_b64) {
677
+ let deferred3_0;
678
+ let deferred3_1;
679
+ try {
680
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
681
+ const ptr0 = passStringToWasm0(data_b64, wasm.__wbindgen_export, wasm.__wbindgen_export2);
682
+ const len0 = WASM_VECTOR_LEN;
683
+ wasm.sha3_256(retptr, ptr0, len0);
684
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
685
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
686
+ var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
687
+ var r3 = getDataViewMemory0().getInt32(retptr + 4 * 3, true);
688
+ var ptr2 = r0;
689
+ var len2 = r1;
690
+ if (r3) {
691
+ ptr2 = 0; len2 = 0;
692
+ throw takeObject(r2);
693
+ }
694
+ deferred3_0 = ptr2;
695
+ deferred3_1 = len2;
696
+ return getStringFromWasm0(ptr2, len2);
697
+ } finally {
698
+ wasm.__wbindgen_add_to_stack_pointer(16);
699
+ wasm.__wbindgen_export4(deferred3_0, deferred3_1, 1);
700
+ }
701
+ }
702
+
703
+ /**
704
+ * SHA3-512 digest of base64-encoded `data`. Returns the 64-byte digest as base64.
705
+ *
706
+ * This is the recommended default hash (NIST Cat-5).
707
+ * @param {string} data_b64
708
+ * @returns {string}
709
+ */
710
+ export function sha3_512(data_b64) {
711
+ let deferred3_0;
712
+ let deferred3_1;
713
+ try {
714
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
715
+ const ptr0 = passStringToWasm0(data_b64, wasm.__wbindgen_export, wasm.__wbindgen_export2);
716
+ const len0 = WASM_VECTOR_LEN;
717
+ wasm.sha3_512(retptr, ptr0, len0);
718
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
719
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
720
+ var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
721
+ var r3 = getDataViewMemory0().getInt32(retptr + 4 * 3, true);
722
+ var ptr2 = r0;
723
+ var len2 = r1;
724
+ if (r3) {
725
+ ptr2 = 0; len2 = 0;
726
+ throw takeObject(r2);
727
+ }
728
+ deferred3_0 = ptr2;
729
+ deferred3_1 = len2;
730
+ return getStringFromWasm0(ptr2, len2);
731
+ } finally {
732
+ wasm.__wbindgen_add_to_stack_pointer(16);
733
+ wasm.__wbindgen_export4(deferred3_0, deferred3_1, 1);
734
+ }
735
+ }
736
+
737
+ /**
738
+ * Domain-separated SHA3-512: binds the digest to a UTF-8 `context` label.
739
+ *
740
+ * `data` is base64; returns the 64-byte digest as base64. Prefer this over
741
+ * `sha3_512` for fingerprints / safety numbers / key-transparency entries.
742
+ *
743
+ * Wire format (reproduce exactly for parity with native/Elixir):
744
+ * `SHA3-512( u64_be(len(context_utf8)) || context_utf8 || data )`.
745
+ * @param {string} context
746
+ * @param {string} data_b64
747
+ * @returns {string}
748
+ */
749
+ export function sha3_512WithContext(context, data_b64) {
750
+ let deferred4_0;
751
+ let deferred4_1;
752
+ try {
753
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
754
+ const ptr0 = passStringToWasm0(context, wasm.__wbindgen_export, wasm.__wbindgen_export2);
755
+ const len0 = WASM_VECTOR_LEN;
756
+ const ptr1 = passStringToWasm0(data_b64, wasm.__wbindgen_export, wasm.__wbindgen_export2);
757
+ const len1 = WASM_VECTOR_LEN;
758
+ wasm.sha3_512WithContext(retptr, ptr0, len0, ptr1, len1);
759
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
760
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
761
+ var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
762
+ var r3 = getDataViewMemory0().getInt32(retptr + 4 * 3, true);
763
+ var ptr3 = r0;
764
+ var len3 = r1;
765
+ if (r3) {
766
+ ptr3 = 0; len3 = 0;
767
+ throw takeObject(r2);
768
+ }
769
+ deferred4_0 = ptr3;
770
+ deferred4_1 = len3;
771
+ return getStringFromWasm0(ptr3, len3);
772
+ } finally {
773
+ wasm.__wbindgen_add_to_stack_pointer(16);
774
+ wasm.__wbindgen_export4(deferred4_0, deferred4_1, 1);
775
+ }
776
+ }
777
+
778
+ /**
779
+ * SHA-512 (SHA-2) digest of base64-encoded `data`. Returns the 64-byte digest as base64.
780
+ * @param {string} data_b64
781
+ * @returns {string}
782
+ */
783
+ export function sha512(data_b64) {
784
+ let deferred3_0;
785
+ let deferred3_1;
786
+ try {
787
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
788
+ const ptr0 = passStringToWasm0(data_b64, wasm.__wbindgen_export, wasm.__wbindgen_export2);
789
+ const len0 = WASM_VECTOR_LEN;
790
+ wasm.sha512(retptr, ptr0, len0);
791
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
792
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
793
+ var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
794
+ var r3 = getDataViewMemory0().getInt32(retptr + 4 * 3, true);
795
+ var ptr2 = r0;
796
+ var len2 = r1;
797
+ if (r3) {
798
+ ptr2 = 0; len2 = 0;
799
+ throw takeObject(r2);
800
+ }
801
+ deferred3_0 = ptr2;
802
+ deferred3_1 = len2;
803
+ return getStringFromWasm0(ptr2, len2);
804
+ } finally {
805
+ wasm.__wbindgen_add_to_stack_pointer(16);
806
+ wasm.__wbindgen_export4(deferred3_0, deferred3_1, 1);
807
+ }
808
+ }
809
+
639
810
  /**
640
811
  * Unseal ciphertext using the user's keys. Auto-detects format.
641
812
  * Returns base64-encoded plaintext.
@@ -680,34 +851,34 @@ export function unsealFromUser(ciphertext_b64, public_key_b64, private_key_b64,
680
851
  function __wbg_get_imports() {
681
852
  const import0 = {
682
853
  __proto__: null,
683
- __wbg___wbindgen_debug_string_0accd80f45e5faa2: function(arg0, arg1) {
854
+ __wbg___wbindgen_debug_string_56c147eb1a51f0c4: function(arg0, arg1) {
684
855
  const ret = debugString(getObject(arg1));
685
856
  const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_export, wasm.__wbindgen_export2);
686
857
  const len1 = WASM_VECTOR_LEN;
687
858
  getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
688
859
  getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
689
860
  },
690
- __wbg___wbindgen_is_function_754e9f305ff6029e: function(arg0) {
861
+ __wbg___wbindgen_is_function_147961669f068cd4: function(arg0) {
691
862
  const ret = typeof(getObject(arg0)) === 'function';
692
863
  return ret;
693
864
  },
694
- __wbg___wbindgen_is_object_56732c2bc353f41d: function(arg0) {
865
+ __wbg___wbindgen_is_object_3a2c414391dbf751: function(arg0) {
695
866
  const val = getObject(arg0);
696
867
  const ret = typeof(val) === 'object' && val !== null;
697
868
  return ret;
698
869
  },
699
- __wbg___wbindgen_is_string_c236cabd84a4d769: function(arg0) {
870
+ __wbg___wbindgen_is_string_6541b0f6ecd4e8e5: function(arg0) {
700
871
  const ret = typeof(getObject(arg0)) === 'string';
701
872
  return ret;
702
873
  },
703
- __wbg___wbindgen_is_undefined_67b456be8673d3d7: function(arg0) {
874
+ __wbg___wbindgen_is_undefined_4410e3c20a99fa97: function(arg0) {
704
875
  const ret = getObject(arg0) === undefined;
705
876
  return ret;
706
877
  },
707
- __wbg___wbindgen_throw_1506f2235d1bdba0: function(arg0, arg1) {
878
+ __wbg___wbindgen_throw_bbadd78c1bac3a77: function(arg0, arg1) {
708
879
  throw new Error(getStringFromWasm0(arg0, arg1));
709
880
  },
710
- __wbg_call_9c758de292015997: function() { return handleError(function (arg0, arg1, arg2) {
881
+ __wbg_call_ec09a4cf93377d3a: function() { return handleError(function (arg0, arg1, arg2) {
711
882
  const ret = getObject(arg0).call(getObject(arg1), getObject(arg2));
712
883
  return addHeapObject(ret);
713
884
  }, arguments); },
@@ -718,7 +889,7 @@ function __wbg_get_imports() {
718
889
  __wbg_getRandomValues_c44a50d8cfdaebeb: function() { return handleError(function (arg0, arg1) {
719
890
  getObject(arg0).getRandomValues(getObject(arg1));
720
891
  }, arguments); },
721
- __wbg_length_4a591ecaa01354d9: function(arg0) {
892
+ __wbg_length_68a9d5278d084f4f: function(arg0) {
722
893
  const ret = getObject(arg0).length;
723
894
  return ret;
724
895
  },
@@ -726,11 +897,11 @@ function __wbg_get_imports() {
726
897
  const ret = getObject(arg0).msCrypto;
727
898
  return addHeapObject(ret);
728
899
  },
729
- __wbg_new_ce1ab61c1c2b300d: function() {
900
+ __wbg_new_20b778a4c5c691c3: function() {
730
901
  const ret = new Object();
731
902
  return addHeapObject(ret);
732
903
  },
733
- __wbg_new_with_length_36a4998e27b014c5: function(arg0) {
904
+ __wbg_new_with_length_4b57a7a5dc67221c: function(arg0) {
734
905
  const ret = new Uint8Array(arg0 >>> 0);
735
906
  return addHeapObject(ret);
736
907
  },
@@ -742,7 +913,7 @@ function __wbg_get_imports() {
742
913
  const ret = getObject(arg0).process;
743
914
  return addHeapObject(ret);
744
915
  },
745
- __wbg_prototypesetcall_3249fc62a0fafa30: function(arg0, arg1, arg2) {
916
+ __wbg_prototypesetcall_956c7493c68e29b4: function(arg0, arg1, arg2) {
746
917
  Uint8Array.prototype.set.call(getArrayU8FromWasm0(arg0, arg1), getObject(arg2));
747
918
  },
748
919
  __wbg_randomFillSync_6c25eac9869eb53c: function() { return handleError(function (arg0, arg1) {
@@ -752,27 +923,27 @@ function __wbg_get_imports() {
752
923
  const ret = module.require;
753
924
  return addHeapObject(ret);
754
925
  }, arguments); },
755
- __wbg_set_6e30c9374c26414c: function() { return handleError(function (arg0, arg1, arg2) {
926
+ __wbg_set_a6ba3ac0e634b822: function() { return handleError(function (arg0, arg1, arg2) {
756
927
  const ret = Reflect.set(getObject(arg0), getObject(arg1), getObject(arg2));
757
928
  return ret;
758
929
  }, arguments); },
759
- __wbg_static_accessor_GLOBAL_9d53f2689e622ca1: function() {
930
+ __wbg_static_accessor_GLOBAL_60a4124bab7dcc9a: function() {
760
931
  const ret = typeof global === 'undefined' ? null : global;
761
932
  return isLikeNone(ret) ? 0 : addHeapObject(ret);
762
933
  },
763
- __wbg_static_accessor_GLOBAL_THIS_a1a35cec07001a8a: function() {
934
+ __wbg_static_accessor_GLOBAL_THIS_95ca6460658b5d13: function() {
764
935
  const ret = typeof globalThis === 'undefined' ? null : globalThis;
765
936
  return isLikeNone(ret) ? 0 : addHeapObject(ret);
766
937
  },
767
- __wbg_static_accessor_SELF_4c59f6c7ea29a144: function() {
938
+ __wbg_static_accessor_SELF_4c95f759a91e9aae: function() {
768
939
  const ret = typeof self === 'undefined' ? null : self;
769
940
  return isLikeNone(ret) ? 0 : addHeapObject(ret);
770
941
  },
771
- __wbg_static_accessor_WINDOW_e70ae9f2eb052253: function() {
942
+ __wbg_static_accessor_WINDOW_44b435597f9e9ee7: function() {
772
943
  const ret = typeof window === 'undefined' ? null : window;
773
944
  return isLikeNone(ret) ? 0 : addHeapObject(ret);
774
945
  },
775
- __wbg_subarray_4aa221f6a4f5ab22: function(arg0, arg1, arg2) {
946
+ __wbg_subarray_42216645a367cd7a: function(arg0, arg1, arg2) {
776
947
  const ret = getObject(arg0).subarray(arg1 >>> 0, arg2 >>> 0);
777
948
  return addHeapObject(ret);
778
949
  },
Binary file
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@f0rest8/metamorphic-crypto",
3
3
  "type": "module",
4
4
  "description": "Zero-knowledge end-to-end encryption with post-quantum hybrid KEM (ML-KEM-768/1024 + X25519)",
5
- "version": "0.3.6",
5
+ "version": "0.4.0",
6
6
  "license": "MIT OR Apache-2.0",
7
7
  "repository": {
8
8
  "type": "git",