Skip to content
Snippets Groups Projects
Commit 4e555895 authored by Damyan Mitev's avatar Damyan Mitev :beach:
Browse files

add verifySMIME function

parent 59184753
No related branches found
No related tags found
1 merge request!56Resolve "Email integrity check"
......@@ -11,9 +11,12 @@ import {
getAttachment,
getGlobalHeaderValue
} from "../helpers/mailparser";
import { getCertificateChain } from "./signingUtilities";
import {
getCertificateChain,
parseSignedData
} from "./signingUtilities";
const SIGNATURE_CONTENT_TYPE = "application/pkcs7-signature";
export const SIGNATURE_CONTENT_TYPE = "application/pkcs7-signature";
export const DEFAULT_ATTACHMENT_NAME = "attachment";
const splitParticipants = participantsList => {
......@@ -44,7 +47,7 @@ export const parseSMIME = smimeString => {
rawAttachment
);
if (contentType.indexOf(SIGNATURE_CONTENT_TYPE) !== -1) {
if (contentType.startsWith(SIGNATURE_CONTENT_TYPE)) {
signatureBase64 = base64;
}
......@@ -58,7 +61,12 @@ export const parseSMIME = smimeString => {
});
}
const certificateChain = getCertificateChain(signatureBase64);
let signedData; //TODO remove from here, move inside if block
let certificateChain;
if (signatureBase64) {
signedData = parseSignedData(signatureBase64);
certificateChain = getCertificateChain(signedData);
}
const from = splitParticipants(getGlobalHeaderValue("from", parts));
const to = splitParticipants(getGlobalHeaderValue("to", parts));
......@@ -72,7 +80,10 @@ export const parseSMIME = smimeString => {
html: extractHtmlBodyFromString(html),
plain,
attachments,
certificateChain
certificateChain,
signedData, //TODO remove
fixedSMIME: emailString, //TODO remove
parts //TODO remove
};
resolve(message);
......
......@@ -8,6 +8,20 @@ import { bufferToHexCodes, stringToArrayBuffer } from "pvutils";
import { fromBER } from "asn1js";
import { ContentInfo, SignedData } from "pkijs";
import { algomap, rdnmap } from "../constants/certificates";
import {
fixNewLines,
getAttachment,
getAttachments,
getGlobalHeaderValue,
getHeaderValue,
parseMIME
} from "../helpers/mailparser";
import dataUriToBlob from "data-uri-to-blob";
import {
extractHtmlBodyFromString,
getFilenameFromHeaders,
SIGNATURE_CONTENT_TYPE
} from "./emailUtilities";
const libmime = require("libmime");
const pkijs = require("pkijs");
......@@ -1006,7 +1020,19 @@ function makeBoundary() {
return "W0RyLiBEYW15YW4gTWl0ZXZd--" + makeid(len);
}
export const parseCertificates = signatureBase64 => {
function hexString(buffer) { //TODO remove
const byteArray = new Uint8Array(buffer);
const hexCodes = [...byteArray].map(value => {
const hexCode = value.toString(16);
const paddedHexCode = hexCode.padStart(2, '0');
return paddedHexCode;
});
return hexCodes.join('');
}
export const parseSignedData = signatureBase64 => {
try {
const certificateDecoded = atob(signatureBase64);
const buffer = stringToArrayBuffer(certificateDecoded);
......@@ -1014,6 +1040,21 @@ export const parseCertificates = signatureBase64 => {
const contentInfo = new ContentInfo({ schema: asn1.result });
const signedData = new SignedData({ schema: contentInfo.content });
return signedData;
} catch (e) {
console.error("Error parsing signed data", e);
return null; //TODO implement proper error handling
}
};
export const parseCertificates = signedData => {
try {
//TODO remove block
console.log("ParseCerts", signedData.signerInfos);
console.log("DigestAttrib",signedData.signerInfos[0].signedAttrs.attributes[2]);
const digest = signedData.signerInfos[0].signedAttrs.attributes[2].values[0].valueBlock.valueHex;
const digestStr = hexString(digest);
console.log("DigestAttrib", digestStr);
return signedData.certificates.map((certificate, index) => {
const certificateData = { issuer: {}, subject: {}, validity: {} };
......@@ -1064,11 +1105,11 @@ export const parseCertificates = signatureBase64 => {
}
};
export const getCertificateChain = signatureBase64 => {
export const getCertificateChain = signedData => {
const certificateChain = [];
try {
const certificates = parseCertificates(signatureBase64);
const certificates = parseCertificates(signedData);
// Add first certificate in the chain
certificateChain.push(certificates[0]);
......@@ -1087,3 +1128,72 @@ export const getCertificateChain = signatureBase64 => {
return certificateChain;
};
export const verifySMIME = (smimeString, rootCaPem) => {
return new Promise(resolve => {
setTimeout(async () => {
const emailString = fixNewLines(smimeString);
const parts = parseMIME(emailString);
//const rawAttachments = getAttachments(emailString, parts);
//const attachments = [];
let signatureBase64;
let signatureBoundary;
for (const part of parts) {
let contentType = getHeaderValue("content-type", part);
if (contentType === null || contentType === undefined) {
continue;
}
contentType = contentType[0];
if (contentType.startsWith(SIGNATURE_CONTENT_TYPE)) {
signatureBase64 = getAttachment(emailString, part).base64;
signatureBoundary = part.boundary;
break;
}
}
let verificationResult = false;
let signedData;
if (signatureBase64) {
const dataPart = parts[0];
if (dataPart.boundary !== signatureBoundary) {
throw new Error(`Invalid SMIME format: wrong boundary on first MIME part`);
}
const data = emailString.slice(
dataPart.indices.from,
dataPart.indices.to
);
const dataBuffer = stringToArrayBuffer(data);
const rootCa = parseCertificate(rootCaPem);
signedData = parseSignedData(signatureBase64);
for (let i = 0; i < signedData.signerInfos.length; i++) {
console.log(`Validating message for signer ${i}`);
const result = await signedData.verify({
signer: i,
data: dataBuffer,
trustedCerts: [rootCa],
checkDate: new Date(),
checkChain: true,
extendedMode: true,
passedWhenNotRevValues: false
});
console.log(`Result for signer ${i}:`,result);
if (!result) {
verificationResult = false;
resolve(verificationResult);
return;
}
}
}
verificationResult = true;
resolve(verificationResult);
}, 50);
});
};
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment