Skip to main content

nodejs/stdlib/
crypto.rs

1//! Node `crypto` module.
2//!
3//! * `createHash(algo)` → a `Hash` instance (`update(...).digest(enc)`), backed
4//!   by the `md-5`/`sha1`/`sha2` crates.
5//! * `createHmac(algo, key)` → an `Hmac` instance (`update(...).digest(enc)`),
6//!   backed by the `hmac` crate over the same digests.
7//! * `randomBytes`, `randomUUID`, `randomInt` — CSPRNG output via `getrandom`.
8//!
9//! `Hash`/`Hmac` are plain objects tagged `@@native = "Hash"` / `"Hmac"`
10//! accumulating input in a hidden `@@data` byte array until `digest` finalizes.
11
12use super::{arg_str, to_base64, to_hex};
13use crate::host::{is_callable, with_host, JsObj};
14use cipher::block_padding::Pkcs7;
15use cipher::{BlockDecryptMut, BlockEncryptMut, KeyIvInit, StreamCipher};
16use fusevm::Value;
17use hkdf::Hkdf;
18use hmac::{Hmac, Mac};
19use indexmap::IndexMap;
20use md5::{Digest as _, Md5};
21use num_bigint::BigUint;
22use p256::elliptic_curve::sec1::ToEncodedPoint;
23use pkcs8::{DecodePrivateKey, EncodePrivateKey, LineEnding};
24use rsa::pkcs1::{DecodeRsaPrivateKey, DecodeRsaPublicKey};
25use sha1::Sha1;
26use sha2::{Sha256, Sha384, Sha512};
27use signature::{SignatureEncoding, Signer, Verifier};
28use spki::{DecodePublicKey, EncodePublicKey};
29use subtle::ConstantTimeEq;
30
31/// Cipher algorithms `createCipheriv`/`createDecipheriv` support (AES CBC/CTR).
32const CIPHERS: &[&str] = &[
33    "aes-128-cbc",
34    "aes-192-cbc",
35    "aes-256-cbc",
36    "aes-128-ctr",
37    "aes-192-ctr",
38    "aes-256-ctr",
39];
40
41/// Digest names `createHash`/`createHmac`/`pbkdf2`/`hkdf` accept.
42const HASHES: &[&str] = &["md5", "sha1", "sha256", "sha512"];
43
44/// Standard EC curve names for `getCurves()`. Key generation over these is not
45/// supported (no EC crate available); the list mirrors the common OpenSSL names
46/// so feature-detection code sees them.
47const CURVES: &[&str] = &[
48    "prime256v1",
49    "secp256k1",
50    "secp384r1",
51    "secp521r1",
52    "secp224r1",
53    "secp192k1",
54    "secp256r1",
55];
56
57pub const METHODS: &[&str] = &[
58    "createHash",
59    "createHmac",
60    "randomBytes",
61    "randomUUID",
62    "randomInt",
63    "pbkdf2Sync",
64    "pbkdf2",
65    "scryptSync",
66    "scrypt",
67    "hkdfSync",
68    "hkdf",
69    "createCipheriv",
70    "createDecipheriv",
71    "randomFillSync",
72    "randomFill",
73    "timingSafeEqual",
74    "getHashes",
75    "getCiphers",
76    "getCurves",
77    "getFips",
78    "pseudoRandomBytes",
79    "prng",
80    "rng",
81    "hash",
82    "randomUUIDv7",
83    "getCipherInfo",
84    // Asymmetric keys & signatures
85    "generateKeyPairSync",
86    "generateKeyPair",
87    "createPrivateKey",
88    "createPublicKey",
89    "createSecretKey",
90    "createSign",
91    "createVerify",
92    "sign",
93    "verify",
94    "publicEncrypt",
95    "privateDecrypt",
96    "privateEncrypt",
97    "publicDecrypt",
98    // Diffie-Hellman / ECDH
99    "createDiffieHellman",
100    "createDiffieHellmanGroup",
101    "getDiffieHellman",
102    "createECDH",
103    "diffieHellman",
104    // Primes
105    "checkPrime",
106    "checkPrimeSync",
107    "generatePrime",
108    "generatePrimeSync",
109    // Misc
110    "argon2",
111    "argon2Sync",
112    "getRandomValues",
113];
114
115/// The `webcrypto` / `globalThis.crypto` members this runtime implements.
116///
117/// `subtle` carries `digest` ONLY. The rest of SubtleCrypto — `encrypt`,
118/// `generateKey`, `importKey`, the key-derivation and key-wrapping methods, and
119/// the `CryptoKey` type they exchange — is not implemented, and is absent rather
120/// than present-and-always-rejecting so that a `typeof subtle.encrypt` feature
121/// probe correctly takes its fallback path.
122pub const WEBCRYPTO_METHODS: &[&str] = &["getRandomValues", "randomUUID"];
123
124/// The implemented half of `crypto.subtle`.
125pub const SUBTLE_METHODS: &[&str] = &["digest"];
126
127/// `crypto.subtle.<method>`.
128pub fn subtle_call(method: &str, args: &[Value]) -> Option<Result<Value, String>> {
129    match method {
130        // `subtle.digest(algorithm, data)` resolves with an `ArrayBuffer`. The
131        // algorithm is named the WebCrypto way (`"SHA-256"`, or
132        // `{ name: "SHA-256" }`), which is node's `sha256` with the dash
133        // dropped.
134        "digest" => {
135            let spec = args.first().cloned().unwrap_or(Value::Undef);
136            let named = crate::builtins::get_property(&spec, "name").unwrap_or(Value::Undef);
137            let raw = match named {
138                Value::Undef => with_host(|h| h.str_of(&spec)),
139                n => with_host(|h| h.str_of(&n)),
140            };
141            let algo = raw.replace('-', "").to_ascii_lowercase();
142            if !supported(&algo) {
143                return Some(Err(crate::host::dom_error(
144                    "NotSupportedError",
145                    "Unrecognized algorithm name",
146                )));
147            }
148            let data = val_bytes_at(args, 1);
149            let out = digest(&algo, &data);
150            let buf = crate::stdlib::typedarray::new_array_buffer(out.len());
151            crate::stdlib::typedarray::write_buffer_bytes(&buf, &out);
152            Some(crate::builtins::promise_resolve_pub(buf))
153        }
154        _ => None,
155    }
156}
157
158pub fn call(method: &str, args: &[Value]) -> Option<Result<Value, String>> {
159    Some(match method {
160        "createHash" => {
161            let algo = arg_str(args, 0).to_ascii_lowercase();
162            if !supported(&algo) {
163                // Node names no algorithm here — the reference message is the bare
164                // `Digest method not supported`, and it carries no `code`.
165                return Some(Err("Error: Digest method not supported".into()));
166            }
167            Ok(with_host(|h| {
168                let data = h.new_array(Vec::new());
169                let mut m = IndexMap::new();
170                m.insert("@@native".into(), h.new_str("Hash"));
171                m.insert("@@algo".into(), h.new_str(algo));
172                m.insert("@@data".into(), data);
173                h.new_object(m)
174            }))
175        }
176        "createHmac" => {
177            let algo = arg_str(args, 0).to_ascii_lowercase();
178            if !supported(&algo) {
179                // Node names no algorithm here — the reference message is the bare
180                // `Digest method not supported`, and it carries no `code`.
181                return Some(Err("Error: Digest method not supported".into()));
182            }
183            // Key may be a Buffer (raw bytes) or a string (utf8 by default).
184            let key = key_bytes(args.get(1));
185            Ok(with_host(|h| {
186                let data = h.new_array(Vec::new());
187                let keyv = h.new_array(key.iter().map(|b| Value::Float(*b as f64)).collect());
188                let mut m = IndexMap::new();
189                m.insert("@@native".into(), h.new_str("Hmac"));
190                m.insert("@@algo".into(), h.new_str(algo));
191                m.insert("@@key".into(), keyv);
192                m.insert("@@data".into(), data);
193                h.new_object(m)
194            }))
195        }
196        "randomBytes" => {
197            let n = super::arg_num(args, 0).max(0.0) as usize;
198            let mut buf = vec![0u8; n];
199            if let Err(e) = getrandom::getrandom(&mut buf) {
200                return Some(Err(format!("Error: failed to generate random bytes: {e}")));
201            }
202            // Callback form: randomBytes(n, (err, buf) => ...). Build the Buffer,
203            // release the host borrow, then queue the callback with (null, buf).
204            let cb = args
205                .get(1)
206                .cloned()
207                .filter(|v| with_host(|h| is_callable(h, v)));
208            if let Some(cb) = cb {
209                let bufv = super::buffer::from_bytes(&buf);
210                with_host(|h| {
211                    let nullv = h.null();
212                    h.queue_micro(cb, vec![nullv, bufv]);
213                });
214                Ok(Value::Undef)
215            } else {
216                Ok(super::buffer::from_bytes(&buf))
217            }
218        }
219        "randomUUID" => {
220            let mut b = [0u8; 16];
221            if let Err(e) = getrandom::getrandom(&mut b) {
222                return Some(Err(format!("Error: failed to generate random bytes: {e}")));
223            }
224            // RFC 4122 v4: version nibble = 4, variant nibble ∈ [8..b].
225            b[6] = (b[6] & 0x0f) | 0x40;
226            b[8] = (b[8] & 0x3f) | 0x80;
227            let h = to_hex(&b);
228            let uuid = format!(
229                "{}-{}-{}-{}-{}",
230                &h[0..8],
231                &h[8..12],
232                &h[12..16],
233                &h[16..20],
234                &h[20..32],
235            );
236            Ok(with_host(|host| host.new_str(uuid)))
237        }
238        "randomInt" => {
239            // randomInt([min, ]max) — uniform integer in [min, max).
240            let (min, max) = if args.len() >= 2 {
241                (super::arg_num(args, 0), super::arg_num(args, 1))
242            } else {
243                (0.0, super::arg_num(args, 0))
244            };
245            let (min, max) = (min as i64, max as i64);
246            if max <= min {
247                return Some(Err(crate::host::coded_error(
248                    "RangeError",
249                    "ERR_OUT_OF_RANGE",
250                    &format!(
251                        "The value of \"max\" is out of range. It must be greater than \
252                         the value of \"min\" ({min}). Received {max}"
253                    ),
254                )));
255            }
256            let range = (max - min) as u64;
257            match random_below(range) {
258                Ok(r) => Ok(Value::Float((min + r as i64) as f64)),
259                Err(e) => Err(format!("Error: failed to generate random bytes: {e}")),
260            }
261        }
262        // ── Key derivation ──────────────────────────────────────────────
263        "pbkdf2Sync" => pbkdf2(args, None),
264        "pbkdf2" => pbkdf2(args, args.get(5).cloned()),
265        "scryptSync" => scrypt(args, None),
266        "scrypt" => scrypt(args, trailing_cb(args)),
267        "hkdfSync" => hkdf(args, None),
268        "hkdf" => hkdf(args, args.get(5).cloned()),
269        // ── Symmetric ciphers ───────────────────────────────────────────
270        "createCipheriv" => make_cipher("Cipheriv", args),
271        "createDecipheriv" => make_cipher("Decipheriv", args),
272        // ── Random fill ─────────────────────────────────────────────────
273        "randomFillSync" => random_fill(args),
274        "randomFill" => {
275            let cb = trailing_cb(args);
276            let res = random_fill(args);
277            match (cb, res) {
278                (Some(cb), Ok(buf)) => {
279                    with_host(|h| {
280                        let nullv = h.null();
281                        h.queue_micro(cb, vec![nullv, buf]);
282                    });
283                    Ok(Value::Undef)
284                }
285                (Some(cb), Err(e)) => {
286                    let errv = with_host(|h| h.new_str(e));
287                    with_host(|h| h.queue_micro(cb, vec![errv]));
288                    Ok(Value::Undef)
289                }
290                (None, r) => r,
291            }
292        }
293        // ── Constant-time compare ───────────────────────────────────────
294        "timingSafeEqual" => {
295            let a = val_bytes_at(args, 0);
296            let b = val_bytes_at(args, 1);
297            if a.len() != b.len() {
298                return Some(Err(crate::host::plain_coded_error(
299                    "RangeError",
300                    "ERR_CRYPTO_TIMING_SAFE_EQUAL_LENGTH",
301                    "Input buffers must have the same byte length",
302                )));
303            }
304            Ok(Value::Bool(a.ct_eq(&b).into()))
305        }
306        // ── Introspection ───────────────────────────────────────────────
307        "getHashes" => Ok(with_host(|h| {
308            let items: Vec<Value> = HASHES.iter().map(|s| h.new_str(*s)).collect();
309            h.new_array(items)
310        })),
311        "getCiphers" => Ok(with_host(|h| {
312            let items: Vec<Value> = CIPHERS.iter().map(|s| h.new_str(*s)).collect();
313            h.new_array(items)
314        })),
315        "getCurves" => Ok(with_host(|h| {
316            let items: Vec<Value> = CURVES.iter().map(|s| h.new_str(*s)).collect();
317            h.new_array(items)
318        })),
319        // node v26 returns the number 0 (not the boolean false) from getFips().
320        "getFips" => Ok(Value::Float(0.0)),
321        "getCipherInfo" => cipher_info(&arg_str(args, 0).to_ascii_lowercase()),
322        // ── randomBytes aliases (all return a Buffer of CSPRNG bytes) ────
323        "pseudoRandomBytes" | "prng" | "rng" => {
324            let n = super::arg_num(args, 0).max(0.0) as usize;
325            let mut buf = vec![0u8; n];
326            if let Err(e) = getrandom::getrandom(&mut buf) {
327                return Some(Err(format!("Error: failed to generate random bytes: {e}")));
328            }
329            let cb = args
330                .get(1)
331                .cloned()
332                .filter(|v| with_host(|h| is_callable(h, v)));
333            if let Some(cb) = cb {
334                let bufv = super::buffer::from_bytes(&buf);
335                with_host(|h| {
336                    let nullv = h.null();
337                    h.queue_micro(cb, vec![nullv, bufv]);
338                });
339                Ok(Value::Undef)
340            } else {
341                Ok(super::buffer::from_bytes(&buf))
342            }
343        }
344        // ── One-shot hash (node 21+) ────────────────────────────────────
345        "hash" => {
346            let algo = arg_str(args, 0).to_ascii_lowercase();
347            if !supported(&algo) {
348                // Node names no algorithm here — the reference message is the bare
349                // `Digest method not supported`, and it carries no `code`.
350                return Some(Err("Error: Digest method not supported".into()));
351            }
352            // args[1] is the data (utf8 string or Buffer); there is no input-encoding param.
353            let data = val_bytes_at(args, 1);
354            // args[2] is the output encoding (default "hex"); "buffer" yields a Buffer.
355            let out = digest(&algo, &data);
356            let out_enc = if args.len() > 2 {
357                arg_str(args, 2)
358            } else {
359                "hex".into()
360            };
361            Ok(if out_enc == "buffer" {
362                super::buffer::from_bytes(&out)
363            } else {
364                encode_out(&out, Some(&out_enc))
365            })
366        }
367        // ── time-ordered UUID v7 ────────────────────────────────────────
368        "randomUUIDv7" => uuid_v7(),
369        // ── Asymmetric key generation ───────────────────────────────────
370        "generateKeyPairSync" => {
371            generate_key_pair(&arg_str(args, 0).to_ascii_lowercase(), args.get(1))
372        }
373        "generateKeyPair" => {
374            let cb = trailing_cb(args);
375            let res = generate_key_pair(&arg_str(args, 0).to_ascii_lowercase(), args.get(1));
376            match cb {
377                Some(cb) => {
378                    match res {
379                        Ok(pair) => with_host(|h| {
380                            // Deliver (err=null, publicKey, privateKey).
381                            let nullv = h.null();
382                            let (pubk, prvk) = match h.get(&pair) {
383                                Some(JsObj::Object(p)) => (
384                                    p.get("publicKey").cloned().unwrap_or(Value::Undef),
385                                    p.get("privateKey").cloned().unwrap_or(Value::Undef),
386                                ),
387                                _ => (Value::Undef, Value::Undef),
388                            };
389                            h.queue_micro(cb, vec![nullv, pubk, prvk]);
390                        }),
391                        Err(e) => {
392                            let errv = with_host(|h| h.new_str(e));
393                            with_host(|h| h.queue_micro(cb, vec![errv]));
394                        }
395                    }
396                    Ok(Value::Undef)
397                }
398                None => res,
399            }
400        }
401        "createPrivateKey" => create_private_key(args.first()),
402        "createPublicKey" => create_public_key(args.first()),
403        "createSecretKey" => Ok(secret_key_object(&val_bytes_at(args, 0))),
404        // ── Sign / Verify (streaming instances) ─────────────────────────
405        "createSign" => Ok(new_sign_verify("Sign", &arg_str(args, 0))),
406        "createVerify" => Ok(new_sign_verify("Verify", &arg_str(args, 0))),
407        // ── Sign / Verify (one-shot) ────────────────────────────────────
408        "sign" => {
409            let algo = arg_str(args, 0);
410            let data = val_bytes_at(args, 1);
411            let key = key_material(args.get(2).unwrap_or(&Value::Undef));
412            match sign_data(&key, &algo, &data) {
413                Ok(sig) => Ok(super::buffer::from_bytes(&sig)),
414                Err(e) => Err(e),
415            }
416        }
417        "verify" => {
418            let algo = arg_str(args, 0);
419            let data = val_bytes_at(args, 1);
420            let key = key_material(args.get(2).unwrap_or(&Value::Undef));
421            let sig = val_bytes_at(args, 3);
422            match verify_data(&key, &algo, &data, &sig) {
423                Ok(ok) => Ok(Value::Bool(ok)),
424                Err(e) => Err(e),
425            }
426        }
427        // ── RSA public/private encryption ───────────────────────────────
428        "publicEncrypt" => rsa_public_op(args, true, true),
429        "privateDecrypt" => rsa_public_op(args, false, false),
430        "privateEncrypt" => rsa_private_encrypt(args),
431        "publicDecrypt" => rsa_public_decrypt(args),
432        // ── Diffie-Hellman ──────────────────────────────────────────────
433        "createDiffieHellman" => create_diffie_hellman(args),
434        "createDiffieHellmanGroup" | "getDiffieHellman" => diffie_hellman_group(&arg_str(args, 0)),
435        "createECDH" => create_ecdh(&arg_str(args, 0)),
436        "diffieHellman" => diffie_hellman_oneshot(args.first()),
437        // ── Primes ──────────────────────────────────────────────────────
438        "checkPrimeSync" => Ok(Value::Bool(check_prime(args.first()))),
439        "checkPrime" => {
440            let ok = check_prime(args.first());
441            let cb = trailing_cb(args);
442            match cb {
443                Some(cb) => {
444                    with_host(|h| {
445                        let nullv = h.null();
446                        h.queue_micro(cb, vec![nullv, Value::Bool(ok)]);
447                    });
448                    Ok(Value::Undef)
449                }
450                None => Ok(Value::Bool(ok)),
451            }
452        }
453        "generatePrimeSync" => {
454            generate_prime(super::arg_num(args, 0) as usize, opts_object(args, 1))
455        }
456        "generatePrime" => {
457            let res = generate_prime(super::arg_num(args, 0) as usize, opts_object(args, 1));
458            let cb = trailing_cb(args);
459            match (cb, res) {
460                (Some(cb), Ok(v)) => {
461                    with_host(|h| {
462                        let nullv = h.null();
463                        h.queue_micro(cb, vec![nullv, v]);
464                    });
465                    Ok(Value::Undef)
466                }
467                (Some(cb), Err(e)) => {
468                    let errv = with_host(|h| h.new_str(e));
469                    with_host(|h| h.queue_micro(cb, vec![errv]));
470                    Ok(Value::Undef)
471                }
472                (None, r) => r,
473            }
474        }
475        // ── argon2 ──────────────────────────────────────────────────────
476        "argon2Sync" => match argon2_hash(&arg_str(args, 0), args.get(1)) {
477            Ok(out) => Ok(super::buffer::from_bytes(&out)),
478            Err(e) => Err(e),
479        },
480        "argon2" => deliver_async(
481            trailing_cb(args),
482            argon2_hash(&arg_str(args, 0), args.get(1)),
483        ),
484        // ── WebCrypto getRandomValues ───────────────────────────────────
485        "getRandomValues" => get_random_values(args.first()),
486        _ => return None,
487    })
488}
489
490/// `Hash` instance methods: `update` (chainable) and `digest`.
491pub fn instance_call(recv: &Value, method: &str, args: &[Value]) -> Result<Value, String> {
492    hashlike_call("Hash", recv, method, args)
493}
494
495/// `Hmac` instance methods: `update` (chainable) and `digest`.
496pub fn hmac_instance_call(recv: &Value, method: &str, args: &[Value]) -> Result<Value, String> {
497    hashlike_call("Hmac", recv, method, args)
498}
499
500/// `Cipheriv`/`Decipheriv` instance methods: `update(data[,inEnc,outEnc])` and
501/// `final([outEnc])`. Input is accumulated in `@@data`; the transform runs at
502/// `final` (CBC needs the full block/padding stream, so `update` returns empty
503/// and `final` returns the whole result — the standard `update()+final()`
504/// concatenation is byte-identical to node).
505pub fn cipher_instance_call(
506    tag: &str,
507    recv: &Value,
508    method: &str,
509    args: &[Value],
510) -> Result<Value, String> {
511    match method {
512        "update" => {
513            // Input after `final` is rejected — this accepted it and answered
514            // with an empty string, so the data silently went nowhere.
515            if finalized(recv) {
516                return Err("Error: Trying to add data in unsupported state".into());
517            }
518            // Shared with `Hash`/`Hmac`: any byte VIEW contributes its bytes,
519            // only a string is decoded, and anything else is rejected. Only a
520            // Buffer was recognised here, so a Uint8Array was stringified.
521            let enc = if args.len() > 1 {
522                arg_str(args, 1)
523            } else {
524                "utf8".into()
525            };
526            let bytes = update_bytes(args, &enc)?;
527            with_host(|h| {
528                if let Some(JsObj::Object(p)) = h.get(recv).cloned() {
529                    if let Some(arr) = p.get("@@data").cloned() {
530                        if let Some(JsObj::Array(items)) = h.get_mut(&arr) {
531                            items.extend(bytes.iter().map(|b| Value::Float(*b as f64)));
532                        }
533                    }
534                }
535            });
536            let out_enc = if args.len() > 2 {
537                Some(arg_str(args, 2))
538            } else {
539                None
540            };
541            Ok(encode_out(&[], out_enc.as_deref()))
542        }
543        "final" => {
544            // A cipher finalizes ONCE. Running the transform again returned a
545            // second, valid-looking ciphertext for input that was already
546            // consumed.
547            if finalized(recv) {
548                return Err(crate::host::coded_error(
549                    "Error",
550                    "ERR_CRYPTO_INVALID_STATE",
551                    "Invalid state",
552                ));
553            }
554            mark_finalized(recv);
555            let (algo, key, iv, data) = with_host(|h| {
556                let (mut algo, mut key, mut iv, mut data) =
557                    (String::new(), Vec::new(), Vec::new(), Vec::new());
558                if let Some(JsObj::Object(p)) = h.get(recv) {
559                    algo = p.get("@@algo").map(|v| h.str_of(v)).unwrap_or_default();
560                    if let Some(JsObj::Array(it)) = p.get("@@key").and_then(|v| h.get(v)) {
561                        key = it.iter().map(|v| h.to_number(v) as u8).collect();
562                    }
563                    if let Some(JsObj::Array(it)) = p.get("@@iv").and_then(|v| h.get(v)) {
564                        iv = it.iter().map(|v| h.to_number(v) as u8).collect();
565                    }
566                    if let Some(JsObj::Array(it)) = p.get("@@data").and_then(|v| h.get(v)) {
567                        data = it.iter().map(|v| h.to_number(v) as u8).collect();
568                    }
569                }
570                (algo, key, iv, data)
571            });
572            let out = cipher_crypt(&algo, &key, &iv, &data, tag == "Cipheriv")?;
573            let out_enc = if args.is_empty() {
574                None
575            } else {
576                Some(arg_str(args, 0))
577            };
578            Ok(encode_out(&out, out_enc.as_deref()))
579        }
580        "setAutoPadding" => Ok(recv.clone()),
581        _ => Err(crate::host::type_error(&format!(
582            "{}.{method} is not a function",
583            tag.to_ascii_lowercase()
584        ))),
585    }
586}
587
588/// Shared `update`/`digest` for `Hash` and `Hmac` (both accumulate into `@@data`;
589/// `digest` finalizes via a plain digest or an HMAC keyed by `@@key`).
590fn hashlike_call(kind: &str, recv: &Value, method: &str, args: &[Value]) -> Result<Value, String> {
591    match method {
592        "update" => {
593            // A finalized digest cannot take more input. This accepted it
594            // silently and folded it into a hash nobody would read, so the
595            // mistake produced no symptom at all.
596            if finalized(recv) {
597                return Err(hash_finalized());
598            }
599            let enc = if args.len() > 1 {
600                arg_str(args, 1)
601            } else {
602                "utf8".into()
603            };
604            // A byte-like VIEW contributes its bytes; only a string goes
605            // through the encoding. Every input used to be stringified first,
606            // so `hash.update(new TextEncoder().encode(s))` hashed
607            // `"[object Object]"`, and a Buffer only worked because
608            // stringifying it happened to yield its utf8 text — which is wrong
609            // the moment the bytes are not valid utf8.
610            let bytes = update_bytes(args, &enc)?;
611            with_host(|h| {
612                if let Some(JsObj::Object(p)) = h.get(recv).cloned() {
613                    if let Some(arr) = p.get("@@data").cloned() {
614                        if let Some(JsObj::Array(items)) = h.get_mut(&arr) {
615                            items.extend(bytes.iter().map(|b| Value::Float(*b as f64)));
616                        }
617                    }
618                }
619            });
620            Ok(recv.clone())
621        }
622        // `Hash.copy()` forks the running state, so the two go on to hash
623        // different tails. Without it the only way to hash a common prefix twice
624        // was to replay the prefix. An Hmac has no `copy`.
625        "copy" if kind == "Hash" => {
626            if finalized(recv) {
627                return Err(hash_finalized());
628            }
629            let (algo, data) = with_host(|h| {
630                let (mut algo, mut data) = (String::new(), Vec::new());
631                if let Some(JsObj::Object(p)) = h.get(recv) {
632                    algo = p.get("@@algo").map(|v| h.str_of(v)).unwrap_or_default();
633                    if let Some(JsObj::Array(items)) = p.get("@@data").and_then(|v| h.get(v)) {
634                        data = items.clone();
635                    }
636                }
637                (algo, data)
638            });
639            Ok(with_host(|h| {
640                let data = h.new_array(data);
641                let mut m = IndexMap::new();
642                m.insert("@@native".into(), h.new_str("Hash"));
643                m.insert("@@algo".into(), h.new_str(algo));
644                m.insert("@@data".into(), data);
645                h.new_object(m)
646            }))
647        }
648        "digest" => {
649            // A second `digest` on a Hash throws; on an Hmac node answers with
650            // an EMPTY buffer instead. Re-running the digest — what this did —
651            // hands back a value that looks valid and is not what node returns.
652            if finalized(recv) {
653                if kind == "Hash" {
654                    return Err(hash_finalized());
655                }
656                let enc = args.first().map(|_| arg_str(args, 0));
657                return Ok(encode_digest(&[], enc.as_deref()));
658            }
659            mark_finalized(recv);
660            let (algo, key, data) = with_host(|h| {
661                let (mut algo, mut key, mut data) = (String::new(), Vec::new(), Vec::new());
662                if let Some(JsObj::Object(p)) = h.get(recv) {
663                    algo = p.get("@@algo").map(|v| h.str_of(v)).unwrap_or_default();
664                    if let Some(JsObj::Array(items)) = p.get("@@data").and_then(|v| h.get(v)) {
665                        data = items.iter().map(|v| h.to_number(v) as u8).collect();
666                    }
667                    if let Some(JsObj::Array(items)) = p.get("@@key").and_then(|v| h.get(v)) {
668                        key = items.iter().map(|v| h.to_number(v) as u8).collect();
669                    }
670                }
671                (algo, key, data)
672            });
673            let out = if kind == "Hmac" {
674                hmac_digest(&algo, &key, &data)
675            } else {
676                digest(&algo, &data)
677            };
678            let enc = if args.is_empty() {
679                None
680            } else {
681                Some(arg_str(args, 0))
682            };
683            Ok(encode_digest(&out, enc.as_deref()))
684        }
685        _ => Err(crate::host::type_error(&format!(
686            "{}.{method} is not a function",
687            kind.to_ascii_lowercase()
688        ))),
689    }
690}
691
692/// The bytes an `update(data[, encoding])` contributes.
693///
694/// A byte-like VIEW contributes its bytes; only a STRING goes through the
695/// encoding. Anything else is REJECTED rather than stringified: `update(5)`
696/// hashing the text "5" is a silent corruption, since the caller believes a
697/// number went in and gets a digest of something else.
698fn update_bytes(args: &[Value], enc: &str) -> Result<Vec<u8>, String> {
699    let first = args.first().cloned().unwrap_or(Value::Undef);
700    if let Some(b) = super::buffer::view_bytes(&first) {
701        return Ok(b);
702    }
703    if with_host(|h| h.as_str(&first)).is_none() {
704        return Err(crate::host::invalid_arg_type(
705            "data",
706            "argument",
707            "string or an instance of Buffer, TypedArray, or DataView",
708            &first,
709        ));
710    }
711    Ok(decode(&arg_str(args, 0), enc))
712}
713
714/// Render a finished digest in the requested encoding (a Buffer with none).
715fn encode_digest(out: &[u8], enc: Option<&str>) -> Value {
716    match enc {
717        Some("hex") => with_host(|h| h.new_str(to_hex(out))),
718        Some("base64") | Some("base64url") => with_host(|h| h.new_str(to_base64(out))),
719        Some("latin1") | Some("binary") => {
720            with_host(|h| h.new_str(out.iter().map(|b| *b as char).collect::<String>()))
721        }
722        _ => super::buffer::from_bytes(out),
723    }
724}
725
726/// Whether `digest` has already run on this `Hash`/`Hmac`.
727fn finalized(recv: &Value) -> bool {
728    with_host(|h| matches!(h.get(recv), Some(JsObj::Object(p)) if p.contains_key("@@done")))
729}
730
731fn mark_finalized(recv: &Value) {
732    with_host(|h| {
733        if let Some(JsObj::Object(p)) = h.get_mut(recv) {
734            p.insert("@@done".into(), Value::Bool(true));
735        }
736    });
737}
738
739fn hash_finalized() -> String {
740    crate::host::coded_error(
741        "Error",
742        "ERR_CRYPTO_HASH_FINALIZED",
743        "Digest already called",
744    )
745}
746
747fn supported(algo: &str) -> bool {
748    matches!(algo, "md5" | "sha1" | "sha256" | "sha512")
749}
750
751fn digest(algo: &str, data: &[u8]) -> Vec<u8> {
752    match algo {
753        "md5" => {
754            let mut h = Md5::new();
755            h.update(data);
756            h.finalize().to_vec()
757        }
758        "sha1" => {
759            let mut h = Sha1::new();
760            h.update(data);
761            h.finalize().to_vec()
762        }
763        "sha512" => {
764            let mut h = Sha512::new();
765            h.update(data);
766            h.finalize().to_vec()
767        }
768        _ => {
769            let mut h = Sha256::new();
770            h.update(data);
771            h.finalize().to_vec()
772        }
773    }
774}
775
776fn hmac_digest(algo: &str, key: &[u8], data: &[u8]) -> Vec<u8> {
777    // HMAC accepts any key length, so `new_from_slice` never fails here.
778    match algo {
779        "md5" => {
780            let mut m = Hmac::<Md5>::new_from_slice(key).expect("HMAC accepts any key length");
781            m.update(data);
782            m.finalize().into_bytes().to_vec()
783        }
784        "sha1" => {
785            let mut m = Hmac::<Sha1>::new_from_slice(key).expect("HMAC accepts any key length");
786            m.update(data);
787            m.finalize().into_bytes().to_vec()
788        }
789        "sha512" => {
790            let mut m = Hmac::<Sha512>::new_from_slice(key).expect("HMAC accepts any key length");
791            m.update(data);
792            m.finalize().into_bytes().to_vec()
793        }
794        _ => {
795            let mut m = Hmac::<Sha256>::new_from_slice(key).expect("HMAC accepts any key length");
796            m.update(data);
797            m.finalize().into_bytes().to_vec()
798        }
799    }
800}
801
802/// The `createHmac` key argument as raw bytes: a Buffer's bytes, else the value's
803/// utf8 string encoding.
804fn key_bytes(v: Option<&Value>) -> Vec<u8> {
805    v.map(val_bytes).unwrap_or_default()
806}
807
808/// A value's raw bytes: a Buffer/TypedArray's backing bytes, else its utf8
809/// string encoding. Used by pbkdf2/scrypt/hkdf/cipher/timingSafeEqual inputs.
810fn val_bytes(v: &Value) -> Vec<u8> {
811    // Any byte-like VIEW, not just a `Buffer`. Only the Buffer case was
812    // handled, so a `Uint8Array` — what `TextEncoder.encode` returns, and the
813    // form every WebCrypto call takes — fell through to `str_of` and hashed the
814    // string `"[object Object]"` instead of its bytes.
815    if let Some(bytes) = super::buffer::view_bytes(v) {
816        return bytes;
817    }
818    // A `KeyObject` carries its bytes in a hidden slot. Stringifying it yielded
819    // the text of an object, so `hkdf(digest, secretKey, …)` derived from that
820    // text — a plausible key for input the caller never supplied.
821    if is_key_object(v) {
822        return with_host(|h| {
823            match h.get(v) {
824                Some(JsObj::Object(p)) => p.get("@@secret").cloned(),
825                _ => None,
826            }
827            .and_then(|arr| match h.get(&arr) {
828                Some(JsObj::Array(items)) => {
829                    Some(items.iter().map(|b| h.to_number(b) as u8).collect())
830                }
831                _ => None,
832            })
833            .unwrap_or_default()
834        });
835    }
836    with_host(|h| h.str_of(v)).into_bytes()
837}
838
839/// The byte-like argument forms each KDF position accepts, spelled the way node
840/// spells them in its `ERR_INVALID_ARG_TYPE` message. They differ per position,
841/// so the text cannot be shared: `hkdf`'s `ikm` takes a `SecretKeyObject` and
842/// `pbkdf2`'s `password` does not.
843const KDF_BYTES: &str = "string or an instance of ArrayBuffer, Buffer, TypedArray, or DataView";
844const HKDF_IKM: &str =
845    "string or an instance of SecretKeyObject, ArrayBuffer, TypedArray, DataView, or Buffer";
846const HKDF_BYTES: &str = "string or an instance of ArrayBuffer, TypedArray, DataView, or Buffer";
847
848/// `val_bytes_at` that REJECTS a value which is neither a string nor byte-like,
849/// as node does. `pbkdf2Sync(5, …)` deriving a key from the text "5" is the
850/// worst kind of silent pass-through: the result is a plausible key for the
851/// wrong input.
852fn bytes_arg(args: &[Value], i: usize, name: &str, expected: &str) -> Result<Vec<u8>, String> {
853    let v = args.get(i).cloned().unwrap_or(Value::Undef);
854    if super::buffer::view_bytes(&v).is_none()
855        && with_host(|h| h.as_str(&v)).is_none()
856        && !(expected == HKDF_IKM && is_key_object(&v))
857    {
858        return Err(crate::host::invalid_arg_type(
859            name, "argument", expected, &v,
860        ));
861    }
862    Ok(val_bytes_at(args, i))
863}
864
865/// `val_bytes` for the arg at index `i` (`Value::Undef` → empty).
866fn val_bytes_at(args: &[Value], i: usize) -> Vec<u8> {
867    args.get(i).map(val_bytes).unwrap_or_default()
868}
869
870/// The trailing argument if it is a callback (async form detection).
871fn trailing_cb(args: &[Value]) -> Option<Value> {
872    args.last()
873        .cloned()
874        .filter(|v| with_host(|h| is_callable(h, v)))
875}
876
877/// The arg at index `i` if it is a plain (non-callable) options object.
878fn opts_object(args: &[Value], i: usize) -> Option<Value> {
879    let v = args.get(i)?.clone();
880    let is_obj = with_host(|h| matches!(h.get(&v), Some(JsObj::Object(_))) && !is_callable(h, &v));
881    is_obj.then_some(v)
882}
883
884/// Queue a derived-key result to an async callback as `(null, buf)` / `(err)`;
885/// if there is no callback, return the value/error synchronously.
886fn deliver_async(cb: Option<Value>, res: Result<Vec<u8>, String>) -> Result<Value, String> {
887    // A KDF's arguments are validated SYNCHRONOUSLY, so a bad digest or bad
888    // scrypt parameter throws out of the call in BOTH forms. This queued the
889    // failure as a callback argument instead — and as a bare string, so the
890    // handler's `err.code` was undefined and `err instanceof Error` false.
891    let out = res?;
892    match cb.filter(|v| with_host(|h| is_callable(h, v))) {
893        Some(cb) => {
894            let bufv = super::buffer::from_bytes(&out);
895            with_host(|h| {
896                let nullv = h.null();
897                h.queue_micro(cb, vec![nullv, bufv]);
898            });
899            Ok(Value::Undef)
900        }
901        None => Ok(super::buffer::from_bytes(&out)),
902    }
903}
904
905/// `pbkdf2Sync(password, salt, iterations, keylen, digest)` and its callback
906/// form, which differ only in how the finished bytes are delivered.
907fn pbkdf2(args: &[Value], cb: Option<Value>) -> Result<Value, String> {
908    let digest = arg_str(args, 4).to_ascii_lowercase();
909    let res = pbkdf2_derive(
910        &digest,
911        &bytes_arg(args, 0, "password", KDF_BYTES)?,
912        &bytes_arg(args, 1, "salt", KDF_BYTES)?,
913        super::arg_num(args, 2) as u32,
914        super::arg_num(args, 3).max(0.0) as usize,
915    );
916    deliver_async(cb, res)
917}
918
919/// `scryptSync(password, salt, keylen[, options])` and its callback form.
920fn scrypt(args: &[Value], cb: Option<Value>) -> Result<Value, String> {
921    let keylen = super::arg_num(args, 2).max(0.0) as usize;
922    let res = scrypt_derive(
923        &bytes_arg(args, 0, "password", KDF_BYTES)?,
924        &bytes_arg(args, 1, "salt", KDF_BYTES)?,
925        keylen,
926        opts_object(args, 3),
927    );
928    deliver_async(cb, res)
929}
930
931/// `hkdfSync(digest, ikm, salt, info, keylen)` and its callback form.
932fn hkdf(args: &[Value], cb: Option<Value>) -> Result<Value, String> {
933    let digest = arg_str(args, 0).to_ascii_lowercase();
934    let res = hkdf_derive(
935        &digest,
936        &bytes_arg(args, 1, "ikm", HKDF_IKM)?,
937        &bytes_arg(args, 2, "salt", HKDF_BYTES)?,
938        &bytes_arg(args, 3, "info", HKDF_BYTES)?,
939        super::arg_num(args, 4).max(0.0) as usize,
940    );
941    deliver_async(cb, res)
942}
943
944/// PBKDF2-HMAC derivation over a supported digest.
945fn pbkdf2_derive(
946    digest: &str,
947    pass: &[u8],
948    salt: &[u8],
949    iters: u32,
950    keylen: usize,
951) -> Result<Vec<u8>, String> {
952    let mut out = vec![0u8; keylen];
953    match digest {
954        "sha1" => pbkdf2::pbkdf2_hmac::<Sha1>(pass, salt, iters, &mut out),
955        "sha256" => pbkdf2::pbkdf2_hmac::<Sha256>(pass, salt, iters, &mut out),
956        "sha512" => pbkdf2::pbkdf2_hmac::<Sha512>(pass, salt, iters, &mut out),
957        "md5" => pbkdf2::pbkdf2_hmac::<Md5>(pass, salt, iters, &mut out),
958        _ => {
959            return Err(crate::host::coded_error(
960                "TypeError",
961                "ERR_CRYPTO_INVALID_DIGEST",
962                &format!("Invalid digest: {digest}"),
963            ))
964        }
965    }
966    Ok(out)
967}
968
969/// scrypt derivation. Reads node's `N`/`cost`, `r`/`blockSize`, `p`/
970/// `parallelization` options (defaults 16384/8/1).
971fn scrypt_derive(
972    pass: &[u8],
973    salt: &[u8],
974    keylen: usize,
975    opts: Option<Value>,
976) -> Result<Vec<u8>, String> {
977    let (mut n, mut r, mut p) = (16384.0f64, 8.0f64, 1.0f64);
978    if let Some(o) = opts {
979        n = opt_num(&o, &["N", "cost"], n);
980        r = opt_num(&o, &["r", "blockSize"], r);
981        p = opt_num(&o, &["p", "parallelization"], p);
982    }
983    let n = n as u64;
984    if n < 2 || (n & (n - 1)) != 0 {
985        // Node names no parameter and the class is a RangeError.
986        return Err(crate::host::coded_error(
987            "RangeError",
988            "ERR_CRYPTO_INVALID_SCRYPT_PARAMS",
989            "Invalid scrypt params",
990        ));
991    }
992    let params = scrypt::Params::new(n.trailing_zeros() as u8, r as u32, p as u32, keylen)
993        .map_err(|e| format!("Error: {e}"))?;
994    let mut out = vec![0u8; keylen];
995    scrypt::scrypt(pass, salt, &params, &mut out).map_err(|e| format!("Error: {e}"))?;
996    Ok(out)
997}
998
999/// HKDF (extract + expand) over a supported digest.
1000fn hkdf_derive(
1001    digest: &str,
1002    ikm: &[u8],
1003    salt: &[u8],
1004    info: &[u8],
1005    keylen: usize,
1006) -> Result<Vec<u8>, String> {
1007    let mut out = vec![0u8; keylen];
1008    let ok = match digest {
1009        "sha1" => Hkdf::<Sha1>::new(Some(salt), ikm).expand(info, &mut out),
1010        "sha256" => Hkdf::<Sha256>::new(Some(salt), ikm).expand(info, &mut out),
1011        "sha512" => Hkdf::<Sha512>::new(Some(salt), ikm).expand(info, &mut out),
1012        "md5" => Hkdf::<Md5>::new(Some(salt), ikm).expand(info, &mut out),
1013        _ => {
1014            return Err(crate::host::coded_error(
1015                "TypeError",
1016                "ERR_CRYPTO_INVALID_DIGEST",
1017                &format!("Invalid digest: {digest}"),
1018            ))
1019        }
1020    };
1021    ok.map_err(|_| "Error: Invalid key length".to_string())?;
1022    Ok(out)
1023}
1024
1025/// Read the first present, finite numeric property among `keys` from an options
1026/// object, else `default`.
1027fn opt_num(obj: &Value, keys: &[&str], default: f64) -> f64 {
1028    with_host(|h| {
1029        if let Some(JsObj::Object(p)) = h.get(obj) {
1030            for k in keys {
1031                if let Some(v) = p.get(*k) {
1032                    let n = h.to_number(v);
1033                    if !n.is_nan() {
1034                        return n;
1035                    }
1036                }
1037            }
1038        }
1039        default
1040    })
1041}
1042
1043/// Build a `Cipheriv`/`Decipheriv` instance object from `(algo, key, iv)`.
1044fn make_cipher(tag: &str, args: &[Value]) -> Result<Value, String> {
1045    let algo = arg_str(args, 0).to_ascii_lowercase();
1046    if !CIPHERS.contains(&algo.as_str()) {
1047        // Node names no algorithm here and the code is what callers branch on.
1048        // This appended the name and carried no code at all.
1049        return Err(crate::host::coded_error(
1050            "Error",
1051            "ERR_CRYPTO_UNKNOWN_CIPHER",
1052            "Unknown cipher",
1053        ));
1054    }
1055    let key = val_bytes_at(args, 1);
1056    let iv = val_bytes_at(args, 2);
1057    let want_key = key_len(&algo);
1058    // A wrong key length is a RangeError and a wrong IV a TypeError — the class
1059    // is part of what `catch` dispatches on, and both were plain `Error`s.
1060    if key.len() != want_key {
1061        return Err(crate::host::coded_error(
1062            "RangeError",
1063            "ERR_CRYPTO_INVALID_KEYLEN",
1064            "Invalid key length",
1065        ));
1066    }
1067    if iv.len() != 16 {
1068        return Err(crate::host::coded_error(
1069            "TypeError",
1070            "ERR_CRYPTO_INVALID_IV",
1071            "Invalid initialization vector",
1072        ));
1073    }
1074    Ok(with_host(|h| {
1075        let keyv = h.new_array(key.iter().map(|b| Value::Float(*b as f64)).collect());
1076        let ivv = h.new_array(iv.iter().map(|b| Value::Float(*b as f64)).collect());
1077        let data = h.new_array(Vec::new());
1078        let mut m = IndexMap::new();
1079        m.insert("@@native".into(), h.new_str(tag));
1080        m.insert("@@algo".into(), h.new_str(algo));
1081        m.insert("@@key".into(), keyv);
1082        m.insert("@@iv".into(), ivv);
1083        m.insert("@@data".into(), data);
1084        h.new_object(m)
1085    }))
1086}
1087
1088/// Cipher `update` input: a Buffer's bytes, else the string decoded by the input
1089/// encoding (arg 1, default utf8).
1090/// AES-CBC (Pkcs7) / AES-CTR transform. `encrypt` selects direction (CTR is
1091/// symmetric so the flag is unused there).
1092fn cipher_crypt(
1093    algo: &str,
1094    key: &[u8],
1095    iv: &[u8],
1096    data: &[u8],
1097    encrypt: bool,
1098) -> Result<Vec<u8>, String> {
1099    let keyerr = || {
1100        crate::host::coded_error(
1101            "RangeError",
1102            "ERR_CRYPTO_INVALID_KEYLEN",
1103            "Invalid key length",
1104        )
1105    };
1106    // OpenSSL's own message, under the code node reports for it.
1107    let decerr = || {
1108        crate::host::coded_error(
1109            "Error",
1110            "ERR_OSSL_BAD_DECRYPT",
1111            "error:1C800064:Provider routines::bad decrypt",
1112        )
1113    };
1114    match (algo, encrypt) {
1115        ("aes-128-cbc", true) => Ok(cbc::Encryptor::<aes::Aes128>::new_from_slices(key, iv)
1116            .map_err(|_| keyerr())?
1117            .encrypt_padded_vec_mut::<Pkcs7>(data)),
1118        ("aes-192-cbc", true) => Ok(cbc::Encryptor::<aes::Aes192>::new_from_slices(key, iv)
1119            .map_err(|_| keyerr())?
1120            .encrypt_padded_vec_mut::<Pkcs7>(data)),
1121        ("aes-256-cbc", true) => Ok(cbc::Encryptor::<aes::Aes256>::new_from_slices(key, iv)
1122            .map_err(|_| keyerr())?
1123            .encrypt_padded_vec_mut::<Pkcs7>(data)),
1124        ("aes-128-cbc", false) => cbc::Decryptor::<aes::Aes128>::new_from_slices(key, iv)
1125            .map_err(|_| keyerr())?
1126            .decrypt_padded_vec_mut::<Pkcs7>(data)
1127            .map_err(|_| decerr()),
1128        ("aes-192-cbc", false) => cbc::Decryptor::<aes::Aes192>::new_from_slices(key, iv)
1129            .map_err(|_| keyerr())?
1130            .decrypt_padded_vec_mut::<Pkcs7>(data)
1131            .map_err(|_| decerr()),
1132        ("aes-256-cbc", false) => cbc::Decryptor::<aes::Aes256>::new_from_slices(key, iv)
1133            .map_err(|_| keyerr())?
1134            .decrypt_padded_vec_mut::<Pkcs7>(data)
1135            .map_err(|_| decerr()),
1136        ("aes-128-ctr", _) => {
1137            let mut buf = data.to_vec();
1138            ctr::Ctr128BE::<aes::Aes128>::new_from_slices(key, iv)
1139                .map_err(|_| keyerr())?
1140                .apply_keystream(&mut buf);
1141            Ok(buf)
1142        }
1143        ("aes-192-ctr", _) => {
1144            let mut buf = data.to_vec();
1145            ctr::Ctr128BE::<aes::Aes192>::new_from_slices(key, iv)
1146                .map_err(|_| keyerr())?
1147                .apply_keystream(&mut buf);
1148            Ok(buf)
1149        }
1150        ("aes-256-ctr", _) => {
1151            let mut buf = data.to_vec();
1152            ctr::Ctr128BE::<aes::Aes256>::new_from_slices(key, iv)
1153                .map_err(|_| keyerr())?
1154                .apply_keystream(&mut buf);
1155            Ok(buf)
1156        }
1157        _ => Err(format!("Error: Unsupported cipher: {algo}")),
1158    }
1159}
1160
1161/// Required key length in bytes for a supported cipher name.
1162fn key_len(algo: &str) -> usize {
1163    if algo.starts_with("aes-128") {
1164        16
1165    } else if algo.starts_with("aes-192") {
1166        24
1167    } else {
1168        32
1169    }
1170}
1171
1172/// Encode bytes for a cipher `update`/`final` (or one-shot `hash`) output.
1173fn encode_out(bytes: &[u8], enc: Option<&str>) -> Value {
1174    match enc {
1175        Some("hex") => with_host(|h| h.new_str(to_hex(bytes))),
1176        Some("base64") | Some("base64url") => with_host(|h| h.new_str(to_base64(bytes))),
1177        Some("latin1") | Some("binary") => {
1178            with_host(|h| h.new_str(bytes.iter().map(|b| *b as char).collect::<String>()))
1179        }
1180        Some("utf8") | Some("utf-8") => {
1181            with_host(|h| h.new_str(String::from_utf8_lossy(bytes).into_owned()))
1182        }
1183        _ => super::buffer::from_bytes(bytes),
1184    }
1185}
1186
1187/// `getCipherInfo(name)` → `{ name, nid, blockSize, ivLength, mode, keyLength }`.
1188fn cipher_info(algo: &str) -> Result<Value, String> {
1189    if !CIPHERS.contains(&algo) {
1190        return Ok(Value::Undef);
1191    }
1192    let nid = match algo {
1193        "aes-128-cbc" => 419,
1194        "aes-192-cbc" => 423,
1195        "aes-256-cbc" => 427,
1196        "aes-128-ctr" => 904,
1197        "aes-192-ctr" => 905,
1198        _ => 906,
1199    };
1200    let (mode, block) = if algo.ends_with("ctr") {
1201        ("ctr", 1)
1202    } else {
1203        ("cbc", 16)
1204    };
1205    Ok(with_host(|h| {
1206        let mut m = IndexMap::new();
1207        m.insert("mode".into(), h.new_str(mode));
1208        m.insert("name".into(), h.new_str(algo));
1209        m.insert("nid".into(), Value::Float(nid as f64));
1210        m.insert("keyLength".into(), Value::Float(key_len(algo) as f64));
1211        m.insert("blockSize".into(), Value::Float(block as f64));
1212        m.insert("ivLength".into(), Value::Float(16.0));
1213        h.new_object(m)
1214    }))
1215}
1216
1217/// `randomFillSync`/`randomFill` core: fill `buf[offset..offset+size]` with
1218/// CSPRNG bytes in place, returning the same Buffer.
1219fn random_fill(args: &[Value]) -> Result<Value, String> {
1220    let buf = args.first().cloned().unwrap_or(Value::Undef);
1221    if super::native_tag(&buf).as_deref() != Some("Buffer") {
1222        return Err("Error: The \"buf\" argument must be a Buffer".into());
1223    }
1224    let cur = val_bytes(&buf);
1225    let len = cur.len();
1226    let offset = if args.len() > 1 {
1227        super::arg_num(args, 1).max(0.0) as usize
1228    } else {
1229        0
1230    };
1231    let offset = offset.min(len);
1232    let size = if args.len() > 2 {
1233        super::arg_num(args, 2).max(0.0) as usize
1234    } else {
1235        len - offset
1236    };
1237    let end = (offset + size).min(len);
1238    let mut rnd = vec![0u8; end.saturating_sub(offset)];
1239    if let Err(e) = getrandom::getrandom(&mut rnd) {
1240        return Err(format!("Error: failed to generate random bytes: {e}"));
1241    }
1242    let mut out = cur;
1243    out[offset..end].copy_from_slice(&rnd);
1244    with_host(|h| {
1245        let arr = match h.get(&buf) {
1246            Some(JsObj::Object(p)) => p.get("@@bytes").cloned(),
1247            _ => None,
1248        };
1249        if let Some(a) = arr {
1250            if let Some(JsObj::Array(items)) = h.get_mut(&a) {
1251                *items = out.iter().map(|b| Value::Float(*b as f64)).collect();
1252            }
1253        }
1254    });
1255    Ok(buf)
1256}
1257
1258/// `randomUUIDv7()` — RFC 9562 v7: 48-bit unix-ms timestamp prefix, version 7,
1259/// variant, and CSPRNG tail.
1260fn uuid_v7() -> Result<Value, String> {
1261    let ms = std::time::SystemTime::now()
1262        .duration_since(std::time::UNIX_EPOCH)
1263        .map(|d| d.as_millis() as u64)
1264        .unwrap_or(0);
1265    let mut b = [0u8; 16];
1266    if let Err(e) = getrandom::getrandom(&mut b) {
1267        return Err(format!("Error: failed to generate random bytes: {e}"));
1268    }
1269    b[0..6].copy_from_slice(&ms.to_be_bytes()[2..8]);
1270    b[6] = (b[6] & 0x0f) | 0x70;
1271    b[8] = (b[8] & 0x3f) | 0x80;
1272    let h = to_hex(&b);
1273    let uuid = format!(
1274        "{}-{}-{}-{}-{}",
1275        &h[0..8],
1276        &h[8..12],
1277        &h[12..16],
1278        &h[16..20],
1279        &h[20..32]
1280    );
1281    Ok(with_host(|host| host.new_str(uuid)))
1282}
1283
1284/// A uniform random `u64` in `[0, range)` via rejection sampling over 8 CSPRNG
1285/// bytes (discards the biased tail so the distribution stays exactly uniform).
1286fn random_below(range: u64) -> Result<u64, getrandom::Error> {
1287    // Largest multiple of `range` that fits in u64; values at/above it are biased.
1288    let limit = u64::MAX - (u64::MAX % range);
1289    loop {
1290        let mut b = [0u8; 8];
1291        getrandom::getrandom(&mut b)?;
1292        let n = u64::from_le_bytes(b);
1293        if n < limit {
1294            return Ok(n % range);
1295        }
1296    }
1297}
1298
1299fn decode(s: &str, enc: &str) -> Vec<u8> {
1300    match enc.to_ascii_lowercase().as_str() {
1301        "hex" => super::from_hex(s),
1302        "base64" | "base64url" => super::from_base64(s),
1303        "ascii" | "latin1" | "binary" => s.chars().map(|c| c as u8).collect(),
1304        _ => s.as_bytes().to_vec(),
1305    }
1306}
1307
1308// ════════════════════════════════════════════════════════════════════════
1309//  Asymmetric cryptography: key generation, KeyObjects, sign/verify, RSA
1310//  encryption, Diffie-Hellman / ECDH, primes, argon2, X.509.
1311// ════════════════════════════════════════════════════════════════════════
1312
1313/// A key object for an asymmetric key: carries `type` (`private`/`public`),
1314/// `asymmetricKeyType`, and the PEM material in the hidden `@@pem`.
1315///
1316/// The class is the LEAF one node hands back — `PublicKeyObject` or
1317/// `PrivateKeyObject` — so the chain reaches `asymmetricKeyType` where node
1318/// keeps it. Both values are read through prototype getters, which is why they
1319/// live in hidden slots rather than as own enumerable properties.
1320fn key_object(kind: &str, asym: &str, pem: &str) -> Value {
1321    let class = if kind == "private" {
1322        "PrivateKeyObject"
1323    } else {
1324        "PublicKeyObject"
1325    };
1326    with_host(|h| {
1327        let mut m = IndexMap::new();
1328        m.insert("@@native".into(), h.new_str(class));
1329        m.insert("@@type".into(), h.new_str(kind));
1330        m.insert("@@asymmetricKeyType".into(), h.new_str(asym));
1331        m.insert("@@pem".into(), h.new_str(pem));
1332        h.new_object(m)
1333    })
1334}
1335
1336/// Whether `v` is any of the key-object classes.
1337fn is_key_object(v: &Value) -> bool {
1338    matches!(
1339        super::native_tag(v).as_deref(),
1340        Some("KeyObject" | "SecretKeyObject" | "PublicKeyObject" | "PrivateKeyObject")
1341    )
1342}
1343
1344/// A secret (symmetric) `KeyObject` wrapping raw bytes.
1345fn secret_key_object(bytes: &[u8]) -> Value {
1346    with_host(|h| {
1347        let arr = h.new_array(bytes.iter().map(|b| Value::Float(*b as f64)).collect());
1348        let mut m = IndexMap::new();
1349        m.insert("@@native".into(), h.new_str("SecretKeyObject"));
1350        m.insert("@@type".into(), h.new_str("secret"));
1351        m.insert("@@secret".into(), arr);
1352        h.new_object(m)
1353    })
1354}
1355
1356/// Wrap DER bytes as a PEM block with the given label (64-char lines, LF).
1357fn pem_wrap(label: &str, der: &[u8]) -> String {
1358    let b64 = to_base64(der);
1359    let mut s = format!("-----BEGIN {label}-----\n");
1360    for chunk in b64.as_bytes().chunks(64) {
1361        s.push_str(std::str::from_utf8(chunk).unwrap_or_default());
1362        s.push('\n');
1363    }
1364    s.push_str(&format!("-----END {label}-----\n"));
1365    s
1366}
1367
1368/// Extract the DER bytes from a single PEM block (any label).
1369fn pem_body(pem: &str) -> Vec<u8> {
1370    let body: String = pem.lines().filter(|l| !l.starts_with("-----")).collect();
1371    super::from_base64(&body)
1372}
1373
1374/// PKCS#8 PEM for a raw X25519 private key (OID 1.3.101.110).
1375fn x25519_private_pem(raw: &[u8]) -> String {
1376    let mut der = vec![
1377        0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x6e, 0x04, 0x22, 0x04,
1378        0x20,
1379    ];
1380    der.extend_from_slice(raw);
1381    pem_wrap("PRIVATE KEY", &der)
1382}
1383
1384/// SPKI PEM for a raw X25519 public key.
1385fn x25519_public_pem(raw: &[u8]) -> String {
1386    let mut der = vec![
1387        0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x6e, 0x03, 0x21, 0x00,
1388    ];
1389    der.extend_from_slice(raw);
1390    pem_wrap("PUBLIC KEY", &der)
1391}
1392
1393/// The trailing 32 raw bytes of an X25519 PKCS#8/SPKI PEM.
1394fn x25519_raw(pem: &str) -> Option<[u8; 32]> {
1395    let der = pem_body(pem);
1396    if der.len() < 32 {
1397        return None;
1398    }
1399    let mut out = [0u8; 32];
1400    out.copy_from_slice(&der[der.len() - 32..]);
1401    Some(out)
1402}
1403
1404/// The options object at `args[1]` for key generation.
1405fn keygen_encoding(opts: Option<&Value>, priv_side: bool) -> Option<String> {
1406    let o = opts?;
1407    let field = if priv_side {
1408        "privateKeyEncoding"
1409    } else {
1410        "publicKeyEncoding"
1411    };
1412    with_host(|h| {
1413        let JsObj::Object(p) = h.get(o)? else {
1414            return None;
1415        };
1416        let enc = p.get(field)?;
1417        let JsObj::Object(e) = h.get(enc)? else {
1418            return None;
1419        };
1420        e.get("format").map(|v| h.str_of(v))
1421    })
1422}
1423
1424/// `generateKeyPairSync(type, opts)`: RSA / EC (P-256, P-384) / Ed25519 / X25519.
1425/// Returns `{ publicKey, privateKey }` — PEM strings when the matching
1426/// `*KeyEncoding.format` is `'pem'`, else `KeyObject`s.
1427fn generate_key_pair(kind: &str, opts: Option<&Value>) -> Result<Value, String> {
1428    let mut rng = rand_core::OsRng;
1429    let (asym, priv_pem, pub_pem) = match kind {
1430        "rsa" => {
1431            let bits = opt_num(opts.unwrap_or(&Value::Undef), &["modulusLength"], 2048.0) as usize;
1432            let sk = rsa::RsaPrivateKey::new(&mut rng, bits).map_err(|e| format!("Error: {e}"))?;
1433            let pk = rsa::RsaPublicKey::from(&sk);
1434            let priv_pem = sk
1435                .to_pkcs8_pem(LineEnding::LF)
1436                .map_err(|e| format!("Error: {e}"))?
1437                .to_string();
1438            let pub_pem = pk
1439                .to_public_key_pem(LineEnding::LF)
1440                .map_err(|e| format!("Error: {e}"))?;
1441            ("rsa", priv_pem, pub_pem)
1442        }
1443        "ec" => {
1444            let curve = opt_str(opts.unwrap_or(&Value::Undef), "namedCurve");
1445            match ec_curve_id(&curve) {
1446                Some("p256") => {
1447                    let sk = p256::SecretKey::random(&mut rng);
1448                    let priv_pem = sk
1449                        .to_pkcs8_pem(LineEnding::LF)
1450                        .map_err(|e| format!("Error: {e}"))?
1451                        .to_string();
1452                    let pub_pem = sk
1453                        .public_key()
1454                        .to_public_key_pem(LineEnding::LF)
1455                        .map_err(|e| format!("Error: {e}"))?;
1456                    ("ec", priv_pem, pub_pem)
1457                }
1458                Some("p384") => {
1459                    let sk = p384::SecretKey::random(&mut rng);
1460                    let priv_pem = sk
1461                        .to_pkcs8_pem(LineEnding::LF)
1462                        .map_err(|e| format!("Error: {e}"))?
1463                        .to_string();
1464                    let pub_pem = sk
1465                        .public_key()
1466                        .to_public_key_pem(LineEnding::LF)
1467                        .map_err(|e| format!("Error: {e}"))?;
1468                    ("ec", priv_pem, pub_pem)
1469                }
1470                _ => return Err(format!("Error: Unsupported EC curve: {curve}")),
1471            }
1472        }
1473        "ed25519" => {
1474            let sk = ed25519_dalek::SigningKey::generate(&mut rng);
1475            let priv_pem = sk
1476                .to_pkcs8_pem(LineEnding::LF)
1477                .map_err(|e| format!("Error: {e}"))?
1478                .to_string();
1479            let pub_pem = sk
1480                .verifying_key()
1481                .to_public_key_pem(LineEnding::LF)
1482                .map_err(|e| format!("Error: {e}"))?;
1483            ("ed25519", priv_pem, pub_pem)
1484        }
1485        "x25519" => {
1486            let sk = x25519_dalek::StaticSecret::random_from_rng(rng);
1487            let pk = x25519_dalek::PublicKey::from(&sk);
1488            (
1489                "x25519",
1490                x25519_private_pem(&sk.to_bytes()),
1491                x25519_public_pem(pk.as_bytes()),
1492            )
1493        }
1494        _ => return Err(format!("Error: Unsupported key type: {kind}")),
1495    };
1496    let pub_is_pem = keygen_encoding(opts, false).as_deref() == Some("pem");
1497    let priv_is_pem = keygen_encoding(opts, true).as_deref() == Some("pem");
1498    let publik = if pub_is_pem {
1499        with_host(|h| h.new_str(pub_pem.clone()))
1500    } else {
1501        key_object("public", asym, &pub_pem)
1502    };
1503    let privat = if priv_is_pem {
1504        with_host(|h| h.new_str(priv_pem.clone()))
1505    } else {
1506        key_object("private", asym, &priv_pem)
1507    };
1508    Ok(with_host(|h| {
1509        let mut m = IndexMap::new();
1510        m.insert("publicKey".into(), publik);
1511        m.insert("privateKey".into(), privat);
1512        h.new_object(m)
1513    }))
1514}
1515
1516/// Map a Node EC curve name to an internal id.
1517fn ec_curve_id(name: &str) -> Option<&'static str> {
1518    match name {
1519        "P-256" | "prime256v1" | "secp256r1" => Some("p256"),
1520        "P-384" | "secp384r1" => Some("p384"),
1521        _ => None,
1522    }
1523}
1524
1525/// A string option on an options object (empty if absent).
1526fn opt_str(obj: &Value, key: &str) -> String {
1527    with_host(|h| match h.get(obj) {
1528        Some(JsObj::Object(p)) => p.get(key).map(|v| h.str_of(v)).unwrap_or_default(),
1529        _ => String::new(),
1530    })
1531}
1532
1533/// Detect the asymmetric type of a private-key PEM/DER by trial parsing.
1534fn detect_private(bytes: &[u8]) -> Option<&'static str> {
1535    let pem = std::str::from_utf8(bytes).ok();
1536    if pem
1537        .map(|p| {
1538            rsa::RsaPrivateKey::from_pkcs8_pem(p).is_ok()
1539                || rsa::RsaPrivateKey::from_pkcs1_pem(p).is_ok()
1540        })
1541        .unwrap_or(false)
1542        || rsa::RsaPrivateKey::from_pkcs8_der(bytes).is_ok()
1543    {
1544        return Some("rsa");
1545    }
1546    if pem
1547        .map(|p| p256::SecretKey::from_pkcs8_pem(p).is_ok())
1548        .unwrap_or(false)
1549    {
1550        return Some("ec");
1551    }
1552    if pem
1553        .map(|p| p384::SecretKey::from_pkcs8_pem(p).is_ok())
1554        .unwrap_or(false)
1555    {
1556        return Some("ec");
1557    }
1558    if pem
1559        .map(|p| ed25519_dalek::SigningKey::from_pkcs8_pem(p).is_ok())
1560        .unwrap_or(false)
1561    {
1562        return Some("ed25519");
1563    }
1564    if pem.map(|p| p.contains("PRIVATE KEY")).unwrap_or(false)
1565        && x25519_raw(pem.unwrap_or("")).is_some()
1566    {
1567        // X25519 PKCS#8 is a fixed 48-byte structure; distinguish by OID byte.
1568        let der = pem_body(pem.unwrap_or(""));
1569        if der.len() == 48 && der[9..12] == [0x2b, 0x65, 0x6e] {
1570            return Some("x25519");
1571        }
1572    }
1573    None
1574}
1575
1576/// Detect the asymmetric type of a public-key PEM/DER by trial parsing.
1577fn detect_public(bytes: &[u8]) -> Option<&'static str> {
1578    let pem = std::str::from_utf8(bytes).ok();
1579    if pem
1580        .map(|p| rsa::RsaPublicKey::from_public_key_pem(p).is_ok())
1581        .unwrap_or(false)
1582    {
1583        return Some("rsa");
1584    }
1585    if pem
1586        .map(|p| p256::PublicKey::from_public_key_pem(p).is_ok())
1587        .unwrap_or(false)
1588    {
1589        return Some("ec");
1590    }
1591    if pem
1592        .map(|p| p384::PublicKey::from_public_key_pem(p).is_ok())
1593        .unwrap_or(false)
1594    {
1595        return Some("ec");
1596    }
1597    if pem
1598        .map(|p| ed25519_dalek::VerifyingKey::from_public_key_pem(p).is_ok())
1599        .unwrap_or(false)
1600    {
1601        return Some("ed25519");
1602    }
1603    if let Some(p) = pem {
1604        let der = pem_body(p);
1605        if der.len() == 44 && der[6..9] == [0x2b, 0x65, 0x6e] {
1606            return Some("x25519");
1607        }
1608    }
1609    None
1610}
1611
1612/// `createPrivateKey(input)` → a private `KeyObject`.
1613fn create_private_key(input: Option<&Value>) -> Result<Value, String> {
1614    let bytes = input.map(key_material).unwrap_or_default();
1615    let asym = detect_private(&bytes).ok_or("Error: Failed to read private key")?;
1616    let pem = String::from_utf8_lossy(&bytes).into_owned();
1617    Ok(key_object("private", asym, &pem))
1618}
1619
1620/// `createPublicKey(input)` → a public `KeyObject`. Accepts a public key, a
1621/// private key/`KeyObject` (derives the public half), or a PEM/DER buffer.
1622fn create_public_key(input: Option<&Value>) -> Result<Value, String> {
1623    let bytes = input.map(key_material).unwrap_or_default();
1624    if let Some(asym) = detect_public(&bytes) {
1625        let pem = String::from_utf8_lossy(&bytes).into_owned();
1626        return Ok(key_object("public", asym, &pem));
1627    }
1628    // Derive the public key from a private key.
1629    if let Some(asym) = detect_private(&bytes) {
1630        let pem = public_pem_from_private(&bytes, asym)?;
1631        return Ok(key_object("public", asym, &pem));
1632    }
1633    Err("Error: Failed to read public key".into())
1634}
1635
1636/// The SPKI public PEM derived from a private-key PEM/DER of a known type.
1637fn public_pem_from_private(bytes: &[u8], asym: &str) -> Result<String, String> {
1638    let pem = std::str::from_utf8(bytes).ok();
1639    let err = || "Error: Failed to derive public key".to_string();
1640    match asym {
1641        "rsa" => {
1642            let sk = pem
1643                .and_then(|p| rsa::RsaPrivateKey::from_pkcs8_pem(p).ok())
1644                .or_else(|| rsa::RsaPrivateKey::from_pkcs8_der(bytes).ok())
1645                .ok_or_else(err)?;
1646            rsa::RsaPublicKey::from(&sk)
1647                .to_public_key_pem(LineEnding::LF)
1648                .map_err(|e| format!("Error: {e}"))
1649        }
1650        "ec" => {
1651            if let Some(sk) = pem.and_then(|p| p256::SecretKey::from_pkcs8_pem(p).ok()) {
1652                return sk
1653                    .public_key()
1654                    .to_public_key_pem(LineEnding::LF)
1655                    .map_err(|e| format!("Error: {e}"));
1656            }
1657            let sk = pem
1658                .and_then(|p| p384::SecretKey::from_pkcs8_pem(p).ok())
1659                .ok_or_else(err)?;
1660            sk.public_key()
1661                .to_public_key_pem(LineEnding::LF)
1662                .map_err(|e| format!("Error: {e}"))
1663        }
1664        "ed25519" => {
1665            let sk = pem
1666                .and_then(|p| ed25519_dalek::SigningKey::from_pkcs8_pem(p).ok())
1667                .ok_or_else(err)?;
1668            sk.verifying_key()
1669                .to_public_key_pem(LineEnding::LF)
1670                .map_err(|e| format!("Error: {e}"))
1671        }
1672        "x25519" => {
1673            let raw = pem.and_then(x25519_raw).ok_or_else(err)?;
1674            let sk = x25519_dalek::StaticSecret::from(raw);
1675            Ok(x25519_public_pem(
1676                x25519_dalek::PublicKey::from(&sk).as_bytes(),
1677            ))
1678        }
1679        _ => Err(err()),
1680    }
1681}
1682
1683/// Raw key material of a key argument: a `KeyObject`'s stored PEM, a `{ key }`
1684/// wrapper's inner key, a Buffer's bytes, or a PEM/DER string's bytes.
1685fn key_material(v: &Value) -> Vec<u8> {
1686    if is_key_object(v) {
1687        return with_host(|h| match h.get(v) {
1688            Some(JsObj::Object(p)) => p.get("@@pem").map(|s| h.str_of(s)).unwrap_or_default(),
1689            _ => String::new(),
1690        })
1691        .into_bytes();
1692    }
1693    if super::native_tag(v).as_deref() != Some("Buffer") {
1694        let inner = with_host(|h| match h.get(v) {
1695            Some(JsObj::Object(p)) => p.get("key").cloned(),
1696            _ => None,
1697        });
1698        if let Some(k) = inner {
1699            return key_material(&k);
1700        }
1701    }
1702    val_bytes(v)
1703}
1704
1705/// A `Sign`/`Verify` streaming instance (`update(...).sign(key)` /
1706/// `update(...).verify(key, sig)`).
1707fn new_sign_verify(tag: &str, algo: &str) -> Value {
1708    with_host(|h| {
1709        let data = h.new_array(Vec::new());
1710        let mut m = IndexMap::new();
1711        m.insert("@@native".into(), h.new_str(tag));
1712        m.insert("@@algo".into(), h.new_str(algo.to_ascii_lowercase()));
1713        m.insert("@@data".into(), data);
1714        h.new_object(m)
1715    })
1716}
1717
1718/// Normalize a Node signature-algorithm name to a bare digest (`sha256`).
1719fn digest_of(algo: &str) -> String {
1720    let a = algo.to_ascii_lowercase();
1721    let a = a.strip_prefix("rsa-").unwrap_or(&a);
1722    a.replace('-', "")
1723}
1724
1725/// One-shot asymmetric sign over `data` with a private key (auto key-type).
1726fn sign_data(key: &[u8], algo: &str, data: &[u8]) -> Result<Vec<u8>, String> {
1727    let pem = std::str::from_utf8(key).ok();
1728    let d = digest_of(algo);
1729    if let Some(sk) = pem
1730        .and_then(|p| rsa::RsaPrivateKey::from_pkcs8_pem(p).ok())
1731        .or_else(|| pem.and_then(|p| rsa::RsaPrivateKey::from_pkcs1_pem(p).ok()))
1732        .or_else(|| rsa::RsaPrivateKey::from_pkcs8_der(key).ok())
1733    {
1734        return rsa_sign(&sk, &d, data);
1735    }
1736    if let Some(sk) = pem.and_then(|p| p256::ecdsa::SigningKey::from_pkcs8_pem(p).ok()) {
1737        let sig: p256::ecdsa::Signature = sk.try_sign(data).map_err(|e| format!("Error: {e}"))?;
1738        return Ok(sig.to_der().as_bytes().to_vec());
1739    }
1740    if let Some(sk) = pem.and_then(|p| p384::ecdsa::SigningKey::from_pkcs8_pem(p).ok()) {
1741        let sig: p384::ecdsa::Signature = sk.try_sign(data).map_err(|e| format!("Error: {e}"))?;
1742        return Ok(sig.to_der().as_bytes().to_vec());
1743    }
1744    if let Some(sk) = pem.and_then(|p| ed25519_dalek::SigningKey::from_pkcs8_pem(p).ok()) {
1745        return Ok(sk.sign(data).to_bytes().to_vec());
1746    }
1747    Err("Error: Invalid or unsupported private key for signing".into())
1748}
1749
1750/// RSA PKCS#1 v1.5 signature over `data` for the selected digest.
1751fn rsa_sign(sk: &rsa::RsaPrivateKey, digest: &str, data: &[u8]) -> Result<Vec<u8>, String> {
1752    let sig = match digest {
1753        "sha256" => rsa::pkcs1v15::SigningKey::<Sha256>::new(sk.clone())
1754            .sign(data)
1755            .to_vec(),
1756        "sha384" => rsa::pkcs1v15::SigningKey::<Sha384>::new(sk.clone())
1757            .sign(data)
1758            .to_vec(),
1759        "sha512" => rsa::pkcs1v15::SigningKey::<Sha512>::new(sk.clone())
1760            .sign(data)
1761            .to_vec(),
1762        _ => {
1763            return Err(format!(
1764                "Error: Unsupported digest for RSA signing: {digest}"
1765            ))
1766        }
1767    };
1768    Ok(sig)
1769}
1770
1771/// One-shot asymmetric verify (auto key-type). ECDSA signatures are DER.
1772fn verify_data(key: &[u8], algo: &str, data: &[u8], sig: &[u8]) -> Result<bool, String> {
1773    let pem = std::str::from_utf8(key).ok();
1774    let d = digest_of(algo);
1775    if let Some(pk) = pem
1776        .and_then(|p| rsa::RsaPublicKey::from_public_key_pem(p).ok())
1777        .or_else(|| pem.and_then(|p| rsa::RsaPublicKey::from_pkcs1_pem(p).ok()))
1778        .or_else(|| {
1779            pem.and_then(|p| rsa::RsaPrivateKey::from_pkcs8_pem(p).ok())
1780                .map(|s| rsa::RsaPublicKey::from(&s))
1781        })
1782    {
1783        return rsa_verify(&pk, &d, data, sig);
1784    }
1785    if let Some(vk) = pem
1786        .and_then(|p| p256::ecdsa::VerifyingKey::from_public_key_pem(p).ok())
1787        .or_else(|| {
1788            pem.and_then(|p| p256::ecdsa::SigningKey::from_pkcs8_pem(p).ok())
1789                .map(|s| *s.verifying_key())
1790        })
1791    {
1792        let s = p256::ecdsa::Signature::from_der(sig).map_err(|e| format!("Error: {e}"))?;
1793        return Ok(vk.verify(data, &s).is_ok());
1794    }
1795    if let Some(vk) = pem
1796        .and_then(|p| p384::ecdsa::VerifyingKey::from_public_key_pem(p).ok())
1797        .or_else(|| {
1798            pem.and_then(|p| p384::ecdsa::SigningKey::from_pkcs8_pem(p).ok())
1799                .map(|s| *s.verifying_key())
1800        })
1801    {
1802        let s = p384::ecdsa::Signature::from_der(sig).map_err(|e| format!("Error: {e}"))?;
1803        return Ok(vk.verify(data, &s).is_ok());
1804    }
1805    if let Some(vk) = pem
1806        .and_then(|p| ed25519_dalek::VerifyingKey::from_public_key_pem(p).ok())
1807        .or_else(|| {
1808            pem.and_then(|p| ed25519_dalek::SigningKey::from_pkcs8_pem(p).ok())
1809                .map(|s| s.verifying_key())
1810        })
1811    {
1812        let s = ed25519_dalek::Signature::from_slice(sig).map_err(|e| format!("Error: {e}"))?;
1813        return Ok(vk.verify(data, &s).is_ok());
1814    }
1815    Err("Error: Invalid or unsupported public key for verifying".into())
1816}
1817
1818/// RSA PKCS#1 v1.5 verify for the selected digest.
1819fn rsa_verify(
1820    pk: &rsa::RsaPublicKey,
1821    digest: &str,
1822    data: &[u8],
1823    sig: &[u8],
1824) -> Result<bool, String> {
1825    let signature = match rsa::pkcs1v15::Signature::try_from(sig) {
1826        Ok(s) => s,
1827        Err(_) => return Ok(false),
1828    };
1829    let ok = match digest {
1830        "sha256" => rsa::pkcs1v15::VerifyingKey::<Sha256>::new(pk.clone())
1831            .verify(data, &signature)
1832            .is_ok(),
1833        "sha384" => rsa::pkcs1v15::VerifyingKey::<Sha384>::new(pk.clone())
1834            .verify(data, &signature)
1835            .is_ok(),
1836        "sha512" => rsa::pkcs1v15::VerifyingKey::<Sha512>::new(pk.clone())
1837            .verify(data, &signature)
1838            .is_ok(),
1839        _ => {
1840            return Err(format!(
1841                "Error: Unsupported digest for RSA verifying: {digest}"
1842            ))
1843        }
1844    };
1845    Ok(ok)
1846}
1847
1848/// `Sign`/`Verify` instance dispatch.
1849pub fn sign_verify_instance_call(
1850    tag: &str,
1851    recv: &Value,
1852    method: &str,
1853    args: &[Value],
1854) -> Result<Value, String> {
1855    match method {
1856        "update" => {
1857            let enc = if args.len() > 1 {
1858                arg_str(args, 1)
1859            } else {
1860                "utf8".into()
1861            };
1862            // Any byte-like VIEW is taken as bytes; only a STRING goes through
1863            // the encoding. The Buffer case alone was handled, so
1864            // `hash.update(new TextEncoder().encode(s))` — the standard way to
1865            // hash bytes — stringified the view and hashed
1866            // `"[object Object]"`.
1867            let bytes = update_bytes(args, &enc)?;
1868            with_host(|h| {
1869                if let Some(JsObj::Object(p)) = h.get(recv).cloned() {
1870                    if let Some(arr) = p.get("@@data").cloned() {
1871                        if let Some(JsObj::Array(items)) = h.get_mut(&arr) {
1872                            items.extend(bytes.iter().map(|b| Value::Float(*b as f64)));
1873                        }
1874                    }
1875                }
1876            });
1877            Ok(recv.clone())
1878        }
1879        "sign" => {
1880            let (algo, data) = sign_verify_state(recv);
1881            let key = key_material(args.first().unwrap_or(&Value::Undef));
1882            let sig = sign_data(&key, &algo, &data)?;
1883            let out_enc = if args.len() > 1 {
1884                Some(arg_str(args, 1))
1885            } else {
1886                None
1887            };
1888            Ok(encode_out(&sig, out_enc.as_deref()))
1889        }
1890        "verify" => {
1891            let (algo, data) = sign_verify_state(recv);
1892            let key = key_material(args.first().unwrap_or(&Value::Undef));
1893            let sig = if args.len() > 2 {
1894                decode(&arg_str(args, 1), &arg_str(args, 2))
1895            } else {
1896                val_bytes_at(args, 1)
1897            };
1898            Ok(Value::Bool(verify_data(&key, &algo, &data, &sig)?))
1899        }
1900        _ => Err(crate::host::type_error(&format!(
1901            "{}.{method} is not a function",
1902            tag.to_ascii_lowercase()
1903        ))),
1904    }
1905}
1906
1907/// Read the `@@algo`/`@@data` of a `Sign`/`Verify` instance.
1908fn sign_verify_state(recv: &Value) -> (String, Vec<u8>) {
1909    with_host(|h| {
1910        let (mut algo, mut data) = (String::new(), Vec::new());
1911        if let Some(JsObj::Object(p)) = h.get(recv) {
1912            algo = p.get("@@algo").map(|v| h.str_of(v)).unwrap_or_default();
1913            if let Some(JsObj::Array(items)) = p.get("@@data").and_then(|v| h.get(v)) {
1914                data = items.iter().map(|v| h.to_number(v) as u8).collect();
1915            }
1916        }
1917        (algo, data)
1918    })
1919}
1920
1921/// Read the numeric `padding`/oaepHash options from a key argument object.
1922fn rsa_padding(v: &Value) -> (i64, String) {
1923    let padding = opt_num(v, &["padding"], 4.0) as i64; // RSA_PKCS1_OAEP_PADDING
1924    let oaep = {
1925        let h = opt_str(v, "oaepHash");
1926        if h.is_empty() {
1927            "sha1".to_string()
1928        } else {
1929            h.to_ascii_lowercase()
1930        }
1931    };
1932    (padding, oaep)
1933}
1934
1935/// Parse an RSA public key from PEM/DER (or derive from a private key).
1936fn parse_rsa_public(key: &[u8]) -> Result<rsa::RsaPublicKey, String> {
1937    let pem = std::str::from_utf8(key).ok();
1938    pem.and_then(|p| rsa::RsaPublicKey::from_public_key_pem(p).ok())
1939        .or_else(|| pem.and_then(|p| rsa::RsaPublicKey::from_pkcs1_pem(p).ok()))
1940        .or_else(|| rsa::RsaPublicKey::from_public_key_der(key).ok())
1941        .or_else(|| {
1942            pem.and_then(|p| rsa::RsaPrivateKey::from_pkcs8_pem(p).ok())
1943                .map(|s| rsa::RsaPublicKey::from(&s))
1944        })
1945        .ok_or_else(|| "Error: Failed to parse RSA public key".into())
1946}
1947
1948/// Parse an RSA private key from PEM/DER.
1949fn parse_rsa_private(key: &[u8]) -> Result<rsa::RsaPrivateKey, String> {
1950    let pem = std::str::from_utf8(key).ok();
1951    pem.and_then(|p| rsa::RsaPrivateKey::from_pkcs8_pem(p).ok())
1952        .or_else(|| pem.and_then(|p| rsa::RsaPrivateKey::from_pkcs1_pem(p).ok()))
1953        .or_else(|| rsa::RsaPrivateKey::from_pkcs8_der(key).ok())
1954        .ok_or_else(|| "Error: Failed to parse RSA private key".into())
1955}
1956
1957/// `publicEncrypt` / `privateDecrypt`: OAEP by default, PKCS#1 v1.5 when
1958/// `padding == RSA_PKCS1_PADDING (1)`.
1959fn rsa_public_op(args: &[Value], _public: bool, encrypt: bool) -> Result<Value, String> {
1960    let key_arg = args.first().cloned().unwrap_or(Value::Undef);
1961    let key = key_material(&key_arg);
1962    let (padding, oaep) = rsa_padding(&key_arg);
1963    let data = val_bytes_at(args, 1);
1964    let out = if encrypt {
1965        let pk = parse_rsa_public(&key)?;
1966        let mut rng = rand_core::OsRng;
1967        if padding == 1 {
1968            pk.encrypt(&mut rng, rsa::Pkcs1v15Encrypt, &data)
1969        } else if oaep == "sha256" {
1970            pk.encrypt(&mut rng, rsa::Oaep::new::<Sha256>(), &data)
1971        } else {
1972            pk.encrypt(&mut rng, rsa::Oaep::new::<Sha1>(), &data)
1973        }
1974        .map_err(|e| format!("Error: {e}"))?
1975    } else {
1976        let sk = parse_rsa_private(&key)?;
1977        if padding == 1 {
1978            sk.decrypt(rsa::Pkcs1v15Encrypt, &data)
1979        } else if oaep == "sha256" {
1980            sk.decrypt(rsa::Oaep::new::<Sha256>(), &data)
1981        } else {
1982            sk.decrypt(rsa::Oaep::new::<Sha1>(), &data)
1983        }
1984        .map_err(|e| format!("Error: {e}"))?
1985    };
1986    Ok(super::buffer::from_bytes(&out))
1987}
1988
1989/// `privateEncrypt`: raw RSA PKCS#1 v1.5 (type 1) block signed with the
1990/// private key.
1991fn rsa_private_encrypt(args: &[Value]) -> Result<Value, String> {
1992    let key = key_material(args.first().unwrap_or(&Value::Undef));
1993    let data = val_bytes_at(args, 1);
1994    let sk = parse_rsa_private(&key)?;
1995    let out = sk
1996        .sign(rsa::Pkcs1v15Sign::new_unprefixed(), &data)
1997        .map_err(|e| format!("Error: {e}"))?;
1998    Ok(super::buffer::from_bytes(&out))
1999}
2000
2001/// `publicDecrypt`: recover a `privateEncrypt` block via raw modular
2002/// exponentiation `s^e mod n`, then strip PKCS#1 type-1 padding.
2003fn rsa_public_decrypt(args: &[Value]) -> Result<Value, String> {
2004    use rsa::traits::PublicKeyParts;
2005    let key = key_material(args.first().unwrap_or(&Value::Undef));
2006    let ct = val_bytes_at(args, 1);
2007    let pk = parse_rsa_public(&key)?;
2008    let n = BigUint::from_bytes_be(&pk.n().to_bytes_be());
2009    let e = BigUint::from_bytes_be(&pk.e().to_bytes_be());
2010    let k = pk.n().to_bytes_be().len();
2011    let s = BigUint::from_bytes_be(&ct);
2012    let m = s.modpow(&e, &n);
2013    let mut em = m.to_bytes_be();
2014    while em.len() < k {
2015        em.insert(0, 0);
2016    }
2017    // EM = 0x00 0x01 0xFF..0xFF 0x00 || message
2018    if em.len() < 11 || em[0] != 0x00 || em[1] != 0x01 {
2019        return Err("Error: error:0200006E:rsa routines::padding check failed".into());
2020    }
2021    let sep = em[2..].iter().position(|&b| b == 0x00).map(|i| i + 2);
2022    match sep {
2023        Some(i) => Ok(super::buffer::from_bytes(&em[i + 1..])),
2024        None => Err("Error: error:0200006E:rsa routines::padding check failed".into()),
2025    }
2026}
2027
2028// ── Diffie-Hellman (finite-field) ───────────────────────────────────────
2029
2030/// RFC 2409/3526 MODP group primes (hex), generator 2.
2031const MODP_GROUPS: &[(&str, &str)] = &[
2032    ("modp1", MODP1),
2033    ("modp2", MODP2),
2034    ("modp5", MODP5),
2035    ("modp14", MODP14),
2036    ("modp15", MODP15),
2037    ("modp16", MODP16),
2038    ("modp17", MODP17),
2039    ("modp18", MODP18),
2040];
2041
2042/// Byte-array property of an object as raw bytes.
2043fn obj_bytes(recv: &Value, key: &str) -> Vec<u8> {
2044    with_host(|h| {
2045        if let Some(JsObj::Object(p)) = h.get(recv) {
2046            if let Some(JsObj::Array(it)) = p.get(key).and_then(|v| h.get(v)) {
2047                return it.iter().map(|v| h.to_number(v) as u8).collect();
2048            }
2049        }
2050        Vec::new()
2051    })
2052}
2053
2054/// Store raw bytes as a hidden byte-array property.
2055fn set_obj_bytes(recv: &Value, key: &str, bytes: &[u8]) {
2056    with_host(|h| {
2057        let arr = h.new_array(bytes.iter().map(|b| Value::Float(*b as f64)).collect());
2058        if let Some(JsObj::Object(p)) = h.get_mut(recv) {
2059            p.insert(key.to_string(), arr);
2060        }
2061    });
2062}
2063
2064/// Build a `DiffieHellman` instance from a prime + generator.
2065fn dh_object(prime: &[u8], gen: &[u8]) -> Value {
2066    with_host(|h| {
2067        let pv = h.new_array(prime.iter().map(|b| Value::Float(*b as f64)).collect());
2068        let gv = h.new_array(gen.iter().map(|b| Value::Float(*b as f64)).collect());
2069        let mut m = IndexMap::new();
2070        m.insert("@@native".into(), h.new_str("DiffieHellman"));
2071        m.insert("@@prime".into(), pv);
2072        m.insert("@@gen".into(), gv);
2073        h.new_object(m)
2074    })
2075}
2076
2077/// `createDiffieHellman(primeLength)` or `createDiffieHellman(prime[, generator])`.
2078fn create_diffie_hellman(args: &[Value]) -> Result<Value, String> {
2079    // Numeric first arg → generate a prime of that bit length; generator 2.
2080    let first = args.first().cloned().unwrap_or(Value::Undef);
2081    if matches!(first, Value::Int(_) | Value::Float(_)) {
2082        let bits = super::arg_num(args, 0) as usize;
2083        let prime = gen_prime(bits)?;
2084        return Ok(dh_object(&prime.to_bytes_be(), &[2]));
2085    }
2086    let prime = val_bytes_at(args, 0);
2087    let gen = if args.len() > 1 {
2088        match args.get(1) {
2089            Some(Value::Int(_)) | Some(Value::Float(_)) => {
2090                let g = super::arg_num(args, 1) as u64;
2091                BigUint::from(g).to_bytes_be()
2092            }
2093            _ => val_bytes_at(args, 1),
2094        }
2095    } else {
2096        vec![2]
2097    };
2098    Ok(dh_object(&prime, &gen))
2099}
2100
2101/// `getDiffieHellman(group)` / `createDiffieHellmanGroup(group)`.
2102fn diffie_hellman_group(name: &str) -> Result<Value, String> {
2103    let hex = MODP_GROUPS
2104        .iter()
2105        .find(|(n, _)| *n == name)
2106        .map(|(_, h)| *h)
2107        .ok_or_else(|| format!("Error: Unknown group: {name}"))?;
2108    Ok(dh_object(&super::from_hex(hex), &[2]))
2109}
2110
2111/// `DiffieHellman` instance dispatch.
2112pub fn dh_instance_call(recv: &Value, method: &str, args: &[Value]) -> Result<Value, String> {
2113    let enc = |args: &[Value], i: usize| -> Option<String> {
2114        if args.len() > i {
2115            let s = arg_str(args, i);
2116            if s.is_empty() {
2117                None
2118            } else {
2119                Some(s)
2120            }
2121        } else {
2122            None
2123        }
2124    };
2125    match method {
2126        "generateKeys" => {
2127            let p = BigUint::from_bytes_be(&obj_bytes(recv, "@@prime"));
2128            let g = BigUint::from_bytes_be(&obj_bytes(recv, "@@gen"));
2129            let priv_key = dh_random_priv(&p)?;
2130            let pub_key = g.modpow(&priv_key, &p);
2131            set_obj_bytes(recv, "@@priv", &priv_key.to_bytes_be());
2132            set_obj_bytes(recv, "@@pub", &pub_key.to_bytes_be());
2133            Ok(encode_out(&pub_key.to_bytes_be(), enc(args, 0).as_deref()))
2134        }
2135        "computeSecret" => {
2136            let other = if args.len() > 1
2137                && !arg_str(args, 1).is_empty()
2138                && !matches!(args.first(), Some(v) if super::native_tag(v).as_deref() == Some("Buffer"))
2139            {
2140                decode(&arg_str(args, 0), &arg_str(args, 1))
2141            } else {
2142                val_bytes_at(args, 0)
2143            };
2144            let p = BigUint::from_bytes_be(&obj_bytes(recv, "@@prime"));
2145            let priv_key = BigUint::from_bytes_be(&obj_bytes(recv, "@@priv"));
2146            let their_pub = BigUint::from_bytes_be(&other);
2147            let secret = their_pub.modpow(&priv_key, &p);
2148            let out_enc = if args.len() > 2 { enc(args, 2) } else { None };
2149            Ok(encode_out(&secret.to_bytes_be(), out_enc.as_deref()))
2150        }
2151        "getPrime" => Ok(encode_out(
2152            &obj_bytes(recv, "@@prime"),
2153            enc(args, 0).as_deref(),
2154        )),
2155        "getGenerator" => Ok(encode_out(
2156            &obj_bytes(recv, "@@gen"),
2157            enc(args, 0).as_deref(),
2158        )),
2159        "getPublicKey" => Ok(encode_out(
2160            &obj_bytes(recv, "@@pub"),
2161            enc(args, 0).as_deref(),
2162        )),
2163        "getPrivateKey" => Ok(encode_out(
2164            &obj_bytes(recv, "@@priv"),
2165            enc(args, 0).as_deref(),
2166        )),
2167        "setPublicKey" => {
2168            set_obj_bytes(recv, "@@pub", &val_bytes_at(args, 0));
2169            Ok(recv.clone())
2170        }
2171        "setPrivateKey" => {
2172            set_obj_bytes(recv, "@@priv", &val_bytes_at(args, 0));
2173            Ok(recv.clone())
2174        }
2175        _ => Err(crate::host::type_error(&format!(
2176            "dh.{method} is not a function"
2177        ))),
2178    }
2179}
2180
2181/// A random DH private exponent in `[2, p-2]`.
2182fn dh_random_priv(p: &BigUint) -> Result<BigUint, String> {
2183    let nbytes = p.to_bytes_be().len();
2184    let mut buf = vec![0u8; nbytes];
2185    getrandom::getrandom(&mut buf).map_err(|e| format!("Error: {e}"))?;
2186    let two = BigUint::from(2u32);
2187    let modulus = p - &two; // p-2 range size
2188    let x = BigUint::from_bytes_be(&buf) % &modulus;
2189    Ok(x + &two)
2190}
2191
2192// ── ECDH ────────────────────────────────────────────────────────────────
2193
2194/// `createECDH(curve)` — P-256 / P-384.
2195fn create_ecdh(curve: &str) -> Result<Value, String> {
2196    let id = ec_curve_id(curve).ok_or_else(|| format!("Error: Unsupported curve: {curve}"))?;
2197    Ok(with_host(|h| {
2198        let mut m = IndexMap::new();
2199        m.insert("@@native".into(), h.new_str("ECDH"));
2200        m.insert("@@curve".into(), h.new_str(id));
2201        h.new_object(m)
2202    }))
2203}
2204
2205/// `ECDH` instance dispatch.
2206pub fn ecdh_instance_call(recv: &Value, method: &str, args: &[Value]) -> Result<Value, String> {
2207    let curve = obj_str(recv, "@@curve");
2208    match method {
2209        "generateKeys" => {
2210            let (priv_b, pub_b) = ecdh_generate(&curve)?;
2211            set_obj_bytes(recv, "@@priv", &priv_b);
2212            set_obj_bytes(recv, "@@pub", &pub_b);
2213            let out_enc = if args.len() > 1 {
2214                Some(arg_str(args, 1))
2215            } else {
2216                None
2217            };
2218            Ok(encode_out(&pub_b, out_enc.as_deref()))
2219        }
2220        "computeSecret" => {
2221            let other = if args.len() > 1
2222                && !arg_str(args, 1).is_empty()
2223                && super::native_tag(args.first().unwrap_or(&Value::Undef)).as_deref()
2224                    != Some("Buffer")
2225            {
2226                decode(&arg_str(args, 0), &arg_str(args, 1))
2227            } else {
2228                val_bytes_at(args, 0)
2229            };
2230            let secret = ecdh_compute(&curve, &obj_bytes(recv, "@@priv"), &other)?;
2231            let out_enc = if args.len() > 2 {
2232                Some(arg_str(args, 2))
2233            } else {
2234                None
2235            };
2236            Ok(encode_out(&secret, out_enc.as_deref()))
2237        }
2238        "getPublicKey" => {
2239            let out_enc = if args.len() > 1 {
2240                Some(arg_str(args, 1))
2241            } else {
2242                None
2243            };
2244            Ok(encode_out(&obj_bytes(recv, "@@pub"), out_enc.as_deref()))
2245        }
2246        "getPrivateKey" => {
2247            let out_enc = if !args.is_empty() {
2248                Some(arg_str(args, 0))
2249            } else {
2250                None
2251            };
2252            Ok(encode_out(&obj_bytes(recv, "@@priv"), out_enc.as_deref()))
2253        }
2254        "setPrivateKey" => {
2255            let priv_b = val_bytes_at(args, 0);
2256            let pub_b = ecdh_public_from_private(&curve, &priv_b)?;
2257            set_obj_bytes(recv, "@@priv", &priv_b);
2258            set_obj_bytes(recv, "@@pub", &pub_b);
2259            Ok(recv.clone())
2260        }
2261        _ => Err(crate::host::type_error(&format!(
2262            "ecdh.{method} is not a function"
2263        ))),
2264    }
2265}
2266
2267/// Generate an ECDH keypair; public key is the uncompressed SEC1 point.
2268fn ecdh_generate(curve: &str) -> Result<(Vec<u8>, Vec<u8>), String> {
2269    let mut rng = rand_core::OsRng;
2270    match curve {
2271        "p256" => {
2272            let sk = p256::SecretKey::random(&mut rng);
2273            let pubk = sk.public_key().to_encoded_point(false).as_bytes().to_vec();
2274            Ok((sk.to_bytes().to_vec(), pubk))
2275        }
2276        "p384" => {
2277            let sk = p384::SecretKey::random(&mut rng);
2278            let pubk = sk.public_key().to_encoded_point(false).as_bytes().to_vec();
2279            Ok((sk.to_bytes().to_vec(), pubk))
2280        }
2281        _ => Err(format!("Error: Unsupported curve: {curve}")),
2282    }
2283}
2284
2285/// The uncompressed SEC1 public point for a raw ECDH private scalar.
2286fn ecdh_public_from_private(curve: &str, priv_b: &[u8]) -> Result<Vec<u8>, String> {
2287    match curve {
2288        "p256" => {
2289            let sk = p256::SecretKey::from_slice(priv_b).map_err(|e| format!("Error: {e}"))?;
2290            Ok(sk.public_key().to_encoded_point(false).as_bytes().to_vec())
2291        }
2292        "p384" => {
2293            let sk = p384::SecretKey::from_slice(priv_b).map_err(|e| format!("Error: {e}"))?;
2294            Ok(sk.public_key().to_encoded_point(false).as_bytes().to_vec())
2295        }
2296        _ => Err(format!("Error: Unsupported curve: {curve}")),
2297    }
2298}
2299
2300/// ECDH shared secret (raw X coordinate) from a private scalar + peer point.
2301fn ecdh_compute(curve: &str, priv_b: &[u8], pub_b: &[u8]) -> Result<Vec<u8>, String> {
2302    match curve {
2303        "p256" => {
2304            let sk = p256::SecretKey::from_slice(priv_b).map_err(|e| format!("Error: {e}"))?;
2305            let pk = p256::PublicKey::from_sec1_bytes(pub_b).map_err(|e| format!("Error: {e}"))?;
2306            let shared = p256::ecdh::diffie_hellman(sk.to_nonzero_scalar(), pk.as_affine());
2307            Ok(shared.raw_secret_bytes().to_vec())
2308        }
2309        "p384" => {
2310            let sk = p384::SecretKey::from_slice(priv_b).map_err(|e| format!("Error: {e}"))?;
2311            let pk = p384::PublicKey::from_sec1_bytes(pub_b).map_err(|e| format!("Error: {e}"))?;
2312            let shared = p384::ecdh::diffie_hellman(sk.to_nonzero_scalar(), pk.as_affine());
2313            Ok(shared.raw_secret_bytes().to_vec())
2314        }
2315        _ => Err(format!("Error: Unsupported curve: {curve}")),
2316    }
2317}
2318
2319/// String property helper.
2320fn obj_str(recv: &Value, key: &str) -> String {
2321    with_host(|h| match h.get(recv) {
2322        Some(JsObj::Object(p)) => p.get(key).map(|v| h.str_of(v)).unwrap_or_default(),
2323        _ => String::new(),
2324    })
2325}
2326
2327/// `crypto.diffieHellman({ privateKey, publicKey })` one-shot (EC / X25519).
2328fn diffie_hellman_oneshot(opts: Option<&Value>) -> Result<Value, String> {
2329    let o = opts.ok_or("Error: options object required")?;
2330    let priv_v = with_host(|h| match h.get(o) {
2331        Some(JsObj::Object(p)) => p.get("privateKey").cloned(),
2332        _ => None,
2333    })
2334    .ok_or("Error: privateKey required")?;
2335    let pub_v = with_host(|h| match h.get(o) {
2336        Some(JsObj::Object(p)) => p.get("publicKey").cloned(),
2337        _ => None,
2338    })
2339    .ok_or("Error: publicKey required")?;
2340    let priv_bytes = key_material(&priv_v);
2341    let pub_bytes = key_material(&pub_v);
2342    let asym = detect_private(&priv_bytes).ok_or("Error: unsupported private key")?;
2343    let priv_pem = std::str::from_utf8(&priv_bytes).ok();
2344    let pub_pem = std::str::from_utf8(&pub_bytes).ok();
2345    let secret = match asym {
2346        "ec" => {
2347            if let Some(sk) = priv_pem.and_then(|p| p256::SecretKey::from_pkcs8_pem(p).ok()) {
2348                let pk = pub_pem
2349                    .and_then(|p| p256::PublicKey::from_public_key_pem(p).ok())
2350                    .ok_or("Error: bad public key")?;
2351                p256::ecdh::diffie_hellman(sk.to_nonzero_scalar(), pk.as_affine())
2352                    .raw_secret_bytes()
2353                    .to_vec()
2354            } else {
2355                let sk = priv_pem
2356                    .and_then(|p| p384::SecretKey::from_pkcs8_pem(p).ok())
2357                    .ok_or("Error: bad private key")?;
2358                let pk = pub_pem
2359                    .and_then(|p| p384::PublicKey::from_public_key_pem(p).ok())
2360                    .ok_or("Error: bad public key")?;
2361                p384::ecdh::diffie_hellman(sk.to_nonzero_scalar(), pk.as_affine())
2362                    .raw_secret_bytes()
2363                    .to_vec()
2364            }
2365        }
2366        "x25519" => {
2367            let sraw = priv_pem
2368                .and_then(x25519_raw)
2369                .ok_or("Error: bad private key")?;
2370            let praw = pub_pem
2371                .and_then(x25519_raw)
2372                .ok_or("Error: bad public key")?;
2373            let sk = x25519_dalek::StaticSecret::from(sraw);
2374            let pk = x25519_dalek::PublicKey::from(praw);
2375            sk.diffie_hellman(&pk).as_bytes().to_vec()
2376        }
2377        _ => return Err("Error: diffieHellman requires EC or X25519 keys".into()),
2378    };
2379    Ok(super::buffer::from_bytes(&secret))
2380}
2381
2382// ── Primes ──────────────────────────────────────────────────────────────
2383
2384/// A key/number argument as a `BigUint` (BigInt, Buffer big-endian, or number).
2385fn arg_biguint(v: Option<&Value>) -> BigUint {
2386    let Some(v) = v else {
2387        return BigUint::from(0u32);
2388    };
2389    if let Some(b) = with_host(|h| match h.get(v) {
2390        Some(JsObj::BigInt(b)) => b.to_biguint(),
2391        _ => None,
2392    }) {
2393        return b;
2394    }
2395    if super::native_tag(v).as_deref() == Some("Buffer") {
2396        return BigUint::from_bytes_be(&val_bytes(v));
2397    }
2398    let n = with_host(|h| h.to_number(v));
2399    BigUint::from(n.max(0.0) as u64)
2400}
2401
2402/// `checkPrimeSync(candidate)` — probabilistic primality.
2403fn check_prime(v: Option<&Value>) -> bool {
2404    let n = arg_biguint(v);
2405    num_prime::nt_funcs::is_prime(&n, None).probably()
2406}
2407
2408/// `generatePrimeSync(size[, {bigint}])` — a random prime of `size` bits.
2409fn generate_prime(bits: usize, opts: Option<Value>) -> Result<Value, String> {
2410    let p = gen_prime(bits)?;
2411    let want_bigint = opts
2412        .map(|o| with_host(|h| matches!(h.get(&o), Some(JsObj::Object(m)) if m.get("bigint").map(|v| h.truthy(v)).unwrap_or(false))))
2413        .unwrap_or(false);
2414    if want_bigint {
2415        Ok(with_host(|h| {
2416            h.alloc(JsObj::BigInt(num_bigint::BigInt::from(p)))
2417        }))
2418    } else {
2419        // Node returns an ArrayBuffer; this runtime's ArrayBuffer carries no
2420        // backing bytes, so a Buffer (also a byte view) is returned instead.
2421        Ok(super::buffer::from_bytes(&p.to_bytes_be()))
2422    }
2423}
2424
2425/// A random probable prime of `bits` bits (top bit set, odd, then next_prime).
2426fn gen_prime(bits: usize) -> Result<BigUint, String> {
2427    if bits < 2 {
2428        return Err("Error: size must be >= 2".into());
2429    }
2430    let nbytes = bits.div_ceil(8);
2431    let mut buf = vec![0u8; nbytes];
2432    getrandom::getrandom(&mut buf).map_err(|e| format!("Error: {e}"))?;
2433    let excess = nbytes * 8 - bits;
2434    buf[0] &= 0xffu8 >> excess;
2435    buf[0] |= 0x80u8 >> excess;
2436    let last = nbytes - 1;
2437    buf[last] |= 1;
2438    let start = BigUint::from_bytes_be(&buf);
2439    num_prime::nt_funcs::next_prime(&start, None)
2440        .ok_or_else(|| "Error: prime generation failed".into())
2441}
2442
2443// ── argon2 ──────────────────────────────────────────────────────────────
2444
2445/// `argon2Sync(algorithm, options)` → raw tag bytes.
2446fn argon2_hash(algo: &str, opts: Option<&Value>) -> Result<Vec<u8>, String> {
2447    let o = opts.cloned().ok_or("Error: argon2 options required")?;
2448    let msg = prop_bytes(&o, "message");
2449    let salt = prop_bytes(&o, "nonce");
2450    let secret = prop_bytes(&o, "secret");
2451    let taglen = opt_num(&o, &["tagLength"], 32.0).max(4.0) as usize;
2452    let mem = opt_num(&o, &["memory"], 65536.0) as u32;
2453    let passes = opt_num(&o, &["passes"], 3.0) as u32;
2454    let par = opt_num(&o, &["parallelism"], 4.0) as u32;
2455    let algorithm = match algo.to_ascii_lowercase().as_str() {
2456        "argon2d" => argon2::Algorithm::Argon2d,
2457        "argon2i" => argon2::Algorithm::Argon2i,
2458        _ => argon2::Algorithm::Argon2id,
2459    };
2460    let params =
2461        argon2::Params::new(mem, passes, par, Some(taglen)).map_err(|e| format!("Error: {e}"))?;
2462    let ctx = if secret.is_empty() {
2463        argon2::Argon2::new(algorithm, argon2::Version::V0x13, params)
2464    } else {
2465        argon2::Argon2::new_with_secret(&secret, algorithm, argon2::Version::V0x13, params)
2466            .map_err(|e| format!("Error: {e}"))?
2467    };
2468    let mut out = vec![0u8; taglen];
2469    ctx.hash_password_into(&msg, &salt, &mut out)
2470        .map_err(|e| format!("Error: {e}"))?;
2471    Ok(out)
2472}
2473
2474/// The bytes of a Buffer/typed-array/string property of an object.
2475fn prop_bytes(obj: &Value, key: &str) -> Vec<u8> {
2476    let v = with_host(|h| match h.get(obj) {
2477        Some(JsObj::Object(p)) => p.get(key).cloned(),
2478        _ => None,
2479    });
2480    v.map(|x| val_bytes(&x)).unwrap_or_default()
2481}
2482
2483// ── WebCrypto getRandomValues ───────────────────────────────────────────
2484
2485/// `getRandomValues(typedArray)` — fill in place with CSPRNG bytes, return it.
2486fn get_random_values(v: Option<&Value>) -> Result<Value, String> {
2487    let ta = v
2488        .cloned()
2489        .ok_or("Error: argument must be an integer-type TypedArray")?;
2490    let tag = super::native_tag(&ta);
2491    if tag.as_deref() == Some("Buffer") {
2492        let len = val_bytes(&ta).len();
2493        let mut rnd = vec![0u8; len];
2494        getrandom::getrandom(&mut rnd).map_err(|e| format!("Error: {e}"))?;
2495        set_obj_bytes_named(&ta, "@@bytes", &rnd);
2496        return Ok(ta);
2497    }
2498    if tag.as_deref() != Some("TypedArray") {
2499        return Err("Error: argument must be an integer-type TypedArray".into());
2500    }
2501    let kind = obj_str(&ta, "@@kind");
2502    if kind.starts_with("Float") {
2503        return Err("Error: The provided ArrayBufferView is of type 'Float', which is not an integer array type".into());
2504    }
2505    let len = crate::stdlib::typedarray::index_len(&ta).unwrap_or(0);
2506    let bpe = match kind.as_str() {
2507        "Int16Array" | "Uint16Array" => 2,
2508        "Int32Array" | "Uint32Array" => 4,
2509        _ => 1,
2510    };
2511    let mut raw = vec![0u8; len * bpe];
2512    getrandom::getrandom(&mut raw).map_err(|e| format!("Error: {e}"))?;
2513    let elems: Vec<Value> = (0..len)
2514        .map(|i| {
2515            let mut acc: u64 = 0;
2516            for j in 0..bpe {
2517                acc |= (raw[i * bpe + j] as u64) << (8 * j);
2518            }
2519            Value::Float(ta_coerce(&kind, acc))
2520        })
2521        .collect();
2522    // Written through the view, so the bytes land in the backing ArrayBuffer
2523    // and every other view over it sees them.
2524    for (i, e) in elems.iter().enumerate() {
2525        crate::stdlib::typedarray::elem_set(&ta, &i.to_string(), e)?;
2526    }
2527    Ok(ta)
2528}
2529
2530/// Coerce a raw little-endian integer into a typed-array element value.
2531fn ta_coerce(kind: &str, raw: u64) -> f64 {
2532    match kind {
2533        "Int8Array" => (raw as i8) as f64,
2534        "Int16Array" => (raw as i16) as f64,
2535        "Int32Array" => (raw as i32) as f64,
2536        "Uint16Array" => (raw as u16) as f64,
2537        "Uint32Array" => (raw as u32) as f64,
2538        _ => (raw as u8) as f64, // Uint8Array / Uint8ClampedArray
2539    }
2540}
2541
2542/// Store raw bytes into a named byte-array property (for Buffer `@@bytes`).
2543fn set_obj_bytes_named(recv: &Value, key: &str, bytes: &[u8]) {
2544    with_host(|h| {
2545        let arr = match h.get(recv) {
2546            Some(JsObj::Object(p)) => p.get(key).cloned(),
2547            _ => None,
2548        };
2549        if let Some(a) = arr {
2550            if let Some(JsObj::Array(items)) = h.get_mut(&a) {
2551                *items = bytes.iter().map(|b| Value::Float(*b as f64)).collect();
2552            }
2553        }
2554    });
2555}
2556
2557// ── KeyObject instance methods ──────────────────────────────────────────
2558
2559/// `KeyObject` instance dispatch (`export({type,format})`, `equals`).
2560pub fn key_object_instance_call(
2561    recv: &Value,
2562    method: &str,
2563    args: &[Value],
2564) -> Result<Value, String> {
2565    match method {
2566        "export" => {
2567            // Secret key: raw bytes (Buffer) unless format 'jwk' (unsupported).
2568            let secret = obj_bytes(recv, "@@secret");
2569            if !secret.is_empty() {
2570                return Ok(super::buffer::from_bytes(&secret));
2571            }
2572            let pem = obj_str(recv, "@@pem");
2573            let format = args
2574                .first()
2575                .map(|o| opt_str(o, "format"))
2576                .filter(|s| !s.is_empty())
2577                .unwrap_or_else(|| "pem".into());
2578            if format == "der" {
2579                Ok(super::buffer::from_bytes(&pem_body(&pem)))
2580            } else {
2581                Ok(with_host(|h| h.new_str(pem)))
2582            }
2583        }
2584        "equals" => {
2585            let Some(other) = args.first() else {
2586                return Ok(Value::Bool(false));
2587            };
2588            // A SECRET key has no PEM, so comparing `@@pem` compared two empty
2589            // strings and every pair of secret keys was equal — including keys
2590            // built from different bytes. The comparison has to be over the same
2591            // material the key actually holds, and a key never equals one of a
2592            // different `type`.
2593            let kind = |v: &Value| obj_str(v, "@@type");
2594            if kind(recv) != kind(other) {
2595                return Ok(Value::Bool(false));
2596            }
2597            let mine = obj_bytes(recv, "@@secret");
2598            if !mine.is_empty() {
2599                return Ok(Value::Bool(mine == obj_bytes(other, "@@secret")));
2600            }
2601            Ok(Value::Bool(
2602                obj_str(recv, "@@pem") == obj_str(other, "@@pem"),
2603            ))
2604        }
2605        // `symmetricKeySize` is the secret's length in BYTES, and `undefined`
2606        // for an asymmetric key rather than zero.
2607        "@get@symmetricKeySize" => {
2608            let secret = obj_bytes(recv, "@@secret");
2609            Ok(if obj_str(recv, "@@type") == "secret" {
2610                Value::Float(secret.len() as f64)
2611            } else {
2612                Value::Undef
2613            })
2614        }
2615        _ => Err(crate::host::type_error(&format!(
2616            "keyObject.{method} is not a function"
2617        ))),
2618    }
2619}
2620
2621// ── X.509 certificates ──────────────────────────────────────────────────
2622
2623/// `new X509Certificate(pemOrDer)` → an `X509Certificate` instance.
2624pub fn construct_x509(args: &[Value]) -> Result<Value, String> {
2625    use x509_cert::der::{Decode, DecodePem, Encode};
2626    let bytes = val_bytes_at(args, 0);
2627    let is_pem = bytes.starts_with(b"-----BEGIN");
2628    let cert = if is_pem {
2629        x509_cert::Certificate::from_pem(&bytes).map_err(|e| format!("Error: {e}"))?
2630    } else {
2631        x509_cert::Certificate::from_der(&bytes).map_err(|e| format!("Error: {e}"))?
2632    };
2633    // The canonical DER (for fingerprint + raw).
2634    let der = cert.to_der().map_err(|e| format!("Error: {e}"))?;
2635    let fp = {
2636        let d = digest("sha1", &der);
2637        d.iter()
2638            .map(|b| format!("{b:02X}"))
2639            .collect::<Vec<_>>()
2640            .join(":")
2641    };
2642    let subject = x509_name_node(&cert.tbs_certificate.subject.to_string());
2643    let issuer = x509_name_node(&cert.tbs_certificate.issuer.to_string());
2644    let not_before = x509_time(&cert.tbs_certificate.validity.not_before);
2645    let not_after = x509_time(&cert.tbs_certificate.validity.not_after);
2646    let serial = cert
2647        .tbs_certificate
2648        .serial_number
2649        .as_bytes()
2650        .iter()
2651        .map(|b| format!("{b:02X}"))
2652        .collect::<String>();
2653    let spki_der = cert
2654        .tbs_certificate
2655        .subject_public_key_info
2656        .to_der()
2657        .map_err(|e| format!("Error: {e}"))?;
2658    let pub_pem = pem_wrap("PUBLIC KEY", &spki_der);
2659    let pub_key =
2660        create_public_key(Some(&with_host(|h| h.new_str(pub_pem)))).unwrap_or(Value::Undef);
2661    let cert_pem = pem_wrap("CERTIFICATE", &der);
2662    let raw = super::buffer::from_bytes(&der);
2663    Ok(with_host(|h| {
2664        let mut m = IndexMap::new();
2665        m.insert("@@native".into(), h.new_str("X509Certificate"));
2666        m.insert("subject".into(), h.new_str(subject));
2667        m.insert("issuer".into(), h.new_str(issuer));
2668        m.insert("validFrom".into(), h.new_str(not_before));
2669        m.insert("validTo".into(), h.new_str(not_after));
2670        m.insert("serialNumber".into(), h.new_str(serial));
2671        m.insert("fingerprint".into(), h.new_str(fp));
2672        m.insert("publicKey".into(), pub_key);
2673        m.insert("raw".into(), raw);
2674        m.insert("@@pem".into(), h.new_str(cert_pem));
2675        h.new_object(m)
2676    }))
2677}
2678
2679/// `X509Certificate` instance dispatch (`toString`, `toLegacyObject`).
2680pub fn x509_instance_call(recv: &Value, method: &str, _args: &[Value]) -> Result<Value, String> {
2681    match method {
2682        // Same nested-borrow hazard as `events::getEventListeners`: `obj_str`
2683        // takes the host, so reading it inside `with_host` aborts the process.
2684        "toString" => {
2685            let pem = obj_str(recv, "@@pem");
2686            Ok(with_host(|h| h.new_str(pem)))
2687        }
2688        _ => Err(crate::host::type_error(&format!(
2689            "x509Certificate.{method} is not a function"
2690        ))),
2691    }
2692}
2693
2694/// Convert an RFC 4514 name string ("O=Org,CN=Name") to Node's newline,
2695/// most-significant-last form ("CN=Name\nO=Org").
2696fn x509_name_node(rfc4514: &str) -> String {
2697    rfc4514
2698        .split(',')
2699        .map(|s| s.trim())
2700        .rev()
2701        .collect::<Vec<_>>()
2702        .join("\n")
2703}
2704
2705/// Render an X.509 time in OpenSSL's `%b %e %H:%M:%S %Y GMT` form.
2706fn x509_time(t: &x509_cert::time::Time) -> String {
2707    const MON: [&str; 12] = [
2708        "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
2709    ];
2710    let dt = t.to_date_time();
2711    let mon = MON
2712        .get(dt.month().saturating_sub(1) as usize)
2713        .copied()
2714        .unwrap_or("Jan");
2715    format!(
2716        "{} {:2} {:02}:{:02}:{:02} {} GMT",
2717        mon,
2718        dt.day(),
2719        dt.hour(),
2720        dt.minutes(),
2721        dt.seconds(),
2722        dt.year()
2723    )
2724}
2725
2726// RFC 2409/3526 MODP group primes (generator 2).
2727const MODP1: &str = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A63A3620FFFFFFFFFFFFFFFF";
2728const MODP2: &str = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF";
2729const MODP5: &str = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF";
2730const MODP14: &str = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF";
2731const MODP15: &str = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF";
2732const MODP16: &str = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF";
2733const MODP17: &str = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DCC4024FFFFFFFFFFFFFFFF";
2734const MODP18: &str = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD922222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC50846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E7160C980DD98EDD3DFFFFFFFFFFFFFFFFF";