added masterlists parsing and csca deduplication

This commit is contained in:
0xturboblitz
2024-06-14 15:14:43 -07:00
parent 9fe490d094
commit 40d2084114
5 changed files with 118 additions and 186 deletions

4
registry/.gitignore vendored
View File

@@ -1,3 +1,5 @@
node_modules/
certificates
csca_certificates
cscas
masterlists
unique_cscas

View File

@@ -39,14 +39,14 @@ ts-node src/dsc/extract_sig_algs.ts
### CSCAs (WIP)
Extract pem certificates from ldif file:
Extract pem certificates from all the masterlists from the ldif file:
```
ts-node src/csca/extract_certificates.ts
ts-node src/csca/extract_masterlists.ts
```
Extract readable public keys from pem certicates:
Visualize the content of a PEM file:
```
ts-node src/csca/extract_pubkeys.ts
openssl x509 -text -in outputs/unique_cscas/unique_cert_0.pem
```
More info: [ICAO website](https://www.icao.int/Security/FAL/PKD/Pages/icao-master-list.aspx)

View File

@@ -1,27 +0,0 @@
import * as fs from 'fs';
import * as path from 'path';
// extract certificates from ldif file
const fileContent = fs.readFileSync("inputs/icao_download_section/icaopkd-002-complete-000243.ldif", "utf-8");
const regex = /pkdMasterListContent::\s*([\s\S]*?)(?=\w+:|\n\n|$)/g;
let match: RegExpExecArray | null;
const certificates: string[] = [];
while ((match = regex.exec(fileContent)) !== null) {
const certificate = match[1].replace(/\s+/g, "");
certificates.push(certificate);
}
if (!fs.existsSync("outputs/csca_certificates/")) {
fs.mkdirSync("outputs/csca_certificates/");
}
for (let i = 0; i < certificates.length; i++) {
fs.writeFileSync(
path.join("outputs/csca_certificates/", `certificate_${i}.pem`),
`-----BEGIN CERTIFICATE-----\n${certificates[i]}\n-----END CERTIFICATE-----\n`
);
}
console.log(`Extracted ${certificates.length} certificates.`);

View File

@@ -0,0 +1,111 @@
import * as fs from 'fs';
import * as path from 'path';
import * as util from 'util';
import { execSync } from 'child_process';
const execAsync = util.promisify(execSync);
// extract masterlists from ICAO ldif file
const fileContent = fs.readFileSync("inputs/icao_download_section/icaopkd-002-complete-000243.ldif", "utf-8");
const regex = /pkdMasterListContent::\s*([\s\S]*?)(?=\w+:|\n\n|$)/g;
let match: RegExpExecArray | null;
const masterlists: string[] = [];
while ((match = regex.exec(fileContent)) !== null) {
const masterlist = match[1].replace(/\s+/g, "");
masterlists.push(masterlist);
}
if (!fs.existsSync("outputs/masterlists/")) {
fs.mkdirSync("outputs/masterlists/");
}
for (let i = 0; i < masterlists.length; i++) {
fs.writeFileSync(
path.join("outputs/masterlists/", `masterlist_${i}.pem`),
`-----BEGIN CERTIFICATE-----\n${masterlists[i]}\n-----END CERTIFICATE-----\n`
);
}
console.log(`Extracted ${masterlists.length} masterlists.`);
for (let i = 0; i < masterlists.length; i++) {
execSync(`openssl asn1parse -in outputs/masterlists/masterlist_${i}.pem -inform PEM -i > outputs/masterlists/masterlist_${i}_structure.txt`);
}
console.log(`Extracted ${masterlists.length} masterlist structures.`);
for (let i = 0; i < masterlists.length; i++) {
const asn1Output = fs.readFileSync(`outputs/masterlists/masterlist_${i}_structure.txt`, 'utf8');
// Extract the first hex dump using a regex
const hexDumpMatch = asn1Output.match(/\[HEX DUMP\]:([A-Fa-f0-9]+)/);
if (!hexDumpMatch) {
console.error('No hex dump found');
process.exit(1);
}
const hexDump = hexDumpMatch[1];
// Convert hex dump to binary
const binaryDump = Buffer.from(hexDump, 'hex');
fs.writeFileSync(`outputs/masterlists/masterlist_${i}_binary_dump.bin`, binaryDump);
// Parse binary data using OpenSSL and extract individual certificates
const asn1ParseOutput = execSync(`openssl asn1parse -inform DER -in outputs/masterlists/masterlist_${i}_binary_dump.bin`, { maxBuffer: 10485770 }).toString();
fs.writeFileSync(`outputs/masterlists/masterlist_${i}_asn1_parse_output.txt`, asn1ParseOutput);
const certificateMatches = asn1ParseOutput.matchAll(/(\d+):d=2\s+hl=4\s+l=\s*(\d+)\s+cons:\s+SEQUENCE/g);
if (!fs.existsSync(`outputs/cscas/`)) {
fs.mkdirSync(`outputs/cscas/`);
}
if (!fs.existsSync(`outputs/cscas/masterlist_${i}`)) {
fs.mkdirSync(`outputs/cscas/masterlist_${i}`);
}
let count = 0;
for (const match of certificateMatches) {
const startOffset = parseInt(match[1]);
const certificateOutput = execSync(`openssl asn1parse -inform DER -in outputs/masterlists/masterlist_${i}_binary_dump.bin -strparse ${startOffset} -out outputs/cscas/masterlist_${i}/cert_${count}.pem`).toString();
console.log(`Extracted certificate ${count} to cert_${count}.pem`);
count++;
}
}
console.log('Deduplicating certificates...');
// Deduplicate certificates
const uniqueCertificates = new Set<string>();
const masterlistDirectories = fs.readdirSync('outputs/cscas/');
masterlistDirectories.forEach((directory) => {
const files = fs.readdirSync(`outputs/cscas/${directory}`);
files.forEach((file) => {
const filePath = path.join(`outputs/cscas/${directory}`, file);
const certContent = fs.readFileSync(filePath); // Read as binary
const certBase64 = certContent.toString('base64'); // Convert to base64 for comparison
if (!uniqueCertificates.has(certBase64)) {
uniqueCertificates.add(certBase64);
}
});
});
// Write unique certificates to new files
const uniqueCertsDir = 'outputs/unique_cscas/';
if (!fs.existsSync(uniqueCertsDir)) {
fs.mkdirSync(uniqueCertsDir);
}
let uniqueCertCount = 0;
uniqueCertificates.forEach((certBase64) => {
const certBuffer = Buffer.from(certBase64, 'base64'); // Convert back to binary
fs.writeFileSync(path.join(uniqueCertsDir, `unique_cert_${uniqueCertCount}.pem`), certBuffer);
uniqueCertCount++;
});
console.log(`Deduplicated and saved ${uniqueCertCount} unique certificates.`);

View File

@@ -1,154 +0,0 @@
import * as fs from 'fs';
import * as util from 'util';
import { exec } from 'child_process';
const execAsync = util.promisify(exec);
// Count the number of files in certificates/
const numCertificates = fs.readdirSync('outputs/csca_certificates/').length;
const concurrencyLimit = 1; // Number of tasks to run at once
const publicKeysParsed: {
signatureAlgorithm: string,
modulus?: string,
exponent?: string,
publicKeyAlgorithm?: string
publicKeyBit?: string
pub?: string
fieldType?: string
prime?: string
a?: string
b?: string
generator?: string
order?: string
cofactor?: string
}[] = [];
async function main() {
for (let i = 0; i < numCertificates; i += concurrencyLimit) {
const tasks: any = [];
for (let j = 0; j < concurrencyLimit && i + j < numCertificates; j++) {
tasks.push(extractModulus(i + j));
}
await Promise.all(tasks);
}
// console.log('publicKeysParsed 0', publicKeysParsed[0]);
// const filteredPublicKeysParsed = publicKeysParsed.filter(item => item !== null);
// fs.writeFileSync('public_keys_parsed.json', JSON.stringify(filteredPublicKeysParsed, null, 2));
// console.log("public_keys_parsed.json written!")
}
async function extractModulus(i: number): Promise<void> {
try {
const certTextres = await execAsync(`openssl x509 -text -in outputs/certificates/certificate_${i}.pem`);
const certText = certTextres.stdout as string;
// const signatureAlgorithm = (certText.match(/Signature Algorithm: (.*)/) as RegExpExecArray)[1].trim();
console.log('certText', certText)
// const issuerRegex = /Issuer: ([^\n]+)/;
// const issuer = extractData(issuerRegex, certText);
// // console.log('issuer', issuer)
// const pubkey = parsePubkey(certText, signatureAlgorithm);
// if (!pubkey) {
// console.error(`Failed to extract data from certificate ${i}`);
// return;
// }
// publicKeysParsed[i] = {
// signatureAlgorithm,
// issuer,
// ...pubkey,
// }
} catch (error) {
console.error(`Failed to extract data from certificate ${i}: ${error}`);
}
}
function parsePubkey(certText: string, signatureAlgorithm: string): any {
if (
signatureAlgorithm.includes("sha256WithRSAEncryption")
|| signatureAlgorithm.includes("rsassaPss")
|| signatureAlgorithm.includes("sha1WithRSAEncryption")
|| signatureAlgorithm.includes("sha512WithRSAEncryption")
) {
const modulusRegex = /Modulus:\s+([0-9a-f:\s]+?)\s+Exponent:/;
const exponentRegex = /Exponent:\s+(\d+)/;
const modulusMatch = certText.match(modulusRegex);
const exponentMatch = certText.match(exponentRegex);
const modulusHex = modulusMatch ? modulusMatch[1].replace(/[\s:]/g, '') : '';
const exponent = exponentMatch ? exponentMatch[1] : '';
if (!modulusHex) {
console.error(`Modulus not found`);
return null;
}
return {
modulus: BigInt('0x' + modulusHex).toString(),
exponent: exponent
};
} else if (
signatureAlgorithm.includes("ecdsa-with-SHA1")
|| signatureAlgorithm.includes("ecdsa-with-SHA384")
|| signatureAlgorithm.includes("ecdsa-with-SHA256")
|| signatureAlgorithm.includes("ecdsa-with-SHA512")
) {
const publicKeyAlgorithmRegex = /Public Key Algorithm: ([^\n]+)/;
const publicKeyBitRegex = /Public-Key: \((\d+) bit\)/;
const pubRegex = /pub:\n([0-9A-Fa-f:\n ]+?)\n\s{4}/;
const fieldTypeRegex = /Field Type: ([^\n]+)/;
const primeRegex = /Prime:\n([0-9A-Fa-f:\n ]+?)\n\s{4}/;
const aRegex = /A:\s+\n([0-9A-Fa-f:\n ]+?)\n\s{4}/;
const bRegex = /B:\s+\n([0-9A-Fa-f:\n ]+?)\n\s{4}/;
const generatorRegex = /Generator \(uncompressed\):\n([0-9A-Fa-f:\n ]+?)\n\s{4}/;
const orderRegex = /Order: \n([0-9A-Fa-f:\n ]+?)\n\s{4}/;
const cofactorRegex = /Cofactor:\s+(\d+)/;
// Extracting fields
const publicKeyAlgorithm = extractData(publicKeyAlgorithmRegex, certText);
const publicKeyBit = extractData(publicKeyBitRegex, certText);
const pub = extractData(pubRegex, certText);
const fieldType = extractData(fieldTypeRegex, certText);
const prime = extractData(primeRegex, certText);
const a = extractData(aRegex, certText);
const b = extractData(bRegex, certText);
const generator = extractData(generatorRegex, certText);
const order = extractData(orderRegex, certText);
const cofactor = extractData(cofactorRegex, certText);
if (!prime) {
console.error(`Prime not found`);
return null;
}
return {
publicKeyAlgorithm: publicKeyAlgorithm,
publicKeyBit: publicKeyBit,
pub: hexToDecimal(pub as string),
fieldType: fieldType,
prime: hexToDecimal(prime as string),
a: hexToDecimal(a as string),
b: hexToDecimal(b as string),
generator: hexToDecimal(generator as string),
order: hexToDecimal(order as string),
cofactor: cofactor,
};
};
}
function extractData(regex: RegExp, text: string): string | null {
const match = text.match(regex);
return match ? match[1].trim().replace(/\n/g, '') : null;
}
function hexToDecimal(hexString: string): string {
return BigInt("0x" + hexString.replace(/[\n: ]/g, '')).toString();
}
main();