Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
226 changes: 171 additions & 55 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,9 @@
.btn-tile:hover { transform: translateY(-3px); box-shadow: 0 5px 15px rgba(0,0,0,0.15); }
.btn-tile i { font-size: 1.5rem; margin-bottom: 5px; }

.bg-csv { background-color: #4666ff; color: white; }
.bg-pdf { background-color: #dc3545; color: white; }
.bg-csv { background-color: #4666ff; color: white; }
.bg-excel { background-color: #198754; color: white; }
.bg-pdf { background-color: #dc3545; color: white; }
.bg-word { background-color: #000000; color: white; }
.bg-email { background-color: #ffffff; color: #dc3545; border: 1px solid #dc3545; }
.bg-guide { background-color: #cfb137; color: #000; }
Expand Down Expand Up @@ -237,14 +238,19 @@ <h2 class="fw-bold text-dark mb-1">Evidence Validation Suite</h2>

<!-- DASHBOARD BUTTONS -->
<div class="row g-3 mb-4">
<div class="col-6 col-md-2">
<button class="btn-tile bg-csv" onclick="downloadSelectedCSV()">
<i class="bi bi-filetype-csv"></i> CSV Report
</button>
</div>
<div class="col-6 col-md-2">
<button class="btn-tile bg-pdf" onclick="generateHashPDF()">
<i class="bi bi-file-earmark-pdf-fill"></i> Hash PDF
<div class="col-6 col-md-2">
<button class="btn-tile bg-csv" onclick="downloadSelectedCSV()">
<i class="bi bi-filetype-csv"></i> CSV Report
</button>
</div>
<div class="col-6 col-md-2">
<button class="btn-tile bg-excel" onclick="generateHashExcel()">
<i class="bi bi-file-earmark-spreadsheet-fill"></i> Hash Excel
</button>
</div>
<div class="col-6 col-md-2">
<button class="btn-tile bg-pdf" onclick="generateHashPDF()">
<i class="bi bi-file-earmark-pdf-fill"></i> Hash PDF
</button>
</div>
<div class="col-6 col-md-2">
Expand Down Expand Up @@ -287,9 +293,10 @@ <h5 class="fw-bold">Drag & Drop Evidence Files Here</h5>
<tr class="table-header">
<th width="5%" class="ps-4"><input type="checkbox" id="selectAll" onclick="toggleAll(this)"></th>
<th width="5%">#</th>
<th width="30%">Filename</th>
<th width="20%">MD5 Hash</th>
<th width="40%">SHA-256 (Section 63 BSA Compliant)</th>
<th width="25%">Filename</th>
<th width="18%">MD5 Hash</th>
<th width="27%">SHA-256 (Section 63 BSA Compliant)</th>
<th width="20%">SHA-3 / Keccak-256</th>
</tr>
</thead>
<tbody id="hashTableBody" style="background: white;"></tbody>
Expand Down Expand Up @@ -496,7 +503,8 @@ <h6 class="fw-bold mt-3">Yahoo/Outlook:</h6>
${isEml ? '<span class="badge bg-danger text-white ms-2">EMAIL</span>' : ''}
</td>
<td class="hash-font md5">Calculating...</td>
<td class="hash-font sha256">Calculating...</td>
<td class="hash-font sha256">Calculating...</td>
<td class="hash-font sha3">Calculating...</td>
</tr>
`);
fileQueue.push({ file, rowId });
Expand All @@ -517,8 +525,14 @@ <h6 class="fw-bold mt-3">Yahoo/Outlook:</h6>
const wordArray = CryptoJS.lib.WordArray.create(e.target.result);
setTimeout(() => {
if (row) {
row.querySelector('.md5').innerText = CryptoJS.MD5(wordArray).toString();
row.querySelector('.sha256').innerText = CryptoJS.SHA256(wordArray).toString();
const md5 = CryptoJS.MD5(wordArray).toString();
const sha256 = CryptoJS.SHA256(wordArray).toString();
const sha3 = CryptoJS.SHA3(wordArray, { outputLength: 256 }).toString();

row.querySelector('.md5').innerText = md5;
row.querySelector('.sha256').innerText = sha256;
row.querySelector('.sha3').innerText = sha3;
console.info(`[H# Check] SHA-3 / Keccak-256 calculated for "${file.name}": ${sha3}`);
}
processQueue();
}, 50);
Expand Down Expand Up @@ -612,7 +626,8 @@ <h6 class="fw-bold mt-3">Yahoo/Outlook:</h6>

const text = await readFile(files[i]);
const emailData = parseEml(text);
const sha256 = await calculateSHA256(files[i]);
const sha256 = await calculateSHA256(files[i]);
const sha3 = await calculateSHA3(files[i]);

// HEADER WITH WRAPPED FILENAME
doc.setFillColor(0, 51, 102);
Expand Down Expand Up @@ -646,7 +661,8 @@ <h6 class="fw-bold mt-3">Yahoo/Outlook:</h6>
['Message-ID', emailData.messageId],
['Originating IP', emailData.serverIP],
['DKIM Status', emailData.dkim],
['SHA-256 Hash', sha256]
['SHA-256 Hash', sha256],
['SHA-3 / Keccak-256 Hash', sha3]
],
theme: 'grid',
headStyles: {
Expand Down Expand Up @@ -715,7 +731,8 @@ <h6 class="fw-bold mt-3">Yahoo/Outlook:</h6>
rows.push([
cells[1].innerText,
cells[2].innerText,
cells[4].innerText
cells[4].innerText,
cells[5].innerText
]);
}
});
Expand All @@ -727,7 +744,7 @@ <h6 class="fw-bold mt-3">Yahoo/Outlook:</h6>

doc.autoTable({
startY: 70,
head: [['#', 'Filename', 'SHA-256']],
head: [['#', 'Filename', 'SHA-256', 'SHA-3 / Keccak-256']],
body: rows,
theme: 'grid',
headStyles: {
Expand All @@ -741,8 +758,9 @@ <h6 class="fw-bold mt-3">Yahoo/Outlook:</h6>
},
columnStyles: {
0: { cellWidth: 40 },
1: { cellWidth: 300 },
2: { cellWidth: 450, font: 'courier' }
1: { cellWidth: 250 },
2: { cellWidth: 300, font: 'courier' },
3: { cellWidth: 230, font: 'courier' }
}
});

Expand Down Expand Up @@ -778,10 +796,17 @@ <h6 class="fw-bold mt-3">Yahoo/Outlook:</h6>
}),
new TableCell({
children: [new Paragraph({
children: [new TextRun({ text: "SHA-256", bold: true, color: "FFFFFF" })],
alignment: AlignmentType.CENTER
})],
shading: { fill: "003366" }
children: [new TextRun({ text: "SHA-256", bold: true, color: "FFFFFF" })],
alignment: AlignmentType.CENTER
})],
shading: { fill: "003366" }
}),
new TableCell({
children: [new Paragraph({
children: [new TextRun({ text: "SHA-3 / Keccak-256", bold: true, color: "FFFFFF" })],
alignment: AlignmentType.CENTER
})],
shading: { fill: "003366" }
})
]
}));
Expand All @@ -793,9 +818,12 @@ <h6 class="fw-bold mt-3">Yahoo/Outlook:</h6>
children: [
new TableCell({ children: [new Paragraph(cells[1].innerText)] }),
new TableCell({ children: [new Paragraph(cells[2].innerText)] }),
new TableCell({ children: [new Paragraph({
children: [new TextRun({ text: cells[4].innerText, font: "Courier New", size: 18 })]
})] })
new TableCell({ children: [new Paragraph({
children: [new TextRun({ text: cells[4].innerText, font: "Courier New", size: 18 })]
})] }),
new TableCell({ children: [new Paragraph({
children: [new TextRun({ text: cells[5].innerText, font: "Courier New", size: 18 })]
})] })
]
}));
}
Expand Down Expand Up @@ -827,17 +855,76 @@ <h6 class="fw-bold mt-3">Yahoo/Outlook:</h6>
}]
});

Packer.toBlob(doc).then(blob => {
const a = document.createElement("a");
a.href = URL.createObjectURL(blob);
a.download = "Hash_Report.docx";
a.click();
});
}
Packer.toBlob(doc).then(blob => {
const a = document.createElement("a");
a.href = URL.createObjectURL(blob);
a.download = "Hash_Report.docx";
a.click();
});
}

// ===== GENERATE HASH EXCEL =====
function generateHashExcel() {
const rows = [];

document.querySelectorAll("#hashTableBody tr").forEach(tr => {
if (tr.querySelector('input').checked) {
const cells = tr.querySelectorAll('td');
rows.push([
cells[1].innerText,
cells[2].innerText,
cells[3].innerText,
cells[4].innerText,
cells[5].innerText
]);
}
});

if (rows.length === 0) {
alert("Please select files to include in report.");
return;
}

const tableRows = rows.map(row => `
<tr>
${row.map(value => `<td>${escapeHtml(value)}</td>`).join('')}
</tr>
`).join('');

const workbook = `
<html>
<head>
<meta charset="UTF-8">
<style>
table { border-collapse: collapse; }
th, td { border: 1px solid #999; padding: 6px; font-family: Arial, sans-serif; }
th { background: #003366; color: #ffffff; }
td.hash { font-family: "Courier New", monospace; }
</style>
</head>
<body>
<table>
<thead>
<tr>
<th>#</th>
<th>Filename</th>
<th>MD5</th>
<th>SHA-256</th>
<th>SHA-3 / Keccak-256</th>
</tr>
</thead>
<tbody>${tableRows}</tbody>
</table>
</body>
</html>
`;

downloadBlob(workbook, "Hash_Report.xls", "application/vnd.ms-excel");
}

// ===== DOWNLOAD CSV =====
function downloadSelectedCSV() {
let csv = "#,Subject,From,To,Date,Originating IP,DKIM,SHA-256\n";
let csv = "#,Subject,From,To,Date,Originating IP,DKIM,SHA-256,SHA-3 / Keccak-256\n";

let counter = 1;
const promises = [];
Expand All @@ -848,7 +935,7 @@ <h6 class="fw-bold mt-3">Yahoo/Outlook:</h6>
promises.push(
readFile(tr.fileData).then(text => {
const eml = parseEml(text);
return `${counter++},"${eml.subject}","${eml.from}","${eml.to}","${eml.date}","${eml.serverIP}","${eml.dkim}","${cells[4].innerText}"\n`;
return `${counter++},"${eml.subject}","${eml.from}","${eml.to}","${eml.date}","${eml.serverIP}","${eml.dkim}","${cells[4].innerText}","${cells[5].innerText}"\n`;
})
);
}
Expand All @@ -875,24 +962,53 @@ <h6 class="fw-bold mt-3">Yahoo/Outlook:</h6>
return files;
}

function readFile(file) {
return new Promise(resolve => {
const reader = new FileReader();
reader.onload = e => resolve(e.target.result);
reader.readAsText(file);
});
}

async function calculateSHA256(file) {
return new Promise(resolve => {
const reader = new FileReader();
reader.onload = e => {
const wordArray = CryptoJS.lib.WordArray.create(e.target.result);
function readFile(file) {
return new Promise(resolve => {
const reader = new FileReader();
reader.onload = e => resolve(e.target.result);
reader.readAsText(file);
});
}

function downloadBlob(content, filename, type) {
const blob = new Blob([content], { type });
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = filename;
a.click();
URL.revokeObjectURL(a.href);
}

function escapeHtml(value) {
return String(value)
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}

async function calculateSHA256(file) {
return new Promise(resolve => {
const reader = new FileReader();
reader.onload = e => {
const wordArray = CryptoJS.lib.WordArray.create(e.target.result);
resolve(CryptoJS.SHA256(wordArray).toString());
};
reader.readAsArrayBuffer(file);
});
}
reader.readAsArrayBuffer(file);
});
}

async function calculateSHA3(file) {
return new Promise(resolve => {
const reader = new FileReader();
reader.onload = e => {
const wordArray = CryptoJS.lib.WordArray.create(e.target.result);
resolve(CryptoJS.SHA3(wordArray, { outputLength: 256 }).toString());
};
reader.readAsArrayBuffer(file);
});
}

function toggleAll(checkbox) {
document.querySelectorAll('input[name="fileSelect"]').forEach(c => c.checked = checkbox.checked);
Expand Down Expand Up @@ -924,7 +1040,7 @@ <h6 class="fw-bold mt-3">Yahoo/Outlook:</h6>
const response = document.getElementById('botResponse');
let text = "";
if (type === 'BSA') text = "Section 63(4) of BSA 2023 (formerly 65B IEA) requires electronic evidence to be accompanied by a certificate for court admissibility.";
if (type === 'HASH') text = "SHA-256 cryptographic hash ensures file integrity and proves the evidence hasn't been tampered with since collection.";
if (type === 'HASH') text = "SHA-256 and SHA-3 / Keccak-256 cryptographic hashes help verify file integrity and prove the evidence has not changed since collection.";
if (type === 'EML') text = "Gmail: Open email → Three Dots (⋮) → Download message. Always use .EML format for legal integrity.";
response.innerHTML = `<span class="bg-primary text-white p-2 rounded shadow-sm d-inline-block mt-2">${text}</span>`;
}
Expand Down