// Detectorul Rust (0.3.0, 2026-10-18; scris dupa prima rulare a inventarului pe un arbore strain, cu 1.320 de fisiere Rust si // zero constatari). Ce se recunoaste: // - declaratiile `use` si `extern crate`: ele spun din ce crate vine fiecare nume din scop. Acelasi nume inseamna lucruri // diferite (SigningKey e Ed25519 in ed25519-dalek, ML-DSA in ml-dsa, ECDSA in p256/k256, RSA in rsa), deci un nume // ambiguu fara `use` nu se ghiceste: iese unknown, cu motivul scris; // - apelurile statice Tip::metoda (Sha256::new, SigningKey::generate, Aes256Gcm::new_from_slice, RsaPrivateKey::new(rng, 2048)), // parametrii generici (Hmac::, Hkdf::, type HmacSha256 = Hmac), functiile pe cale (blake3::hash, // pbkdf2::pbkdf2_hmac::, pqcrypto_kyber::kyber1024::keypair), constantele lui ring (aead::AES_256_GCM), // tiparele Noise ale lui snow, furnizorul criptografic al lui rustls, variantele Algorithm ale lui jsonwebtoken si oqs, // parametrii ML-DSA / SLH-DSA / ML-KEM (MlDsa65, SlhDsaSha2_128s, MlKem768) si prekey-urile Kyber ale lui libsignal; // - in Rust un import nefolosit e un avertisment, nu o eroare, deci importul e dovada SLABA: un crate criptografic importat se // raporteaza ca 'import' (unknown) numai cand fisierul nu are niciun apel recunoscut din el. // Nu se recunosc: metodele pe valori (.sign(), .update(), .encrypt()) fara constructorul lor in acelasi fisier, macro-urile, // tipurile proprii care invelesc un crate (acolo se raporteaza fisierul care il cheama direct). import { argumente, escapeRe } from './context.mjs'; import { jws } from './catalog.mjs'; const RADACINI_PROPRII = new Set(['crate', 'self', 'super', 'std', 'core', 'alloc']); // crate-urile criptografice cunoscute (numele din `use`, cu liniuta jos) si grupurile pe care le pot produce (suprimarea importului) const CRATE = { sha2: { ofera: 'SHA-224/256/384/512', grupuri: ['HASH', 'MAC', 'KDF'] }, sha1: { ofera: 'SHA-1', grupuri: ['HASH', 'MAC', 'KDF'] }, md5: { ofera: 'MD5', grupuri: ['HASH'] }, sha3: { ofera: 'SHA3, Keccak, SHAKE', grupuri: ['HASH'] }, blake2: { ofera: 'BLAKE2b/BLAKE2s', grupuri: ['HASH', 'MAC'] }, blake3: { ofera: 'BLAKE3', grupuri: ['HASH'] }, hmac: { ofera: 'HMAC', grupuri: ['MAC'] }, hkdf: { ofera: 'HKDF', grupuri: ['KDF'] }, argon2: { ofera: 'Argon2', grupuri: ['KDF'] }, pbkdf2: { ofera: 'PBKDF2', grupuri: ['KDF'] }, scrypt: { ofera: 'scrypt', grupuri: ['KDF'] }, bcrypt: { ofera: 'bcrypt', grupuri: ['KDF'] }, ed25519_dalek: { ofera: 'Ed25519 signatures', grupuri: ['EDDSA'] }, x25519_dalek: { ofera: 'X25519 key agreement', grupuri: ['XDH'] }, curve25519_dalek: { ofera: 'Curve25519 group arithmetic (Ristretto, Edwards, Montgomery)', grupuri: ['EC', 'XDH', 'EDDSA'] }, chacha20poly1305: { ofera: 'ChaCha20-Poly1305 and XChaCha20-Poly1305', grupuri: ['CIPHER'] }, aes_gcm: { ofera: 'AES-GCM', grupuri: ['CIPHER'] }, aes_gcm_siv: { ofera: 'AES-GCM-SIV', grupuri: ['CIPHER'] }, aes_siv: { ofera: 'AES-SIV', grupuri: ['CIPHER'] }, aes: { ofera: 'the AES block cipher (mode chosen by the caller)', grupuri: ['CIPHER'] }, cbc: { ofera: 'the CBC mode', grupuri: ['CIPHER'] }, ctr: { ofera: 'the CTR mode', grupuri: ['CIPHER'] }, ecb: { ofera: 'the ECB mode', grupuri: ['CIPHER'] }, rsa: { ofera: 'RSA', grupuri: ['RSA'] }, p256: { ofera: 'NIST P-256 (ECDSA, ECDH)', grupuri: ['EC', 'ECDSA', 'ECDH'] }, p384: { ofera: 'NIST P-384 (ECDSA, ECDH)', grupuri: ['EC', 'ECDSA', 'ECDH'] }, p521: { ofera: 'NIST P-521 (ECDSA, ECDH)', grupuri: ['EC', 'ECDSA', 'ECDH'] }, k256: { ofera: 'secp256k1 (ECDSA, ECDH)', grupuri: ['SECP256K1', 'ECDH'] }, secp256k1: { ofera: 'secp256k1 (ECDSA, Schnorr)', grupuri: ['SECP256K1'] }, ml_kem: { ofera: 'ML-KEM (FIPS 203)', grupuri: ['MLKEM'] }, ml_dsa: { ofera: 'ML-DSA (FIPS 204)', grupuri: ['MLDSA'] }, slh_dsa: { ofera: 'SLH-DSA (FIPS 205)', grupuri: ['SLHDSA'] }, pqcrypto_kyber: { ofera: 'Kyber (pre-standard ML-KEM)', grupuri: ['PREPQ'] }, pqcrypto_mlkem: { ofera: 'ML-KEM (PQClean)', grupuri: ['MLKEM'] }, pqcrypto_dilithium: { ofera: 'Dilithium (pre-standard ML-DSA)', grupuri: ['PREPQ'] }, pqcrypto_mldsa: { ofera: 'ML-DSA (PQClean)', grupuri: ['MLDSA'] }, pqcrypto_sphincsplus: { ofera: 'SPHINCS+ (pre-standard SLH-DSA)', grupuri: ['PREPQ'] }, pqcrypto_falcon: { ofera: 'Falcon', grupuri: ['FALCON'] }, pqcrypto_hqc: { ofera: 'HQC', grupuri: ['HQC'] }, pqcrypto_classicmceliece: { ofera: 'Classic McEliece (NIST round 4, not standardized)', grupuri: ['UNKNOWN'] }, oqs: { ofera: 'liboqs: post-quantum KEMs and signatures', grupuri: ['MLKEM', 'MLDSA', 'FALCON', 'PREPQ', 'HQC', 'UNKNOWN'] }, libsignal_protocol: { ofera: 'Signal sessions: Curve25519 identity and prekeys, Kyber prekeys (PQXDH), Double Ratchet', grupuri: ['XDH', 'PREPQ', 'MLKEM', 'LIB-MULTI'] }, ring: { ofera: 'ring: AES-GCM, ChaCha20-Poly1305, SHA-2, HMAC, HKDF, PBKDF2, Ed25519, ECDSA P-256/P-384, RSA, X25519, ECDH', grupuri: ['HASH', 'MAC', 'KDF', 'CIPHER', 'EDDSA', 'ECDSA', 'RSA', 'XDH', 'ECDH'] }, openssl: { ofera: 'OpenSSL bindings', grupuri: ['HASH', 'CIPHER', 'RSA', 'EC', 'ECDSA', 'EDDSA', 'XDH', 'TLS'] }, rustls: { ofera: 'TLS (versions and key-exchange groups come from the crypto provider and the Cargo features)', grupuri: ['TLS', 'XDH', 'UNKNOWN'] }, tokio_rustls: { ofera: 'TLS over tokio (rustls)', grupuri: ['TLS'] }, hyper_rustls: { ofera: 'HTTPS for hyper (rustls)', grupuri: ['TLS'] }, native_tls: { ofera: 'the platform TLS stack', grupuri: ['TLS'] }, snow: { ofera: 'the Noise protocol framework', grupuri: ['XDH', 'CIPHER', 'HASH'] }, jsonwebtoken: { ofera: 'JWS HS*/RS*/PS*/ES*/EdDSA', grupuri: ['JWT-HMAC', 'RSA', 'ECDSA', 'EDDSA', 'JWT-NONE'] }, age: { ofera: 'age file encryption: X25519 recipients, scrypt passphrases, ChaCha20-Poly1305', grupuri: ['XDH', 'KDF', 'CIPHER'] }, crypto_box: { ofera: 'NaCl crypto_box: X25519 + XSalsa20/XChaCha20-Poly1305', grupuri: ['XDH', 'CIPHER'] }, arti_client: { ofera: 'a Tor client (circuit cryptography negotiated with relays)', grupuri: ['UNKNOWN'] }, }; const NUME_CARGO = (c) => c.replace(/_/g, '-'); // tipuri care au un singur proprietar printre crate-urile cunoscute: se recunosc si fara `use` (de ex. dupa un `use sha2::*`) const UNIC = {}; for (const [crate, tipuri] of Object.entries({ sha2: ['Sha224', 'Sha256', 'Sha384', 'Sha512', 'Sha512_224', 'Sha512_256'], sha1: ['Sha1'], md5: ['Md5'], sha3: ['Sha3_224', 'Sha3_256', 'Sha3_384', 'Sha3_512', 'Keccak256', 'Shake128', 'Shake256'], blake2: ['Blake2b512', 'Blake2s256', 'Blake2b', 'Blake2s', 'Blake2bMac', 'Blake2sMac'], hmac: ['Hmac', 'SimpleHmac'], hkdf: ['Hkdf'], argon2: ['Argon2'], chacha20poly1305: ['ChaCha20Poly1305', 'XChaCha20Poly1305', 'ChaCha8Poly1305', 'ChaCha12Poly1305', 'XChaCha8Poly1305', 'XChaCha12Poly1305'], aes_gcm: ['Aes128Gcm', 'Aes256Gcm'], aes_gcm_siv: ['Aes128GcmSiv', 'Aes256GcmSiv'], aes_siv: ['Aes128SivAead', 'Aes256SivAead'], aes: ['Aes128', 'Aes192', 'Aes256'], rsa: ['RsaPrivateKey', 'RsaPublicKey', 'Pkcs1v15Sign', 'Pkcs1v15Encrypt', 'Oaep', 'Pss'], x25519_dalek: ['StaticSecret', 'EphemeralSecret', 'ReusableSecret'], curve25519_dalek: ['RistrettoPoint', 'EdwardsPoint', 'MontgomeryPoint'], ml_kem: ['MlKem512', 'MlKem768', 'MlKem1024'], ml_dsa: ['MlDsa44', 'MlDsa65', 'MlDsa87'], libsignal_protocol: ['PreKeyBundle', 'IdentityKeyPair', 'SignalProtocolAddress', 'KyberPreKeyRecord'], arti_client: ['TorClient'], })) for (const t of tipuri) UNIC[t] = crate; const HASH_TIP = { Sha224: 'SHA224', Sha256: 'SHA256', Sha384: 'SHA384', Sha512: 'SHA512', Sha512_224: 'SHA512/224', Sha512_256: 'SHA512/256', Sha1: 'SHA1', Md5: 'MD5', Sha3_224: 'SHA3-224', Sha3_256: 'SHA3-256', Sha3_384: 'SHA3-384', Sha3_512: 'SHA3-512', Keccak256: 'KECCAK256', Shake128: 'SHAKE128', Shake256: 'SHAKE256', Blake2b512: 'BLAKE2B512', Blake2s256: 'BLAKE2S256', Blake2b: 'BLAKE2B', Blake2s: 'BLAKE2S', }; const hashDinTip = (t) => HASH_TIP[String(t || '').split('::').pop().trim()] || null; // crate-ul care detine tipul de hash dintr-un parametru generic (sha2::Sha256 -> sha2), ca `use sha2` sa conteze ca folosit const crateHashDinTip = (t) => { const n = String(t || '').split('::').pop().trim(); return UNIC[n] || null; }; const CTOR = /^(new|default|new_from_slice|new_varkey|new_with_prefix|digest|digest_variable|new_variable|generate|generate_key|generate_nonce|random|random_from_rng|from|from_bytes|from_slice|from_keypair_bytes|from_pkcs8_der|from_pkcs8_pem|from_pkcs1_der|from_pkcs1_pem|from_public_key_der|from_public_key_pem|from_components|try_from|new_with_secret|from_seed|key_gen|keypair|try_sign|sign|verify)$/; // --- `use` --------------------------------------------------------------------------------------------------------------------- export function useuriRust(ctx) { const r = []; // { crate, cale: [segmente], local, glob, pos } for (const m of ctx.potriviri(/(? r.push({ crate: cale[0], cale, local, glob, pos: m.index, sf: m.index + m[0].length })); } for (const m of ctx.potriviri(/(? u.cale.length && !RADACINI_PROPRII.has(u.crate)); } function dezvolta(text, prefix, emite) { let t = text.trim(); if (t.startsWith('::')) t = t.slice(2); const k = t.indexOf('{'); if (k >= 0) { const baza = t.slice(0, k).replace(/::\s*$/, '').trim(); const inauntru = t.slice(k + 1, t.lastIndexOf('}')); const pre = baza ? [...prefix, ...baza.split('::').map((s) => s.trim()).filter(Boolean)] : prefix; for (const parte of imparteVirgule(inauntru)) if (parte.trim()) dezvolta(parte, pre, emite); return; } const alias = /^(.*?)\s+as\s+([A-Za-z_]\w*|_)$/.exec(t); const seg = (alias ? alias[1] : t).trim().split('::').map((s) => s.trim()).filter(Boolean); if (!seg.length) return; const cale = [...prefix, ...seg]; if (cale[cale.length - 1] === '*') { if (cale.length > 1) emite(cale.slice(0, -1), null, true); return; } if (cale[cale.length - 1] === 'self') cale.pop(); if (!cale.length) return; emite(cale, alias ? alias[2] : cale[cale.length - 1], false); } function imparteVirgule(s) { const r = []; let ad = 0; let inc = 0; for (let i = 0; i < s.length; i++) { const c = s[i]; if (c === '{') ad++; else if (c === '}') ad--; else if (c === ',' && ad === 0) { r.push(s.slice(inc, i)); inc = i + 1; } } r.push(s.slice(inc)); return r; } // --- detectorul ----------------------------------------------------------------------------------------------------------------- export function detecteazaRust(ctx) { const useuri = useuriRust(ctx); const locale = new Map(); // numele din scop -> { crate, cale } const globuri = new Set(); // crate-uri aduse cu `use crate::*` (numele lor UNIC se recunosc) for (const u of useuri) { if (u.glob) { globuri.add(u.crate); continue; } if (u.local && u.local !== '_' && !locale.has(u.local)) locale.set(u.local, { crate: u.crate, cale: u.cale }); } // numele aduse din propriul crate (use crate::chei::KeyPair, use super::*) nu sunt ambigue intre crate-urile criptografice const proprii = new Set(); for (const m of ctx.potriviri(/(? { if (local) proprii.add(local); }); const folosite = new Set(); // crate-urile cu cel putin un apel recunoscut in acest fisier // o potrivire din interiorul unei declaratii `use` nu e o folosire (MlDsa65 din `use ml_dsa::MlDsa65` nu e un apel) const inUse = (pos) => useuri.some((u) => pos >= u.pos && pos < u.sf); const potriviri = (re) => ctx.potriviri(re).filter((m) => !inUse(m.index)); // parametrul PQ al unui rand se raporteaza o singura data, oricate forme il poarta (SigningKey::::new si MlDsa65 din generic) const pqPeRand = new Set(); const adauga = (crate, pos, api, fel, activ, bucata) => { if (/^(MLDSA|MLKEM|SLHDSA)$/.test(activ.grup)) { const k = `${ctx.poz(pos).line}|${activ.grup}|${activ.param || ''}`; if (pqPeRand.has(k)) return; pqPeRand.add(k); } if (crate) folosite.add(crate); if (activ.hash && activ.crateHash) folosite.add(activ.crateHash); const curat = { ...activ }; delete curat.crateHash; delete curat.bucataExtra; ctx.adauga(pos, api, fel, curat, { bucata }); }; // o cale scrisa in cod (prefixul `a::b::`) sau un nume de tip -> calea completa prin `use` const rezolvaCale = (prefix, tip) => { const seg = prefix ? prefix.split('::').filter(Boolean) : []; if (seg.length) { if (RADACINI_PROPRII.has(seg[0])) return null; const u = locale.get(seg[0]); const cale = u ? [...u.cale, ...seg.slice(1)] : seg; return CRATE[cale[0]] ? { crate: cale[0], cale: [...cale, tip] } : null; } const u = locale.get(tip); if (u) return CRATE[u.crate] ? { crate: u.crate, cale: u.cale } : null; if (UNIC[tip]) return { crate: UNIC[tip], cale: [UNIC[tip], tip] }; return null; }; // 1. apelurile statice Tip::metoda, cu sau fara turbofish (Hmac::::new_from_slice) const RE_STATIC = /(?)?\s*::\s*([a-z_]\w*)\b/g; for (const m of ctx.potriviri(RE_STATIC)) { const [intreg, prefix, tip, generic, metoda] = m; const r = rezolvaCale(prefix, tip); if (!r) { if (!prefix && /^(SigningKey|VerifyingKey|KeyPair|Keypair|SecretKey|PublicKey|Signature)$/.test(tip) && CTOR.test(metoda) && !locale.has(tip) && !proprii.has(tip)) { // nume ambiguu, fara `use` care sa spuna crate-ul: nu se ghiceste const tipuriLocale = new RegExp(`\\b(struct|enum|type|trait)\\s+${tip}\\b`).test(ctx.code); if (!tipuriLocale) ctx.adauga(m.index, `${tip}::${metoda}`, 'call', { grup: 'UNKNOWN', nume: `${tip} (crate not resolved)`, motiv: `${tip}::${metoda} with no \`use\` in this file naming its crate: it may be Ed25519 (ed25519-dalek), ML-DSA (ml-dsa), SLH-DSA (slh-dsa), ECDSA (p256, k256) or RSA (rsa).` }, { bucata: intreg }); } continue; } const activ = activStatic(r, metoda, generic, ctx, m); if (activ) adauga(r.crate, m.index, `${prefix}${tip}::${metoda}`, 'call', activ, intreg + (activ.bucataExtra || '')); } // 2. aliasuri de tip cu parametru generic: type HmacSha256 = Hmac; type Kdf = Hkdf; for (const m of ctx.potriviri(/(?;]+)>/g)) { const r = rezolvaCale(m[1], m[2]); if (!r) continue; const h = hashDinTip(m[3]); const activ = m[2] === 'Hkdf' ? { grup: 'KDF', nume: 'HKDF', hash: h || undefined, functii: ['derive'], crateHash: crateHashDinTip(m[3]) } : (h ? { grup: 'MAC', hash: h, functii: ['tag'], crateHash: crateHashDinTip(m[3]) } : { grup: 'UNKNOWN', motiv: `HMAC over "${m[3].trim()}", a hash this tool does not resolve.` }); adauga(r.crate, m.index, `type = ${m[2]}<${m[3].trim()}>`, 'config', activ, m[0]); } // 3. functii pe cale: blake3::hash(...), pbkdf2::pbkdf2_hmac::(...), pqcrypto_kyber::kyber1024::keypair(), scrypt::scrypt(...), // rustls::crypto::ring::default_provider(), libsignal_protocol::process_prekey_bundle(...) for (const m of ctx.potriviri(/(?)?\s*\(/g)) { const seg = m[1].split('::').filter(Boolean); const u = locale.get(seg[0]); const cale = u ? [...u.cale, ...seg.slice(1), m[2]] : [...seg, m[2]]; if (RADACINI_PROPRII.has(cale[0]) || !CRATE[cale[0]]) continue; const activ = activFunctie(cale, m[3], ctx, m); if (activ) adauga(cale[0], m.index, cale.join('::'), 'call', activ, m[0].trim() + (activ.bucataExtra || '')); } // functii importate direct: use pbkdf2::pbkdf2_hmac; use blake3::hash; use libsignal_protocol::process_prekey_bundle; for (const [local, u] of locale) { if (!/^[a-z_]\w*$/.test(local) || !CRATE[u.crate] || u.cale.length < 2) continue; for (const m of ctx.potriviri(new RegExp(`(?)?\\s*\\(`, 'g'))) { const activ = activFunctie(u.cale, m[1], ctx, m); if (activ) adauga(u.crate, m.index, u.cale.join('::'), 'call', activ, m[0].trim() + (activ.bucataExtra || '')); } } // 4. constantele lui ring (aead::AES_256_GCM, signature::ED25519) si variantele enum (Algorithm::RS256, KeyType::Kyber1024, // SslVersion::TLS1_2), rezolvate prin `use` for (const m of ctx.potriviri(/(? u.crate === 'slh_dsa') || globuri.has('slh_dsa')) { for (const m of potriviri(/(?, ecb::Decryptor, ctr::Ctr128BE for (const m of ctx.potriviri(/(?`, 'call', { grup: 'CIPHER', nume: 'AES', param: m[3], mod, functii: [m[2] === 'Decryptor' ? 'decrypt' : 'encrypt'] }, m[0]); } // 6. tiparul Noise al lui snow, scris ca sir: Noise_XX_25519_ChaChaPoly_SHA256 if ([...locale.values()].some((u) => u.crate === 'snow') || /(? u.crate === 'secp256k1')) { for (const m of ctx.potriviri(/\.\s*(sign_ecdsa|sign_ecdsa_recoverable|sign_schnorr|verify_ecdsa|verify_schnorr)\s*\(/g)) { const schnorr = /schnorr/.test(m[1]); adauga('secp256k1', m.index, `secp256k1 .${m[1]}`, 'call', { grup: 'SECP256K1', nume: schnorr ? 'Schnorr-secp256k1' : undefined, functii: [/^sign/.test(m[1]) ? 'sign' : 'verify'] }, m[0]); } } // 8. openssl: versiunea minima/maxima TLS if ([...locale.values()].some((u) => u.crate === 'openssl') || /(? [k]; const rol = /^(generate|random|random_from_rng|key_gen|keypair|new|default|from_seed)$/.test(metoda) ? 'keygen' : /^(try_sign|sign)$/.test(metoda) ? 'sign' : /^verify/.test(metoda) ? 'verify' : 'other'; switch (crate) { case 'sha2': case 'sha1': case 'md5': case 'sha3': case 'blake2': { const h = HASH_TIP[tip]; if (!h || !/^(new|default|digest|new_with_prefix|digest_variable|new_variable|new_keyed)$/.test(metoda)) return null; if (/Mac$/.test(tip)) return { grup: 'MAC', hash: h, functii: ['tag'] }; return { grup: 'HASH', hash: h, functii: ['digest'] }; } case 'blake3': return tip === 'Hasher' && /^(new|new_keyed|new_derive_key|default)$/.test(metoda) ? { grup: 'HASH', hash: 'BLAKE3', functii: ['digest'] } : null; case 'hmac': { if (!/^(Hmac|SimpleHmac)$/.test(tip) || !/^(new|new_from_slice|new_varkey|default)$/.test(metoda)) return null; const h = hashDinTip(generic); return h ? { grup: 'MAC', hash: h, functii: ['tag'], crateHash: crateHashDinTip(generic) } : { grup: 'UNKNOWN', motiv: generic ? `HMAC over "${generic.trim()}", a hash this tool does not resolve.` : 'HMAC whose hash is a type alias or an inferred type parameter, not visible at this call.' }; } case 'hkdf': { if (tip !== 'Hkdf' || !/^(new|extract|from_prk)$/.test(metoda)) return null; return { grup: 'KDF', nume: 'HKDF', hash: hashDinTip(generic) || undefined, functii: ['derive'], crateHash: crateHashDinTip(generic) }; } case 'argon2': { if (tip !== 'Argon2' || !/^(new|default|new_with_secret)$/.test(metoda)) return null; const varianta = /Argon2(id|i|d)\b/.exec(ctx.code.slice(m.index, m.index + m[0].length + 200) || ''); return { grup: 'KDF', nume: varianta ? `Argon2${varianta[1]}` : 'Argon2', functii: ['derive'], bucataExtra: varianta ? ` ${varianta[0]}` : '' }; } case 'ed25519_dalek': if (/^(SigningKey|Keypair|ExpandedSecretKey)$/.test(tip)) return { grup: 'EDDSA', nume: 'Ed25519', functii: fn(rol === 'other' ? 'keygen' : rol) }; if (tip === 'VerifyingKey' || tip === 'PublicKey') return { grup: 'EDDSA', nume: 'Ed25519', functii: ['verify'] }; return null; case 'x25519_dalek': if (/^(StaticSecret|EphemeralSecret|ReusableSecret)$/.test(tip)) return { grup: 'XDH', nume: 'X25519', functii: ['keygen'] }; if (tip === 'PublicKey') return { grup: 'XDH', nume: 'X25519', functii: ['other'] }; return null; case 'curve25519_dalek': if (/^(RistrettoPoint|EdwardsPoint|MontgomeryPoint|Scalar)$/.test(tip)) return { grup: 'EC', curba: 'Curve25519', nume: 'Curve25519 group arithmetic', primitiv: 'other', nota: 'curve25519-dalek: Ristretto/Edwards/Montgomery group operations; the protocol built on them (signatures, key agreement, VRF) decides the primitive.' }; return null; case 'chacha20poly1305': { const n = { ChaCha20Poly1305: 'ChaCha20-Poly1305', XChaCha20Poly1305: 'XChaCha20-Poly1305', ChaCha8Poly1305: 'ChaCha8-Poly1305', ChaCha12Poly1305: 'ChaCha12-Poly1305', XChaCha8Poly1305: 'XChaCha8-Poly1305', XChaCha12Poly1305: 'XChaCha12-Poly1305' }[tip]; return n && /^(new|new_from_slice|generate_key|generate_nonce)$/.test(metoda) ? { grup: 'CIPHER', nume: n, functii: ['encrypt'] } : null; } case 'aes_gcm': case 'aes_gcm_siv': case 'aes_siv': case 'aes': { const k = /^Aes(128|192|256)(Gcm|GcmSiv|SivAead)?$/.exec(tip); if (!k || !/^(new|new_from_slice|generate_key|generate_nonce)$/.test(metoda)) return null; const mod = k[2] === 'Gcm' ? 'gcm' : k[2] === 'GcmSiv' ? 'gcm-siv' : k[2] === 'SivAead' ? 'siv' : undefined; return { grup: 'CIPHER', nume: 'AES', param: k[1], mod, functii: ['encrypt'], nota: mod ? undefined : 'Raw block cipher (one block at a time); the mode of operation is chosen by the caller.' }; } case 'rsa': { if (tip === 'RsaPrivateKey') { let bits; if (metoda === 'new' || metoda === 'new_with_exp') { const parti = argsDupa(ctx, m).split(','); const b = parti[1] && /^\s*(\d{3,5})\s*$/.exec(parti[1]); if (b) bits = b[1]; } return { grup: 'RSA', param: bits, functii: fn(rol === 'other' ? 'other' : rol), bucataExtra: bits ? ` ${bits}` : '' }; } if (tip === 'RsaPublicKey') return { grup: 'RSA', functii: ['other'] }; if (tip === 'Pkcs1v15Sign') return { grup: 'RSA', primitiv: 'signature', padding: 'pkcs1v15', hash: hashDinTip(generic) || undefined, functii: ['sign', 'verify'] }; if (tip === 'Pss') return { grup: 'RSA', primitiv: 'signature', padding: 'pss', hash: hashDinTip(generic) || undefined, functii: ['sign', 'verify'] }; if (tip === 'Oaep') return { grup: 'RSA', primitiv: 'pke', padding: 'oaep', hash: hashDinTip(generic) || undefined, functii: ['encrypt'] }; if (tip === 'Pkcs1v15Encrypt') return { grup: 'RSA', primitiv: 'pke', padding: 'pkcs1v15', functii: ['encrypt'] }; return null; } case 'p256': case 'p384': case 'p521': case 'k256': { const curba = { p256: 'secp256r1', p384: 'secp384r1', p521: 'secp521r1', k256: 'secp256k1' }[crate]; const g = crate === 'k256' ? 'SECP256K1' : 'ECDSA'; if (cale.includes('ecdh') || /^(EphemeralSecret)$/.test(tip)) return { grup: 'ECDH', curba, functii: ['keygen'] }; if (cale.includes('ecdsa') || /^(SigningKey|VerifyingKey|Signature)$/.test(tip)) return { grup: g, curba, functii: fn(rol === 'other' ? (tip === 'VerifyingKey' ? 'verify' : 'keygen') : rol) }; if (/^(SecretKey|PublicKey|NonZeroScalar|AffinePoint|ProjectivePoint)$/.test(tip)) return { grup: crate === 'k256' ? 'SECP256K1' : 'EC', curba, functii: ['keygen'] }; return null; } case 'secp256k1': if (/^(Secp256k1|SecretKey|Keypair|KeyPair|PublicKey|XOnlyPublicKey)$/.test(tip)) return { grup: 'SECP256K1', functii: ['keygen'] }; return null; case 'ml_kem': { const k = /^MlKem(512|768|1024)$/.exec(tip); return k ? { grup: 'MLKEM', param: k[1], functii: fn(/^generate/.test(metoda) ? 'keygen' : /encaps/.test(metoda) ? 'encapsulate' : /decaps/.test(metoda) ? 'decapsulate' : 'other') } : null; } case 'ml_dsa': { const k = /^MlDsa(44|65|87)$/.exec(tip); if (k) return { grup: 'MLDSA', param: k[1], functii: fn(rol) }; const g = /MlDsa(44|65|87)/.exec(generic || ''); if (/^(SigningKey|VerifyingKey|KeyPair|Signature)$/.test(tip)) return { grup: 'MLDSA', param: g ? g[1] : undefined, functii: fn(rol === 'other' ? (tip === 'VerifyingKey' ? 'verify' : 'keygen') : rol) }; return null; } case 'slh_dsa': { const k = /^(?:SlhDsa)?(Sha2|Shake)_(128|192|256)([sf])$/.exec(tip); if (k) return { grup: 'SLHDSA', param: `${k[1].toUpperCase()}-${k[2]}${k[3]}`, functii: fn(rol) }; const g = /(?:SlhDsa)?(Sha2|Shake)_(128|192|256)([sf])/.exec(generic || ''); if (/^(SigningKey|VerifyingKey|Signature)$/.test(tip)) return { grup: 'SLHDSA', param: g ? `${g[1].toUpperCase()}-${g[2]}${g[3]}` : undefined, functii: fn(rol === 'other' ? (tip === 'VerifyingKey' ? 'verify' : 'keygen') : rol) }; return null; } case 'libsignal_protocol': if (tip === 'PreKeyBundle' && metoda === 'new') return { grup: 'LIB-MULTI', nume: 'libsignal session setup (X3DH/PQXDH)', motiv: 'libsignal prekey bundle: Curve25519 identity and signed prekey; a Kyber prekey in the bundle makes the session PQXDH (hybrid). Which prekeys the bundle carries is decided at run time.' }; if (/^(IdentityKeyPair|KeyPair|PrivateKey)$/.test(tip) && /^(generate|new|from_private_key_bytes|deserialize)$/.test(metoda)) return { grup: 'XDH', nume: 'X25519', functii: ['keygen'], nota: 'libsignal identity and prekey keys are Curve25519 (signatures over them are XEdDSA).' }; if (tip === 'KyberPreKeyRecord' || tip === 'KeyType') return null; return null; case 'ring': if (tip === 'Ed25519KeyPair') return { grup: 'EDDSA', nume: 'Ed25519', functii: fn(/generate/.test(metoda) ? 'keygen' : 'other') }; if (tip === 'RsaKeyPair') return { grup: 'RSA', primitiv: 'signature', functii: ['sign'] }; if (tip === 'EcdsaKeyPair') { const c = /P(256|384)/.exec(ctx.code.slice(m.index, m.index + 200)); return { grup: 'ECDSA', curba: c ? `secp${c[1]}r1` : undefined, functii: fn(/generate/.test(metoda) ? 'keygen' : 'other') }; } if (tip === 'EphemeralPrivateKey' && metoda === 'generate') { const c = /(X25519|ECDH_P256|ECDH_P384)/.exec(ctx.code.slice(m.index, m.index + 200)); return c && c[1] !== 'X25519' ? { grup: 'ECDH', curba: c[1] === 'ECDH_P256' ? 'secp256r1' : 'secp384r1', functii: ['keygen'] } : { grup: 'XDH', nume: 'X25519', functii: ['keygen'] }; } return null; case 'openssl': { if (tip === 'MessageDigest') { const h = { md5: 'MD5', sha1: 'SHA1', sha224: 'SHA224', sha256: 'SHA256', sha384: 'SHA384', sha512: 'SHA512', sha3_224: 'SHA3-224', sha3_256: 'SHA3-256', sha3_384: 'SHA3-384', sha3_512: 'SHA3-512', ripemd160: 'RIPEMD160', shake_128: 'SHAKE128', shake_256: 'SHAKE256' }[metoda]; return h ? { grup: 'HASH', hash: h, functii: ['digest'] } : null; } if (tip === 'Cipher') { const k = /^(aes|des_ede3|des|rc4|rc2|bf|chacha20_poly1305|chacha20|camellia|sm4|aria)_?(\d{3})?_?(\w+)?$/.exec(metoda); if (!k) return null; const nume = k[1] === 'des_ede3' ? '3DES' : k[1] === 'bf' ? 'Blowfish' : k[1] === 'chacha20_poly1305' ? 'ChaCha20-Poly1305' : k[1] === 'chacha20' ? 'ChaCha20' : k[1].toUpperCase(); return { grup: 'CIPHER', nume, param: k[2], mod: k[3] ? k[3].replace(/_/g, '-') : undefined, functii: ['encrypt'] }; } if (tip === 'Rsa' && /^(generate|generate_with_e)$/.test(metoda)) { const b = /^\s*(\d{3,5})\s*(?:,|$)/.exec(argsDupa(ctx, m)); return { grup: 'RSA', param: b ? b[1] : undefined, functii: ['keygen'], bucataExtra: b ? ` ${b[1]}` : '' }; } if (tip === 'EcKey' && /^(generate|from_curve_name)$/.test(metoda)) { const c = /Nid::(X9_62_PRIME256V1|SECP256K1|SECP384R1|SECP521R1|SECP224R1|BRAINPOOL_P256R1|BRAINPOOL_P384R1|BRAINPOOL_P512R1)/.exec(ctx.code.slice(m.index, m.index + 300)); const curba = c ? ({ X9_62_PRIME256V1: 'secp256r1', SECP256K1: 'secp256k1', SECP384R1: 'secp384r1', SECP521R1: 'secp521r1', SECP224R1: 'secp224r1', BRAINPOOL_P256R1: 'brainpoolP256r1', BRAINPOOL_P384R1: 'brainpoolP384r1', BRAINPOOL_P512R1: 'brainpoolP512r1' })[c[1]] : undefined; return { grup: 'EC', curba, functii: ['keygen'] }; } if (tip === 'PKey') { const k = /^generate_(ed25519|ed448|x25519|x448)$/.exec(metoda); if (!k) return null; return /^ed/.test(k[1]) ? { grup: 'EDDSA', nume: k[1] === 'ed448' ? 'Ed448' : 'Ed25519', functii: ['keygen'] } : { grup: 'XDH', nume: k[1] === 'x448' ? 'X448' : 'X25519', functii: ['keygen'] }; } if (/^(SslConnector|SslAcceptor|SslContext)$/.test(tip) && /^(builder|mozilla_modern|mozilla_intermediate|mozilla_modern_v5|mozilla_intermediate_v5)$/.test(metoda)) return { grup: 'TLS', param: 'negotiated', nume: `openssl ${tip}` }; return null; } case 'rustls': case 'tokio_rustls': case 'hyper_rustls': case 'native_tls': if (/^(ClientConfig|ServerConfig)$/.test(tip) && /^(builder|builder_with_provider|builder_with_protocol_versions)$/.test(metoda)) return { grup: 'TLS', param: 'negotiated', nume: `rustls ${tip}` }; if (/^(TlsConnector|TlsAcceptor|HttpsConnectorBuilder)$/.test(tip)) return { grup: 'TLS', param: 'negotiated', nume: `${NUME_CARGO(crate)} ${tip}` }; return null; case 'pqcrypto_kyber': case 'pqcrypto_mlkem': case 'pqcrypto_dilithium': case 'pqcrypto_mldsa': case 'pqcrypto_sphincsplus': case 'pqcrypto_falcon': case 'pqcrypto_hqc': case 'pqcrypto_classicmceliece': return null; // functiile lor sunt pe cale (pasul 3) case 'oqs': return null; // variantele Algorithm (pasul 4) case 'age': if (cale.includes('x25519') && /^(Identity|Recipient)$/.test(tip)) return { grup: 'XDH', nume: 'X25519', functii: ['keygen'], nota: 'age: X25519 recipients; the file key is wrapped and the payload encrypted with ChaCha20-Poly1305.' }; if (cale.includes('scrypt') && /^(Identity|Recipient)$/.test(tip)) return { grup: 'KDF', nume: 'scrypt', functii: ['derive'] }; return null; case 'crypto_box': if (/^(SalsaBox|ChaChaBox)$/.test(tip) && metoda === 'new') return { grup: 'XDH', nume: 'X25519', functii: ['keygen'], nota: `crypto_box: X25519 key agreement with ${tip === 'SalsaBox' ? 'XSalsa20-Poly1305' : 'XChaCha20-Poly1305'}.` }; if (tip === 'SecretKey' && /^(generate|from_bytes)$/.test(metoda)) return { grup: 'XDH', nume: 'X25519', functii: ['keygen'] }; return null; case 'arti_client': if (tip === 'TorClient') return { grup: 'UNKNOWN', nume: 'Tor client (arti)', motiv: 'Tor circuit and channel cryptography is negotiated with the relays by the Tor protocol version in use, not visible in this file.' }; return null; case 'jsonwebtoken': return null; // variantele Algorithm (pasul 4) default: return null; } } // --- functii pe cale ------------------------------------------------------------------------------------------------------------ function activFunctie(cale, generic, ctx, m) { const crate = cale[0]; const fnume = cale[cale.length - 1]; const modul = cale.length >= 3 ? cale[cale.length - 2] : null; switch (crate) { case 'blake3': return /^(hash|keyed_hash|derive_key)$/.test(fnume) ? { grup: 'HASH', hash: 'BLAKE3', functii: ['digest'] } : null; case 'pbkdf2': { if (!/^pbkdf2(_hmac|_hmac_array|_array|_check|_simple)?$/.test(fnume)) return null; const h = hashDinTip(generic ? generic.replace(/^.*?<\s*/, '').replace(/\s*>.*$/, '') : ''); return { grup: 'KDF', nume: 'PBKDF2', hash: h || undefined, functii: ['derive'], crateHash: crateHashDinTip(generic ? generic.replace(/^.*?<\s*/, '').replace(/\s*>.*$/, '') : '') }; } case 'scrypt': return /^(scrypt|scrypt_simple|scrypt_check)$/.test(fnume) ? { grup: 'KDF', nume: 'scrypt', functii: ['derive'] } : null; case 'bcrypt': return /^(hash|hash_with_salt|verify|hash_with_result)$/.test(fnume) ? { grup: 'KDF', nume: 'bcrypt', functii: ['derive'] } : null; case 'rustls': if (fnume === 'default_provider' && modul === 'ring') return { grup: 'XDH', nume: 'X25519', functii: ['keygen'], nota: 'rustls with the ring provider: ring implements no ML-KEM, so the key-exchange groups are X25519, P-256 and P-384 only (no X25519MLKEM768).' }; if (fnume === 'default_provider' && modul === 'aws_lc_rs') return { grup: 'UNKNOWN', nume: 'rustls aws-lc-rs provider', motiv: 'rustls with the aws-lc-rs provider: X25519MLKEM768 is offered by default from rustls 0.23.22 (and only with the prefer-post-quantum feature before 0.23.22); the rustls version is in Cargo.lock, not here.' }; return null; case 'libsignal_protocol': if (fnume === 'process_prekey_bundle') return { grup: 'LIB-MULTI', nume: 'libsignal session setup (X3DH/PQXDH)', motiv: 'libsignal session setup from a prekey bundle: Curve25519 identity and signed prekey; a Kyber prekey in the bundle makes the session PQXDH (hybrid). Which prekeys the bundle carries is decided at run time.' }; return null; case 'pqcrypto_kyber': case 'pqcrypto_mlkem': case 'pqcrypto_dilithium': case 'pqcrypto_mldsa': case 'pqcrypto_sphincsplus': case 'pqcrypto_falcon': case 'pqcrypto_hqc': case 'pqcrypto_classicmceliece': { if (!/^(keypair|encapsulate|decapsulate|sign|open|detached_sign|verify_detached_signature)$/.test(fnume) || !modul) return null; const f = /^(keypair)$/.test(fnume) ? 'keygen' : /encaps/.test(fnume) ? 'encapsulate' : /decaps/.test(fnume) ? 'decapsulate' : /sign$/.test(fnume) ? 'sign' : /open|verify/.test(fnume) ? 'verify' : 'other'; let k; if ((k = /^kyber(512|768|1024)(90s)?$/.exec(modul))) return { grup: 'PREPQ', nume: `Kyber-${k[1]}`, primitiv: 'kem', functii: [f] }; if ((k = /^mlkem(512|768|1024)$/.exec(modul))) return { grup: 'MLKEM', param: k[1], functii: [f] }; if ((k = /^dilithium(2|3|5)(aes)?$/.exec(modul))) return { grup: 'PREPQ', nume: `Dilithium${k[1]}`, primitiv: 'signature', functii: [f] }; if ((k = /^mldsa(44|65|87)$/.exec(modul))) return { grup: 'MLDSA', param: k[1], functii: [f] }; if (/^sphincs/.test(modul)) return { grup: 'PREPQ', nume: `SPHINCS+ (${modul})`, primitiv: 'signature', functii: [f] }; if ((k = /^falcon(?:padded)?(512|1024)$/.exec(modul))) return { grup: 'FALCON', param: k[1], functii: [f] }; if ((k = /^hqc(128|192|256)$/.exec(modul))) return { grup: 'HQC', nume: `HQC-${k[1]}`, functii: [f] }; if (/^mceliece/.test(modul)) return { grup: 'UNKNOWN', nume: `Classic McEliece (${modul})`, motiv: 'Classic McEliece: code-based KEM, NIST round-4 candidate, not standardized.' }; return null; } default: return null; } } // --- constante si variante enum (ring, jsonwebtoken, oqs, libsignal kem::KeyType) ---------------------------------------------------- function activConstanta(cale, nume, ctx, m) { const crate = cale[0]; const modul = cale[cale.length - 1]; let k; if (crate === 'ring') { if (modul === 'signature') { if (nume === 'ED25519') return { grup: 'EDDSA', nume: 'Ed25519', functii: ['verify'] }; if ((k = /^ECDSA_P(256|384)_SHA(256|384)_(ASN1|FIXED)(_SIGNING)?$/.exec(nume))) return { grup: 'ECDSA', curba: `secp${k[1]}r1`, hash: `SHA${k[2]}`, functii: [k[4] ? 'sign' : 'verify'] }; if ((k = /^RSA_(PKCS1|PSS)_(?:\d+_\d+_)?SHA(1|256|384|512)(_LEGACY_KEY)?$/.exec(nume))) return { grup: 'RSA', primitiv: 'signature', padding: k[1] === 'PSS' ? 'pss' : 'pkcs1v15', hash: `SHA${k[2]}`, functii: ['sign', 'verify'] }; return null; } if (modul === 'agreement') { if (nume === 'X25519') return { grup: 'XDH', nume: 'X25519', functii: ['keygen'] }; if ((k = /^ECDH_P(256|384)$/.exec(nume))) return { grup: 'ECDH', curba: `secp${k[1]}r1`, functii: ['keygen'] }; return null; } if (modul === 'digest') { if ((k = /^SHA(1_FOR_LEGACY_USE_ONLY|256|384|512|512_256)$/.exec(nume))) return { grup: 'HASH', hash: k[1].startsWith('1') ? 'SHA1' : k[1] === '512_256' ? 'SHA512/256' : `SHA${k[1]}`, functii: ['digest'] }; return null; } if (modul === 'aead') { if ((k = /^AES_(128|256)_GCM$/.exec(nume))) return { grup: 'CIPHER', nume: 'AES', param: k[1], mod: 'gcm', functii: ['encrypt'] }; if (nume === 'CHACHA20_POLY1305') return { grup: 'CIPHER', nume: 'ChaCha20-Poly1305', functii: ['encrypt'] }; return null; } if (modul === 'hmac' && (k = /^HMAC_SHA(1_FOR_LEGACY_USE_ONLY|256|384|512)$/.exec(nume))) return { grup: 'MAC', hash: k[1].startsWith('1') ? 'SHA1' : `SHA${k[1]}`, functii: ['tag'] }; if (modul === 'hkdf' && (k = /^HKDF_SHA(1_FOR_LEGACY_USE_ONLY|256|384|512)$/.exec(nume))) return { grup: 'KDF', nume: 'HKDF', hash: k[1].startsWith('1') ? 'SHA1' : `SHA${k[1]}`, functii: ['derive'] }; if (modul === 'pbkdf2' && (k = /^PBKDF2_HMAC_SHA(1|256|384|512)$/.exec(nume))) return { grup: 'KDF', nume: 'PBKDF2', hash: `SHA${k[1]}`, functii: ['derive'] }; return null; } if (crate === 'jsonwebtoken' && modul === 'Algorithm') { return /^(HS256|HS384|HS512|RS256|RS384|RS512|PS256|PS384|PS512|ES256|ES384|EdDSA)$/.test(nume) ? { ...jws(nume), functii: ['sign', 'verify'] } : null; } if (crate === 'oqs' && modul === 'Algorithm') { if ((k = /^MlKem(512|768|1024)$/.exec(nume))) return { grup: 'MLKEM', param: k[1] }; if ((k = /^Kyber(512|768|1024)/.exec(nume))) return { grup: 'PREPQ', nume: `Kyber-${k[1]}`, primitiv: 'kem' }; if ((k = /^MlDsa(44|65|87)$/.exec(nume))) return { grup: 'MLDSA', param: k[1] }; if ((k = /^Dilithium(2|3|5)/.exec(nume))) return { grup: 'PREPQ', nume: `Dilithium${k[1]}`, primitiv: 'signature' }; if ((k = /^Falcon(512|1024)$/.exec(nume))) return { grup: 'FALCON', param: k[1] }; if (/^Sphincs/.test(nume)) return { grup: 'PREPQ', nume: `SPHINCS+ (${nume})`, primitiv: 'signature' }; if ((k = /^Hqc(128|192|256)$/.exec(nume))) return { grup: 'HQC', nume: `HQC-${k[1]}` }; if (/^(BikeL|ClassicMcEliece|FrodoKem|Ntru|Mayo|Cross|Uov|Snova)/.test(nume)) return { grup: 'UNKNOWN', nume: `liboqs ${nume}`, motiv: `${nume}: a post-quantum scheme that is not a NIST standard (liboqs experimental or round-4 algorithm).` }; return null; } if (crate === 'libsignal_protocol' && modul === 'KeyType') { if ((k = /^Kyber(768|1024)$/.exec(nume))) return { grup: 'PREPQ', nume: `Kyber-${k[1]}`, primitiv: 'kem', nota: 'Signal PQXDH prekeys: CRYSTALS-Kyber (the pre-standard form of ML-KEM).' }; if ((k = /^M[lL][kK][eE][mM](768|1024)$/.exec(nume))) return { grup: 'MLKEM', param: k[1] }; return null; } return null; } export { CRATE as CRATE_RUST };