240 lines
27 KiB
JavaScript
240 lines
27 KiB
JavaScript
// Detectorul Swift (0.4.0, 2026-10-18; scris dupa rularea pe libsignal: 210 fisiere Swift, 44.923 randuri, neanalizate prin
|
|
// constructie). In Swift un modul se vede in fisier numai dupa `import Modul`, deci importurile spun ce poate insemna un nume:
|
|
// - CryptoKit / Crypto (swift-crypto, acelasi API): SHA256.hash, Insecure.MD5/SHA1, HMAC<SHA256>, AES.GCM.seal/open, ChaChaPoly,
|
|
// Curve25519.KeyAgreement / .Signing, P256/P384/P521.Signing / .KeyAgreement, HKDF<SHA256>.deriveKey, SymmetricKey(size:),
|
|
// _RSA (din _CryptoExtras);
|
|
// - CommonCrypto: CC_SHA256 & co, CCCrypt / CCCryptorCreate cu kCCAlgorithm* si kCCOption*, CCHmac cu kCCHmacAlg*, CCKeyDerivationPBKDF;
|
|
// - Security: SecKeyCreateRandomKey (tipul si marimea din dictionarul de atribute, inline sau dintr-o variabila a fisierului),
|
|
// SecKeyCreateSignature / SecKeyVerifySignature / SecKeyCreateEncryptedData / SecKeyCreateDecryptedData cu algoritmul .rsa*/.ecdsa*/.ecies*,
|
|
// SecTrustEvaluate* si SecPolicyCreateSSL (verificare de certificate: autentificare, nu schimb de cheie);
|
|
// - TLS-ul clientului: tlsMinimumSupportedProtocolVersion / tlsMaximumSupportedProtocolVersion (URLSessionConfiguration),
|
|
// sec_protocol_options_set_min/max_tls_protocol_version, NWProtocolTLS.Options();
|
|
// - CryptoSwift: AES(key:blockMode:), SHA2(variant:), .md5()/.sha256(), HMAC(key:variant:), PBKDF2, ChaCha20, Poly1305, RSA(keySize:), Blowfish;
|
|
// - Sodium / Clibsodium: sodium.box / .sign / .keyExchange / .secretBox / .aead.xchacha20poly1305ietf / .pwHash / .genericHash si functiile C crypto_*;
|
|
// - LibSignalClient (si SignalFfi, modulul FFI pe care il importa chiar sursele bibliotecii): IdentityKeyPair.generate(), PrivateKey.generate(),
|
|
// KEMKeyPair.generate() (Kyber-1024: API-ul Swift nu are parametru de tip, puntea genereaza kem::KeyType::Kyber1024), KEMPublicKey(...),
|
|
// Aes256GcmSiv(key:), Aes256GcmEncryptedData, Aes256GcmEncryption/Decryption, Aes256Ctr32, hkdf(...), PreKeyBundle(...), processPreKeyBundle(...).
|
|
// Importul unei biblioteci terte cu multe primitive (LibSignalClient, CryptoSwift, Sodium) fara niciun apel recunoscut e dovada slaba
|
|
// (unknown, o data pe fisier); cadrele de sistem (CryptoKit, CommonCrypto, Security) nu se raporteaza la import: Security se importa
|
|
// pentru keychain si SecRandomCopyBytes in aproape orice aplicatie.
|
|
// Ce NU se recunoaste: un modul adus printr-un bridging header Objective-C (fara `import` in fisierul Swift), tipurile proprii care
|
|
// invelesc un cadru, metodele pe valori fara constructorul lor in acelasi fisier, `#if DEBUG` (scopul se deriva din cale, nu din modul).
|
|
import { argumente, escapeRe } from './context.mjs';
|
|
import { hashCanonic } from './catalog.mjs';
|
|
|
|
const NUME_PKIX = 'X.509 chain validation (PKIX)';
|
|
const NOTA_EC_LIBSIGNAL = 'libsignal Curve25519 key pair: used for Diffie-Hellman (X3DH/PQXDH, Double Ratchet) and for XEdDSA signatures over the same key.';
|
|
export const NOTA_KEM_SWIFT = 'libsignal Swift KEMKeyPair.generate() takes no key type: the bridge generates kem::KeyType::Kyber1024 (rust/bridge/shared/src/protocol.rs, KYBER_KEY_TYPE), the PQXDH prekey; CRYSTALS-Kyber is the pre-standard form of ML-KEM.';
|
|
const BUNDLE = { 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.' };
|
|
const TLSV = { TLSv10: '1.0', TLSv11: '1.1', TLSv12: '1.2', TLSv13: '1.3', tlsProtocol1: '1.0', tlsProtocol11: '1.1', tlsProtocol12: '1.2', tlsProtocol13: '1.3', sslProtocol3: 'ssl3' };
|
|
const CURBA_P = { P256: 'secp256r1', P384: 'secp384r1', P521: 'secp521r1' };
|
|
|
|
export function importuriSwift(ctx) {
|
|
const r = new Map(); // modul -> pozitia primului import
|
|
for (const m of ctx.potriviri(/^[ \t]*(?:@\w+(?:\([^)]*\))?[ \t]+)*import[ \t]+(?:(?:class|struct|enum|protocol|typealias|func|var|let)[ \t]+)?([A-Za-z_]\w*)/gm)) {
|
|
if (!r.has(m[1])) r.set(m[1], m.index + m[0].indexOf('import'));
|
|
}
|
|
return r;
|
|
}
|
|
|
|
// algoritmul SecKeyAlgorithm (.rsaSignatureMessagePKCS1v15SHA256, .ecdsaSignatureMessageX962SHA256, .rsaEncryptionOAEPSHA256, ...)
|
|
export function algoritmSecKey(nume, functie) {
|
|
let m;
|
|
const hash = (s) => { const h = /SHA(1|224|256|384|512)$/.exec(s); return h ? hashCanonic('SHA' + h[1]) : undefined; };
|
|
if ((m = /^rsaSignature(?:Message|Digest)(PKCS1v15|PSS)(?:SHA\d+|Raw)?$/.exec(nume))) return { grup: 'RSA', primitiv: 'signature', padding: m[1] === 'PSS' ? 'other' : 'pkcs1v15', hash: hash(nume), functii: [functie] };
|
|
if ((m = /^rsaSignatureRaw$/.exec(nume))) return { grup: 'RSA', primitiv: 'signature', padding: 'raw', functii: [functie] };
|
|
if ((m = /^rsaEncryption(OAEP|PKCS1|Raw)/.exec(nume))) return { grup: 'RSA', primitiv: 'pke', padding: m[1] === 'OAEP' ? 'oaep' : m[1] === 'PKCS1' ? 'pkcs1v15' : 'raw', hash: hash(nume.replace(/AESGCM$/, '')), functii: [functie] };
|
|
if ((m = /^ecdsaSignature(?:Message|Digest|Rfc4754)(?:X962|RFC4754)?(?:SHA\d+)?$/.exec(nume))) return { grup: 'ECDSA', hash: hash(nume), functii: [functie] };
|
|
if (/^eciesEncryption/.test(nume)) return { grup: 'ECDH', nume: `ECIES (${nume})`, primitiv: 'pke', functii: [functie] };
|
|
if (/^ecdhKeyExchange/.test(nume)) return { grup: 'ECDH', functii: ['keygen'] };
|
|
return { grup: 'UNKNOWN', nume: `SecKeyAlgorithm.${nume}`, motiv: `SecKeyAlgorithm .${nume} is not in this tool's catalog.` };
|
|
}
|
|
|
|
// tipul si marimea cheii dintr-un dictionar de atribute SecKey (text): kSecAttrKeyTypeRSA / kSecAttrKeyTypeECSECPrimeRandom, kSecAttrKeySizeInBits
|
|
function cheieDinAtribute(text) {
|
|
const tip = /kSecAttrKeyType(RSA|ECSECPrimeRandom|ECDSA|EC)\b/.exec(text);
|
|
const bits = /kSecAttrKeySizeInBits(?:\s+as\s+String)?\s*:\s*(\d+)/.exec(text);
|
|
if (!tip) return null;
|
|
if (tip[1] === 'RSA') return { grup: 'RSA', param: bits ? bits[1] : undefined, functii: ['keygen'] };
|
|
const curba = bits ? ({ 256: 'secp256r1', 384: 'secp384r1', 521: 'secp521r1', 224: 'secp224r1', 192: 'secp192r1' })[bits[1]] : undefined;
|
|
return { grup: 'EC', curba, functii: ['keygen'] };
|
|
}
|
|
|
|
export function detecteazaSwift(ctx) {
|
|
const c = ctx.code;
|
|
const imp = importuriSwift(ctx);
|
|
const are = (...module) => module.some((m) => imp.has(m));
|
|
const folosite = new Set(); // bibliotecile terte cu cel putin un apel recunoscut (nu primesc dovada slaba de import)
|
|
const adauga = (lib, pos, api, fel, activ, bucata) => { if (lib) folosite.add(lib); ctx.adauga(pos, api, fel, activ, { bucata }); };
|
|
const randul = (pos) => { const a = c.lastIndexOf('\n', pos) + 1; const b = c.indexOf('\n', pos); return c.slice(a, b < 0 ? c.length : b); };
|
|
const argsDupa = (m) => argumente(ctx, m.index + m[0].length);
|
|
|
|
// --- CryptoKit / swift-crypto ---------------------------------------------------------------------------------------------------
|
|
if (are('CryptoKit', 'Crypto', '_CryptoExtras')) {
|
|
const bitsPeRand = (pos) => { const b = /SymmetricKey\s*\(\s*size\s*:\s*\.bits(128|192|256)\s*\)/.exec(randul(pos)); return b ? b[1] : undefined; };
|
|
for (const m of ctx.potriviri(/(?<![\w.])(SHA256|SHA384|SHA512)\s*(?:\.\s*hash\s*\(|\(\s*\))/g)) adauga(null, m.index, `${m[1]}${/hash/.test(m[0]) ? '.hash' : '()'}`, 'call', { grup: 'HASH', hash: m[1], functii: ['digest'] }, m[0]);
|
|
for (const m of ctx.potriviri(/(?<![\w.])Insecure\s*\.\s*(MD5|SHA1)\s*(?:\.\s*hash\s*\(|\(\s*\))/g)) adauga(null, m.index, `Insecure.${m[1]}`, 'call', { grup: 'HASH', hash: m[1], functii: ['digest'] }, m[0]);
|
|
for (const m of ctx.potriviri(/(?<![\w.])HMAC\s*<\s*(?:Insecure\s*\.\s*)?(MD5|SHA1|SHA256|SHA384|SHA512)\s*>/g)) adauga(null, m.index, `HMAC<${m[1]}>`, 'call', { grup: 'MAC', hash: m[1], functii: ['tag'] }, m[0]);
|
|
for (const m of ctx.potriviri(/(?<![\w.])HKDF\s*<\s*(?:Insecure\s*\.\s*)?(MD5|SHA1|SHA256|SHA384|SHA512)\s*>\s*\.\s*(deriveKey|extract|expand)/g)) adauga(null, m.index, `HKDF<${m[1]}>.${m[2]}`, 'call', { grup: 'KDF', nume: 'HKDF', hash: m[1], functii: ['keyderive'] }, m[0]);
|
|
for (const m of ctx.potriviri(/(?<![\w.])AES\s*\.\s*GCM\s*\.\s*(seal|open|SealedBox|Nonce)\b/g)) adauga(null, m.index, `AES.GCM.${m[1]}`, 'call', { grup: 'CIPHER', nume: 'AES', param: bitsPeRand(m.index), mod: 'gcm', functii: [m[1] === 'open' ? 'decrypt' : 'encrypt'] }, m[0]);
|
|
for (const m of ctx.potriviri(/(?<![\w.])AES\s*\.\s*KeyWrap\s*\.\s*(wrap|unwrap)\b/g)) adauga(null, m.index, `AES.KeyWrap.${m[1]}`, 'call', { grup: 'CIPHER', nume: 'AES', param: bitsPeRand(m.index), mod: 'kw', functii: ['encrypt'], nota: 'AES key wrap (RFC 3394).' }, m[0]);
|
|
for (const m of ctx.potriviri(/(?<![\w.])ChaChaPoly\s*\.\s*(seal|open|SealedBox|Nonce)\b/g)) adauga(null, m.index, `ChaChaPoly.${m[1]}`, 'call', { grup: 'CIPHER', nume: 'ChaCha20-Poly1305', functii: [m[1] === 'open' ? 'decrypt' : 'encrypt'] }, m[0]);
|
|
// constructorii (PrivateKey(), PublicKey(rawRepresentation:)), nu tipul scris intr-o semnatura (`peer: Curve25519.KeyAgreement.PublicKey`)
|
|
for (const m of ctx.potriviri(/(?<![\w.])Curve25519\s*\.\s*(KeyAgreement|Signing)\s*\.\s*(PrivateKey|PublicKey)\s*\(/g)) {
|
|
adauga(null, m.index, `Curve25519.${m[1]}.${m[2]}`, 'call', m[1] === 'KeyAgreement' ? { grup: 'XDH', nume: 'X25519', functii: [m[2] === 'PrivateKey' ? 'keygen' : 'other'] } : { grup: 'EDDSA', nume: 'Ed25519', functii: [m[2] === 'PrivateKey' ? 'keygen' : 'verify'] }, m[0]);
|
|
}
|
|
for (const m of ctx.potriviri(/(?<![\w.])(P256|P384|P521)\s*\.\s*(KeyAgreement|Signing)\s*\.\s*(PrivateKey|PublicKey|ECDSASignature)\s*\(/g)) {
|
|
const curba = CURBA_P[m[1]];
|
|
adauga(null, m.index, `${m[1]}.${m[2]}.${m[3]}`, 'call', m[2] === 'KeyAgreement' ? { grup: 'ECDH', curba, functii: [m[3] === 'PrivateKey' ? 'keygen' : 'other'] } : { grup: 'ECDSA', curba, functii: [m[3] === 'PrivateKey' ? 'keygen' : 'verify'] }, m[0]);
|
|
}
|
|
for (const m of ctx.potriviri(/(?<![\w.])_RSA\s*\.\s*(Signing|Encryption)\s*\.\s*(PrivateKey|PublicKey)\s*(\(\s*keySize\s*:\s*\.bits(\d+)|\()/g)) {
|
|
adauga(null, m.index, `_RSA.${m[1]}.${m[2]}`, 'call', { grup: 'RSA', param: m[4], primitiv: m[1] === 'Signing' ? 'signature' : 'pke', functii: [m[2] === 'PrivateKey' ? 'keygen' : 'other'] }, m[0]);
|
|
}
|
|
for (const m of ctx.potriviri(/(?<![\w.])SymmetricKey\s*\(\s*size\s*:\s*\.bits(128|192|256)\s*\)/g)) {
|
|
if (/AES\s*\.\s*(GCM|KeyWrap)|ChaChaPoly|HMAC\s*<|HKDF\s*</.test(randul(m.index))) continue; // marimea e deja in cifrul de pe acelasi rand
|
|
adauga(null, m.index, 'SymmetricKey(size:)', 'call', { grup: 'UNKNOWN', nume: `SymmetricKey (${m[1]}-bit)`, motiv: `CryptoKit SymmetricKey of ${m[1]} bits: the algorithm that uses it (AES-GCM, ChaChaPoly, HMAC, HKDF) is chosen where the key is used, not here.` }, m[0]);
|
|
}
|
|
}
|
|
|
|
// --- CommonCrypto ------------------------------------------------------------------------------------------------------------------
|
|
if (are('CommonCrypto')) {
|
|
for (const m of ctx.potriviri(/(?<![\w.])CC_(MD2|MD4|MD5|SHA1|SHA224|SHA256|SHA384|SHA512)(?:_Init|_Update|_Final)?\s*\(/g)) adauga(null, m.index, m[0].replace(/\s*\($/, ''), 'call', { grup: 'HASH', hash: m[1], functii: ['digest'] }, m[0]);
|
|
for (const m of ctx.potriviri(/(?<![\w.])(CCCrypt|CCCryptorCreate(?:WithMode)?)\s*\(/g)) {
|
|
const t = argsDupa(m).text;
|
|
const alg = /kCCAlgorithm(AES128|AES|DES|3DES|CAST|RC4|RC2|Blowfish)\b/.exec(t);
|
|
if (!alg) { adauga(null, m.index, m[1], 'call', { grup: 'UNKNOWN', nume: m[1], motiv: `${m[1]}: the algorithm constant (kCCAlgorithm*) is not among the arguments of this call.` }, m[0]); continue; }
|
|
const bits = /kCCKeySizeAES(128|192|256)\b/.exec(t);
|
|
const mod = /kCCOptionECBMode\b/.test(t) ? 'ecb' : /kCCModeCTR\b/.test(t) ? 'ctr' : /kCCModeGCM\b/.test(t) ? 'gcm' : /kCCModeCFB\b/.test(t) ? 'cfb' : /kCCModeOFB\b/.test(t) ? 'ofb' : 'cbc';
|
|
const nume = { AES128: 'AES', AES: 'AES', DES: 'DES', '3DES': '3DES', CAST: 'CAST5', RC4: 'RC4', RC2: 'RC2', Blowfish: 'Blowfish' }[alg[1]];
|
|
adauga(null, m.index, `${m[1]}(kCCAlgorithm${alg[1]})`, 'call', { grup: 'CIPHER', nume, param: nume === 'AES' ? (bits ? bits[1] : undefined) : undefined, mod: /^(RC4)$/.test(nume) ? undefined : mod, functii: [/kCCDecrypt\b/.test(t) ? 'decrypt' : 'encrypt'], nota: nume === 'AES' && mod === 'cbc' && !/kCCMode/.test(t) ? 'CommonCrypto block ciphers default to CBC unless kCCOptionECBMode is set.' : undefined }, m[0]);
|
|
}
|
|
for (const m of ctx.potriviri(/(?<![\w.])CCHmac(?:Init)?\s*\(/g)) {
|
|
const h = /kCCHmacAlg(MD5|SHA1|SHA224|SHA256|SHA384|SHA512)\b/.exec(argsDupa(m).text);
|
|
adauga(null, m.index, 'CCHmac', 'call', h ? { grup: 'MAC', hash: h[1], functii: ['tag'] } : { grup: 'MAC', motiv: 'CCHmac: the hash constant (kCCHmacAlg*) is not among the arguments of this call.' }, m[0]);
|
|
}
|
|
for (const m of ctx.potriviri(/(?<![\w.])CCKeyDerivationPBKDF\s*\(/g)) {
|
|
const h = /kCCPRFHmacAlg(SHA1|SHA224|SHA256|SHA384|SHA512)\b/.exec(argsDupa(m).text);
|
|
adauga(null, m.index, 'CCKeyDerivationPBKDF', 'call', { grup: 'KDF', nume: h ? `PBKDF2-HMAC-${h[1]}` : 'PBKDF2', hash: h ? h[1] : undefined, functii: ['keyderive'] }, m[0]);
|
|
}
|
|
}
|
|
|
|
// --- Security.framework --------------------------------------------------------------------------------------------------------------
|
|
if (are('Security')) {
|
|
for (const m of ctx.potriviri(/(?<![\w.])SecKeyCreateRandomKey\s*\(/g)) {
|
|
const a = argsDupa(m);
|
|
let act = cheieDinAtribute(a.text);
|
|
if (!act) {
|
|
const id = /^\s*([A-Za-z_]\w*)\b/.exec(a.text);
|
|
const def = id && new RegExp(`(?<![\\w.])(?:let|var)\\s+${escapeRe(id[1])}\\b[^=\\n]*=\\s*\\[`).exec(c);
|
|
if (def) act = cheieDinAtribute(c.slice(def.index, argumente(ctx, def.index + def[0].length).end + 1));
|
|
}
|
|
adauga(null, m.index, 'SecKeyCreateRandomKey', 'call', act || { grup: 'UNKNOWN', nume: 'SecKeyCreateRandomKey', motiv: 'SecKeyCreateRandomKey: the key type (kSecAttrKeyType) is not in the attributes of this call nor in a dictionary defined in this file.' }, m[0]);
|
|
}
|
|
for (const m of ctx.potriviri(/(?<![\w.])SecKey(CreateSignature|VerifySignature|CreateEncryptedData|CreateDecryptedData)\s*\(/g)) {
|
|
const a = argsDupa(m);
|
|
const fn = { CreateSignature: 'sign', VerifySignature: 'verify', CreateEncryptedData: 'encrypt', CreateDecryptedData: 'decrypt' }[m[1]];
|
|
let alg = /(?:SecKeyAlgorithm\s*)?\.\s*((?:rsa|ecdsa|ecies|ecdh)\w+)/.exec(a.text);
|
|
if (!alg) {
|
|
const parti = a.text.split(',');
|
|
const id = parti[1] && /^\s*([A-Za-z_]\w*)\s*$/.exec(parti[1]);
|
|
if (id) alg = new RegExp(`(?<![\\w.])(?:let|var)\\s+${escapeRe(id[1])}\\s*(?::\\s*SecKeyAlgorithm\\s*)?=\\s*(?:SecKeyAlgorithm\\s*)?\\.\\s*((?:rsa|ecdsa|ecies|ecdh)\\w+)`).exec(c);
|
|
}
|
|
adauga(null, m.index, `SecKey${m[1]}`, 'call', alg ? algoritmSecKey(alg[1], fn) : { grup: 'UNKNOWN', nume: `SecKey${m[1]}`, motiv: `SecKey${m[1]}: the SecKeyAlgorithm is not a literal in this call nor a variable set once in this file.` }, m[0] + (alg ? `.${alg[1]}` : ''));
|
|
}
|
|
for (const m of ctx.potriviri(/(?<![\w.])(SecTrustEvaluate(?:WithError|AsyncWithError|Async)?|SecPolicyCreateSSL)\s*\(/g)) adauga(null, m.index, m[1], 'call', { grup: 'PKI-VERIFY', nume: NUME_PKIX, functii: ['verify'] }, m[0]);
|
|
}
|
|
|
|
// --- TLS-ul clientului (URLSession, Network) ---------------------------------------------------------------------------------------
|
|
for (const m of ctx.potriviri(/\.\s*tls(Minimum|Maximum)SupportedProtocolVersion\s*=\s*\.(TLSv10|TLSv11|TLSv12|TLSv13)\b/g)) adauga(null, m.index, `tls${m[1]}SupportedProtocolVersion`, 'config', { grup: 'TLS', param: TLSV[m[2]], rol: m[1] === 'Minimum' ? 'min' : 'max' }, m[0].trim());
|
|
for (const m of ctx.potriviri(/\.\s*TLS(Minimum|Maximum)SupportedProtocol\s*=\s*\.(tlsProtocol1|tlsProtocol11|tlsProtocol12|tlsProtocol13|sslProtocol3)\b/g)) adauga(null, m.index, `TLS${m[1]}SupportedProtocol`, 'config', { grup: 'TLS', param: TLSV[m[2]], rol: m[1] === 'Minimum' ? 'min' : 'max' }, m[0].trim());
|
|
for (const m of ctx.potriviri(/(?<![\w.])sec_protocol_options_set_(min|max)_tls_protocol_version\s*\(\s*[^,]+,\s*\.(TLSv10|TLSv11|TLSv12|TLSv13)\b/g)) adauga(null, m.index, `sec_protocol_options_set_${m[1]}_tls_protocol_version`, 'config', { grup: 'TLS', param: TLSV[m[2]], rol: m[1] }, m[0]);
|
|
if (are('Network')) for (const m of ctx.potriviri(/(?<![\w.])NWProtocolTLS\s*\.\s*Options\s*\(/g)) adauga(null, m.index, 'NWProtocolTLS.Options', 'config', { grup: 'TLS', param: 'negotiated' }, m[0]);
|
|
|
|
// --- CryptoSwift -----------------------------------------------------------------------------------------------------------------------
|
|
if (are('CryptoSwift')) {
|
|
const L = 'CryptoSwift';
|
|
for (const m of ctx.potriviri(/(?<![\w.])(AES|Blowfish)\s*\(\s*key\s*:/g)) {
|
|
const mod = /blockMode\s*:\s*(GCM|CBC|ECB|CTR|CCM|OFB|CFB|PCBC)\b/.exec(argsDupa(m).text);
|
|
adauga(L, m.index, `${m[1]}(key:blockMode:)`, 'call', { grup: 'CIPHER', nume: m[1], mod: mod ? mod[1].toLowerCase() : undefined, functii: ['encrypt', 'decrypt'] }, m[0]);
|
|
}
|
|
for (const m of ctx.potriviri(/(?<![\w.])SHA2\s*\(\s*variant\s*:\s*\.(sha224|sha256|sha384|sha512)\b/g)) adauga(L, m.index, 'SHA2(variant:)', 'call', { grup: 'HASH', hash: m[1].toUpperCase(), functii: ['digest'] }, m[0]);
|
|
for (const m of ctx.potriviri(/(?<![\w.])SHA3\s*\(\s*variant\s*:\s*\.(sha224|sha256|sha384|sha512|keccak224|keccak256|keccak384|keccak512)\b/g)) adauga(L, m.index, 'SHA3(variant:)', 'call', { grup: 'HASH', hash: /^keccak/.test(m[1]) ? 'KECCAK256' : 'SHA3-' + m[1].slice(3), functii: ['digest'] }, m[0]);
|
|
for (const m of ctx.potriviri(/(?<![\w.])(?:MD5|SHA1)\s*\(\s*\)/g)) adauga(L, m.index, m[0].replace(/\s+/g, ''), 'call', { grup: 'HASH', hash: m[0].startsWith('MD5') ? 'MD5' : 'SHA1', functii: ['digest'] }, m[0]);
|
|
for (const m of ctx.potriviri(/(?<![\w.])Digest\s*\.\s*(md5|sha1|sha224|sha256|sha384|sha512)\s*\(/g)) adauga(L, m.index, `Digest.${m[1]}`, 'call', { grup: 'HASH', hash: m[1].toUpperCase(), functii: ['digest'] }, m[0]);
|
|
for (const m of ctx.potriviri(/\.\s*(md5|sha1|sha224|sha256|sha384|sha512)\s*\(\s*\)/g)) adauga(L, m.index, `.${m[1]}()`, 'call', { grup: 'HASH', hash: m[1].toUpperCase(), functii: ['digest'] }, m[0].trim());
|
|
for (const m of ctx.potriviri(/(?<![\w.])HMAC\s*\(\s*key\s*:/g)) {
|
|
const v = /variant\s*:\s*\.(md5|sha1|sha224|sha256|sha384|sha512)\b/.exec(argsDupa(m).text);
|
|
adauga(L, m.index, 'HMAC(key:variant:)', 'call', v ? { grup: 'MAC', hash: v[1].toUpperCase(), functii: ['tag'] } : { grup: 'MAC', hash: 'MD5', functii: ['tag'], nota: 'CryptoSwift HMAC without variant: defaults to MD5.' }, m[0]);
|
|
}
|
|
for (const m of ctx.potriviri(/(?<![\w.])(?:PKCS5\s*\.\s*)?PBKDF2\s*\(/g)) {
|
|
const v = /variant\s*:\s*\.(md5|sha1|sha224|sha256|sha384|sha512)\b/.exec(argsDupa(m).text);
|
|
adauga(L, m.index, 'PBKDF2', 'call', { grup: 'KDF', nume: v ? `PBKDF2-HMAC-${v[1].toUpperCase()}` : 'PBKDF2', hash: v ? v[1].toUpperCase() : undefined, functii: ['keyderive'] }, m[0]);
|
|
}
|
|
for (const m of ctx.potriviri(/(?<![\w.])Scrypt\s*\(/g)) adauga(L, m.index, 'Scrypt', 'call', { grup: 'KDF', nume: 'scrypt', functii: ['keyderive'] }, m[0]);
|
|
for (const m of ctx.potriviri(/(?<![\w.])ChaCha20\s*\(\s*key\s*:/g)) adauga(L, m.index, 'ChaCha20(key:iv:)', 'call', { grup: 'CIPHER', nume: 'ChaCha20', functii: ['encrypt', 'decrypt'] }, m[0]);
|
|
for (const m of ctx.potriviri(/(?<![\w.])Poly1305\s*\(\s*key\s*:/g)) adauga(L, m.index, 'Poly1305(key:)', 'call', { grup: 'MAC', nume: 'Poly1305', functii: ['tag'] }, m[0]);
|
|
for (const m of ctx.potriviri(/(?<![\w.])Rabbit\s*\(\s*key\s*:/g)) adauga(L, m.index, 'Rabbit(key:)', 'call', { grup: 'CIPHER', nume: 'Rabbit', functii: ['encrypt', 'decrypt'] }, m[0]);
|
|
for (const m of ctx.potriviri(/(?<![\w.])RSA\s*\(\s*(?:keySize\s*:\s*(\d+)|[nedp]\s*:|rawRepresentation\s*:|publicDER|privateDER)/g)) adauga(L, m.index, 'RSA(...)', 'call', { grup: 'RSA', param: m[1], functii: [m[1] ? 'keygen' : 'other'] }, m[0]);
|
|
}
|
|
|
|
// --- Sodium (swift-sodium) si Clibsodium ------------------------------------------------------------------------------------------------
|
|
if (are('Sodium', 'Clibsodium')) {
|
|
const L = imp.has('Sodium') ? 'Sodium' : 'Clibsodium';
|
|
const X = (nota) => ({ grup: 'XDH', nume: 'X25519', functii: ['keygen'], nota });
|
|
const FORME = [
|
|
[/\.\s*box\s*\.\s*(keyPair|seal|open|beforenm)\s*\(/g, 'sodium.box', () => X('libsodium crypto_box: X25519 key agreement with XSalsa20-Poly1305.')],
|
|
[/\.\s*sealedBox\s*\.\s*(seal|open)\s*\(/g, 'sodium.sealedBox', () => X('libsodium sealed box: an ephemeral X25519 key against the recipient key, XSalsa20-Poly1305.')],
|
|
[/\.\s*keyExchange\s*\.\s*(keyPair|sessionKeyPair)\s*\(/g, 'sodium.keyExchange', () => X('libsodium crypto_kx: X25519 with BLAKE2b.')],
|
|
[/\.\s*sign\s*\.\s*(keyPair|signature|sign|verify|open)\s*\(/g, 'sodium.sign', (m) => ({ grup: 'EDDSA', nume: 'Ed25519', functii: [m[1] === 'keyPair' ? 'keygen' : /verify|open/.test(m[1]) ? 'verify' : 'sign'] })],
|
|
[/\.\s*secretBox\s*\.\s*(seal|open)\s*\(/g, 'sodium.secretBox', (m) => ({ grup: 'CIPHER', nume: 'XSalsa20-Poly1305', functii: [m[1] === 'open' ? 'decrypt' : 'encrypt'] })],
|
|
[/\.\s*aead\s*\.\s*xchacha20poly1305ietf\s*\.\s*(encrypt|decrypt)\s*\(/g, 'sodium.aead.xchacha20poly1305ietf', (m) => ({ grup: 'CIPHER', nume: 'XChaCha20-Poly1305', functii: [m[1]] })],
|
|
[/\.\s*pwHash\s*\.\s*(hash|str|strVerify)\s*\(/g, 'sodium.pwHash', () => ({ grup: 'KDF', nume: 'Argon2id', functii: ['keyderive'] })],
|
|
[/\.\s*genericHash\s*\.\s*(hash|initStream)\s*\(/g, 'sodium.genericHash', () => ({ grup: 'HASH', hash: 'BLAKE2B', functii: ['digest'] })],
|
|
[/(?<![\w.])crypto_(?:box|scalarmult|kx)(?:_\w+)?\s*\(/g, 'crypto_box', () => X('libsodium crypto_box / crypto_kx / crypto_scalarmult: X25519.')],
|
|
[/(?<![\w.])crypto_sign(?:_\w+)?\s*\(/g, 'crypto_sign', (m) => ({ grup: 'EDDSA', nume: 'Ed25519', functii: [/keypair/.test(m[0]) ? 'keygen' : /verify|open/.test(m[0]) ? 'verify' : 'sign'] })],
|
|
[/(?<![\w.])crypto_aead_(xchacha20poly1305|chacha20poly1305|aes256gcm|aegis256|aegis128l)(?:_ietf)?_(encrypt|decrypt)(?:_detached)?\s*\(/g, 'crypto_aead', (m) => ({ grup: 'CIPHER', nume: { xchacha20poly1305: 'XChaCha20-Poly1305', chacha20poly1305: 'ChaCha20-Poly1305', aes256gcm: 'AES', aegis256: 'AEGIS-256', aegis128l: 'AEGIS-128L' }[m[1]], param: m[1] === 'aes256gcm' ? '256' : undefined, mod: m[1] === 'aes256gcm' ? 'gcm' : undefined, functii: [m[2]] })],
|
|
[/(?<![\w.])crypto_secretbox(?:_\w+)?\s*\(/g, 'crypto_secretbox', () => ({ grup: 'CIPHER', nume: 'XSalsa20-Poly1305', functii: ['encrypt'] })],
|
|
[/(?<![\w.])crypto_pwhash(?:_\w+)?\s*\(/g, 'crypto_pwhash', () => ({ grup: 'KDF', nume: 'Argon2id', functii: ['keyderive'] })],
|
|
[/(?<![\w.])crypto_generichash(?:_\w+)?\s*\(/g, 'crypto_generichash', () => ({ grup: 'HASH', hash: 'BLAKE2B', functii: ['digest'] })],
|
|
[/(?<![\w.])crypto_hash_(sha256|sha512)(?:_\w+)?\s*\(/g, 'crypto_hash', (m) => ({ grup: 'HASH', hash: m[1].toUpperCase(), functii: ['digest'] })],
|
|
[/(?<![\w.])crypto_auth(?:_\w+)?\s*\(/g, 'crypto_auth', () => ({ grup: 'MAC', hash: 'SHA512/256', functii: ['tag'] })],
|
|
];
|
|
for (const [re, api, act] of FORME) for (const m of ctx.potriviri(re)) adauga(L, m.index, api, 'call', act(m), m[0].trim());
|
|
}
|
|
|
|
// --- libsignal (LibSignalClient; in sursele bibliotecii, SignalFfi) ----------------------------------------------------------------
|
|
// Swift nu declara modulul in fisier: un fisier din tinta SwiftPM `LibSignalClient` (dosarul Sources/LibSignalClient/) vede numele
|
|
// bibliotecii fara niciun import (masurat pe libsignal: DataStoreInMemory.swift, `IdentityKeyPair.generate()`, importa doar
|
|
// Foundation). A treia ancora e deci dosarul cu numele modulului, scrisa pe cale.
|
|
const inModulLibsignal = /(^|\/)LibSignalClient(Tests)?\//.test(String(ctx.rel || '').replace(/\\/g, '/'));
|
|
const libsignal = are('LibSignalClient', 'SignalFfi') || inModulLibsignal;
|
|
if (libsignal) {
|
|
const L = 'LibSignalClient';
|
|
for (const m of ctx.potriviri(/(?<![\w.])(IdentityKeyPair|PrivateKey)\s*\.\s*generate\s*\(\s*\)/g)) adauga(L, m.index, `${m[1]}.generate`, 'call', { grup: 'XDH', nume: 'X25519', functii: ['keygen'], nota: NOTA_EC_LIBSIGNAL }, m[0]);
|
|
for (const m of ctx.potriviri(/(?<![\w.])KEMKeyPair\s*\.\s*generate\s*\(\s*\)/g)) adauga(L, m.index, 'KEMKeyPair.generate', 'call', { grup: 'PREPQ', nume: 'Kyber-1024', primitiv: 'kem', functii: ['keygen'], nota: NOTA_KEM_SWIFT }, m[0]);
|
|
for (const m of ctx.potriviri(/(?<![\w.])(?<!class\s)(?<!func\s)KEMPublicKey\s*\(/g)) adauga(L, m.index, 'KEMPublicKey(...)', 'call', { grup: 'PREPQ', nume: 'Kyber-1024', primitiv: 'kem', functii: ['other'], nota: 'libsignal KEMPublicKey: a Kyber-1024 public key (a PQXDH prekey), built from bytes or from a native handle.' }, m[0]);
|
|
// constructorii (Aes256GcmSiv(key:), Aes256Ctr32(key:nonce:)) si formele statice (Aes256Ctr32.process(_:key:nonce:), CryptoTests.swift:169;
|
|
// Aes256GcmEncryptedData.encrypt(_:key:)); numele tipului intr-o declaratie (class/struct) sau intr-o constanta (Aes256Ctr32.nonceLength) nu e un apel
|
|
for (const m of ctx.potriviri(/(?<![\w.])(?<!class\s)(?<!struct\s)(Aes256GcmSiv|Aes256GcmEncryption|Aes256GcmDecryption|Aes256Ctr32|Aes256GcmEncryptedData)\s*(?:\.\s*(encrypt|decrypt|process)\s*)?\(/g)) {
|
|
const tip = m[1];
|
|
const mod = tip === 'Aes256GcmSiv' ? 'gcm-siv' : tip === 'Aes256Ctr32' ? 'ctr' : 'gcm';
|
|
const fn = m[2] === 'decrypt' || tip === 'Aes256GcmDecryption' || (tip === 'Aes256GcmEncryptedData' && !m[2]) ? 'decrypt' : 'encrypt';
|
|
adauga(L, m.index, m[0].replace(/\s+/g, '').replace(/\($/, ''), 'call', { grup: 'CIPHER', nume: 'AES', param: '256', mod, functii: [fn] }, m[0]);
|
|
}
|
|
for (const m of ctx.potriviri(/(?<![\w.])(?<!func\s)hkdf\s*\(/g)) adauga(L, m.index, 'hkdf', 'call', { grup: 'KDF', nume: 'HKDF', hash: 'SHA256', functii: ['keyderive'], nota: 'libsignal HKDF is HKDF-SHA-256.' }, m[0]);
|
|
for (const m of ctx.potriviri(/(?<![\w.])(?<!class\s)PreKeyBundle\s*\(|(?<![\w.])(?<!func\s)processPreKeyBundle\s*\(/g)) adauga(L, m.index, m[0].replace(/\s*\($/, ''), 'call', BUNDLE, m[0]);
|
|
}
|
|
|
|
// --- importurile bibliotecilor terte fara niciun apel recunoscut: dovada slaba, o data pe fisier ----------------------------------------
|
|
const TERTE = {
|
|
LibSignalClient: 'libsignal for Swift (Curve25519 identity and prekeys, Kyber prekeys (PQXDH), Double Ratchet, SPQR, sealed sender)',
|
|
CryptoSwift: 'CryptoSwift (AES, ChaCha20, Blowfish, RSA, SHA-2/3, MD5, SHA-1, HMAC, PBKDF2)',
|
|
Sodium: 'swift-sodium (libsodium: X25519, Ed25519, XSalsa20/XChaCha20-Poly1305, Argon2, BLAKE2b)',
|
|
Clibsodium: 'libsodium (X25519, Ed25519, XSalsa20/XChaCha20-Poly1305, Argon2, BLAKE2b)',
|
|
};
|
|
for (const [modul, descriere] of Object.entries(TERTE)) {
|
|
if (!imp.has(modul) || folosite.has(modul) || (modul === 'Clibsodium' && folosite.has('Sodium'))) continue;
|
|
ctx.adauga(imp.get(modul), `import ${modul}`, 'import', { grup: 'LIB-MULTI', nume: modul === 'LibSignalClient' ? 'libsignal (LibSignalClient)' : modul, motiv: `Imports ${descriere}; no specific call recognized in this file.` }, { suprimaDe: ['XDH', 'EDDSA', 'PREPQ', 'MLKEM', 'KDF', 'CIPHER', 'HASH', 'MAC', 'RSA', 'LIB-MULTI'], bucata: `import ${modul}` });
|
|
}
|
|
}
|