Verify the ARC (Authenticated Received Chain) signature of an email to ensure its authenticity and integrity,
especially for messages that have been forwarded or handled by multiple mail servers.
Raw Email
Upload File
Drag & drop your .eml file here, or click to select
Selected file: ()
tab.classList.remove('active');
}
});
// Update tab contents
tabContents.forEach(content => {
if (content.id === `${tabName}-tab`) {
content.classList.add('active');
} else {
content.classList.remove('active');
}
});
// Update verify button state
updateVerifyButtonState();
}
// Add click handlers to tabs
tabs.forEach(tab => {
tab.addEventListener('click', () => {
const tabName = tab.getAttribute('data-tab');
switchTab(tabName);
});
});
// Clear file function
window.clearFile = function() {
currentFile = null;
fileInput.value = '';
fileInfo.classList.add('hidden');
updateVerifyButtonState();
};
// File upload handling
fileInput.addEventListener('change', (e) => {
if (e.target.files && e.target.files.length > 0) {
handleFile(e.target.files[0]);
}
});
// Handle drag and drop
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
dropZone.addEventListener(eventName, preventDefaults, false);
document.body.addEventListener(eventName, preventDefaults, false);
});
function preventDefaults(e) {
e.preventDefault();
e.stopPropagation();
}
['dragenter', 'dragover'].forEach(eventName => {
dropZone.addEventListener(eventName, highlight, false);
});
['dragleave', 'drop'].forEach(eventName => {
dropZone.addEventListener(eventName, unhighlight, false);
});
function highlight() {
dropZone.style.borderColor = '#4a6fa5';
dropZone.style.backgroundColor = 'rgba(74, 111, 165, 0.05)';
}
function unhighlight() {
dropZone.style.borderColor = '';
dropZone.style.backgroundColor = '';
}
dropZone.addEventListener('drop', (e) => {
const dt = e.dataTransfer;
const file = dt.files[0];
if (file) {
handleFile(file);
}
});
async function handleFile(file) {
// Check file type
const validTypes = ['message/rfc822', 'application/mbox', 'text/plain'];
const fileExt = file.name.split('.').pop().toLowerCase();
if (!validTypes.includes(file.type) && !['eml', 'mbox', 'txt'].includes(fileExt)) {
showError('Invalid file type. Please upload an .eml or .mbox file.');
return;
}
currentFile = file;
// Update UI
fileName.textContent = file.name;
fileSize.textContent = formatFileSize(file.size);
fileInfo.classList.remove('hidden');
// Read file content
const content = await file.text();
emailContent.value = content;
updateVerifyButtonState();
}
function formatFileSize(bytes) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
// Update verify button state based on input
function updateVerifyButtonState() {
const pasteTab = document.querySelector('.tab[data-tab="paste"]');
const isPasteTab = pasteTab && pasteTab.classList.contains('active');
const hasContent = isPasteTab ?
(emailContent && emailContent.value.trim() !== '') :
(currentFile !== null);
if (verifyBtn) {
verifyBtn.disabled = !hasContent;
}
}
emailContent.addEventListener('input', updateVerifyButtonState);
// Verify button click handler
verifyBtn.addEventListener('click', async () => {
let content = emailContent.value.trim();
if (!content && currentFile) {
content = await currentFile.text();
}
if (!content) {
showError('Please provide email content to verify.');
return;
}
verifyArcSignature(content);
});
// Verify ARC signature
async function verifyArcSignature(emailContent) {
// Show loading state
loading.style.display = 'block';
resultDiv.className = 'result';
resultDiv.style.display = 'none';
try {
// Basic validation
if (!emailContent || typeof emailContent !== 'string' || emailContent.trim() === '') {
throw new Error('No email content provided');
}
// Check for minimum required headers
if (!emailContent.includes('ARC-Seal:') || !emailContent.includes('ARC-Message-Signature:')) {
throw new Error('Email does not contain required ARC headers. Make sure to include the full email headers.');
}
// Use the deployed worker
const workerUrl = 'https://arc-verifier.denis3015.workers.dev';
try {
const response = await fetch(workerUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ email: emailContent })
});
const result = await response.json();
if (!response.ok || !result.success) {
const errorMsg = result.error || 'Failed to verify ARC signature';
console.error('Verification error:', errorMsg, result);
throw new Error(errorMsg);
}
displayResult(result);
} catch (error) {
console.error('Request failed:', error);
throw new Error(`Network error: ${error.message}`);
}
} catch (error) {
// If we have a detailed error message, use it
let errorMessage = error.message || 'An error occurred while verifying the ARC signature.';
// Add more context to common errors
if (errorMessage.includes('No ARC headers found')) {
errorMessage += '\n\nThis usually means the email was not properly forwarded with full headers.';
} else if (errorMessage.includes('DKIM verification failed')) {
errorMessage += '\n\nThis could be due to missing or invalid DKIM signatures.';
} else if (errorMessage.includes('ARC-Seal')) {
errorMessage += '\n\nThis indicates the ARC chain might be broken or incomplete.';
}
showError(errorMessage);
} finally {
loading.style.display = 'none';
}
}
function createSection(title, className = '') {
const section = document.createElement('div');
section.className = `result-section ${className}`;
const titleEl = document.createElement('h3');
titleEl.textContent = title;
section.appendChild(titleEl);
return { section, content: section };
}
function createStatusBadge(text, type = 'info') {
const badge = document.createElement('span');
badge.className = `status-badge ${type}`;
badge.textContent = text;
return badge;
}
function createKeyValuePair(key, value, isCode = false) {
const row = document.createElement('div');
row.className = 'key-value-pair';
const keyEl = document.createElement('span');
keyEl.className = 'key';
keyEl.textContent = key;
const valueEl = document.createElement('span');
valueEl.className = `value ${isCode ? 'code' : ''}`;
valueEl.textContent = value;
row.appendChild(keyEl);
row.appendChild(valueEl);
return row;
}
function createStatusHeader(result) {
const statusDiv = document.createElement('div');
const isSuccess = result.verified;
statusDiv.className = `verification-status ${isSuccess ? 'success' : 'warning'}`;
const statusIcon = document.createElement('i');
statusIcon.className = `fas ${isSuccess ? 'fa-check-circle' : 'fa-exclamation-triangle'}`;
const statusText = document.createElement('span');
statusText.className = 'status-text';
statusText.textContent = isSuccess ? 'ARC Verification Successful' : 'ARC Verification Issues Found';
statusDiv.appendChild(statusIcon);
statusDiv.appendChild(statusText);
if (result.chainStatus) {
const chainStatus = document.createElement('div');
chainStatus.className = 'chain-status';
chainStatus.textContent = `Chain Status: ${result.chainStatus.toUpperCase()}`;
statusDiv.appendChild(chainStatus);
}
return statusDiv;
}
function createSummarySection(result) {
const { section: summarySection, content: summaryContent } = createSection('Summary');
if (result.details.verificationSteps && result.details.verificationSteps.length > 0) {
const stepsList = document.createElement('div');
stepsList.className = 'steps-list';
result.details.verificationSteps.forEach(step => {
const stepItem = document.createElement('div');
stepItem.className = 'step-item';
const stepIcon = document.createElement('i');
stepIcon.className = 'fas fa-check-circle success';
const stepText = document.createElement('span');
stepText.textContent = step;
stepItem.appendChild(stepIcon);
stepItem.appendChild(stepText);
stepsList.appendChild(stepItem);
});
summaryContent.appendChild(stepsList);
}
return summarySection;
}
function createChainAnalysisSection(result) {
if (!result.chainAnalysis) return null;
const { section: chainSection, content: chainContent } = createSection('Chain Analysis');
if (result.chainStatus) {
chainContent.appendChild(createKeyValuePair('Chain Status', result.chainStatus.toUpperCase()));
}
if (result.chainAnalysis.hopCount) {
chainContent.appendChild(createKeyValuePair('Number of Hops', result.chainAnalysis.hopCount));
}
if (result.chainAnalysis.cvStatus) {
const cvStatus = document.createElement('div');
cvStatus.className = 'detail-item';
cvStatus.innerHTML = `
Chain Validation Status:
${result.chainAnalysis.cvStatus.toUpperCase()}
`;
chainContent.appendChild(cvStatus);
}
return chainSection;
}
function createAuthResultsSection(result) {
if (!result.authResults) return null;
const { section: authSection, content: authContent } = createSection('Authentication Results');
// Helper function to create auth result row
const createAuthResult = (type, authData) => {
if (!authData) return null;
const authDiv = document.createElement('div');
authDiv.className = 'auth-result';
authDiv.innerHTML = `
${type}${authData.result ? authData.result.toUpperCase() : 'N/A'}${authData.details || ''}
`;
return authDiv;
};
// Add SPF, DKIM, and DMARC results
if (result.authResults.spf) {
const spfDiv = createAuthResult('SPF', result.authResults.spf);
if (spfDiv) authContent.appendChild(spfDiv);
}
if (result.authResults.dkim) {
const dkimDiv = createAuthResult('DKIM', result.authResults.dkim);
if (dkimDiv) authContent.appendChild(dkimDiv);
}
if (result.authResults.dmarc) {
const dmarcDiv = createAuthResult('DMARC', result.authResults.dmarc);
if (dmarcDiv) authContent.appendChild(dmarcDiv);
}
return authSection;
}
function createWarningsSection(result) {
if (!result.warnings || result.warnings.length === 0) return null;
const { section: warningsSection, content: warningsContent } = createSection('Warnings', 'warnings');
const warningsList = document.createElement('div');
warningsList.className = 'warnings-list';
result.warnings.forEach(warning => {
const warningItem = document.createElement('div');
warningItem.className = 'warning-item';
warningItem.innerHTML = `
${warning}
`;
warningsList.appendChild(warningItem);
});
warningsContent.appendChild(warningsList);
return warningsSection;
}
function createRawDetailsSection(result) {
const { section: detailsSection } = createSection('Raw Details', 'raw-details');
detailsSection.classList.add('collapsible');
const detailsToggle = document.createElement('div');
detailsToggle.className = 'details-toggle';
detailsToggle.innerHTML = `
Show Raw Details
`;
const detailsContentWrapper = document.createElement('div');
detailsContentWrapper.className = 'details-content';
detailsContentWrapper.style.display = 'none';
const pre = document.createElement('pre');
pre.textContent = JSON.stringify(result, null, 2);
detailsContentWrapper.appendChild(pre);
detailsToggle.addEventListener('click', () => {
const isHidden = detailsContentWrapper.style.display === 'none';
detailsContentWrapper.style.display = isHidden ? 'block' : 'none';
const icon = detailsToggle.querySelector('i');
const span = detailsToggle.querySelector('span');
icon.className = isHidden ? 'fas fa-chevron-up' : 'fas fa-chevron-down';
span.textContent = isHidden ? 'Hide Raw Details' : 'Show Raw Details';
});
detailsSection.appendChild(detailsToggle);
detailsSection.appendChild(detailsContentWrapper);
return detailsSection;
}
function createHelpSection(result) {
if (result.verified) return null;
const helpSection = document.createElement('div');
helpSection.className = 'help-section';
helpSection.innerHTML = `
Need Help?
This email failed ARC verification. Here are some common issues:
The email might have been modified in transit
One or more mail servers in the delivery path might not properly handle ARC
The email might be missing required ARC headers
Check the raw details above for more specific information about the verification failure.