visaljs 0.0.9 → 0.0.11

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 (232) hide show
  1. package/README.md +12 -4
  2. package/THIRD_PARTY_LICENSES +31 -1
  3. package/cli.js +3745 -890
  4. package/node/include/js_node.h +12 -1
  5. package/node/prelude/_http_common.js +29 -0
  6. package/node/prelude/async_hooks.js +50 -11
  7. package/node/prelude/child_process.js +31 -4
  8. package/node/prelude/console.js +311 -143
  9. package/node/prelude/crypto.js +6 -4
  10. package/node/prelude/dgram.js +1169 -0
  11. package/node/prelude/diagnostics_channel.js +3 -1
  12. package/node/prelude/dns.js +250 -45
  13. package/node/prelude/dns_promises.js +8 -2
  14. package/node/prelude/fs_promises.js +9 -1
  15. package/node/prelude/http.js +405 -94
  16. package/node/prelude/https.js +13 -4
  17. package/node/prelude/internal/console_class.js +7 -0
  18. package/node/prelude/internal/console_global.js +25 -0
  19. package/node/prelude/internal/crypto/key_encryption.js +217 -0
  20. package/node/prelude/internal/crypto/keys.js +161 -26
  21. package/node/prelude/internal/crypto/random.js +24 -0
  22. package/node/prelude/internal/crypto/util.js +2 -1
  23. package/node/prelude/internal/crypto_x509.js +161 -0
  24. package/node/prelude/internal/dns_classes.js +6 -0
  25. package/node/prelude/internal/dns_resolver.js +371 -0
  26. package/node/prelude/internal/errors.js +9 -0
  27. package/node/prelude/internal/events_on.js +82 -37
  28. package/node/prelude/internal/features.js +8 -4
  29. package/node/prelude/internal/fs_filehandle.js +10 -4
  30. package/node/prelude/internal/http_chars.ts +22 -0
  31. package/node/prelude/internal/perf_histogram.js +1 -1
  32. package/node/prelude/internal/process_more.js +32 -17
  33. package/node/prelude/internal/process_signals.js +8 -2
  34. package/node/prelude/internal/punycode.ts +163 -0
  35. package/node/prelude/internal/readline.js +648 -274
  36. package/node/prelude/internal/readline_editor.js +1240 -0
  37. package/node/prelude/internal/string_width.js +43 -0
  38. package/node/prelude/internal/tls/common.js +145 -33
  39. package/node/prelude/internal/tls/wrap.js +182 -49
  40. package/node/prelude/internal/tls/x509.js +4 -1
  41. package/node/prelude/internal/udp.js +177 -0
  42. package/node/prelude/net.js +30 -14
  43. package/node/prelude/os.js +24 -6
  44. package/node/prelude/perf_hooks.js +37 -2
  45. package/node/prelude/punycode.js +2 -130
  46. package/node/prelude/readline.js +113 -4
  47. package/node/prelude/readline_promises.js +56 -32
  48. package/node/prelude/tls.js +21 -4
  49. package/node/prelude/util.js +3 -16
  50. package/node/src/host_dns.c +623 -0
  51. package/node/src/host_http.c +12 -0
  52. package/node/src/host_net.c +46 -0
  53. package/node/src/host_os.c +1 -6
  54. package/node/src/host_sys.c +79 -0
  55. package/node/src/host_udp.c +418 -0
  56. package/node/third_party/c-ares/LICENSE.md +24 -0
  57. package/node/third_party/c-ares/config/darwin/ares_config.h +573 -0
  58. package/node/third_party/c-ares/config/freebsd/ares_config.h +573 -0
  59. package/node/third_party/c-ares/config/linux/ares_config.h +573 -0
  60. package/node/third_party/c-ares/config/openbsd/ares_config.h +583 -0
  61. package/node/third_party/c-ares/include/ares.h +1237 -0
  62. package/node/third_party/c-ares/include/ares_build.h +202 -0
  63. package/node/third_party/c-ares/include/ares_dns.h +127 -0
  64. package/node/third_party/c-ares/include/ares_dns_record.h +1119 -0
  65. package/node/third_party/c-ares/include/ares_nameser.h +510 -0
  66. package/node/third_party/c-ares/include/ares_version.h +47 -0
  67. package/node/third_party/c-ares/src/lib/ares_addrinfo2hostent.c +305 -0
  68. package/node/third_party/c-ares/src/lib/ares_addrinfo_localhost.c +245 -0
  69. package/node/third_party/c-ares/src/lib/ares_android.c +496 -0
  70. package/node/third_party/c-ares/src/lib/ares_android.h +38 -0
  71. package/node/third_party/c-ares/src/lib/ares_cancel.c +87 -0
  72. package/node/third_party/c-ares/src/lib/ares_close_sockets.c +137 -0
  73. package/node/third_party/c-ares/src/lib/ares_conn.c +511 -0
  74. package/node/third_party/c-ares/src/lib/ares_conn.h +202 -0
  75. package/node/third_party/c-ares/src/lib/ares_cookie.c +461 -0
  76. package/node/third_party/c-ares/src/lib/ares_data.c +168 -0
  77. package/node/third_party/c-ares/src/lib/ares_data.h +92 -0
  78. package/node/third_party/c-ares/src/lib/ares_destroy.c +159 -0
  79. package/node/third_party/c-ares/src/lib/ares_free_hostent.c +54 -0
  80. package/node/third_party/c-ares/src/lib/ares_free_string.c +33 -0
  81. package/node/third_party/c-ares/src/lib/ares_freeaddrinfo.c +67 -0
  82. package/node/third_party/c-ares/src/lib/ares_getaddrinfo.c +806 -0
  83. package/node/third_party/c-ares/src/lib/ares_getenv.c +38 -0
  84. package/node/third_party/c-ares/src/lib/ares_getenv.h +34 -0
  85. package/node/third_party/c-ares/src/lib/ares_gethostbyaddr.c +229 -0
  86. package/node/third_party/c-ares/src/lib/ares_gethostbyname.c +341 -0
  87. package/node/third_party/c-ares/src/lib/ares_getnameinfo.c +437 -0
  88. package/node/third_party/c-ares/src/lib/ares_hosts_file.c +960 -0
  89. package/node/third_party/c-ares/src/lib/ares_inet_net_pton.h +31 -0
  90. package/node/third_party/c-ares/src/lib/ares_init.c +631 -0
  91. package/node/third_party/c-ares/src/lib/ares_ipv6.h +115 -0
  92. package/node/third_party/c-ares/src/lib/ares_library_init.c +190 -0
  93. package/node/third_party/c-ares/src/lib/ares_metrics.c +261 -0
  94. package/node/third_party/c-ares/src/lib/ares_options.c +492 -0
  95. package/node/third_party/c-ares/src/lib/ares_parse_into_addrinfo.c +179 -0
  96. package/node/third_party/c-ares/src/lib/ares_private.h +648 -0
  97. package/node/third_party/c-ares/src/lib/ares_process.c +1649 -0
  98. package/node/third_party/c-ares/src/lib/ares_qcache.c +442 -0
  99. package/node/third_party/c-ares/src/lib/ares_query.c +151 -0
  100. package/node/third_party/c-ares/src/lib/ares_search.c +623 -0
  101. package/node/third_party/c-ares/src/lib/ares_send.c +300 -0
  102. package/node/third_party/c-ares/src/lib/ares_set_socket_functions.c +590 -0
  103. package/node/third_party/c-ares/src/lib/ares_setup.h +224 -0
  104. package/node/third_party/c-ares/src/lib/ares_socket.c +427 -0
  105. package/node/third_party/c-ares/src/lib/ares_socket.h +163 -0
  106. package/node/third_party/c-ares/src/lib/ares_sortaddrinfo.c +447 -0
  107. package/node/third_party/c-ares/src/lib/ares_strerror.c +91 -0
  108. package/node/third_party/c-ares/src/lib/ares_sysconfig.c +642 -0
  109. package/node/third_party/c-ares/src/lib/ares_sysconfig_files.c +851 -0
  110. package/node/third_party/c-ares/src/lib/ares_sysconfig_mac.c +373 -0
  111. package/node/third_party/c-ares/src/lib/ares_sysconfig_win.c +673 -0
  112. package/node/third_party/c-ares/src/lib/ares_timeout.c +162 -0
  113. package/node/third_party/c-ares/src/lib/ares_update_servers.c +1376 -0
  114. package/node/third_party/c-ares/src/lib/ares_version.c +36 -0
  115. package/node/third_party/c-ares/src/lib/config-dos.h +114 -0
  116. package/node/third_party/c-ares/src/lib/config-win32.h +396 -0
  117. package/node/third_party/c-ares/src/lib/dsa/ares_array.c +409 -0
  118. package/node/third_party/c-ares/src/lib/dsa/ares_htable.c +451 -0
  119. package/node/third_party/c-ares/src/lib/dsa/ares_htable.h +174 -0
  120. package/node/third_party/c-ares/src/lib/dsa/ares_htable_asvp.c +224 -0
  121. package/node/third_party/c-ares/src/lib/dsa/ares_htable_dict.c +230 -0
  122. package/node/third_party/c-ares/src/lib/dsa/ares_htable_strvp.c +210 -0
  123. package/node/third_party/c-ares/src/lib/dsa/ares_htable_szvp.c +188 -0
  124. package/node/third_party/c-ares/src/lib/dsa/ares_htable_vpstr.c +186 -0
  125. package/node/third_party/c-ares/src/lib/dsa/ares_htable_vpvp.c +194 -0
  126. package/node/third_party/c-ares/src/lib/dsa/ares_llist.c +385 -0
  127. package/node/third_party/c-ares/src/lib/dsa/ares_slist.c +479 -0
  128. package/node/third_party/c-ares/src/lib/dsa/ares_slist.h +207 -0
  129. package/node/third_party/c-ares/src/lib/event/ares_event.h +191 -0
  130. package/node/third_party/c-ares/src/lib/event/ares_event_configchg.c +746 -0
  131. package/node/third_party/c-ares/src/lib/event/ares_event_epoll.c +192 -0
  132. package/node/third_party/c-ares/src/lib/event/ares_event_kqueue.c +248 -0
  133. package/node/third_party/c-ares/src/lib/event/ares_event_poll.c +140 -0
  134. package/node/third_party/c-ares/src/lib/event/ares_event_select.c +159 -0
  135. package/node/third_party/c-ares/src/lib/event/ares_event_thread.c +575 -0
  136. package/node/third_party/c-ares/src/lib/event/ares_event_wake_pipe.c +172 -0
  137. package/node/third_party/c-ares/src/lib/event/ares_event_win32.c +981 -0
  138. package/node/third_party/c-ares/src/lib/event/ares_event_win32.h +161 -0
  139. package/node/third_party/c-ares/src/lib/include/ares_array.h +276 -0
  140. package/node/third_party/c-ares/src/lib/include/ares_buf.h +732 -0
  141. package/node/third_party/c-ares/src/lib/include/ares_htable_asvp.h +130 -0
  142. package/node/third_party/c-ares/src/lib/include/ares_htable_dict.h +123 -0
  143. package/node/third_party/c-ares/src/lib/include/ares_htable_strvp.h +130 -0
  144. package/node/third_party/c-ares/src/lib/include/ares_htable_szvp.h +118 -0
  145. package/node/third_party/c-ares/src/lib/include/ares_htable_vpstr.h +111 -0
  146. package/node/third_party/c-ares/src/lib/include/ares_htable_vpvp.h +128 -0
  147. package/node/third_party/c-ares/src/lib/include/ares_llist.h +239 -0
  148. package/node/third_party/c-ares/src/lib/include/ares_mem.h +41 -0
  149. package/node/third_party/c-ares/src/lib/include/ares_str.h +261 -0
  150. package/node/third_party/c-ares/src/lib/inet_net_pton.c +451 -0
  151. package/node/third_party/c-ares/src/lib/inet_ntop.c +219 -0
  152. package/node/third_party/c-ares/src/lib/legacy/ares_create_query.c +78 -0
  153. package/node/third_party/c-ares/src/lib/legacy/ares_expand_name.c +99 -0
  154. package/node/third_party/c-ares/src/lib/legacy/ares_expand_string.c +107 -0
  155. package/node/third_party/c-ares/src/lib/legacy/ares_fds.c +80 -0
  156. package/node/third_party/c-ares/src/lib/legacy/ares_getsock.c +85 -0
  157. package/node/third_party/c-ares/src/lib/legacy/ares_parse_a_reply.c +107 -0
  158. package/node/third_party/c-ares/src/lib/legacy/ares_parse_aaaa_reply.c +109 -0
  159. package/node/third_party/c-ares/src/lib/legacy/ares_parse_caa_reply.c +137 -0
  160. package/node/third_party/c-ares/src/lib/legacy/ares_parse_mx_reply.c +110 -0
  161. package/node/third_party/c-ares/src/lib/legacy/ares_parse_naptr_reply.c +132 -0
  162. package/node/third_party/c-ares/src/lib/legacy/ares_parse_ns_reply.c +154 -0
  163. package/node/third_party/c-ares/src/lib/legacy/ares_parse_ptr_reply.c +213 -0
  164. package/node/third_party/c-ares/src/lib/legacy/ares_parse_soa_reply.c +115 -0
  165. package/node/third_party/c-ares/src/lib/legacy/ares_parse_srv_reply.c +114 -0
  166. package/node/third_party/c-ares/src/lib/legacy/ares_parse_txt_reply.c +144 -0
  167. package/node/third_party/c-ares/src/lib/legacy/ares_parse_uri_reply.c +113 -0
  168. package/node/third_party/c-ares/src/lib/record/ares_dns_mapping.c +982 -0
  169. package/node/third_party/c-ares/src/lib/record/ares_dns_multistring.c +309 -0
  170. package/node/third_party/c-ares/src/lib/record/ares_dns_multistring.h +72 -0
  171. package/node/third_party/c-ares/src/lib/record/ares_dns_name.c +736 -0
  172. package/node/third_party/c-ares/src/lib/record/ares_dns_parse.c +1360 -0
  173. package/node/third_party/c-ares/src/lib/record/ares_dns_private.h +273 -0
  174. package/node/third_party/c-ares/src/lib/record/ares_dns_record.c +1701 -0
  175. package/node/third_party/c-ares/src/lib/record/ares_dns_write.c +1229 -0
  176. package/node/third_party/c-ares/src/lib/str/ares_buf.c +1510 -0
  177. package/node/third_party/c-ares/src/lib/str/ares_str.c +562 -0
  178. package/node/third_party/c-ares/src/lib/str/ares_strsplit.c +90 -0
  179. package/node/third_party/c-ares/src/lib/str/ares_strsplit.h +51 -0
  180. package/node/third_party/c-ares/src/lib/thirdparty/apple/dnsinfo.h +122 -0
  181. package/node/third_party/c-ares/src/lib/util/ares_iface_ips.c +640 -0
  182. package/node/third_party/c-ares/src/lib/util/ares_iface_ips.h +140 -0
  183. package/node/third_party/c-ares/src/lib/util/ares_math.c +167 -0
  184. package/node/third_party/c-ares/src/lib/util/ares_math.h +58 -0
  185. package/node/third_party/c-ares/src/lib/util/ares_rand.c +389 -0
  186. package/node/third_party/c-ares/src/lib/util/ares_rand.h +36 -0
  187. package/node/third_party/c-ares/src/lib/util/ares_threads.c +613 -0
  188. package/node/third_party/c-ares/src/lib/util/ares_threads.h +60 -0
  189. package/node/third_party/c-ares/src/lib/util/ares_time.h +49 -0
  190. package/node/third_party/c-ares/src/lib/util/ares_timeval.c +95 -0
  191. package/node/third_party/c-ares/src/lib/util/ares_uri.c +1627 -0
  192. package/node/third_party/c-ares/src/lib/util/ares_uri.h +252 -0
  193. package/node/third_party/c-ares/src/lib/windows_port.c +29 -0
  194. package/package.json +1 -1
  195. package/prebuilt/darwin-arm64/cares-a6286c65ca9374a3.a +0 -0
  196. package/prebuilt/darwin-x64/cares-a6286c65ca9374a3.a +0 -0
  197. package/prebuilt/linux-arm64/cares-4ba05ac8e948af8e.a +0 -0
  198. package/prebuilt/linux-arm64-musl/cares-4ba05ac8e948af8e.a +0 -0
  199. package/prebuilt/linux-x64/cares-4ba05ac8e948af8e.a +0 -0
  200. package/prebuilt/linux-x64-musl/cares-4ba05ac8e948af8e.a +0 -0
  201. package/prebuilt/win32-x64/boringssl-ce573858472b7fc9.a +0 -0
  202. package/prebuilt/win32-x64/brotli-20d86a61f30baeb3.a +0 -0
  203. package/prebuilt/win32-x64/cares-49c9ec95162b912d.a +0 -0
  204. package/prebuilt/win32-x64/uv-b11bae65d7fced82.a +0 -0
  205. package/prebuilt/win32-x64/zlib-4ce2317a4c3f5fb5.a +0 -0
  206. package/prebuilt/win32-x64/zstd-015315ac7c784c73.a +0 -0
  207. package/runtime/include/js_buffer.h +185 -0
  208. package/runtime/include/js_builtins.h +58 -0
  209. package/runtime/include/js_crypto.h +11 -0
  210. package/runtime/include/js_exception.h +3 -0
  211. package/runtime/include/js_function.h +1 -1
  212. package/runtime/include/js_gc.h +13 -10
  213. package/runtime/include/js_jobs.h +2 -0
  214. package/runtime/include/js_object.h +79 -3
  215. package/runtime/include/js_ops.h +27 -0
  216. package/runtime/include/js_runtime.h +1 -0
  217. package/runtime/include/js_web.h +0 -3
  218. package/runtime/include/jscontext.h +8 -0
  219. package/runtime/src/builtins.c +17 -4
  220. package/runtime/src/crypto_internal.h +1 -0
  221. package/runtime/src/crypto_subtle.c +66 -7
  222. package/runtime/src/crypto_tls.c +265 -19
  223. package/runtime/src/function.c +37 -10
  224. package/runtime/src/gc.c +76 -93
  225. package/runtime/src/inspect.c +18 -1
  226. package/runtime/src/iterator.c +5 -1
  227. package/runtime/src/jobs.c +37 -22
  228. package/runtime/src/object.c +91 -8
  229. package/runtime/src/os.c +16 -6
  230. package/runtime/src/os.h +2 -0
  231. package/runtime/src/process.c +97 -10
  232. package/runtime/src/typedarray.c +23 -0
@@ -18,10 +18,11 @@ class ServerClass extends TlsServer {
18
18
  options = {};
19
19
  } else if (options === undefined || options === null) {
20
20
  options = {};
21
- } else if (typeof options !== "object") {
21
+ } else if (typeof options !== "object" || Array.isArray(options)) {
22
22
  throw invalidArgType("options", "of type object", options);
23
23
  }
24
- super({ noDelay: true, ALPNProtocols: ["http/1.1"], ...options });
24
+ /* only one of ALPNProtocols and ALPNCallback can be set: the default only when neither is */
25
+ super({ noDelay: true, ALPNProtocols: options.ALPNProtocols || options.ALPNCallback ? undefined : ["http/1.1"], ...options });
25
26
  this._initHttp(options, requestListener, "secureConnection");
26
27
  /* a handshake that fails is the client's error, as a request that does */
27
28
  this.on("tlsClientError", (err, conn) => {
@@ -84,17 +85,25 @@ const nameOptions = ["ca", "cert", "clientCertEngine", "ciphers", "key", "pfx",
84
85
  "maxVersion", "secureProtocol", "crl", "honorCipherOrder", "ecdhCurve", "dhparam", "secureOptions", "sessionIdContext", "sigalgs",
85
86
  "privateKeyIdentifier", "privateKeyEngine"];
86
87
 
88
+ /* the options a value of which, false or 0 too, names a different connection */
89
+ const nameDefined = new Set(["rejectUnauthorized", "honorCipherOrder", "secureOptions"]);
90
+
91
+ function pfxAgentKey(pfx, passphrase) {
92
+ if (!Array.isArray(pfx)) return pfx;
93
+ return pfx.map((value) => `:${value?.buf || value}:${value?.passphrase || passphrase}`).join("");
94
+ }
95
+
87
96
  Agent.prototype.getName = function getName(options = {}) {
88
97
  let name = HttpAgent.prototype.getName.call(this, options);
89
98
  for (const key of nameOptions) {
90
99
  name += ":";
91
100
  const value = options[key];
92
- if (key === "rejectUnauthorized") {
101
+ if (nameDefined.has(key)) {
93
102
  if (value !== undefined) name += value;
94
103
  } else if (key === "servername") {
95
104
  if (value && value !== options.host) name += value;
96
105
  } else if (value) {
97
- name += value;
106
+ name += key === "sigalgs" ? JSON.stringify(value) : key === "pfx" ? pfxAgentKey(value, options.passphrase) : value;
98
107
  }
99
108
  }
100
109
  return name;
@@ -0,0 +1,7 @@
1
+ // console.Console on the global console, as node:console puts it there: a feature module, linked
2
+ // into programs that name Console without importing node:console.
3
+ import { Console } from "../console.js";
4
+
5
+ if (globalThis.console.Console === undefined) {
6
+ Object.defineProperty(globalThis.console, "Console", { value: Console, writable: true, configurable: true });
7
+ }
@@ -0,0 +1,25 @@
1
+ // The global console as Node's (lib/internal/console/global.js): Console's methods bound to an
2
+ // instance writing to process.stdout and process.stderr, looked up when first written to, and
3
+ // its _stdout and _stderr settable. A feature module (implicitModules in node/index.ts): linked
4
+ // into a program that replaces a stdio stream's write, which the runtime's console, writing to
5
+ // the fds, wouldn't go through. The runtime's console object stays the global (code may hold it).
6
+ // Ported from Node.js (Copyright Node.js contributors, MIT): node/third_party/nodejs/LICENSE.
7
+ import { Console, kBindProperties, kBindStreamsLazy } from "../console.js";
8
+
9
+ const global = globalThis.console;
10
+ const instance = Object.create(Console.prototype);
11
+ instance[kBindStreamsLazy](globalThis.process);
12
+ instance[kBindProperties](true, "auto");
13
+
14
+ for (const key of Object.keys(Console.prototype)) {
15
+ const method = Console.prototype[key].bind(instance);
16
+ Object.defineProperty(method, "name", { value: key });
17
+ Object.defineProperty(global, key, { value: method, writable: true, enumerable: true, configurable: true });
18
+ }
19
+ for (const name of ["_stdout", "_stderr"]) {
20
+ Object.defineProperty(global, name, {
21
+ get: () => instance[name],
22
+ set: (stream) => { instance[name] = stream; },
23
+ configurable: true,
24
+ });
25
+ }
@@ -0,0 +1,217 @@
1
+ // Encrypted private keys, as OpenSSL writes and reads them for Node: PKCS#8's
2
+ // EncryptedPrivateKeyInfo (PBES2, RFC 8018: PBKDF2 and a CBC cipher; "ENCRYPTED PRIVATE KEY" in
3
+ // PEM), and the PEM headers of a PKCS#1 or SEC1 key (Proc-Type, DEK-Info; the key from OpenSSL's
4
+ // EVP_BytesToKey with MD5). The ciphers and digests are the runtime's; the DER here is only these
5
+ // structures'.
6
+ import {
7
+ cryptoCipherFinal, cryptoCipherInfo, cryptoCipherNew, cryptoCipherUpdate, cryptoDigestFinal, cryptoDigestUpdate, cryptoHashNew,
8
+ cryptoPbkdf2, cryptoRandomFill,
9
+ } from "visaljs:host";
10
+ import { codeError, notSupported } from "../errors.js";
11
+
12
+ /* OpenSSL 3's choices for PEM_write_bio_PKCS8PrivateKey with a cipher */
13
+ const PBKDF2_ITERATIONS = 2048;
14
+ const SALT_LENGTH = 16;
15
+ /* the buffer OpenSSL's PEM password callback reads into */
16
+ const PEM_PASSPHRASE_MAX = 1024;
17
+
18
+ /* object identifiers' contents, in hex */
19
+ const OID_PBES2 = "2a864886f70d01050d";
20
+ const OID_PBKDF2 = "2a864886f70d01050c";
21
+ const PRFS = {
22
+ "2a864886f70d0207": "sha1", "2a864886f70d0208": "sha224", "2a864886f70d0209": "sha256", "2a864886f70d020a": "sha384",
23
+ "2a864886f70d020b": "sha512",
24
+ };
25
+ const OID_HMAC_SHA256 = "2a864886f70d0209";
26
+ const CIPHERS = {
27
+ "608648016503040102": "aes-128-cbc", "608648016503040116": "aes-192-cbc", "60864801650304012a": "aes-256-cbc",
28
+ "2a864886f70d0307": "des-ede3-cbc", "2b0e030207": "des-cbc",
29
+ };
30
+ const CIPHER_OIDS = Object.fromEntries(Object.entries(CIPHERS).map(([oid, name]) => [name, oid]));
31
+
32
+ export const missingPassphrase = () => codeError(TypeError, "ERR_MISSING_PASSPHRASE", "Passphrase required for encrypted key");
33
+ /* OpenSSL 3's PEM reader without a passphrase: its password callback gives up */
34
+ const pemCancelled = () => codeError(Error, "ERR_OSSL_CRYPTO_INTERRUPTED_OR_CANCELLED", "error:07880109:common libcrypto routines::interrupted or cancelled");
35
+ const badDecrypt = () => codeError(Error, "ERR_OSSL_BAD_DECRYPT", "error:1C800064:Provider routines::bad decrypt");
36
+ const decodeError = () => codeError(Error, "ERR_OSSL_ASN1_WRONG_TAG", "error:0680007B:asn1 encoding routines::wrong tag");
37
+
38
+ /* ---- DER: just the shapes above ---------------------------------------------------------------- */
39
+
40
+ function tlv(tag, body) {
41
+ const n = body.length;
42
+ const len = n < 0x80 ? [n] : n < 0x100 ? [0x81, n] : n < 0x10000 ? [0x82, n >> 8, n & 0xff] : [0x83, n >> 16, (n >> 8) & 0xff, n & 0xff];
43
+ return Buffer.concat([Buffer.from([tag, ...len]), body]);
44
+ }
45
+ const seq = (...items) => tlv(0x30, Buffer.concat(items));
46
+ const oid = (hex) => tlv(0x06, Buffer.from(hex, "hex"));
47
+ const octets = (bytes) => tlv(0x04, bytes);
48
+ function integer(n) {
49
+ const bytes = [];
50
+ do {
51
+ bytes.unshift(n & 0xff);
52
+ n = Math.floor(n / 256);
53
+ } while (n > 0);
54
+ if (bytes[0] & 0x80) bytes.unshift(0);
55
+ return tlv(0x02, Buffer.from(bytes));
56
+ }
57
+
58
+ /** The elements of the TLV at `pos`: { tag, body, end }; a decode error past the input. */
59
+ function read(der, pos) {
60
+ if (pos + 2 > der.length) throw decodeError();
61
+ const tag = der[pos];
62
+ let len = der[pos + 1];
63
+ let start = pos + 2;
64
+ if (len & 0x80) {
65
+ const n = len & 0x7f;
66
+ if (n === 0 || n > 3 || start + n > der.length) throw decodeError();
67
+ len = 0;
68
+ for (let i = 0; i < n; i++) len = len * 256 + der[start + i];
69
+ start += n;
70
+ }
71
+ if (start + len > der.length) throw decodeError();
72
+ return { tag, body: der.subarray(start, start + len), end: start + len };
73
+ }
74
+
75
+ function children(der) {
76
+ const out = [];
77
+ for (let pos = 0; pos < der.length;) {
78
+ const el = read(der, pos);
79
+ out.push(el);
80
+ pos = el.end;
81
+ }
82
+ return out;
83
+ }
84
+
85
+ function expect(el, tag) {
86
+ if (el === undefined || el.tag !== tag) throw decodeError();
87
+ return el.body;
88
+ }
89
+
90
+ /** Whether DER PKCS#8 is an EncryptedPrivateKeyInfo: its first element is an AlgorithmIdentifier
91
+ * (a SEQUENCE), where a PrivateKeyInfo's is its version (an INTEGER). */
92
+ export function isEncryptedPkcs8(der) {
93
+ try {
94
+ const [first] = children(expect(read(der, 0), 0x30));
95
+ return first !== undefined && first.tag === 0x30;
96
+ } catch {
97
+ return false;
98
+ }
99
+ }
100
+
101
+ /* ---- the primitives ---------------------------------------------------------------------------- */
102
+
103
+ function cipherInfo(name) {
104
+ const info = cryptoCipherInfo(name);
105
+ if (info === null) throw codeError(Error, "ERR_CRYPTO_UNKNOWN_CIPHER", "Unknown cipher");
106
+ const [canonical, , , , ivLength, keyLength] = info;
107
+ return { name: canonical, ivLength, keyLength };
108
+ }
109
+
110
+ function runCipher(name, encrypt, key, iv, data) {
111
+ const handle = cryptoCipherNew(name, encrypt, key, iv, -1);
112
+ if (typeof handle === "number") throw notSupported(`The cipher ${name} for a private key`);
113
+ const head = cryptoCipherUpdate(handle, data);
114
+ const tail = cryptoCipherFinal(handle);
115
+ if (typeof head === "number" || typeof tail === "number") throw badDecrypt();
116
+ return Buffer.concat([head, tail]);
117
+ }
118
+
119
+ function pbkdf2(digest, passphrase, salt, iterations, keyLength) {
120
+ const key = cryptoPbkdf2(digest, passphrase, salt, iterations, keyLength);
121
+ if (!key) throw decodeError();
122
+ return key;
123
+ }
124
+
125
+ /* OpenSSL's EVP_BytesToKey with MD5 and one iteration: D(i) = MD5(D(i-1) ‖ passphrase ‖ salt) */
126
+ function bytesToKey(passphrase, salt, keyLength) {
127
+ const parts = [];
128
+ let total = 0;
129
+ let prev = Buffer.alloc(0);
130
+ while (total < keyLength) {
131
+ const md5 = cryptoHashNew("md5", undefined);
132
+ cryptoDigestUpdate(md5, prev);
133
+ cryptoDigestUpdate(md5, passphrase);
134
+ cryptoDigestUpdate(md5, salt);
135
+ prev = cryptoDigestFinal(md5);
136
+ parts.push(prev);
137
+ total += prev.length;
138
+ }
139
+ return Buffer.concat(parts).subarray(0, keyLength);
140
+ }
141
+
142
+ function random(n) {
143
+ const b = Buffer.alloc(n);
144
+ cryptoRandomFill(b, 0, n);
145
+ return b;
146
+ }
147
+
148
+ /* ---- PKCS#8 ------------------------------------------------------------------------------------ */
149
+
150
+ /** A PrivateKeyInfo in an EncryptedPrivateKeyInfo, as OpenSSL 3 writes one. */
151
+ export function encryptPkcs8(der, cipherName, passphrase) {
152
+ const { name, ivLength, keyLength } = cipherInfo(cipherName);
153
+ const cipherOid = CIPHER_OIDS[name];
154
+ if (cipherOid === undefined) throw notSupported(`Encrypting a private key with ${cipherName}`);
155
+ const salt = random(SALT_LENGTH);
156
+ const iv = random(ivLength);
157
+ const key = pbkdf2("sha256", passphrase, salt, PBKDF2_ITERATIONS, keyLength);
158
+ const encrypted = runCipher(name, true, key, iv, der);
159
+ const kdf = seq(oid(OID_PBKDF2), seq(octets(salt), integer(PBKDF2_ITERATIONS), seq(oid(OID_HMAC_SHA256), Buffer.from([5, 0]))));
160
+ return seq(seq(oid(OID_PBES2), seq(kdf, seq(oid(cipherOid), octets(iv)))), octets(encrypted));
161
+ }
162
+
163
+ /** The PrivateKeyInfo an EncryptedPrivateKeyInfo holds (PBES2 with PBKDF2). */
164
+ export function decryptPkcs8(der, passphrase) {
165
+ if (passphrase === undefined) throw missingPassphrase();
166
+ const [algorithm, data] = children(expect(read(der, 0), 0x30));
167
+ const [scheme, params] = children(expect(algorithm, 0x30));
168
+ if (expect(scheme, 0x06).toString("hex") !== OID_PBES2) throw notSupported("A private key encrypted without PBES2");
169
+ const [kdf, enc] = children(expect(params, 0x30));
170
+ const [kdfOid, kdfParams] = children(expect(kdf, 0x30));
171
+ if (expect(kdfOid, 0x06).toString("hex") !== OID_PBKDF2) throw notSupported("A private key encrypted without PBKDF2");
172
+ const [salt, iterations, ...rest] = children(expect(kdfParams, 0x30));
173
+ let digest = "sha1"; /* RFC 8018's default PRF */
174
+ for (const el of rest) {
175
+ if (el.tag !== 0x30) continue; /* keyLength: the cipher's anyway */
176
+ const prf = PRFS[expect(children(el.body)[0], 0x06).toString("hex")];
177
+ if (prf === undefined) throw notSupported("This private key's PBKDF2 digest");
178
+ digest = prf;
179
+ }
180
+ const [cipherOid, iv] = children(expect(enc, 0x30));
181
+ const name = CIPHERS[expect(cipherOid, 0x06).toString("hex")];
182
+ if (name === undefined) throw notSupported("This private key's cipher");
183
+ const count = expect(iterations, 0x02).reduce((n, b) => n * 256 + b, 0);
184
+ const { keyLength } = cipherInfo(name);
185
+ const key = pbkdf2(digest, passphrase, expect(salt, 0x04), count, keyLength);
186
+ return runCipher(name, false, key, expect(iv, 0x04), expect(data, 0x04));
187
+ }
188
+
189
+ /* ---- PEM's own encryption (PKCS#1, SEC1) ------------------------------------------------------- */
190
+
191
+ /** The headers and body of an encrypted PEM key, as OpenSSL's PEM_write_bio_PrivateKey writes them. */
192
+ export function encryptPemBody(der, cipherName, passphrase) {
193
+ const { name, ivLength, keyLength } = cipherInfo(cipherName);
194
+ const iv = random(ivLength);
195
+ const key = bytesToKey(passphrase, iv.subarray(0, 8), keyLength);
196
+ const body = runCipher(name, true, key, iv, der);
197
+ return { headers: `Proc-Type: 4,ENCRYPTED\nDEK-Info: ${name.toUpperCase()},${iv.toString("hex").toUpperCase()}\n\n`, body };
198
+ }
199
+
200
+ /** The DER of an encrypted PEM key: its DEK-Info header names the cipher and the IV. */
201
+ export function decryptPemBody(headers, body, passphrase) {
202
+ if (passphrase === undefined) throw pemCancelled();
203
+ if (passphrase.length > PEM_PASSPHRASE_MAX) throw codeError(Error, "ERR_OSSL_PEM_BAD_PASSWORD_READ", "error:0480006D:PEM routines::bad password read");
204
+ const dek = /^DEK-Info:\s*([A-Za-z0-9-]+),([0-9A-Fa-f]+)\s*$/m.exec(headers);
205
+ if (dek === null) throw codeError(Error, "ERR_OSSL_PEM_BAD_DECRYPT", "error:04800065:PEM routines::bad decrypt");
206
+ const { name, keyLength } = cipherInfo(dek[1].toLowerCase());
207
+ const iv = Buffer.from(dek[2], "hex");
208
+ const key = bytesToKey(passphrase, iv.subarray(0, 8), keyLength);
209
+ return runCipher(name, false, key, iv, body);
210
+ }
211
+
212
+ export function checkPemPassphrase(passphrase) {
213
+ if (passphrase === undefined) throw pemCancelled();
214
+ if (passphrase !== undefined && passphrase.length > PEM_PASSPHRASE_MAX) {
215
+ throw codeError(Error, "ERR_OSSL_PEM_BAD_PASSWORD_READ", "error:0480006D:PEM routines::bad password read");
216
+ }
217
+ }
@@ -4,12 +4,13 @@
4
4
  // here; DER is the runtime's.
5
5
  // Ported from Node.js (Copyright Node.js contributors, MIT): node/third_party/nodejs/LICENSE.
6
6
  import {
7
- cryptoHashInfo, cryptoKeyEquals, cryptoRandomFill, cryptoKeyExport, cryptoKeyFromParts, cryptoKeyGenerate, cryptoKeyHandleOf, cryptoKeyInfo,
7
+ cryptoCipherInfo, cryptoEcPoint, cryptoHashInfo, cryptoKeyEquals, cryptoRandomFill, cryptoKeyExport, cryptoKeyFromParts, cryptoKeyGenerate, cryptoKeyHandleOf, cryptoKeyInfo,
8
8
  cryptoKeyParse, cryptoKeyParts, cryptoKeyPublic, cryptoKeySecret, cryptoKeySecretBytes, cryptoToCryptoKey,
9
9
  } from "visaljs:host";
10
10
  import { codeError, invalidArgType, invalidArgValue, notSupported, outOfRange, validateFunction, validateString } from "../errors.js";
11
11
  import { dhKeyParts, dhParameters } from "./diffiehellman.js";
12
12
  import { kKeyType } from "./key_type.js";
13
+ import { checkPemPassphrase, decryptPemBody, decryptPkcs8, encryptPemBody, encryptPkcs8, isEncryptedPkcs8 } from "./key_encryption.js";
13
14
  import { isAnyArrayBuffer, isArrayBufferView, kHandle, validateNumber, validateObject } from "./util.js";
14
15
 
15
16
  const kAsymmetricKeyType = Symbol("kAsymmetricKeyType");
@@ -130,6 +131,8 @@ export class KeyObject {
130
131
  }
131
132
  }
132
133
 
134
+ Object.defineProperty(KeyObject.prototype, Symbol.toStringTag, { configurable: true, value: "KeyObject" });
135
+
133
136
  class SecretKeyObject extends KeyObject {
134
137
  constructor(handle) {
135
138
  super("secret", handle);
@@ -201,6 +204,7 @@ class PublicKeyObject extends AsymmetricKeyObject {
201
204
 
202
205
  export(options) {
203
206
  if (options && options.format === "jwk") return exportJwk(this[kHandle], false);
207
+ if (options && options.format === "raw-public") return exportRawPublic(this[kHandle], options.type);
204
208
  const { format, type } = parseKeyEncoding(options, this.asymmetricKeyType, true);
205
209
  return exportKey(this[kHandle], format, type, false);
206
210
  }
@@ -212,13 +216,13 @@ class PrivateKeyObject extends AsymmetricKeyObject {
212
216
  }
213
217
 
214
218
  export(options) {
215
- if (options && options.format === "jwk") {
216
- if (options.passphrase !== undefined) throw incompatible("jwk", "does not support encryption");
217
- return exportJwk(this[kHandle], true);
219
+ if (options?.passphrase !== undefined && options.format !== "pem" && options.format !== "der") {
220
+ throw incompatible(options.format, "does not support encryption");
218
221
  }
219
- const { format, type, cipher } = parseKeyEncoding(options, this.asymmetricKeyType, false);
220
- if (cipher != null) throw notSupported("Encrypting a private key");
221
- return exportKey(this[kHandle], format, type, true);
222
+ if (options && options.format === "jwk") return exportJwk(this[kHandle], true);
223
+ if (options && (options.format === "raw-private" || options.format === "raw-seed")) return exportRawPrivate(this[kHandle], options.format);
224
+ const { format, type, cipher, passphrase } = parseKeyEncoding(options, this.asymmetricKeyType, false);
225
+ return exportKey(this[kHandle], format, type, true, cipher, passphrase);
222
226
  }
223
227
  }
224
228
 
@@ -268,31 +272,66 @@ function parseKeyEncoding(enc, keyType, isPublic, objName) {
268
272
  const format = parseKeyFormat(enc.format, isInput ? "pem" : undefined, option("format", objName));
269
273
  const isRequired = (!isInput || format === "der") && format !== "jwk";
270
274
  const type = parseKeyType(enc.type, isRequired, keyType, isPublic, option("type", objName));
271
- let cipher, passphrase;
275
+ let cipher, passphrase, encoding;
272
276
  if (isPublic !== true) {
273
- ({ cipher, passphrase } = enc);
277
+ ({ cipher, passphrase, encoding } = enc);
274
278
  if (!isInput) {
275
279
  if (cipher != null) {
276
280
  if (typeof cipher !== "string") throw invalidArgValue(option("cipher", objName), "is invalid", cipher);
277
281
  if (format === "der" && (type === "pkcs1" || type === "sec1")) throw incompatible(type, "does not support encryption");
278
282
  } else if (passphrase !== undefined) {
279
- throw invalidArgValue(option("cipher", objName), "is invalid", cipher);
283
+ throw invalidArgValue(option("cipher", objName), "is required when a passphrase is specified", cipher);
280
284
  }
281
285
  }
286
+ if ((isInput && passphrase !== undefined && !isStringOrBuffer(passphrase)) || (!isInput && cipher != null && !isStringOrBuffer(passphrase))) {
287
+ throw invalidArgValue(option("passphrase", objName), "is invalid", passphrase);
288
+ }
289
+ /* as Node's: an unknown cipher before any work (a key pair isn't generated for nothing) */
290
+ if (!isInput && cipher != null && cryptoCipherInfo(cipher) === null) throw codeError(Error, "ERR_CRYPTO_UNKNOWN_CIPHER", "Unknown cipher");
282
291
  }
292
+ if (passphrase !== undefined) passphrase = passphraseBytes(passphrase, encoding);
283
293
  return { format, type, cipher, passphrase };
284
294
  }
285
295
 
286
296
  const PEM_LABELS = { [SPKI]: "PUBLIC KEY", [PKCS8]: "PRIVATE KEY", [SEC1]: "EC PRIVATE KEY" };
287
297
 
288
- function exportKey(handle, format, type, isPrivate) {
298
+ /* Node's IsRSAPrivateKey: an RSAPrivateKey begins with its version, a one-byte INTEGER 0 or 1, where
299
+ * an RSAPublicKey begins with its modulus */
300
+ function isRsaPrivateDer(der) {
301
+ if (der.length < 2 || der[0] !== 0x30) return false;
302
+ const lengthBytes = der[1] & 0x80 ? der[1] & 0x7f : 0;
303
+ const i = 2 + lengthBytes;
304
+ return der[i] === 0x02 && der[i + 1] === 1 && (der[i + 2] === 0 || der[i + 2] === 1);
305
+ }
306
+
307
+ /* an ArrayBuffer's or a view's (a DataView too) bytes, not copied */
308
+ const viewBytes = (v) => (isAnyArrayBuffer(v) ? Buffer.from(v) : Buffer.from(v.buffer, v.byteOffset, v.byteLength));
309
+
310
+ /* Node's getArrayBufferOrView: a passphrase's bytes (a string in `encoding`, else UTF-8) */
311
+ function passphraseBytes(passphrase, encoding) {
312
+ if (typeof passphrase === "string") return Buffer.from(passphrase, Buffer.isEncoding(encoding) ? encoding : "utf8");
313
+ return viewBytes(passphrase);
314
+ }
315
+
316
+ const pem = (label, headers, der) => {
317
+ const body = der.toString("base64").replace(/(.{64})/g, "$1\n").replace(/\n$/, "");
318
+ return `-----BEGIN ${label}-----\n${headers}${body}\n-----END ${label}-----\n`;
319
+ };
320
+
321
+ function exportKey(handle, format, type, isPrivate, cipher, passphrase) {
289
322
  const encoding = ENCODING_NAMES[type];
290
- const der = cryptoKeyExport(handle, encoding);
323
+ let der = cryptoKeyExport(handle, encoding);
291
324
  if (typeof der === "number") throw keyError(der);
325
+ const encrypt = cipher != null;
326
+ if (encrypt && encoding === PKCS8) {
327
+ der = encryptPkcs8(der, cipher, passphrase);
328
+ return format === "der" ? der : pem("ENCRYPTED PRIVATE KEY", "", der);
329
+ }
292
330
  if (format === "der") return der;
293
331
  const label = encoding === PKCS1 ? (isPrivate ? "RSA PRIVATE KEY" : "RSA PUBLIC KEY") : PEM_LABELS[encoding];
294
- const body = der.toString("base64").replace(/(.{64})/g, "$1\n").replace(/\n$/, "");
295
- return `-----BEGIN ${label}-----\n${body}\n-----END ${label}-----\n`;
332
+ if (!encrypt) return pem(label, "", der);
333
+ const { headers, body } = encryptPemBody(der, cipher, passphrase);
334
+ return pem(label, headers, body);
296
335
  }
297
336
 
298
337
  /* A PEM's label → the encoding it holds, and whether it is private */
@@ -307,25 +346,101 @@ const PEM_TYPES = {
307
346
 
308
347
  /** A key handle from PEM or DER: the private key when `wantPrivate`, else a public one (a
309
348
  * private key's public part, as createPublicKey takes it). */
310
- function parseKey(data, format, type, wantPrivate) {
349
+ function parseKey(data, format, type, wantPrivate, passphrase) {
311
350
  if (format === "der") {
312
351
  const encoding = ENCODING_NAMES[type];
313
- const isPrivate = encoding === PKCS8 || encoding === SEC1 || (encoding === PKCS1 && wantPrivate);
352
+ const isPrivate = encoding === PKCS8 || encoding === SEC1 || (encoding === PKCS1 && (wantPrivate || isRsaPrivateDer(viewBytes(data))));
353
+ if (encoding === PKCS8) {
354
+ const bytes = viewBytes(data);
355
+ if (isEncryptedPkcs8(bytes)) data = decryptPkcs8(bytes, passphrase);
356
+ }
314
357
  const handle = cryptoKeyParse(data, encoding, isPrivate);
315
358
  if (typeof handle === "number") throw keyError(handle);
316
359
  return wantPrivate || !isPrivate ? handle : cryptoKeyPublic(handle);
317
360
  }
318
- const text = typeof data === "string" ? data : Buffer.from(isAnyArrayBuffer(data) ? new Uint8Array(data) : data).toString("latin1");
319
- const m = /-----BEGIN ([A-Z0-9 ]+)-----\r?\n([\s\S]*?)-----END \1-----/.exec(text);
361
+ const text = typeof data === "string" ? data : viewBytes(data).toString("latin1");
362
+ /* the first block that holds a key, as OpenSSL's PEM reader: an EC PARAMETERS block may come before it */
363
+ const m = [...text.matchAll(/-----BEGIN ([A-Z0-9 ]+)-----\r?\n([\s\S]*?)-----END \1-----/g)]
364
+ .find((b) => b[1] === "ENCRYPTED PRIVATE KEY" || Object.hasOwn(PEM_TYPES, b[1])) ?? null;
320
365
  if (m === null) throw keyError(1);
321
- if (m[1] === "ENCRYPTED PRIVATE KEY" || m[2].includes(":")) throw notSupported("An encrypted private key");
322
- const known = PEM_TYPES[m[1]];
366
+ let label = m[1], body = m[2], der;
367
+ if (label === "ENCRYPTED PRIVATE KEY") {
368
+ checkPemPassphrase(passphrase);
369
+ der = decryptPkcs8(Buffer.from(body, "base64"), passphrase);
370
+ label = "PRIVATE KEY";
371
+ } else if (/^Proc-Type:\s*4,ENCRYPTED/m.test(body)) {
372
+ const split = body.search(/\r?\n\r?\n/);
373
+ der = decryptPemBody(body.slice(0, split), Buffer.from(body.slice(split), "base64"), passphrase);
374
+ } else {
375
+ der = Buffer.from(body, "base64");
376
+ }
377
+ const known = PEM_TYPES[label];
323
378
  if (known === undefined || (wantPrivate && !known[1])) throw keyError(1);
324
- const handle = cryptoKeyParse(Buffer.from(m[2], "base64"), known[0], known[1]);
379
+ const handle = cryptoKeyParse(der, known[0], known[1]);
325
380
  if (typeof handle === "number") throw keyError(handle);
326
381
  return known[1] && !wantPrivate ? cryptoKeyPublic(handle) : handle;
327
382
  }
328
383
 
384
+ /* ---- raw keys (Node 24): the public point or key, the private scalar or key -------------------- */
385
+
386
+ /* the curves raw keys name (Node takes the JWK names too): [OpenSSL's name, JWK's, scalar bytes] */
387
+ const P256 = ["prime256v1", "P-256", 32], P384 = ["secp384r1", "P-384", 48], P521 = ["secp521r1", "P-521", 66];
388
+ const RAW_CURVES = {
389
+ "P-256": P256, prime256v1: P256, "P-384": P384, secp384r1: P384, "P-521": P521, secp521r1: P521, secp256k1: ["secp256k1", "secp256k1", 32],
390
+ };
391
+ const RAW_KINDS = new Set(["ec", "ed25519", "x25519"]);
392
+ const rawUnsupported = (format) => incompatible(format, "is not supported for this key type");
393
+
394
+ function exportRawPublic(handle, type = "uncompressed") {
395
+ const [, kind, namedCurve] = cryptoKeyInfo(handle);
396
+ if (!RAW_KINDS.has(kind)) throw rawUnsupported("raw-public");
397
+ const point = cryptoKeyParts(handle)[0];
398
+ if (kind !== "ec") return point;
399
+ if (type !== "compressed" && type !== "uncompressed") throw invalidArgValue("options.type", "must be one of: 'compressed', 'uncompressed'", type);
400
+ return type === "uncompressed" ? point : cryptoEcPoint(namedCurve, point, 2);
401
+ }
402
+
403
+ function exportRawPrivate(handle, format) {
404
+ const [, kind] = cryptoKeyInfo(handle);
405
+ if (format === "raw-seed" || !RAW_KINDS.has(kind)) throw rawUnsupported(format);
406
+ return cryptoKeyParts(handle)[1];
407
+ }
408
+
409
+ /** A key handle from raw bytes: { key, format, asymmetricKeyType, namedCurve }. */
410
+ function rawKeyHandle(key, format, wantPrivate) {
411
+ const { key: data, asymmetricKeyType: kind } = key;
412
+ if (wantPrivate && format === "raw-public") throw invalidArgValue("key.format", "is invalid", format);
413
+ if (!isArrayBufferView(data) && !isAnyArrayBuffer(data)) {
414
+ throw invalidArgType("key.key", "an instance of ArrayBuffer, Buffer, TypedArray, or DataView", data);
415
+ }
416
+ validateString(kind, "key.asymmetricKeyType");
417
+ if (kind === "ec") validateString(key.namedCurve, "key.namedCurve");
418
+ if (kind === "rsa" || kind === "rsa-pss" || kind === "dsa" || kind === "dh") throw rawUnsupported(format);
419
+ if (!RAW_KINDS.has(kind)) throw invalidArgValue("key.asymmetricKeyType", "is invalid: Invalid asymmetricKeyType", kind);
420
+ if (format === "raw-seed") throw rawUnsupported(format);
421
+ const bytes = viewBytes(data);
422
+ const isPrivate = format === "raw-private";
423
+ let handle;
424
+ if (kind === "ec") {
425
+ const curve = RAW_CURVES[key.namedCurve];
426
+ if (curve === undefined) throw codeError(TypeError, "ERR_CRYPTO_INVALID_CURVE", "Invalid EC curve name");
427
+ const [name, jwk, size] = curve;
428
+ let point;
429
+ if (isPrivate) {
430
+ if (bytes.length !== size) throw invalidArgValue("key.key", "is invalid", data);
431
+ } else {
432
+ point = cryptoEcPoint(name, bytes, 4);
433
+ if (typeof point === "number") throw invalidArgValue("key.key", "is invalid", data);
434
+ }
435
+ handle = cryptoKeyFromParts("ec", isPrivate, jwk, isPrivate ? [undefined, bytes] : [point]);
436
+ } else {
437
+ if (bytes.length !== 32) throw invalidArgValue("key.key", "is invalid", data);
438
+ handle = cryptoKeyFromParts(kind, isPrivate, undefined, isPrivate ? [undefined, bytes] : [bytes]);
439
+ }
440
+ if (typeof handle === "number") throw invalidArgValue("key.key", "is invalid", data);
441
+ return isPrivate && !wantPrivate ? cryptoKeyPublic(handle) : handle;
442
+ }
443
+
329
444
  /* ---- JWK --------------------------------------------------------------------------------------- */
330
445
 
331
446
  const b64u = (buf) => buf.toString("base64url");
@@ -445,12 +560,13 @@ function prepareAsymmetricKey(key, ctx) {
445
560
  validateObject(data, "key.key");
446
561
  return { key: keyFromJwk(data, !wantPrivate) };
447
562
  }
563
+ if (format === "raw-public" || format === "raw-private" || format === "raw-seed") return { key: rawKeyHandle(key, format, wantPrivate) };
448
564
  if (!isStringOrBuffer(data)) {
449
565
  throw invalidArgType("key.key", "of type string or an instance of ArrayBuffer, Buffer, TypedArray, or DataView", data);
450
566
  }
451
- const { format: fmt, type } = parseKeyEncoding(key, undefined, wantPrivate ? false : undefined, "key");
567
+ const { format: fmt, type, passphrase } = parseKeyEncoding(key, undefined, wantPrivate ? false : undefined, "key");
452
568
  const bytes = typeof data === "string" && fmt === "der" ? Buffer.from(data, encoding ?? "utf8") : data;
453
- return { key: parseKey(bytes, fmt, type, wantPrivate) };
569
+ return { key: parseKey(bytes, fmt, type, wantPrivate, passphrase) };
454
570
  }
455
571
  /* Node 24 lists URL where a private key is wanted (an OpenSSL store's; not visaljs's yet) */
456
572
  const expected = ctx === CREATE_PRIVATE
@@ -526,17 +642,34 @@ export function generateKey(type, options, callback) {
526
642
  later(() => callback(null, key));
527
643
  }
528
644
 
645
+ /* Node's parseKeyFormatAndType for the raw formats of a key pair's encodings */
646
+ function rawEncoding(enc, isPublic) {
647
+ const { format, type } = enc;
648
+ const name = isPublic ? "options.publicKeyEncoding" : "options.privateKeyEncoding";
649
+ if (format === "raw-public") {
650
+ if (!isPublic) throw invalidArgValue(`${name}.format`, "is invalid", format);
651
+ if (type !== undefined && type !== "uncompressed" && type !== "compressed") throw invalidArgValue(`${name}.type`, "is invalid", type);
652
+ return { format, type };
653
+ }
654
+ if (format === "raw-private" || format === "raw-seed") {
655
+ if (isPublic) throw invalidArgValue(`${name}.format`, "is invalid", format);
656
+ if (type !== undefined) throw invalidArgValue(`${name}.type`, "is invalid", type);
657
+ if (enc.cipher != null || enc.passphrase !== undefined) throw incompatible("raw format", "does not support encryption");
658
+ return { format };
659
+ }
660
+ return undefined;
661
+ }
662
+
529
663
  function pairEncodings(keyType, options) {
530
664
  const { publicKeyEncoding, privateKeyEncoding } = options ?? {};
531
665
  let pub, priv;
532
666
  if (publicKeyEncoding != null) {
533
667
  if (typeof publicKeyEncoding !== "object") throw invalidArgValue("options.publicKeyEncoding", "is invalid", publicKeyEncoding);
534
- pub = publicKeyEncoding.format === "jwk" ? { format: "jwk" } : parseKeyEncoding(publicKeyEncoding, keyType, true, "options.publicKeyEncoding");
668
+ pub = publicKeyEncoding.format === "jwk" ? { format: "jwk" } : rawEncoding(publicKeyEncoding, true) ?? parseKeyEncoding(publicKeyEncoding, keyType, true, "options.publicKeyEncoding");
535
669
  }
536
670
  if (privateKeyEncoding != null) {
537
671
  if (typeof privateKeyEncoding !== "object") throw invalidArgValue("options.privateKeyEncoding", "is invalid", privateKeyEncoding);
538
- priv = privateKeyEncoding.format === "jwk" ? { format: "jwk" } : parseKeyEncoding(privateKeyEncoding, keyType, false, "options.privateKeyEncoding");
539
- if (priv.cipher != null) throw notSupported("Encrypting a private key");
672
+ priv = privateKeyEncoding.format === "jwk" ? { format: "jwk" } : rawEncoding(privateKeyEncoding, false) ?? parseKeyEncoding(privateKeyEncoding, keyType, false, "options.privateKeyEncoding");
540
673
  }
541
674
  return { pub, priv };
542
675
  }
@@ -644,3 +777,5 @@ export function generateKeyPair(type, options, callback) {
644
777
  }
645
778
  later(() => callback(null, result.publicKey, result.privateKey));
646
779
  }
780
+ /* as Node's: util.promisify(generateKeyPair) resolves to { publicKey, privateKey } */
781
+ Object.defineProperty(generateKeyPair, Symbol.for("visaljs.util.customPromisifyArgs"), { value: ["publicKey", "privateKey"] });
@@ -111,6 +111,30 @@ export function randomInt(min, max, callback) {
111
111
  }
112
112
  }
113
113
 
114
+ let hexBytes = null;
115
+
116
+ /* Node's getUnbufferedUUIDv7: 48 bits of Date.now(), then version 7, the variant and random bits */
117
+ export function randomUUIDv7(options) {
118
+ if (options !== undefined) validateObject(options, "options");
119
+ const { disableEntropyCache = false } = options || {};
120
+ validateBoolean(disableEntropyCache, "options.disableEntropyCache");
121
+ const b = randomFillSync(new Uint8Array(16), 6);
122
+ const now = Date.now();
123
+ const msb = now / 2 ** 32;
124
+ b[0] = msb >>> 8;
125
+ b[1] = msb;
126
+ b[2] = now >>> 24;
127
+ b[3] = now >>> 16;
128
+ b[4] = now >>> 8;
129
+ b[5] = now;
130
+ b[6] = (b[6] & 0x0f) | 0x70;
131
+ b[8] = (b[8] & 0x3f) | 0x80;
132
+ hexBytes ??= Array.from({ length: 256 }, (_, i) => (i < 16 ? "0" : "") + i.toString(16));
133
+ let out = "";
134
+ for (let i = 0; i < 16; i++) out += (i === 4 || i === 6 || i === 8 || i === 10 ? "-" : "") + hexBytes[b[i]];
135
+ return out;
136
+ }
137
+
114
138
  export function randomUUID(options) {
115
139
  if (options !== undefined) validateObject(options, "options");
116
140
  const { disableEntropyCache = false } = options || {};
@@ -36,8 +36,9 @@ export function validateEncoding(data, enc) {
36
36
  }
37
37
  }
38
38
 
39
+ /** Node's validateObject: an object, not null nor an array. */
39
40
  export function validateObject(value, name) {
40
- if (value === null || typeof value !== "object") throw invalidArgType(name, "of type object", value);
41
+ if (value === null || typeof value !== "object" || Array.isArray(value)) throw invalidArgType(name, "of type object", value);
41
42
  }
42
43
 
43
44
  export function validateBoolean(value, name) {