// 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, AES.GCM.seal/open, ChaChaPoly, // Curve25519.KeyAgreement / .Signing, P256/P384/P521.Signing / .KeyAgreement, HKDF.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(/(?/g)) adauga(null, m.index, `HMAC<${m[1]}>`, 'call', { grup: 'MAC', hash: m[1], functii: ['tag'] }, m[0]); for (const m of ctx.potriviri(/(?\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(/(? ({ 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'] })], [/(? X('libsodium crypto_box / crypto_kx / crypto_scalarmult: X25519.')], [/(? ({ grup: 'EDDSA', nume: 'Ed25519', functii: [/keypair/.test(m[0]) ? 'keygen' : /verify|open/.test(m[0]) ? 'verify' : 'sign'] })], [/(? ({ 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]] })], [/(? ({ grup: 'CIPHER', nume: 'XSalsa20-Poly1305', functii: ['encrypt'] })], [/(? ({ grup: 'KDF', nume: 'Argon2id', functii: ['keyderive'] })], [/(? ({ grup: 'HASH', hash: 'BLAKE2B', functii: ['digest'] })], [/(? ({ grup: 'HASH', hash: m[1].toUpperCase(), functii: ['digest'] })], [/(? ({ 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(/(?