208 lines
13 KiB
JavaScript
208 lines
13 KiB
JavaScript
// Detectorul Go: importurile pachetelor crypto/* (in Go un import nefolosit nu compileaza, deci
|
|
// importul e o dovada de folosire, cu exceptia importului gol "_"), apelurile cu parametri si
|
|
// configuratia tls.Config.
|
|
import { argumente, imparteArgumente, escapeRe } from './context.mjs';
|
|
import { grupTls, jws } from './catalog.mjs';
|
|
|
|
const PACHETE = {
|
|
'crypto/rsa': { grup: 'RSA' },
|
|
'crypto/dsa': { grup: 'DSA' },
|
|
'crypto/ecdsa': { grup: 'ECDSA' },
|
|
'crypto/elliptic': { grup: 'EC' },
|
|
'crypto/ecdh': { grup: 'ECDH' },
|
|
'crypto/ed25519': { grup: 'EDDSA', nume: 'Ed25519' },
|
|
'golang.org/x/crypto/ed25519': { grup: 'EDDSA', nume: 'Ed25519' },
|
|
'golang.org/x/crypto/curve25519': { grup: 'XDH', nume: 'X25519' },
|
|
'crypto/md5': { grup: 'HASH', hash: 'MD5' },
|
|
'crypto/sha1': { grup: 'HASH', hash: 'SHA1' },
|
|
'golang.org/x/crypto/md4': { grup: 'HASH', hash: 'MD4' },
|
|
'golang.org/x/crypto/ripemd160': { grup: 'HASH', hash: 'RIPEMD160' },
|
|
'crypto/sha256': { grup: 'HASH', hash: 'SHA256' },
|
|
'crypto/sha512': { grup: 'HASH', hash: 'SHA512' },
|
|
'crypto/sha3': { grup: 'HASH', hash: 'SHA3-256' },
|
|
'golang.org/x/crypto/sha3': { grup: 'HASH', hash: 'SHA3-256' },
|
|
'crypto/des': { grup: 'CIPHER', nume: 'DES' },
|
|
'crypto/rc4': { grup: 'CIPHER', nume: 'RC4' },
|
|
'golang.org/x/crypto/blowfish': { grup: 'CIPHER', nume: 'Blowfish' },
|
|
'crypto/aes': { grup: 'CIPHER', nume: 'AES' },
|
|
'golang.org/x/crypto/chacha20poly1305': { grup: 'CIPHER', nume: 'ChaCha20-Poly1305' },
|
|
'crypto/mlkem': { grup: 'MLKEM' },
|
|
'golang.org/x/crypto/bcrypt': { grup: 'KDF', nume: 'bcrypt' },
|
|
'golang.org/x/crypto/argon2': { grup: 'KDF', nume: 'Argon2' },
|
|
'golang.org/x/crypto/scrypt': { grup: 'KDF', nume: 'scrypt' },
|
|
'golang.org/x/crypto/pbkdf2': { grup: 'KDF', nume: 'PBKDF2' },
|
|
'crypto/pbkdf2': { grup: 'KDF', nume: 'PBKDF2' },
|
|
'crypto/hkdf': { grup: 'KDF', nume: 'HKDF' },
|
|
'golang.org/x/crypto/ssh': { grup: 'SSH' },
|
|
'github.com/ethereum/go-ethereum/crypto': { grup: 'SECP256K1', nota: 'go-ethereum crypto package: accounts and signatures are secp256k1 ECDSA.' },
|
|
'github.com/ethereum/go-ethereum/crypto/secp256k1': { grup: 'SECP256K1' },
|
|
'github.com/decred/dcrd/dcrec/secp256k1': { grup: 'SECP256K1' },
|
|
'github.com/btcsuite/btcd/btcec': { grup: 'SECP256K1' },
|
|
'github.com/cloudflare/circl/sign/ed25519': { grup: 'EDDSA', nume: 'Ed25519' },
|
|
'github.com/cloudflare/circl/sign/slhdsa': { grup: 'SLHDSA' },
|
|
'github.com/cloudflare/circl/kem/hybrid': { grup: 'HYBRID-KEX', nume: 'hybrid KEM (circl)' },
|
|
};
|
|
|
|
function pachetActiv(cale) {
|
|
if (PACHETE[cale]) return PACHETE[cale];
|
|
let m;
|
|
if ((m = /^github\.com\/cloudflare\/circl\/sign\/mldsa\/mldsa(44|65|87)$/.exec(cale))) return { grup: 'MLDSA', param: m[1] };
|
|
if ((m = /^github\.com\/cloudflare\/circl\/kem\/mlkem\/mlkem(512|768|1024)$/.exec(cale))) return { grup: 'MLKEM', param: m[1] };
|
|
if ((m = /^github\.com\/cloudflare\/circl\/sign\/dilithium\/mode(2|3|5)$/.exec(cale))) return { grup: 'PREPQ', nume: `Dilithium${m[1]}`, primitiv: 'signature' };
|
|
if ((m = /^github\.com\/cloudflare\/circl\/kem\/kyber\/kyber(512|768|1024)$/.exec(cale))) return { grup: 'PREPQ', nume: `Kyber${m[1]}`, primitiv: 'kem' };
|
|
if (/^github\.com\/btcsuite\/btcd\/btcec(\/v\d+)?$/.test(cale) || /^github\.com\/decred\/dcrd\/dcrec\/secp256k1(\/v\d+)?$/.test(cale)) return { grup: 'SECP256K1' };
|
|
if (/^github\.com\/golang-jwt\/jwt(\/v\d+)?$/.test(cale)) return { grup: 'LIB-MULTI', nume: 'golang-jwt', motiv: 'Imports golang-jwt; no SigningMethod reference recognized in this file.' };
|
|
if (cale === 'crypto/tls') return { grup: 'TLS', param: 'negotiated', motiv: 'crypto/tls with default settings' };
|
|
return null;
|
|
}
|
|
|
|
export function importuriGo(ctx) {
|
|
const r = [];
|
|
const c = ctx.code;
|
|
const adauga = (alias, cale, pos) => r.push({ alias: alias || cale.split('/').filter((x) => !/^v\d+$/.test(x)).pop(), gol: alias === '_', cale, pos });
|
|
for (const m of c.matchAll(/(^|\n)[ \t]*import[ \t]+(?:([\w.]+)[ \t]+)?("([^"\n]+)")/g)) {
|
|
adauga(m[2], m[4], m.index + m[0].indexOf(m[3]));
|
|
}
|
|
for (const m of c.matchAll(/(^|\n)[ \t]*import[ \t]*\(([^)]*)\)/g)) {
|
|
const bloc = m[2];
|
|
const baza = m.index + m[0].indexOf('(') + 1;
|
|
for (const x of bloc.matchAll(/(?:^|\n)[ \t]*(?:([\w.]+)[ \t]+)?("([^"\n]+)")/g)) {
|
|
adauga(x[1], x[3], baza + x.index + x[0].indexOf(x[2]));
|
|
}
|
|
}
|
|
return r;
|
|
}
|
|
|
|
export function detecteazaGo(ctx) {
|
|
const imp = importuriGo(ctx);
|
|
const alias = (cale) => { const i = imp.find((x) => x.cale === cale && !x.gol); return i ? escapeRe(i.alias) : null; };
|
|
const apel = (cale, functii) => {
|
|
const a = alias(cale);
|
|
if (!a) return [];
|
|
return ctx.potriviri(new RegExp(`(?<![\\w.])${a}\\s*\\.\\s*(${functii})\\b`, 'g'));
|
|
};
|
|
const args = (m) => {
|
|
const dupa = ctx.code.slice(m.index + m[0].length);
|
|
const p = /^\s*\(/.exec(dupa);
|
|
if (!p) return { text: '', parti: [] };
|
|
const a = argumente(ctx, m.index + m[0].length + p[0].length);
|
|
return { ...a, parti: imparteArgumente(ctx, a.start, a.end) };
|
|
};
|
|
const pachetHash = { md5: 'MD5', sha1: 'SHA1', sha256: 'SHA256', sha512: 'SHA512', sha3: 'SHA3-256' };
|
|
|
|
// apelurile cu parametri
|
|
for (const m of apel('crypto/rsa', 'GenerateKey|GenerateMultiPrimeKey')) {
|
|
const a = args(m);
|
|
const ult = a.parti[a.parti.length - 1];
|
|
ctx.adauga(m.index, `rsa.${m[1]}`, 'call', { grup: 'RSA', param: ult && /^\d+$/.test(ult.text) ? ult.text : undefined, functii: ['keygen'] }, { bucata: m[0] + (ult ? ult.text : '') });
|
|
}
|
|
for (const m of apel('crypto/rsa', 'EncryptOAEP|DecryptOAEP|EncryptPKCS1v15|DecryptPKCS1v15|SignPSS|SignPKCS1v15|VerifyPSS|VerifyPKCS1v15')) {
|
|
const a = args(m);
|
|
const h = /\bcrypto\s*\.\s*(MD5|SHA1|SHA224|SHA256|SHA384|SHA512)\b/.exec(a.text);
|
|
const sig = /^(Sign|Verify)/.test(m[1]);
|
|
ctx.adauga(m.index, `rsa.${m[1]}`, 'call', { grup: 'RSA', primitiv: sig ? 'signature' : 'pke', padding: /OAEP/.test(m[1]) ? 'oaep' : /PKCS1v15/.test(m[1]) ? 'pkcs1v15' : 'other', hash: h ? h[1] : undefined, functii: [/^Sign/.test(m[1]) ? 'sign' : /^Verify/.test(m[1]) ? 'verify' : /^Encrypt/.test(m[1]) ? 'encrypt' : 'decrypt'] }, { bucata: m[0] });
|
|
}
|
|
const ell = alias('crypto/elliptic');
|
|
const consumate = new Set();
|
|
for (const m of apel('crypto/ecdsa', 'GenerateKey|Sign|SignASN1|Verify|VerifyASN1')) {
|
|
const a = args(m);
|
|
let curba;
|
|
if (ell && m[1] === 'GenerateKey') {
|
|
const k = new RegExp(`${ell}\\s*\\.\\s*(P224|P256|P384|P521)\\s*\\(`).exec(a.text);
|
|
if (k) { curba = k[1]; consumate.add(a.start + k.index); }
|
|
}
|
|
ctx.adauga(m.index, `ecdsa.${m[1]}`, 'call', { grup: 'ECDSA', curba, functii: [m[1] === 'GenerateKey' ? 'keygen' : /^Sign/.test(m[1]) ? 'sign' : 'verify'] }, { bucata: m[0] + (curba ? ` ${curba}` : '') });
|
|
}
|
|
for (const m of apel('crypto/elliptic', 'P224|P256|P384|P521')) {
|
|
if (consumate.has(m.index)) continue;
|
|
ctx.adauga(m.index, `elliptic.${m[1]}`, 'call', { grup: 'EC', curba: m[1] }, { bucata: m[0] });
|
|
}
|
|
for (const m of apel('crypto/ecdh', 'X25519|P256|P384|P521')) {
|
|
ctx.adauga(m.index, `ecdh.${m[1]}`, 'call', m[1] === 'X25519' ? { grup: 'XDH', nume: 'X25519', functii: ['keygen'] } : { grup: 'ECDH', curba: m[1], functii: ['keygen'] }, { bucata: m[0] });
|
|
}
|
|
for (const cale of ['crypto/ed25519', 'golang.org/x/crypto/ed25519']) {
|
|
for (const m of apel(cale, 'GenerateKey|Sign|Verify|NewKeyFromSeed|VerifyWithOptions')) {
|
|
ctx.adauga(m.index, `ed25519.${m[1]}`, 'call', { grup: 'EDDSA', nume: 'Ed25519', functii: [m[1] === 'Sign' ? 'sign' : /^Verify/.test(m[1]) ? 'verify' : 'keygen'] }, { bucata: m[0] });
|
|
}
|
|
}
|
|
for (const [cale, fn] of [['crypto/md5', 'New|Sum'], ['crypto/sha1', 'New|Sum'], ['crypto/sha256', 'New|New224|Sum256|Sum224'], ['crypto/sha512', 'New|New384|Sum512|Sum384|New512_256'], ['crypto/sha3', 'New256|New384|New512|Sum256|Sum384|Sum512|NewSHAKE128|NewSHAKE256'], ['golang.org/x/crypto/sha3', 'New256|New384|New512|Sum256|Sum384|Sum512|NewLegacyKeccak256']]) {
|
|
for (const m of apel(cale, fn)) {
|
|
const dupa = ctx.code.slice(m.index + m[0].length, m.index + m[0].length + 3);
|
|
if (!/^\s*\(/.test(dupa)) continue; // referinta de functie (de ex. hmac.New(sha1.New, ...)) se raporteaza la hmac
|
|
const pk = cale.split('/').pop();
|
|
let h = pachetHash[pk];
|
|
if (/224/.test(m[1])) h = 'SHA224';
|
|
if (/384/.test(m[1])) h = pk === 'sha3' ? 'SHA3-384' : 'SHA384';
|
|
if (/512_256/.test(m[1])) h = 'SHA512/256';
|
|
if (pk === 'sha3' && /512/.test(m[1])) h = 'SHA3-512';
|
|
if (/SHAKE128/.test(m[1])) h = 'SHAKE128';
|
|
if (/SHAKE256/.test(m[1])) h = 'SHAKE256';
|
|
if (/Keccak/.test(m[1])) h = 'KECCAK256';
|
|
ctx.adauga(m.index, `${pk}.${m[1]}`, 'call', { grup: 'HASH', hash: h, functii: ['digest'] }, { bucata: m[0] });
|
|
}
|
|
}
|
|
for (const m of apel('crypto/hmac', 'New')) {
|
|
const a = args(m);
|
|
const k = /^(\w+)\s*\.\s*New(\d*)\b/.exec(a.parti[0] ? a.parti[0].text : '');
|
|
const pk = k ? imp.find((x) => x.alias === k[1]) : null;
|
|
const baza = pk ? pachetHash[pk.cale.split('/').pop()] : null;
|
|
const h = baza ? (k[2] === '384' ? 'SHA384' : k[2] === '224' ? 'SHA224' : baza) : null;
|
|
ctx.adauga(m.index, 'hmac.New', 'call', h ? { grup: 'MAC', hash: h, functii: ['tag'] } : { grup: 'UNKNOWN', motiv: `hmac.New hash "${a.parti[0] ? a.parti[0].text : '?'}" cannot be resolved statically.` }, { bucata: m[0] + (a.parti[0] ? `(${a.parti[0].text}` : '') });
|
|
}
|
|
for (const m of apel('crypto/des', 'NewCipher|NewTripleDESCipher')) {
|
|
ctx.adauga(m.index, `des.${m[1]}`, 'call', { grup: 'CIPHER', nume: m[1] === 'NewCipher' ? 'DES' : '3DES', functii: ['encrypt'] }, { bucata: m[0] });
|
|
}
|
|
for (const m of apel('crypto/rc4', 'NewCipher')) ctx.adauga(m.index, 'rc4.NewCipher', 'call', { grup: 'CIPHER', nume: 'RC4', functii: ['encrypt'] }, { bucata: m[0] });
|
|
for (const m of apel('crypto/aes', 'NewCipher')) ctx.adauga(m.index, 'aes.NewCipher', 'call', { grup: 'CIPHER', nume: 'AES', functii: ['encrypt'] }, { bucata: m[0] });
|
|
for (const m of apel('crypto/mlkem', 'GenerateKey768|GenerateKey1024|NewEncapsulationKey768|NewEncapsulationKey1024|NewDecapsulationKey768|NewDecapsulationKey1024')) {
|
|
ctx.adauga(m.index, `mlkem.${m[1]}`, 'call', { grup: 'MLKEM', param: /1024/.test(m[1]) ? '1024' : '768', functii: [/^Generate/.test(m[1]) ? 'keygen' : 'other'] }, { bucata: m[0] });
|
|
}
|
|
const eth = alias('github.com/ethereum/go-ethereum/crypto');
|
|
if (eth) {
|
|
for (const m of ctx.potriviri(new RegExp(`(?<![\\w.])${eth}\\s*\\.\\s*(Sign|GenerateKey|Ecrecover|SigToPub|VerifySignature|HexToECDSA|ToECDSA)\\s*\\(`, 'g'))) {
|
|
ctx.adauga(m.index, `${eth}.${m[1]}`, 'call', { grup: 'SECP256K1', functii: [m[1] === 'Sign' ? 'sign' : /Verify|recover|SigToPub/i.test(m[1]) ? 'verify' : 'keygen'] }, { bucata: m[0] });
|
|
}
|
|
}
|
|
const jwtI = imp.find((x) => /^github\.com\/golang-jwt\/jwt(\/v\d+)?$/.test(x.cale));
|
|
if (jwtI) {
|
|
for (const m of ctx.potriviri(new RegExp(`(?<![\\w.])${escapeRe(jwtI.alias)}\\s*\\.\\s*SigningMethod(RS256|RS384|RS512|PS256|PS384|PS512|ES256|ES384|ES512|HS256|HS384|HS512|EdDSA|None)\\b`, 'g'))) {
|
|
const alg = m[1] === 'None' ? 'none' : m[1];
|
|
ctx.adauga(m.index, `jwt.SigningMethod${m[1]}`, 'config', { ...jws(alg), functii: ['sign', 'verify'] }, { bucata: m[0] });
|
|
}
|
|
}
|
|
|
|
// tls.Config
|
|
const tlsA = alias('crypto/tls');
|
|
let tlsGasit = false;
|
|
if (tlsA) {
|
|
const vers = { VersionSSL30: 'ssl3', VersionTLS10: '1.0', VersionTLS11: '1.1', VersionTLS12: '1.2', VersionTLS13: '1.3' };
|
|
for (const m of ctx.potriviri(new RegExp(`(?<![\\w])(MinVersion|MaxVersion)\\s*(?::|=)\\s*${tlsA}\\s*\\.\\s*(VersionSSL30|VersionTLS1[0-3])\\b`, 'g'))) {
|
|
tlsGasit = true;
|
|
ctx.adauga(m.index, `tls.Config.${m[1]}`, 'config', { grup: 'TLS', param: vers[m[2]], rol: m[1] === 'MinVersion' ? 'min' : 'max' }, { bucata: m[0] });
|
|
}
|
|
for (const m of ctx.potriviri(new RegExp(`(?<![\\w])CurvePreferences\\s*(?::|=)\\s*\\[\\]\\s*${tlsA}\\s*\\.\\s*CurveID\\s*\\{`, 'g'))) {
|
|
tlsGasit = true;
|
|
const a = argumente(ctx, m.index + m[0].length);
|
|
for (const x of a.text.matchAll(new RegExp(`${tlsA}\\s*\\.\\s*(\\w+)`, 'g'))) {
|
|
const n = x[1].replace(/^Curve(P\d+)$/, '$1');
|
|
ctx.adauga(a.start + x.index, 'tls.Config.CurvePreferences', 'config', { ...grupTls(n), functii: ['keygen'] }, { bucata: `CurvePreferences ${x[1]}` });
|
|
}
|
|
}
|
|
}
|
|
|
|
// importurile: dovada de folosire in Go, cazute cand fisierul are un apel al aceluiasi grup
|
|
for (const i of imp) {
|
|
const act = pachetActiv(i.cale);
|
|
if (!act) continue;
|
|
if (i.cale === 'crypto/tls') {
|
|
if (tlsGasit) continue;
|
|
ctx.adauga(i.pos, 'import crypto/tls', 'import', { grup: 'UNKNOWN', motiv: 'crypto/tls with default key-exchange settings: Go 1.24 and later offer X25519MLKEM768 by default when Config.CurvePreferences is unset, earlier versions do not. The Go version is not determined by this scan (see the go directive in go.mod).' }, { bucata: 'import "crypto/tls"' });
|
|
continue;
|
|
}
|
|
const nota = i.gol ? `Blank import of ${i.cale}: registers the implementation; not a direct call.` : act.nota;
|
|
const supr = act.grup === 'EC' ? ['EC', 'ECDSA', 'ECDH', 'SECP256K1', 'XDH'] : act.grup === 'ECDSA' ? ['ECDSA', 'SECP256K1'] : act.grup === 'LIB-MULTI' ? ['RSA', 'ECDSA', 'EDDSA', 'JWT-HMAC', 'JWT-NONE', 'SECP256K1'] : i.cale === 'crypto/des' ? ['CIPHER:DES', 'CIPHER:3DES'] : 'AUTO';
|
|
ctx.adauga(i.pos, `import ${i.cale}`, 'import', { ...act, nota }, { suprimaDe: supr, bucata: `import "${i.cale}"` });
|
|
}
|
|
}
|
|
|