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

add some checks and some cleanup

parent 57ca4538
Branches
Tags
1 merge request!56Resolve "Email integrity check"
......@@ -61,11 +61,13 @@ export const parseSMIME = smimeString => {
});
}
let signedData; //TODO remove from here, move inside if block
let certificateChain;
if (signatureBase64) {
signedData = parseSignedData(signatureBase64);
certificateChain = getCertificateChain(signedData);
const signedData = parseSignedData(signatureBase64);
if (signedData) {
//TODO revise and use signedData's generation of cert chain (per signer)
certificateChain = getCertificateChain(signedData);
}
}
const from = splitParticipants(getGlobalHeaderValue("from", parts));
......@@ -80,10 +82,7 @@ export const parseSMIME = smimeString => {
html: extractHtmlBodyFromString(html),
plain,
attachments,
certificateChain,
signedData, //TODO remove
fixedSMIME: emailString, //TODO remove
parts //TODO remove
certificateChain
};
resolve(message);
......
......@@ -4,7 +4,11 @@ import {
getTimeLeftInLocalStorage,
makeid
} from "./appUtility";
import { bufferToHexCodes, stringToArrayBuffer } from "pvutils";
import {
bufferToHexCodes,
stringToArrayBuffer,
isEqualBuffer
} from "pvutils";
import { fromBER } from "asn1js";
import { ContentInfo, SignedData } from "pkijs";
import { algomap, rdnmap } from "../constants/certificates";
......@@ -1020,18 +1024,6 @@ function makeBoundary() {
return "W0RyLiBEYW15YW4gTWl0ZXZd--" + makeid(len);
}
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);
......@@ -1042,20 +1034,13 @@ export const parseSignedData = signatureBase64 => {
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
console.error("Error parsing signed data:", e);
return null;
}
};
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: {} };
const serialNumber = bufferToHexCodes(
......@@ -1129,52 +1114,87 @@ export const getCertificateChain = signedData => {
return certificateChain;
};
const isVereignSignature = (signerInfo, signerVerificationResult) => {
const signerCert = signerVerificationResult.signerCertificate;
for (const typeAndValue of signerCert.subject.typesAndValues) {
try {
if (typeAndValue.type === "2.5.4.10" &&
typeAndValue.value.valueBlock.value === "Vereign AG"
) {
return true;
}
} catch (ignore) {}
}
return false;
};
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) {
if (!contentType) {
continue;
}
contentType = contentType[0];
if (contentType.startsWith(SIGNATURE_CONTENT_TYPE)) {
if (contentType && contentType.startsWith(SIGNATURE_CONTENT_TYPE)) {
signatureBase64 = getAttachment(emailString, part).base64;
signatureBoundary = part.boundary;
break;
}
}
let verificationResult = false;
let signedData;
const verificationResult = {
verified: false,
message: "",
vereignSignatures: 0,
nonVereignSignatures: 0
};
if (signatureBase64) {
const dataPart = parts[0];
if (dataPart.boundary !== signatureBoundary) {
throw new Error(`Invalid SMIME format: wrong boundary on first MIME part`);
}
if (!signatureBase64) {
verificationResult.message = "Not a signed MIME";
resolve(verificationResult);
return;
}
const data = emailString.slice(
dataPart.indices.from,
dataPart.indices.to
);
const dataBuffer = stringToArrayBuffer(data);
const dataPart = parts[0];
if (dataPart.boundary !== signatureBoundary) {
verificationResult.message = "Invalid SMIME format: wrong boundary on first MIME part";
resolve(verificationResult);
return;
}
const data = emailString.slice(
dataPart.indices.from,
dataPart.indices.to
);
const dataBuffer = stringToArrayBuffer(data);
const rootCa = parseCertificate(rootCaPem);
const rootCa = parseCertificate(rootCaPem);
if (rootCa.tbs.byteLength === 0) {
rootCa.tbs = rootCa.encodeTBS();
}
const signedData = parseSignedData(signatureBase64);
if (!signedData) {
verificationResult.message = "Corrupt SMIME signature";
resolve(verificationResult);
return;
}
signedData = parseSignedData(signatureBase64);
for (let i = 0; i < signedData.signerInfos.length; i++) {
console.log(`Validating message for signer ${i}`);
const result = await signedData.verify({
for (let i = 0; i < signedData.signerInfos.length; i++) {
let signerResult;
try {
signerResult = await signedData.verify({
signer: i,
data: dataBuffer,
trustedCerts: [rootCa],
......@@ -1183,16 +1203,51 @@ export const verifySMIME = (smimeString, rootCaPem) => {
extendedMode: true,
passedWhenNotRevValues: false
});
console.log(`Result for signer ${i}:`,result);
if (!result) {
verificationResult = false;
} catch (e) {
verificationResult.message = e.message;
resolve(verificationResult);
return;
}
const signerVerified = !!signerResult.signatureVerified && !!signerResult.signerCertificateVerified;
if (!signerVerified) {
if (signerResult.message) {
verificationResult.message = signerResult.message;
} else {
verificationResult.message = "Message integrity is compromised";
}
resolve(verificationResult);
return;
}
if (isVereignSignature(signedData.signerInfos[i], signerResult)) {
const signerPath = signerResult.certificatePath;
const signerRoot = signerPath[signerPath.length - 1];
if (signerRoot.tbs.byteLength === 0) {
signerRoot.tbs = signerRoot.encodeTBS();
}
if (!isEqualBuffer(signerRoot.tbs, rootCa.tbs)) {
verificationResult.message =
`Vereign signature ${i} has root certificate, different from Vereign root CA`;
resolve(verificationResult);
return;
}
verificationResult.vereignSignatures++;
} else {
verificationResult.nonVereignSignatures++;
}
}
verificationResult = true;
if (signedData.signerInfos.length === 0) {
verificationResult.message = "No signatures found";
} else
if (verificationResult.vereignSignatures === 0) {
verificationResult.message = "Verified succesfully, but no Vereign signatures found";
} else {
verificationResult.message = "Verified succesfully";
}
verificationResult.verified = true;
resolve(verificationResult);
}, 50);
});
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment