584 lines
43 KiB
JavaScript
584 lines
43 KiB
JavaScript
// 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::<Sha256>, Hkdf::<Sha512>, type HmacSha256 = Hmac<Sha256>), functiile pe cale (blake3::hash,
|
|
// pbkdf2::pbkdf2_hmac::<Sha256>, 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(/(?<![\w:])(?:pub(?:\([^)]*\))?\s+)?use\s+([^;{}]*(?:\{[^;]*\})?[^;]*);/g)) {
|
|
dezvolta(m[1].trim(), [], (cale, local, glob) => r.push({ crate: cale[0], cale, local, glob, pos: m.index, sf: m.index + m[0].length }));
|
|
}
|
|
for (const m of ctx.potriviri(/(?<![\w:])extern\s+crate\s+([A-Za-z_]\w*)(?:\s+as\s+([A-Za-z_]\w*))?\s*;/g)) {
|
|
r.push({ crate: m[1], cale: [m[1]], local: m[2] || m[1], glob: false, pos: m.index, sf: m.index + m[0].length });
|
|
}
|
|
return r.filter((u) => 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(/(?<![\w:])(?:pub(?:\([^)]*\))?\s+)?use\s+((?:crate|self|super)::[^;]*);/g)) dezvolta(m[1].trim(), [], (cale, local) => { 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::<MlDsa65>::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::<Sha256>::new_from_slice)
|
|
const RE_STATIC = /(?<![\w:])((?:[A-Za-z_]\w*::)*)([A-Z]\w*)\s*(?:::\s*<([^;{}()=]*?)>)?\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<Sha256>; type Kdf = Hkdf<Sha512>;
|
|
for (const m of ctx.potriviri(/(?<![\w:])type\s+\w+\s*=\s*((?:[A-Za-z_]\w*::)*)(Hmac|SimpleHmac|Hkdf)\s*<\s*([^>;]+)>/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::<Sha256>(...), pqcrypto_kyber::kyber1024::keypair(), scrypt::scrypt(...),
|
|
// rustls::crypto::ring::default_provider(), libsignal_protocol::process_prekey_bundle(...)
|
|
for (const m of ctx.potriviri(/(?<![\w:.])((?:[a-z_]\w*::)+)([a-z_]\w*)\s*(?:::\s*<([^;{}()=]*?)>)?\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(`(?<![\\w:.])${escapeRe(local)}\\s*(?:::\\s*<([^;{}()=]*?)>)?\\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(/(?<![\w:])((?:[A-Za-z_]\w*::)+)([A-Z][A-Za-z0-9_]*)\b(?!\s*::|\s*\()/g)) {
|
|
const seg = m[1].split('::').filter(Boolean);
|
|
if (RADACINI_PROPRII.has(seg[0])) continue;
|
|
const u = locale.get(seg[0]);
|
|
const cale = u ? [...u.cale, ...seg.slice(1)] : seg;
|
|
if (!CRATE[cale[0]]) continue;
|
|
const activ = activConstanta(cale, m[2], ctx, m);
|
|
if (activ) adauga(cale[0], m.index, `${cale.join('::')}::${m[2]}`, activ.fel || 'config', activ, m[0]);
|
|
}
|
|
// variante importate direct (use jsonwebtoken::Algorithm; Algorithm::RS256) si tipuri parametru PQ scrise singure
|
|
for (const m of ctx.potriviri(/(?<![\w:])([A-Z]\w*)::([A-Z][A-Za-z0-9_]*)\b(?!\s*::|\s*\()/g)) {
|
|
const u = locale.get(m[1]);
|
|
if (!u || !CRATE[u.crate]) continue;
|
|
const activ = activConstanta(u.cale, m[2], ctx, m);
|
|
if (activ) adauga(u.crate, m.index, `${u.cale.join('::')}::${m[2]}`, activ.fel || 'config', activ, m[0]);
|
|
}
|
|
const peRand = new Set();
|
|
for (const m of potriviri(/(?<!\w)(MlDsa(44|65|87)|MlKem(512|768|1024)|SlhDsa(Sha2|Shake)_(128|192|256)([sf]))\b(?!\s*::)/g)) {
|
|
const linie = ctx.poz(m.index).line;
|
|
const k = `${linie}|${m[1]}`;
|
|
if (peRand.has(k)) continue;
|
|
peRand.add(k);
|
|
const r = rezolvaCale('', m[1]) || (m[1].startsWith('SlhDsa') ? { crate: 'slh_dsa' } : null);
|
|
if (!r) continue;
|
|
const activ = m[2] ? { grup: 'MLDSA', param: m[2] } : m[3] ? { grup: 'MLKEM', param: m[3] } : { grup: 'SLHDSA', param: `${m[4].toUpperCase()}-${m[5]}${m[6]}` };
|
|
adauga(r.crate, m.index, m[1], 'call', activ, m[0]);
|
|
}
|
|
// SLH-DSA cu numele scurte ale crate-ului (slh_dsa::Sha2_128s), numai cand crate-ul e in scop
|
|
if ([...locale.values()].some((u) => u.crate === 'slh_dsa') || globuri.has('slh_dsa')) {
|
|
for (const m of potriviri(/(?<!\w)(?:slh_dsa::)?(Sha2|Shake)_(128|192|256)([sf])\b(?!\s*::)/g)) {
|
|
const k = `${ctx.poz(m.index).line}|${m[0]}`;
|
|
if (peRand.has(k)) continue;
|
|
peRand.add(k);
|
|
adauga('slh_dsa', m.index, `slh_dsa::${m[1]}_${m[2]}${m[3]}`, 'call', { grup: 'SLHDSA', param: `${m[1].toUpperCase()}-${m[2]}${m[3]}` }, m[0]);
|
|
}
|
|
}
|
|
|
|
// 5. modurile de operare ale RustCrypto: cbc::Encryptor<Aes256>, ecb::Decryptor<aes::Aes128>, ctr::Ctr128BE<Aes256>
|
|
for (const m of ctx.potriviri(/(?<![\w:])(cbc|ecb|ctr|cfb_mode|ofb|ccm)::(Encryptor|Decryptor|Ctr\w*|Ccm)\s*(?:::)?\s*<\s*(?:aes::)?Aes(128|192|256)\b/g)) {
|
|
const mod = m[1] === 'cfb_mode' ? 'cfb' : m[1];
|
|
adauga('aes', m.index, `${m[1]}::${m[2]}<Aes${m[3]}>`, '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') || /(?<![\w:])snow::/.test(ctx.code)) {
|
|
for (const m of ctx.code.matchAll(/Noise_(\w+?)_(25519|448)_(ChaChaPoly|AESGCM)_(SHA256|SHA512|BLAKE2s|BLAKE2b)\b/g)) {
|
|
if (!ctx.inSir(m.index)) continue;
|
|
const pos = m.index;
|
|
adauga('snow', pos, 'snow Noise pattern', 'config', { grup: 'XDH', nume: m[2] === '448' ? 'X448' : 'X25519', functii: ['keygen'] }, m[0]);
|
|
adauga('snow', pos, 'snow Noise pattern', 'config', m[3] === 'AESGCM' ? { grup: 'CIPHER', nume: 'AES', param: '256', mod: 'gcm', functii: ['encrypt'] } : { grup: 'CIPHER', nume: 'ChaCha20-Poly1305', functii: ['encrypt'] }, m[0]);
|
|
adauga('snow', pos, 'snow Noise pattern', 'config', { grup: 'HASH', hash: m[4] === 'SHA256' ? 'SHA256' : m[4] === 'SHA512' ? 'SHA512' : m[4] === 'BLAKE2s' ? 'BLAKE2S' : 'BLAKE2B', functii: ['digest'] }, m[0]);
|
|
}
|
|
}
|
|
|
|
// 7. metodele pe valori ale lui secp256k1 (sign_ecdsa, sign_schnorr), cand crate-ul e in scop
|
|
if ([...locale.values()].some((u) => 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') || /(?<![\w:])openssl::/.test(ctx.code)) {
|
|
const vers = { SSL3: 'ssl3', TLS1: '1.0', TLS1_1: '1.1', TLS1_2: '1.2', TLS1_3: '1.3' };
|
|
for (const m of ctx.potriviri(/\.\s*set_(min|max)_proto_version\s*\(\s*Some\s*\(\s*(?:openssl::ssl::)?SslVersion::(SSL3|TLS1_3|TLS1_2|TLS1_1|TLS1)\b/g)) {
|
|
adauga('openssl', m.index, `openssl set_${m[1]}_proto_version`, 'config', { grup: 'TLS', param: vers[m[2]], rol: m[1] }, m[0]);
|
|
}
|
|
}
|
|
|
|
// 9. importurile: dovada slaba, numai pentru crate-urile fara niciun apel recunoscut in fisier (o singura data pe crate)
|
|
const raportate = new Set();
|
|
for (const u of useuri) {
|
|
const c = CRATE[u.crate];
|
|
if (!c || folosite.has(u.crate) || raportate.has(u.crate)) continue;
|
|
raportate.add(u.crate);
|
|
const nume = NUME_CARGO(u.crate);
|
|
const activ = c.grupuri[0] === 'TLS'
|
|
? { grup: 'UNKNOWN', nume, motiv: `Imports ${nume}: the TLS versions and key-exchange groups come from the crypto provider and the Cargo features (ring has no ML-KEM; aws-lc-rs offers X25519MLKEM768 from rustls 0.23.22), not visible in this file.` }
|
|
: c.grupuri[0] === 'UNKNOWN'
|
|
? { grup: 'UNKNOWN', nume, motiv: `Imports ${nume} (${c.ofera}); the cryptography is negotiated at run time, not visible here.` }
|
|
: { grup: 'LIB-MULTI', nume, motiv: `Imports ${nume} (${c.ofera}); no specific call recognized in this file.` };
|
|
// fara suprimare pe grup: un apel sha2 nu spune nimic despre importul blake2; crate-ul cu apel e deja exclus prin `folosite`
|
|
ctx.adauga(u.pos, `use ${u.crate}`, 'import', activ, { bucata: `use ${u.cale.join('::')}` });
|
|
}
|
|
}
|
|
|
|
// textul argumentelor apelului de dupa o potrivire: '(' trebuie sa urmeze imediat (spatii permise), altfel sir gol
|
|
function argsDupa(ctx, m) {
|
|
const dupa = /^\s*\(/.exec(ctx.code.slice(m.index + m[0].length, m.index + m[0].length + 8));
|
|
if (!dupa) return '';
|
|
const a = argumente(ctx, m.index + m[0].length + dupa[0].length);
|
|
return ctx.code.slice(a.start, a.end);
|
|
}
|
|
|
|
// --- clasificarea unui apel static Tip::metoda -----------------------------------------------------------------------------------
|
|
function activStatic(r, metoda, generic, ctx, m) {
|
|
const { crate, cale } = r;
|
|
const tip = cale[cale.length - 1]; // numele ORIGINAL al tipului, nu aliasul local din `use ... as`
|
|
const fn = (k) => [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 };
|