react-native-quick-crypto 1.1.1 → 1.1.3

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.
Files changed (185) hide show
  1. package/QuickCrypto.podspec +1 -0
  2. package/android/CMakeLists.txt +4 -0
  3. package/cpp/cipher/CCMCipher.cpp +7 -11
  4. package/cpp/cipher/ChaCha20Cipher.cpp +6 -10
  5. package/cpp/cipher/ChaCha20Poly1305Cipher.cpp +10 -16
  6. package/cpp/cipher/GCMCipher.cpp +3 -5
  7. package/cpp/cipher/HybridCipher.cpp +7 -13
  8. package/cpp/cipher/HybridRsaCipher.cpp +19 -27
  9. package/cpp/cipher/OCBCipher.cpp +2 -3
  10. package/cpp/cipher/XChaCha20Poly1305Cipher.cpp +13 -19
  11. package/cpp/cipher/XSalsa20Cipher.cpp +8 -12
  12. package/cpp/cipher/XSalsa20Poly1305Cipher.cpp +11 -16
  13. package/cpp/keys/HybridKeyObjectHandle.cpp +630 -2
  14. package/cpp/keys/HybridKeyObjectHandle.hpp +21 -1
  15. package/cpp/sign/HybridSignHandle.cpp +26 -8
  16. package/cpp/sign/HybridVerifyHandle.cpp +28 -11
  17. package/cpp/slhdsa/HybridSlhDsaKeyPair.cpp +245 -0
  18. package/cpp/slhdsa/HybridSlhDsaKeyPair.hpp +48 -0
  19. package/cpp/turboshake/HybridTurboShake.cpp +379 -0
  20. package/cpp/turboshake/HybridTurboShake.hpp +28 -0
  21. package/cpp/utils/HybridUtils.cpp +83 -21
  22. package/cpp/utils/HybridUtils.hpp +4 -0
  23. package/deps/blake3/README.md +6 -7
  24. package/deps/blake3/c/blake3.c +3 -2
  25. package/deps/blake3/c/blake3.h +2 -2
  26. package/deps/blake3/c/blake3_dispatch.c +2 -2
  27. package/deps/blake3/c/blake3_impl.h +1 -1
  28. package/deps/blake3/c/blake3_neon.c +5 -4
  29. package/deps/ncrypto/include/ncrypto/version.h +2 -2
  30. package/deps/ncrypto/include/ncrypto.h +9 -2
  31. package/deps/ncrypto/src/ncrypto.cpp +130 -35
  32. package/lib/commonjs/dhKeyPair.js +3 -0
  33. package/lib/commonjs/dhKeyPair.js.map +1 -1
  34. package/lib/commonjs/dsa.js +3 -0
  35. package/lib/commonjs/dsa.js.map +1 -1
  36. package/lib/commonjs/ec.js +37 -30
  37. package/lib/commonjs/ec.js.map +1 -1
  38. package/lib/commonjs/ed.js +60 -6
  39. package/lib/commonjs/ed.js.map +1 -1
  40. package/lib/commonjs/expo-plugin/withXCode.js +3 -3
  41. package/lib/commonjs/hash.js +52 -5
  42. package/lib/commonjs/hash.js.map +1 -1
  43. package/lib/commonjs/keys/classes.js +33 -7
  44. package/lib/commonjs/keys/classes.js.map +1 -1
  45. package/lib/commonjs/keys/generateKeyPair.js +85 -4
  46. package/lib/commonjs/keys/generateKeyPair.js.map +1 -1
  47. package/lib/commonjs/keys/index.js +50 -2
  48. package/lib/commonjs/keys/index.js.map +1 -1
  49. package/lib/commonjs/keys/signVerify.js +9 -2
  50. package/lib/commonjs/keys/signVerify.js.map +1 -1
  51. package/lib/commonjs/keys/utils.js +59 -1
  52. package/lib/commonjs/keys/utils.js.map +1 -1
  53. package/lib/commonjs/random.js +63 -9
  54. package/lib/commonjs/random.js.map +1 -1
  55. package/lib/commonjs/rsa.js +3 -0
  56. package/lib/commonjs/rsa.js.map +1 -1
  57. package/lib/commonjs/slhdsa.js +70 -0
  58. package/lib/commonjs/slhdsa.js.map +1 -0
  59. package/lib/commonjs/specs/slhDsaKeyPair.nitro.js +6 -0
  60. package/lib/commonjs/specs/slhDsaKeyPair.nitro.js.map +1 -0
  61. package/lib/commonjs/specs/turboshake.nitro.js +6 -0
  62. package/lib/commonjs/specs/turboshake.nitro.js.map +1 -0
  63. package/lib/commonjs/subtle.js +926 -275
  64. package/lib/commonjs/subtle.js.map +1 -1
  65. package/lib/commonjs/utils/conversion.js +61 -25
  66. package/lib/commonjs/utils/conversion.js.map +1 -1
  67. package/lib/commonjs/utils/errors.js +63 -4
  68. package/lib/commonjs/utils/errors.js.map +1 -1
  69. package/lib/commonjs/utils/types.js.map +1 -1
  70. package/lib/commonjs/utils/validation.js +46 -0
  71. package/lib/commonjs/utils/validation.js.map +1 -1
  72. package/lib/module/dhKeyPair.js +3 -0
  73. package/lib/module/dhKeyPair.js.map +1 -1
  74. package/lib/module/dsa.js +3 -0
  75. package/lib/module/dsa.js.map +1 -1
  76. package/lib/module/ec.js +38 -31
  77. package/lib/module/ec.js.map +1 -1
  78. package/lib/module/ed.js +61 -7
  79. package/lib/module/ed.js.map +1 -1
  80. package/lib/module/expo-plugin/withXCode.js +3 -3
  81. package/lib/module/hash.js +52 -5
  82. package/lib/module/hash.js.map +1 -1
  83. package/lib/module/keys/classes.js +31 -5
  84. package/lib/module/keys/classes.js.map +1 -1
  85. package/lib/module/keys/generateKeyPair.js +86 -5
  86. package/lib/module/keys/generateKeyPair.js.map +1 -1
  87. package/lib/module/keys/index.js +50 -2
  88. package/lib/module/keys/index.js.map +1 -1
  89. package/lib/module/keys/signVerify.js +9 -2
  90. package/lib/module/keys/signVerify.js.map +1 -1
  91. package/lib/module/keys/utils.js +57 -1
  92. package/lib/module/keys/utils.js.map +1 -1
  93. package/lib/module/random.js +63 -10
  94. package/lib/module/random.js.map +1 -1
  95. package/lib/module/rsa.js +3 -0
  96. package/lib/module/rsa.js.map +1 -1
  97. package/lib/module/slhdsa.js +64 -0
  98. package/lib/module/slhdsa.js.map +1 -0
  99. package/lib/module/specs/slhDsaKeyPair.nitro.js +4 -0
  100. package/lib/module/specs/slhDsaKeyPair.nitro.js.map +1 -0
  101. package/lib/module/specs/turboshake.nitro.js +4 -0
  102. package/lib/module/specs/turboshake.nitro.js.map +1 -0
  103. package/lib/module/subtle.js +927 -276
  104. package/lib/module/subtle.js.map +1 -1
  105. package/lib/module/utils/conversion.js +59 -25
  106. package/lib/module/utils/conversion.js.map +1 -1
  107. package/lib/module/utils/errors.js +61 -4
  108. package/lib/module/utils/errors.js.map +1 -1
  109. package/lib/module/utils/types.js.map +1 -1
  110. package/lib/module/utils/validation.js +44 -0
  111. package/lib/module/utils/validation.js.map +1 -1
  112. package/lib/typescript/dhKeyPair.d.ts.map +1 -1
  113. package/lib/typescript/dsa.d.ts.map +1 -1
  114. package/lib/typescript/ec.d.ts.map +1 -1
  115. package/lib/typescript/ed.d.ts.map +1 -1
  116. package/lib/typescript/hash.d.ts.map +1 -1
  117. package/lib/typescript/index.d.ts +12 -7
  118. package/lib/typescript/index.d.ts.map +1 -1
  119. package/lib/typescript/keys/classes.d.ts +10 -1
  120. package/lib/typescript/keys/classes.d.ts.map +1 -1
  121. package/lib/typescript/keys/generateKeyPair.d.ts +12 -1
  122. package/lib/typescript/keys/generateKeyPair.d.ts.map +1 -1
  123. package/lib/typescript/keys/index.d.ts +3 -1
  124. package/lib/typescript/keys/index.d.ts.map +1 -1
  125. package/lib/typescript/keys/signVerify.d.ts.map +1 -1
  126. package/lib/typescript/keys/utils.d.ts +21 -4
  127. package/lib/typescript/keys/utils.d.ts.map +1 -1
  128. package/lib/typescript/random.d.ts +5 -1
  129. package/lib/typescript/random.d.ts.map +1 -1
  130. package/lib/typescript/rsa.d.ts.map +1 -1
  131. package/lib/typescript/slhdsa.d.ts +19 -0
  132. package/lib/typescript/slhdsa.d.ts.map +1 -0
  133. package/lib/typescript/specs/keyObjectHandle.nitro.d.ts +9 -0
  134. package/lib/typescript/specs/keyObjectHandle.nitro.d.ts.map +1 -1
  135. package/lib/typescript/specs/slhDsaKeyPair.nitro.d.ts +16 -0
  136. package/lib/typescript/specs/slhDsaKeyPair.nitro.d.ts.map +1 -0
  137. package/lib/typescript/specs/turboshake.nitro.d.ts +11 -0
  138. package/lib/typescript/specs/turboshake.nitro.d.ts.map +1 -0
  139. package/lib/typescript/subtle.d.ts +3 -2
  140. package/lib/typescript/subtle.d.ts.map +1 -1
  141. package/lib/typescript/utils/conversion.d.ts +9 -6
  142. package/lib/typescript/utils/conversion.d.ts.map +1 -1
  143. package/lib/typescript/utils/errors.d.ts +12 -0
  144. package/lib/typescript/utils/errors.d.ts.map +1 -1
  145. package/lib/typescript/utils/types.d.ts +32 -15
  146. package/lib/typescript/utils/types.d.ts.map +1 -1
  147. package/lib/typescript/utils/validation.d.ts +3 -1
  148. package/lib/typescript/utils/validation.d.ts.map +1 -1
  149. package/nitrogen/generated/android/QuickCrypto+autolinking.cmake +2 -0
  150. package/nitrogen/generated/android/QuickCryptoOnLoad.cpp +20 -0
  151. package/nitrogen/generated/ios/QuickCryptoAutolinking.mm +20 -0
  152. package/nitrogen/generated/shared/c++/AsymmetricKeyType.hpp +48 -0
  153. package/nitrogen/generated/shared/c++/HybridKeyObjectHandleSpec.cpp +9 -0
  154. package/nitrogen/generated/shared/c++/HybridKeyObjectHandleSpec.hpp +9 -0
  155. package/nitrogen/generated/shared/c++/HybridSlhDsaKeyPairSpec.cpp +29 -0
  156. package/nitrogen/generated/shared/c++/HybridSlhDsaKeyPairSpec.hpp +72 -0
  157. package/nitrogen/generated/shared/c++/HybridTurboShakeSpec.cpp +22 -0
  158. package/nitrogen/generated/shared/c++/HybridTurboShakeSpec.hpp +70 -0
  159. package/nitrogen/generated/shared/c++/JWK.hpp +9 -1
  160. package/nitrogen/generated/shared/c++/JWKkty.hpp +4 -0
  161. package/nitrogen/generated/shared/c++/KangarooTwelveVariant.hpp +76 -0
  162. package/nitrogen/generated/shared/c++/TurboShakeVariant.hpp +76 -0
  163. package/package.json +4 -5
  164. package/src/dhKeyPair.ts +8 -0
  165. package/src/dsa.ts +8 -0
  166. package/src/ec.ts +52 -29
  167. package/src/ed.ts +95 -16
  168. package/src/expo-plugin/withXCode.ts +3 -3
  169. package/src/hash.ts +108 -5
  170. package/src/keys/classes.ts +46 -5
  171. package/src/keys/generateKeyPair.ts +151 -5
  172. package/src/keys/index.ts +73 -3
  173. package/src/keys/signVerify.ts +13 -2
  174. package/src/keys/utils.ts +78 -5
  175. package/src/random.ts +93 -9
  176. package/src/rsa.ts +8 -0
  177. package/src/slhdsa.ts +146 -0
  178. package/src/specs/keyObjectHandle.nitro.ts +17 -0
  179. package/src/specs/slhDsaKeyPair.nitro.ts +29 -0
  180. package/src/specs/turboshake.nitro.ts +21 -0
  181. package/src/subtle.ts +1191 -360
  182. package/src/utils/conversion.ts +80 -27
  183. package/src/utils/errors.ts +72 -4
  184. package/src/utils/types.ts +80 -15
  185. package/src/utils/validation.ts +70 -1
@@ -0,0 +1,379 @@
1
+ #include <NitroModules/ArrayBuffer.hpp>
2
+ #include <cstring>
3
+ #include <memory>
4
+ #include <optional>
5
+ #include <stdexcept>
6
+ #include <string>
7
+ #include <vector>
8
+
9
+ #include "HybridTurboShake.hpp"
10
+ #include "QuickCryptoUtils.hpp"
11
+
12
+ // TurboSHAKE128/256 and KangarooTwelve KT128/256 (RFC 9861).
13
+ // Implementation adapted from Node.js src/crypto/crypto_turboshake.cc
14
+ // (commit e0cab9dcf75), which itself adapts the OpenSSL keccak1600.c
15
+ // reference variant. OpenSSL does not yet expose these algorithms via EVP,
16
+ // so the Keccak-p[1600, n_r=12] permutation and sponge are provided here.
17
+
18
+ namespace margelo::nitro::crypto {
19
+
20
+ namespace {
21
+
22
+ // ---------------------------------------------------------------------------
23
+ // Keccak-p[1600, n_r=12] permutation (FIPS 202 §3.3-3.4, RFC 9861 §2.2).
24
+ // ---------------------------------------------------------------------------
25
+
26
+ inline uint64_t ROL64(uint64_t val, int offset) {
27
+ if (offset == 0)
28
+ return val;
29
+ return (val << offset) | (val >> (64 - offset));
30
+ }
31
+
32
+ inline uint64_t LoadLE64(const uint8_t* src) {
33
+ return static_cast<uint64_t>(src[0]) | (static_cast<uint64_t>(src[1]) << 8) | (static_cast<uint64_t>(src[2]) << 16) |
34
+ (static_cast<uint64_t>(src[3]) << 24) | (static_cast<uint64_t>(src[4]) << 32) | (static_cast<uint64_t>(src[5]) << 40) |
35
+ (static_cast<uint64_t>(src[6]) << 48) | (static_cast<uint64_t>(src[7]) << 56);
36
+ }
37
+
38
+ inline void StoreLE64(uint8_t* dst, uint64_t val) {
39
+ dst[0] = static_cast<uint8_t>(val);
40
+ dst[1] = static_cast<uint8_t>(val >> 8);
41
+ dst[2] = static_cast<uint8_t>(val >> 16);
42
+ dst[3] = static_cast<uint8_t>(val >> 24);
43
+ dst[4] = static_cast<uint8_t>(val >> 32);
44
+ dst[5] = static_cast<uint8_t>(val >> 40);
45
+ dst[6] = static_cast<uint8_t>(val >> 48);
46
+ dst[7] = static_cast<uint8_t>(val >> 56);
47
+ }
48
+
49
+ constexpr unsigned char kRhoTates[5][5] = {
50
+ {0, 1, 62, 28, 27}, {36, 44, 6, 55, 20}, {3, 10, 43, 25, 39}, {41, 45, 15, 21, 8}, {18, 2, 61, 56, 14},
51
+ };
52
+
53
+ // Round constants for Keccak-f[1600]; TurboSHAKE uses indices 12..23.
54
+ constexpr uint64_t kIotas[24] = {
55
+ 0x0000000000000001ULL, 0x0000000000008082ULL, 0x800000000000808aULL, 0x8000000080008000ULL, 0x000000000000808bULL,
56
+ 0x0000000080000001ULL, 0x8000000080008081ULL, 0x8000000000008009ULL, 0x000000000000008aULL, 0x0000000000000088ULL,
57
+ 0x0000000080008009ULL, 0x000000008000000aULL, 0x000000008000808bULL, 0x800000000000008bULL, 0x8000000000008089ULL,
58
+ 0x8000000000008003ULL, 0x8000000000008002ULL, 0x8000000000000080ULL, 0x000000000000800aULL, 0x800000008000000aULL,
59
+ 0x8000000080008081ULL, 0x8000000000008080ULL, 0x0000000080000001ULL, 0x8000000080008008ULL,
60
+ };
61
+
62
+ void KeccakP1600_12(uint64_t A[5][5]) {
63
+ for (size_t round = 12; round < 24; round++) {
64
+ uint64_t C[5];
65
+ for (size_t x = 0; x < 5; x++) {
66
+ C[x] = A[0][x] ^ A[1][x] ^ A[2][x] ^ A[3][x] ^ A[4][x];
67
+ }
68
+ uint64_t D[5];
69
+ for (size_t x = 0; x < 5; x++) {
70
+ D[x] = C[(x + 4) % 5] ^ ROL64(C[(x + 1) % 5], 1);
71
+ }
72
+ for (size_t y = 0; y < 5; y++) {
73
+ for (size_t x = 0; x < 5; x++) {
74
+ A[y][x] ^= D[x];
75
+ }
76
+ }
77
+ for (size_t y = 0; y < 5; y++) {
78
+ for (size_t x = 0; x < 5; x++) {
79
+ A[y][x] = ROL64(A[y][x], kRhoTates[y][x]);
80
+ }
81
+ }
82
+ uint64_t T[5][5];
83
+ memcpy(T, A, sizeof(T));
84
+ for (size_t y = 0; y < 5; y++) {
85
+ for (size_t x = 0; x < 5; x++) {
86
+ A[y][x] = T[x][(3 * y + x) % 5];
87
+ }
88
+ }
89
+ for (size_t y = 0; y < 5; y++) {
90
+ uint64_t row[5];
91
+ for (size_t x = 0; x < 5; x++) {
92
+ row[x] = A[y][x] ^ (~A[y][(x + 1) % 5] & A[y][(x + 2) % 5]);
93
+ }
94
+ memcpy(A[y], row, sizeof(row));
95
+ }
96
+ A[0][0] ^= kIotas[round];
97
+ }
98
+ }
99
+
100
+ // ---------------------------------------------------------------------------
101
+ // TurboSHAKE sponge (RFC 9861 §2.2, App. A.2/A.3).
102
+ // TurboSHAKE128: rate = 168 bytes (capacity 256 bits).
103
+ // TurboSHAKE256: rate = 136 bytes (capacity 512 bits).
104
+ // ---------------------------------------------------------------------------
105
+
106
+ constexpr size_t kTurboSHAKE128Rate = 168;
107
+ constexpr size_t kTurboSHAKE256Rate = 136;
108
+
109
+ void TurboSHAKE(const uint8_t* input, size_t input_len, size_t rate, uint8_t domain_sep, uint8_t* output, size_t output_len) {
110
+ uint64_t A[5][5] = {};
111
+ size_t lane_count = rate / 8;
112
+
113
+ size_t offset = 0;
114
+ while (offset + rate <= input_len) {
115
+ for (size_t i = 0; i < lane_count; i++) {
116
+ A[i / 5][i % 5] ^= LoadLE64(input + offset + i * 8);
117
+ }
118
+ KeccakP1600_12(A);
119
+ offset += rate;
120
+ }
121
+
122
+ size_t remaining = input_len - offset;
123
+ // Sized for the larger TurboSHAKE128 rate (168); also fits the 136-byte TurboSHAKE256 rate.
124
+ uint8_t pad[kTurboSHAKE128Rate] = {};
125
+ if (remaining > 0) {
126
+ memcpy(pad, input + offset, remaining);
127
+ }
128
+ pad[remaining] ^= domain_sep;
129
+ pad[rate - 1] ^= 0x80;
130
+
131
+ for (size_t i = 0; i < lane_count; i++) {
132
+ A[i / 5][i % 5] ^= LoadLE64(pad + i * 8);
133
+ }
134
+ KeccakP1600_12(A);
135
+
136
+ size_t out_offset = 0;
137
+ while (out_offset < output_len) {
138
+ size_t block = output_len - out_offset;
139
+ if (block > rate)
140
+ block = rate;
141
+ size_t full_lanes = block / 8;
142
+ for (size_t i = 0; i < full_lanes; i++) {
143
+ StoreLE64(output + out_offset + i * 8, A[i / 5][i % 5]);
144
+ }
145
+ size_t rem = block % 8;
146
+ if (rem > 0) {
147
+ uint8_t tmp[8];
148
+ StoreLE64(tmp, A[full_lanes / 5][full_lanes % 5]);
149
+ memcpy(output + out_offset + full_lanes * 8, tmp, rem);
150
+ }
151
+ out_offset += block;
152
+ if (out_offset < output_len) {
153
+ KeccakP1600_12(A);
154
+ }
155
+ }
156
+ }
157
+
158
+ void TurboSHAKE128(const uint8_t* input, size_t input_len, uint8_t domain_sep, uint8_t* output, size_t output_len) {
159
+ TurboSHAKE(input, input_len, kTurboSHAKE128Rate, domain_sep, output, output_len);
160
+ }
161
+
162
+ void TurboSHAKE256(const uint8_t* input, size_t input_len, uint8_t domain_sep, uint8_t* output, size_t output_len) {
163
+ TurboSHAKE(input, input_len, kTurboSHAKE256Rate, domain_sep, output, output_len);
164
+ }
165
+
166
+ // ---------------------------------------------------------------------------
167
+ // KangarooTwelve tree hashing (RFC 9861 §3).
168
+ // ---------------------------------------------------------------------------
169
+
170
+ constexpr size_t kChunkSize = 8192;
171
+
172
+ // length_encode(x) per RFC 9861 §3.3.
173
+ std::vector<uint8_t> LengthEncode(size_t x) {
174
+ if (x == 0) {
175
+ return {0x00};
176
+ }
177
+ std::vector<uint8_t> result;
178
+ size_t val = x;
179
+ while (val > 0) {
180
+ result.push_back(static_cast<uint8_t>(val & 0xFF));
181
+ val >>= 8;
182
+ }
183
+ size_t n = result.size();
184
+ for (size_t i = 0; i < n / 2; i++) {
185
+ std::swap(result[i], result[n - 1 - i]);
186
+ }
187
+ result.push_back(static_cast<uint8_t>(n));
188
+ return result;
189
+ }
190
+
191
+ using TurboSHAKEFn = void (*)(const uint8_t* input, size_t input_len, uint8_t domain_sep, uint8_t* output, size_t output_len);
192
+
193
+ void KangarooTwelve(const uint8_t* message, size_t msg_len, const uint8_t* customization, size_t custom_len, uint8_t* output,
194
+ size_t output_len, TurboSHAKEFn turboshake, size_t cv_len) {
195
+ auto len_enc = LengthEncode(custom_len);
196
+ size_t s_len = msg_len + custom_len + len_enc.size();
197
+
198
+ // Short message: |S| <= 8192.
199
+ if (s_len <= kChunkSize) {
200
+ std::vector<uint8_t> s(s_len);
201
+ size_t pos = 0;
202
+ if (msg_len > 0) {
203
+ memcpy(s.data() + pos, message, msg_len);
204
+ pos += msg_len;
205
+ }
206
+ if (custom_len > 0) {
207
+ memcpy(s.data() + pos, customization, custom_len);
208
+ pos += custom_len;
209
+ }
210
+ memcpy(s.data() + pos, len_enc.data(), len_enc.size());
211
+
212
+ turboshake(s.data(), s_len, 0x07, output, output_len);
213
+ return;
214
+ }
215
+
216
+ // S is virtual: M || C || length_encode(|C|). Read on demand.
217
+ auto read_s = [&](size_t s_offset, uint8_t* buf, size_t len) {
218
+ size_t copied = 0;
219
+ if (s_offset < msg_len && copied < len) {
220
+ size_t avail = msg_len - s_offset;
221
+ size_t to_copy = avail < (len - copied) ? avail : (len - copied);
222
+ memcpy(buf + copied, message + s_offset, to_copy);
223
+ copied += to_copy;
224
+ s_offset += to_copy;
225
+ }
226
+ size_t custom_start = msg_len;
227
+ if (s_offset < custom_start + custom_len && copied < len) {
228
+ size_t off_in_custom = s_offset - custom_start;
229
+ size_t avail = custom_len - off_in_custom;
230
+ size_t to_copy = avail < (len - copied) ? avail : (len - copied);
231
+ memcpy(buf + copied, customization + off_in_custom, to_copy);
232
+ copied += to_copy;
233
+ s_offset += to_copy;
234
+ }
235
+ size_t le_start = msg_len + custom_len;
236
+ if (s_offset < le_start + len_enc.size() && copied < len) {
237
+ size_t off_in_le = s_offset - le_start;
238
+ size_t avail = len_enc.size() - off_in_le;
239
+ size_t to_copy = avail < (len - copied) ? avail : (len - copied);
240
+ memcpy(buf + copied, len_enc.data() + off_in_le, to_copy);
241
+ copied += to_copy;
242
+ }
243
+ };
244
+
245
+ std::vector<uint8_t> first_chunk(kChunkSize);
246
+ read_s(0, first_chunk.data(), kChunkSize);
247
+
248
+ std::vector<uint8_t> final_node;
249
+ final_node.reserve(kChunkSize + 8 + ((s_len / kChunkSize) * cv_len) + 16);
250
+ final_node.insert(final_node.end(), first_chunk.begin(), first_chunk.end());
251
+ final_node.push_back(0x03);
252
+ final_node.insert(final_node.end(), 7, 0x00);
253
+
254
+ size_t offset = kChunkSize;
255
+ size_t num_blocks = 0;
256
+ std::vector<uint8_t> chunk(kChunkSize);
257
+ std::vector<uint8_t> cv(cv_len);
258
+
259
+ while (offset < s_len) {
260
+ size_t block_size = s_len - offset;
261
+ if (block_size > kChunkSize)
262
+ block_size = kChunkSize;
263
+
264
+ chunk.resize(block_size);
265
+ read_s(offset, chunk.data(), block_size);
266
+
267
+ turboshake(chunk.data(), block_size, 0x0B, cv.data(), cv_len);
268
+ final_node.insert(final_node.end(), cv.begin(), cv.end());
269
+ num_blocks++;
270
+ offset += block_size;
271
+ }
272
+
273
+ auto num_blocks_enc = LengthEncode(num_blocks);
274
+ final_node.insert(final_node.end(), num_blocks_enc.begin(), num_blocks_enc.end());
275
+ final_node.push_back(0xFF);
276
+ final_node.push_back(0xFF);
277
+
278
+ turboshake(final_node.data(), final_node.size(), 0x06, output, output_len);
279
+ }
280
+
281
+ void KT128(const uint8_t* message, size_t msg_len, const uint8_t* customization, size_t custom_len, uint8_t* output, size_t output_len) {
282
+ KangarooTwelve(message, msg_len, customization, custom_len, output, output_len, TurboSHAKE128, 32);
283
+ }
284
+
285
+ void KT256(const uint8_t* message, size_t msg_len, const uint8_t* customization, size_t custom_len, uint8_t* output, size_t output_len) {
286
+ KangarooTwelve(message, msg_len, customization, custom_len, output, output_len, TurboSHAKE256, 64);
287
+ }
288
+
289
+ uint8_t parseDomainSeparation(double value) {
290
+ if (!(value >= 0x01 && value <= 0x7F) || value != static_cast<double>(static_cast<uint8_t>(value))) {
291
+ throw std::runtime_error("TurboSHAKE domainSeparation must be an integer in 0x01..0x7F");
292
+ }
293
+ return static_cast<uint8_t>(value);
294
+ }
295
+
296
+ uint32_t parseOutputLength(double value) {
297
+ if (!(value > 0)) {
298
+ throw std::runtime_error("outputLength must be > 0");
299
+ }
300
+ // 16 MiB upper bound matches HybridHash::setParams to keep memory bounded.
301
+ constexpr double kMaxOutputBytes = 16.0 * 1024.0 * 1024.0;
302
+ if (value > kMaxOutputBytes) {
303
+ throw std::runtime_error("outputLength exceeds maximum allowed size");
304
+ }
305
+ if (value != static_cast<double>(static_cast<uint32_t>(value))) {
306
+ throw std::runtime_error("outputLength must be an integer");
307
+ }
308
+ return static_cast<uint32_t>(value);
309
+ }
310
+
311
+ } // namespace
312
+
313
+ std::shared_ptr<Promise<std::shared_ptr<ArrayBuffer>>> HybridTurboShake::turboShake(TurboShakeVariant variant, double domainSeparation,
314
+ double outputLength,
315
+ const std::shared_ptr<ArrayBuffer>& data) {
316
+ uint8_t ds = parseDomainSeparation(domainSeparation);
317
+ uint32_t outLen = parseOutputLength(outputLength);
318
+
319
+ bool is128 = variant == TurboShakeVariant::TURBOSHAKE128;
320
+
321
+ auto nativeData = ToNativeArrayBuffer(data);
322
+
323
+ return Promise<std::shared_ptr<ArrayBuffer>>::async([is128, ds, outLen, nativeData]() -> std::shared_ptr<ArrayBuffer> {
324
+ auto outBuf = std::make_unique<uint8_t[]>(outLen);
325
+ const uint8_t* in = reinterpret_cast<const uint8_t*>(nativeData->data());
326
+ size_t inLen = nativeData->size();
327
+ if (is128) {
328
+ TurboSHAKE128(in, inLen, ds, outBuf.get(), outLen);
329
+ } else {
330
+ TurboSHAKE256(in, inLen, ds, outBuf.get(), outLen);
331
+ }
332
+ uint8_t* raw = outBuf.get();
333
+ return std::make_shared<NativeArrayBuffer>(outBuf.release(), outLen, [raw]() { delete[] raw; });
334
+ });
335
+ }
336
+
337
+ std::shared_ptr<Promise<std::shared_ptr<ArrayBuffer>>>
338
+ HybridTurboShake::kangarooTwelve(KangarooTwelveVariant variant, double outputLength, const std::shared_ptr<ArrayBuffer>& data,
339
+ const std::optional<std::shared_ptr<ArrayBuffer>>& customization) {
340
+ uint32_t outLen = parseOutputLength(outputLength);
341
+
342
+ bool is128 = variant == KangarooTwelveVariant::KT128;
343
+
344
+ auto nativeData = ToNativeArrayBuffer(data);
345
+ std::optional<std::shared_ptr<ArrayBuffer>> nativeCustom;
346
+ if (customization.has_value()) {
347
+ nativeCustom = ToNativeArrayBuffer(customization.value());
348
+ }
349
+
350
+ return Promise<std::shared_ptr<ArrayBuffer>>::async(
351
+ [is128, outLen, nativeData, nativeCustom = std::move(nativeCustom)]() -> std::shared_ptr<ArrayBuffer> {
352
+ const uint8_t* in = reinterpret_cast<const uint8_t*>(nativeData->data());
353
+ size_t inLen = nativeData->size();
354
+ const uint8_t* custom = nullptr;
355
+ size_t customLen = 0;
356
+ if (nativeCustom.has_value() && nativeCustom.value()->size() > 0) {
357
+ custom = reinterpret_cast<const uint8_t*>(nativeCustom.value()->data());
358
+ customLen = nativeCustom.value()->size();
359
+ }
360
+
361
+ // Mirror Node's overflow guard for s_len = msg + custom + length_encode(|custom|).
362
+ // length_encode produces at most sizeof(size_t) + 1 bytes.
363
+ constexpr size_t kMaxLengthEncodeSize = sizeof(size_t) + 1;
364
+ if (inLen > SIZE_MAX - customLen || inLen + customLen > SIZE_MAX - kMaxLengthEncodeSize) {
365
+ throw std::runtime_error("KangarooTwelve input length overflow");
366
+ }
367
+
368
+ auto outBuf = std::make_unique<uint8_t[]>(outLen);
369
+ if (is128) {
370
+ KT128(in, inLen, custom, customLen, outBuf.get(), outLen);
371
+ } else {
372
+ KT256(in, inLen, custom, customLen, outBuf.get(), outLen);
373
+ }
374
+ uint8_t* raw = outBuf.get();
375
+ return std::make_shared<NativeArrayBuffer>(outBuf.release(), outLen, [raw]() { delete[] raw; });
376
+ });
377
+ }
378
+
379
+ } // namespace margelo::nitro::crypto
@@ -0,0 +1,28 @@
1
+ #pragma once
2
+
3
+ #include <NitroModules/ArrayBuffer.hpp>
4
+ #include <NitroModules/Promise.hpp>
5
+ #include <memory>
6
+ #include <optional>
7
+ #include <string>
8
+
9
+ #include "HybridTurboShakeSpec.hpp"
10
+
11
+ namespace margelo::nitro::crypto {
12
+
13
+ using namespace facebook;
14
+
15
+ class HybridTurboShake : public HybridTurboShakeSpec {
16
+ public:
17
+ HybridTurboShake() : HybridObject(TAG) {}
18
+
19
+ public:
20
+ std::shared_ptr<Promise<std::shared_ptr<ArrayBuffer>>> turboShake(TurboShakeVariant variant, double domainSeparation, double outputLength,
21
+ const std::shared_ptr<ArrayBuffer>& data) override;
22
+
23
+ std::shared_ptr<Promise<std::shared_ptr<ArrayBuffer>>>
24
+ kangarooTwelve(KangarooTwelveVariant variant, double outputLength, const std::shared_ptr<ArrayBuffer>& data,
25
+ const std::optional<std::shared_ptr<ArrayBuffer>>& customization) override;
26
+ };
27
+
28
+ } // namespace margelo::nitro::crypto
@@ -1,6 +1,6 @@
1
1
  #include "HybridUtils.hpp"
2
2
 
3
- #include <NitroModules/JSIConverter+ArrayBuffer.hpp>
3
+ #include <NitroModules/JSIConverter.hpp>
4
4
  #include <bit>
5
5
  #include <cstring>
6
6
  #include <openssl/crypto.h>
@@ -142,11 +142,12 @@ namespace {
142
142
  }
143
143
 
144
144
  size_t offset = result.size();
145
- result.resize(offset + (num * 2)); // This fills the buffer with '\0'
145
+ result.reserve(offset + (num * 2)); // Allocate buffer conservatively
146
146
 
147
- auto* dst = result.data() + offset;
148
147
  if (isAscii) {
149
148
  // Widen ASCII characters from char into char16_t
149
+ result.resize(offset + (num * 2)); // This fills the buffer with '\0'
150
+ auto* dst = result.data() + offset;
150
151
  const auto* asciiSrc = reinterpret_cast<const char*>(data);
151
152
  for (size_t i = 0; i < num; i++, dst += 2) {
152
153
  *dst = asciiSrc[i];
@@ -155,13 +156,16 @@ namespace {
155
156
  return;
156
157
  }
157
158
 
158
- const auto* utf16Src = reinterpret_cast<const char16_t*>(data);
159
159
  if constexpr (kCanDirectCopyUtf16) {
160
160
  // Fast&direct copy path
161
- std::memcpy(dst, utf16Src, num * 2);
161
+ const auto* byteSrc = reinterpret_cast<const uint8_t*>(data);
162
+ result.insert(result.end(), byteSrc, byteSrc + num * 2);
162
163
  return;
163
164
  }
164
165
  // Slow path for unexpected endianness/char16_t size
166
+ result.resize(offset + (num * 2));
167
+ const auto* utf16Src = reinterpret_cast<const char16_t*>(data);
168
+ auto* dst = result.data() + offset;
165
169
  for (size_t i = 0; i < num; i++) {
166
170
  const uint16_t codeUnit = static_cast<uint16_t>(utf16Src[i]);
167
171
  dst[i * 2 + 0] = static_cast<uint8_t>(codeUnit & 0xFFu);
@@ -175,7 +179,7 @@ namespace {
175
179
  throw std::runtime_error("Unsupported encoding: utf16le");
176
180
  }
177
181
 
178
- std::vector<uint8_t> decodeLatin1(const std::string& str) {
182
+ std::vector<uint8_t> decodeLatin1FromUtf8(const std::string& str) {
179
183
  std::vector<uint8_t> result;
180
184
  result.reserve(str.size());
181
185
  size_t i = 0;
@@ -204,6 +208,43 @@ namespace {
204
208
  return result;
205
209
  }
206
210
 
211
+ template <typename JSIString = facebook::jsi::String>
212
+ std::vector<uint8_t> decodeLatin1(facebook::jsi::Runtime& runtime, bool isHermes, const JSIString& str) {
213
+ if constexpr (HasStringGetStringData<JSIString>) {
214
+ if (isHermes) {
215
+ std::vector<uint8_t> result;
216
+ auto chunkCallback = [&result](bool isAscii, const void* data, size_t num) {
217
+ if (num == 0) {
218
+ return;
219
+ }
220
+
221
+ size_t offset = result.size();
222
+ result.reserve(offset + num); // Allocate buffer conservatively
223
+
224
+ if (isAscii) {
225
+ // Fast&direct copy path
226
+ const auto* asciiSrc = reinterpret_cast<const uint8_t*>(data);
227
+ result.insert(result.end(), asciiSrc, asciiSrc + num);
228
+ return;
229
+ }
230
+
231
+ result.resize(offset + num);
232
+ const auto* utf16Src = reinterpret_cast<const char16_t*>(data);
233
+ auto* dst = result.data() + offset;
234
+ for (size_t i = 0; i < num; i++) {
235
+ // Node.js-like behavior
236
+ dst[i] = static_cast<uint8_t>(utf16Src[i] & 0xFFu);
237
+ }
238
+ };
239
+
240
+ str.getStringData(runtime, chunkCallback);
241
+ return result;
242
+ }
243
+ }
244
+ // Slow path for non-Hermes runtime/old RN versions
245
+ return decodeLatin1FromUtf8(str.utf8(runtime));
246
+ }
247
+
207
248
  std::string encodeLatin1(const uint8_t* data, size_t len) {
208
249
  if (len == 0) {
209
250
  return {};
@@ -231,47 +272,68 @@ bool HybridUtils::timingSafeEqual(const std::shared_ptr<ArrayBuffer>& a, const s
231
272
  return CRYPTO_memcmp(a->data(), b->data(), aLen) == 0;
232
273
  }
233
274
 
275
+ bool HybridUtils::isHermesRuntime(facebook::jsi::Runtime& runtime) {
276
+ // Cache assumes runtimes are long-lived and calls happen on the JS thread.
277
+ if (cachedRuntime_ != &runtime) [[unlikely]] {
278
+ cachedRuntime_ = &runtime;
279
+ cachedIsHermesRuntime_ = runtime.global().hasProperty(runtime, "HermesInternal");
280
+ }
281
+ return cachedIsHermesRuntime_;
282
+ }
283
+
234
284
  facebook::jsi::Value HybridUtils::bufferToJsiString(facebook::jsi::Runtime& runtime, const facebook::jsi::Value&,
235
285
  const facebook::jsi::Value* args, size_t argCount) {
236
286
  // Runtime argument check from react-native-nitro-modules/cpp/core/HybridFunction.hpp
237
- if (argCount != 2) [[unlikely]] {
287
+ if (argCount != 4) [[unlikely]] {
238
288
  throw facebook::jsi::JSError(runtime,
239
- "`Utils.bufferToString(...)` expected 2 arguments, but received " + std::to_string(argCount) + "!");
289
+ "`Utils.bufferToString(...)` expected 4 arguments, but received " + std::to_string(argCount) + "!");
240
290
  }
241
291
 
242
292
  // Exception wrapper from react-native-nitro-modules/cpp/core/HybridFunction.hpp
243
293
  try {
244
- // bufferToString(buffer: ArrayBuffer, encoding: string): string; Defined in utils/conversion.ts
294
+ // bufferToString(buffer: ArrayBuffer, encoding: string, start: number, end: number): string;
295
+ // Defined in utils/conversion.ts
245
296
  auto buffer = JSIConverter<std::shared_ptr<ArrayBuffer>>::fromJSI(runtime, args[0]);
246
297
  std::string encoding = JSIConverter<std::string>::fromJSI(runtime, args[1]);
298
+ const size_t bufferSize = buffer->size();
299
+ // `start` and `end` are normalized in the TS code
300
+ // so it's safe to use `static_cast<size_t>` here
301
+ const size_t start = static_cast<size_t>(JSIConverter<double>::fromJSI(runtime, args[2]));
302
+ const size_t end = static_cast<size_t>(JSIConverter<double>::fromJSI(runtime, args[3]));
303
+ if (start > end || end > bufferSize) {
304
+ // This should never happen if called from the TS wrapper
305
+ // Add this check to avoid out of bounds access
306
+ throw std::runtime_error("Invalid start/end value");
307
+ }
308
+ const size_t offset = start;
309
+ const size_t length = end - start;
247
310
 
248
- const auto* data = reinterpret_cast<const uint8_t*>(buffer->data());
249
- size_t len = buffer->size();
311
+ const auto* data = reinterpret_cast<const uint8_t*>(buffer->data() + offset);
250
312
 
251
313
  if (encoding == "hex") {
252
- return facebook::jsi::String::createFromUtf8(runtime, encodeHex(data, len));
314
+ return facebook::jsi::String::createFromAscii(runtime, encodeHex(data, length));
253
315
  }
254
316
  if (encoding == "base64") {
255
- return facebook::jsi::String::createFromUtf8(runtime, encodeBase64(data, len));
317
+ return facebook::jsi::String::createFromAscii(runtime, encodeBase64(data, length));
256
318
  }
257
319
  if (encoding == "base64url") {
258
- return facebook::jsi::String::createFromUtf8(runtime, encodeBase64Url(data, len));
320
+ return facebook::jsi::String::createFromAscii(runtime, encodeBase64Url(data, length));
259
321
  }
260
322
  if (encoding == "utf8" || encoding == "utf-8") {
261
- return facebook::jsi::String::createFromUtf8(runtime, data, len);
323
+ return facebook::jsi::String::createFromUtf8(runtime, data, length);
262
324
  }
263
325
  if (encoding == "latin1" || encoding == "binary") {
264
- return facebook::jsi::String::createFromUtf8(runtime, encodeLatin1(data, len));
326
+ return facebook::jsi::String::createFromUtf8(runtime, encodeLatin1(data, length));
265
327
  }
266
328
  if (encoding == "ascii") {
267
- std::string result(reinterpret_cast<const char*>(data), len);
329
+ std::string result(reinterpret_cast<const char*>(data), length);
268
330
  for (auto& c : result) {
269
331
  c &= 0x7F;
270
332
  }
271
- return facebook::jsi::String::createFromUtf8(runtime, result);
333
+ return facebook::jsi::String::createFromAscii(runtime, result);
272
334
  }
273
335
  if (encoding == "utf16le") {
274
- return createUtf16LeString(runtime, data, len);
336
+ return createUtf16LeString(runtime, data, length);
275
337
  }
276
338
  throw std::runtime_error("Unsupported encoding: " + encoding);
277
339
  } catch (const std::exception& exception) {
@@ -310,7 +372,7 @@ facebook::jsi::Value HybridUtils::jsiStringToBuffer(facebook::jsi::Runtime& runt
310
372
  runtime, ArrayBuffer::copy(reinterpret_cast<const uint8_t*>(utf8Str.data()), utf8Str.size()));
311
373
  }
312
374
  if (encoding == "latin1" || encoding == "binary" || encoding == "ascii") {
313
- auto decoded = decodeLatin1(str.utf8(runtime));
375
+ auto decoded = decodeLatin1(runtime, isHermesRuntime(runtime), str);
314
376
  return JSIConverter<std::shared_ptr<ArrayBuffer>>::toJSI(runtime, ArrayBuffer::move(std::move(decoded)));
315
377
  }
316
378
  if (encoding == "utf16le") {
@@ -329,7 +391,7 @@ facebook::jsi::Value HybridUtils::jsiStringToBuffer(facebook::jsi::Runtime& runt
329
391
  void HybridUtils::loadHybridMethods() {
330
392
  HybridUtilsSpec::loadHybridMethods();
331
393
  registerHybrids(this, [](Prototype& prototype) {
332
- prototype.registerRawHybridMethod("bufferToString", 2, &HybridUtils::bufferToJsiString);
394
+ prototype.registerRawHybridMethod("bufferToString", 4, &HybridUtils::bufferToJsiString);
333
395
  prototype.registerRawHybridMethod("stringToBuffer", 2, &HybridUtils::jsiStringToBuffer);
334
396
  });
335
397
  }
@@ -15,6 +15,10 @@ class HybridUtils : public HybridUtilsSpec {
15
15
  void loadHybridMethods() override;
16
16
 
17
17
  private:
18
+ facebook::jsi::Runtime* cachedRuntime_ = nullptr;
19
+ bool cachedIsHermesRuntime_ = false;
20
+
21
+ bool isHermesRuntime(facebook::jsi::Runtime& runtime);
18
22
  facebook::jsi::Value bufferToJsiString(facebook::jsi::Runtime& runtime, const facebook::jsi::Value& thisArg,
19
23
  const facebook::jsi::Value* args, size_t argCount);
20
24
  facebook::jsi::Value jsiStringToBuffer(facebook::jsi::Runtime& runtime, const facebook::jsi::Value& thisArg,
@@ -176,14 +176,13 @@ See [`c/README.md`](c/README.md).
176
176
 
177
177
  ### Other implementations
178
178
 
179
- We post links to third-party bindings and implementations on the
180
- [@BLAKE3team Twitter account](https://twitter.com/BLAKE3team) whenever
181
- we hear about them. Some highlights include [an optimized Go
179
+ There are too many implementations out there for us to keep track of,
180
+ but some highlights include [an optimized Go
182
181
  implementation](https://github.com/zeebo/blake3), [Wasm bindings for
183
182
  Node.js and browsers](https://github.com/connor4312/blake3), [binary
184
183
  wheels for Python](https://github.com/oconnor663/blake3-py), [.NET
185
- bindings](https://github.com/xoofx/Blake3.NET), and [JNI
186
- bindings](https://github.com/sken77/BLAKE3jni).
184
+ bindings](https://github.com/xoofx/Blake3.NET), and [a pure Java
185
+ implementation](https://commons.apache.org/proper/commons-codec/apidocs/org/apache/commons/codec/digest/Blake3.html).
187
186
 
188
187
  ## Contributing
189
188
 
@@ -202,7 +201,7 @@ Alternatively, it is licensed under any of the following:
202
201
 
203
202
  * [Bazel](https://github.com/bazelbuild/bazel/releases/tag/6.4.0)
204
203
  * [Cargo](https://github.com/rust-lang/cargo/pull/14137)
205
- * [Ccache](https://github.com/ccache/ccache/pull/519)
204
+ * [Ccache](https://ccache.dev/releasenotes.html#_ccache_4_0)
206
205
  * [Chia](https://github.com/Chia-Network/chia-blockchain/blob/main/CHANGELOG.md#10beta8-aka-beta-18---2020-07-16)
207
206
  * [Clickhouse](https://github.com/ClickHouse/ClickHouse/blob/master/rust/chcache/Cargo.toml#L7)
208
207
  * [Farcaster](https://www.farcaster.xyz/)
@@ -220,7 +219,7 @@ Alternatively, it is licensed under any of the following:
220
219
 
221
220
  ## Miscellany
222
221
 
223
- - [@veorq] and [@oconnor663] did [an interview with Cryptography FM](https://www.cryptography.fm/3).
222
+ - [@veorq] and [@oconnor663] did [an interview with Cryptography FM](https://cryptography.fireside.fm/3).
224
223
  - [@oconnor663] did [an interview with Saito](https://www.youtube.com/watch?v=cJkmIt7yN_E).
225
224
 
226
225
  [@oconnor663]: https://github.com/oconnor663
@@ -1,6 +1,7 @@
1
1
  #include <assert.h>
2
2
  #include <stdbool.h>
3
3
  #include <string.h>
4
+ #include <stdint.h>
4
5
 
5
6
  #include "blake3.h"
6
7
  #include "blake3_impl.h"
@@ -303,8 +304,8 @@ size_t blake3_compress_subtree_wide(const uint8_t *input, size_t input_len,
303
304
  uint8_t *right_cvs = &cv_array[degree * BLAKE3_OUT_LEN];
304
305
 
305
306
  // Recurse!
306
- size_t left_n = -1;
307
- size_t right_n = -1;
307
+ size_t left_n = SIZE_MAX;
308
+ size_t right_n = SIZE_MAX;
308
309
 
309
310
  #if defined(BLAKE3_USE_TBB)
310
311
  blake3_compress_subtree_wide_join_tbb(