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

Add try/catch on newSig and appendSig functions

This idented the bodies, but no code changes were made
parent c235efba
No related branches found
No related tags found
1 merge request!48269 implement workflow for signing already uploaded document dev
...@@ -406,209 +406,217 @@ function createOffset(date) { ...@@ -406,209 +406,217 @@ function createOffset(date) {
} }
async function newSig(pdf, root, rootSuccessor, date, signingCert, certificateChain, privateKey) { async function newSig(pdf, root, rootSuccessor, date, signingCert, certificateChain, privateKey) {
// {annotEntry} is the ref to the annot widget. If we enlarge the array, make sure all the offsets try {
// after the modification will be updated -> xref table and startxref // {annotEntry} is the ref to the annot widget. If we enlarge the array, make sure all the offsets
var annotEntry = findFreeXrefNr(pdf.xref.entries); // after the modification will be updated -> xref table and startxref
// we'll store all the modifications we make, as we need to adjust the offset in the PDF var annotEntry = findFreeXrefNr(pdf.xref.entries);
var offsetForm = find(pdf.stream.bytes, '<<', root.offset, rootSuccessor.offset) + 2; // we'll store all the modifications we make, as we need to adjust the offset in the PDF
//first we need to find the root element and add the following: var offsetForm = find(pdf.stream.bytes, '<<', root.offset, rootSuccessor.offset) + 2;
// //first we need to find the root element and add the following:
// /AcroForm<</Fields[{annotEntry} 0 R] /SigFlags 3>> //
// // /AcroForm<</Fields[{annotEntry} 0 R] /SigFlags 3>>
var appendAcroForm = '/AcroForm<</Fields['+annotEntry+' 0 R] /SigFlags 3>>'; //
//before we insert the acroform, we find the right place for annotentry var appendAcroForm = '/AcroForm<</Fields[' + annotEntry + ' 0 R] /SigFlags 3>>';
//before we insert the acroform, we find the right place for annotentry
//we need to add Annots [x y R] to the /Type /Page section. We can do that by searching /Contents[
var pages = pdf.catalog.catDict.get('Pages'); //we need to add Annots [x y R] to the /Type /Page section. We can do that by searching /Contents[
//get first page, we have hidden sig, so don't bother var pages = pdf.catalog.catDict.get('Pages');
var ref = pages.get('Kids')[0]; //get first page, we have hidden sig, so don't bother
var xref = pdf.xref.fetch(ref); var ref = pages.get('Kids')[0];
var offsetContentEnd = xref.get('#Contents_offset'); var xref = pdf.xref.fetch(ref);
//we now search backwards, this is safe as we don't expect user content here var offsetContentEnd = xref.get('#Contents_offset');
var offsetContent = findBackwards(pdf.stream.bytes, '/Contents', offsetContentEnd); //we now search backwards, this is safe as we don't expect user content here
var appendAnnots = '/Annots['+annotEntry+' 0 R]\n '; var offsetContent = findBackwards(pdf.stream.bytes, '/Contents', offsetContentEnd);
var appendAnnots = '/Annots[' + annotEntry + ' 0 R]\n ';
//now insert string into stream
var array = insertIntoArray(pdf.stream.bytes, offsetForm, appendAcroForm); //now insert string into stream
//recalculate the offsets in the xref table, only update those that are affected var array = insertIntoArray(pdf.stream.bytes, offsetForm, appendAcroForm);
updateXrefOffset(pdf.xref, offsetForm, appendAcroForm.length); //recalculate the offsets in the xref table, only update those that are affected
offsetContent = updateOffset(offsetContent, offsetForm, appendAcroForm.length); updateXrefOffset(pdf.xref, offsetForm, appendAcroForm.length);
offsetContent = updateOffset(offsetContent, offsetForm, appendAcroForm.length);
var array = insertIntoArray(array, offsetContent, appendAnnots);
updateXrefOffset(pdf.xref, offsetContent, appendAnnots.length); var array = insertIntoArray(array, offsetContent, appendAnnots);
offsetContent = -1; //not needed anymore, don't update when offset changes updateXrefOffset(pdf.xref, offsetContent, appendAnnots.length);
offsetContent = -1; //not needed anymore, don't update when offset changes
//Then add to the next free object (annotEntry)
//add right before the xref table or stream //Then add to the next free object (annotEntry)
//if its a table, place element before the xref table //add right before the xref table or stream
// //if its a table, place element before the xref table
// sigEntry is the ref to the signature content. Next we need the signature object //
var sigEntry = findFreeXrefNr(pdf.xref.entries, [annotEntry]); // sigEntry is the ref to the signature content. Next we need the signature object
var sigEntry = findFreeXrefNr(pdf.xref.entries, [annotEntry]);
//
// {annotEntry} 0 obj //
// <</F 132/Type/Annot/Subtype/Widget/Rect[0 0 0 0]/FT/Sig/DR<<>>/T(signature)/V Y 0 R>> // {annotEntry} 0 obj
// endobj // <</F 132/Type/Annot/Subtype/Widget/Rect[0 0 0 0]/FT/Sig/DR<<>>/T(signature)/V Y 0 R>>
// // endobj
var append = annotEntry + ' 0 obj\n<</F 132/Type/Annot/Subtype/Widget/Rect[0 0 0 0]/FT/Sig/DR<<>>/T(signature'+annotEntry+')/V '+sigEntry+' 0 R>>\nendobj\n\n'; //
var append = annotEntry + ' 0 obj\n<</F 132/Type/Annot/Subtype/Widget/Rect[0 0 0 0]/FT/Sig/DR<<>>/T(signature' + annotEntry + ')/V ' + sigEntry + ' 0 R>>\nendobj\n\n';
// we want the offset just before the last xref table or entry
var blocks = findXrefBlocks(pdf.xref.xrefBlocks); // we want the offset just before the last xref table or entry
var offsetAnnot = blocks[0].start; var blocks = findXrefBlocks(pdf.xref.xrefBlocks);
array = insertIntoArray(array, offsetAnnot, append); var offsetAnnot = blocks[0].start;
//no updateXrefOffset, as the next entry will be following array = insertIntoArray(array, offsetAnnot, append);
//no updateXrefOffset, as the next entry will be following
//
// {sigEntry} 0 obj //
// <</Contents <0481801e6d931d561563fb254e27c846e08325570847ed63d6f9e35 ... b2c8788a5> // {sigEntry} 0 obj
// /Type/Sig/SubFilter/adbe.pkcs7.detached/Location(Ghent)/M(D:20120928104114+02'00') // <</Contents <0481801e6d931d561563fb254e27c846e08325570847ed63d6f9e35 ... b2c8788a5>
// /ByteRange [A B C D]/Filter/Adobe.PPKLite/Reason(Test)/ContactInfo()>> // /Type/Sig/SubFilter/adbe.pkcs7.detached/Location(Ghent)/M(D:20120928104114+02'00')
// endobj // /ByteRange [A B C D]/Filter/Adobe.PPKLite/Reason(Test)/ContactInfo()>>
// // endobj
//
//the next entry goes below the above
var offsetSig = offsetAnnot + append.length; //the next entry goes below the above
var offsetSig = offsetAnnot + append.length;
// Both {annotEntry} and {sigEntry} objects need to be added to the last xref table. The byte range needs
// to be adjusted. Since the signature will always be in a gap, use first an empty sig // Both {annotEntry} and {sigEntry} objects need to be added to the last xref table. The byte range needs
// to check the size, add ~25% size, then calculate the signature and place in the empty // to be adjusted. Since the signature will always be in a gap, use first an empty sig
// space. // to check the size, add ~25% size, then calculate the signature and place in the empty
var start = sigEntry+ ' 0 obj\n<</Contents <'; // space.
var dummy = await sign_pki(signingCert, certificateChain, privateKey, stringToUint8Array('A'), date); var start = sigEntry + ' 0 obj\n<</Contents <';
//TODO: Adobe thinks its important to have the right size, no idea why this is the case var dummy = await sign_pki(signingCert, certificateChain, privateKey, stringToUint8Array('A'), date);
var crypto = new Array(round256(dummy.length * 2)).join( '0' ); //TODO: Adobe thinks its important to have the right size, no idea why this is the case
var middle = '>\n/Type/Sig/SubFilter/adbe.pkcs7.detached/Location()/M(D:'+now(date)+'\')\n/ByteRange '; var crypto = new Array(round256(dummy.length * 2)).join('0');
var byteRange = '[0000000000 0000000000 0000000000 0000000000]'; var middle = '>\n/Type/Sig/SubFilter/adbe.pkcs7.detached/Location()/M(D:' + now(date) + '\')\n/ByteRange ';
var end = '/Filter/Adobe.PPKLite/Reason()/ContactInfo()>>\nendobj\n\n'; var byteRange = '[0000000000 0000000000 0000000000 0000000000]';
//all together var end = '/Filter/Adobe.PPKLite/Reason()/ContactInfo()>>\nendobj\n\n';
var append2 = start+crypto+middle+byteRange+end; //all together
var offsetByteRange = start.length+crypto.length+middle.length; var append2 = start + crypto + middle + byteRange + end;
var offsetByteRange = start.length + crypto.length + middle.length;
array = insertIntoArray(array, offsetSig, append2);
updateXrefOffset(pdf.xref, offsetAnnot, append2.length + append.length); array = insertIntoArray(array, offsetSig, append2);
updateXrefOffset(pdf.xref, offsetAnnot, append2.length + append.length);
//find the xref tables, remove them and also the EOF, as we'll write a new table
var xrefBlocks = findXrefBlocks(pdf.xref.xrefBlocks); //find the xref tables, remove them and also the EOF, as we'll write a new table
var xrefBlocks = findXrefBlocks(pdf.xref.xrefBlocks);
for(var i in xrefBlocks) {
for (var i in xrefBlocks) {
var oldSize = array.length; var oldSize = array.length;
array = removeFromArray(array, xrefBlocks[i].start, xrefBlocks[i].end); array = removeFromArray(array, xrefBlocks[i].start, xrefBlocks[i].end);
var length = array.length - oldSize; var length = array.length - oldSize;
updateXrefOffset(pdf.xref, xrefBlocks[i].start, length); updateXrefOffset(pdf.xref, xrefBlocks[i].start, length);
//check for %%EOF and remove it as well //check for %%EOF and remove it as well
var offsetEOF = find(array, '%%EOF', xrefBlocks[i].start, xrefBlocks[i].start+20); var offsetEOF = find(array, '%%EOF', xrefBlocks[i].start, xrefBlocks[i].start + 20);
if(offsetEOF > 0) { if (offsetEOF > 0) {
var lengthEOF = '%%EOF'.length; var lengthEOF = '%%EOF'.length;
array = removeFromArray(array, offsetEOF, offsetEOF + lengthEOF); array = removeFromArray(array, offsetEOF, offsetEOF + lengthEOF);
updateXrefOffset(pdf.xref, offsetEOF, -lengthEOF); updateXrefOffset(pdf.xref, offsetEOF, -lengthEOF);
updateXrefBlocks(xrefBlocks, offsetEOF, -lengthEOF); updateXrefBlocks(xrefBlocks, offsetEOF, -lengthEOF);
offsetAnnot = updateOffset(offsetAnnot, offsetEOF, -lengthEOF); offsetAnnot = updateOffset(offsetAnnot, offsetEOF, -lengthEOF);
offsetSig = updateOffset(offsetSig, offsetEOF, -lengthEOF); offsetSig = updateOffset(offsetSig, offsetEOF, -lengthEOF);
} }
updateXrefBlocks(xrefBlocks, xrefBlocks[i].start, length); updateXrefBlocks(xrefBlocks, xrefBlocks[i].start, length);
offsetAnnot = updateOffset(offsetAnnot, xrefBlocks[i].start, length); offsetAnnot = updateOffset(offsetAnnot, xrefBlocks[i].start, length);
offsetSig = updateOffset(offsetSig, xrefBlocks[i].start, length); offsetSig = updateOffset(offsetSig, xrefBlocks[i].start, length);
} }
var sha256Hex = await sha256(array); var sha256Hex = await sha256(array);
//add the new entries to the xref //add the new entries to the xref
pdf.xref.entries[annotEntry] = {offset:offsetAnnot, gen:0, free:false}; pdf.xref.entries[annotEntry] = {offset: offsetAnnot, gen: 0, free: false};
pdf.xref.entries[sigEntry] = {offset:offsetSig, gen:0, free:false}; pdf.xref.entries[sigEntry] = {offset: offsetSig, gen: 0, free: false};
var xrefTable = createXrefTable(pdf.xref.entries); var xrefTable = createXrefTable(pdf.xref.entries);
//also empty entries count as in the PDF spec, page 720 (example) //also empty entries count as in the PDF spec, page 720 (example)
xrefTable += createTrailer(pdf.xref.topDict, array.length, sha256Hex, pdf.xref.entries.length); xrefTable += createTrailer(pdf.xref.topDict, array.length, sha256Hex, pdf.xref.entries.length);
array = insertIntoArray(array, array.length, xrefTable); array = insertIntoArray(array, array.length, xrefTable);
//since we consolidate, no prev! [adjust /Prev -> rawparsing + offset] //since we consolidate, no prev! [adjust /Prev -> rawparsing + offset]
var from1 = 0; var from1 = 0;
var to1 = offsetSig+start.length; var to1 = offsetSig + start.length;
var from2 = to1 + crypto.length; var from2 = to1 + crypto.length;
var to2 = (array.length - from2) - 1; var to2 = (array.length - from2) - 1;
var byteRange = '['+pad10(from1)+' '+pad10(to1 - 1) + ' ' +pad10(from2 + 1)+ ' ' + pad10(to2) + ']'; var byteRange = '[' + pad10(from1) + ' ' + pad10(to1 - 1) + ' ' + pad10(from2 + 1) + ' ' + pad10(to2) + ']';
array = updateArray(array, (offsetSig + offsetByteRange), byteRange); array = updateArray(array, (offsetSig + offsetByteRange), byteRange);
var data = removeFromArray(array, to1 - 1, from2 + 1); var data = removeFromArray(array, to1 - 1, from2 + 1);
var crypto2 = await sign_pki(signingCert, certificateChain, privateKey, data.buffer, date); var crypto2 = await sign_pki(signingCert, certificateChain, privateKey, data.buffer, date);
array = updateArray(array, to1, crypto2); array = updateArray(array, to1, crypto2);
return array; return array;
} catch (err) {
throw new Error('Error creating new signature in PDF: ' + err);
}
} }
async function appendSig(pdf, root, rootSuccessor, date, signingCert, certificateChain, privateKey) { async function appendSig(pdf, root, rootSuccessor, date, signingCert, certificateChain, privateKey) {
//copy root and the entry with contents to the end try {
var startRoot = pdf.stream.bytes.length + 1; //copy root and the entry with contents to the end
var startRoot = pdf.stream.bytes.length + 1;
var array = copyToEnd(pdf.stream.bytes, root.offset - 1, rootSuccessor.offset);
var array = copyToEnd(pdf.stream.bytes, root.offset - 1, rootSuccessor.offset);
//since we signed the first one, we know how the pdf has to look like:
var offsetAcroForm = find(array, '/AcroForm<</Fields', startRoot); //since we signed the first one, we know how the pdf has to look like:
var endOffsetAcroForm = find(array, ']', offsetAcroForm); var offsetAcroForm = find(array, '/AcroForm<</Fields', startRoot);
var endOffsetAcroForm = find(array, ']', offsetAcroForm);
var annotEntry = findFreeXrefNr(pdf.xref.entries);
var sigEntry = findFreeXrefNr(pdf.xref.entries, [annotEntry]); var annotEntry = findFreeXrefNr(pdf.xref.entries);
var sigEntry = findFreeXrefNr(pdf.xref.entries, [annotEntry]);
var appendAnnot = ' ' + annotEntry + ' 0 R';
array = insertIntoArray(array, endOffsetAcroForm, appendAnnot); var appendAnnot = ' ' + annotEntry + ' 0 R';
array = insertIntoArray(array, endOffsetAcroForm, appendAnnot);
//we need to add Annots [x y R] to the /Type /Page section. We can do that by searching /Annots
var pages = pdf.catalog.catDict.get('Pages'); //we need to add Annots [x y R] to the /Type /Page section. We can do that by searching /Annots
//get first page, we have hidden sig, so don't bother var pages = pdf.catalog.catDict.get('Pages');
var contentRef = pages.get('Kids')[0]; //get first page, we have hidden sig, so don't bother
var xref = pdf.xref.fetch(contentRef); var contentRef = pages.get('Kids')[0];
var offsetAnnotEnd = xref.get('#Annots_offset'); var xref = pdf.xref.fetch(contentRef);
//we now search ], this is safe as we signed it previously var offsetAnnotEnd = xref.get('#Annots_offset');
var endOffsetAnnot = find(array, ']', offsetAnnotEnd); //we now search ], this is safe as we signed it previously
var xrefEntry = pdf.xref.getEntry(contentRef.num); var endOffsetAnnot = find(array, ']', offsetAnnotEnd);
var xrefEntrySuccosser = findSuccessorEntry(pdf.xref.entries, xrefEntry); var xrefEntry = pdf.xref.getEntry(contentRef.num);
var offsetAnnotRelative = endOffsetAnnot - xrefEntrySuccosser.offset; var xrefEntrySuccosser = findSuccessorEntry(pdf.xref.entries, xrefEntry);
var startContent = array.length; var offsetAnnotRelative = endOffsetAnnot - xrefEntrySuccosser.offset;
array = copyToEnd(array, xrefEntry.offset, xrefEntrySuccosser.offset); var startContent = array.length;
array = insertIntoArray(array, array.length + offsetAnnotRelative, appendAnnot); array = copyToEnd(array, xrefEntry.offset, xrefEntrySuccosser.offset);
array = insertIntoArray(array, array.length + offsetAnnotRelative, appendAnnot);
var startAnnot = array.length;
var append = annotEntry + ' 0 obj\n<</F 132/Type/Annot/Subtype/Widget/Rect[0 0 0 0]/FT/Sig/DR<<>>/T(signature'+annotEntry+')/V '+sigEntry+' 0 R>>\nendobj\n\n'; var startAnnot = array.length;
array = insertIntoArray(array, startAnnot, append); var append = annotEntry + ' 0 obj\n<</F 132/Type/Annot/Subtype/Widget/Rect[0 0 0 0]/FT/Sig/DR<<>>/T(signature' + annotEntry + ')/V ' + sigEntry + ' 0 R>>\nendobj\n\n';
array = insertIntoArray(array, startAnnot, append);
var startSig = array.length;
var start = sigEntry+ ' 0 obj\n<</Contents <'; var startSig = array.length;
var dummy = await sign_pki(signingCert, certificateChain, privateKey, stringToUint8Array('A'), date); var start = sigEntry + ' 0 obj\n<</Contents <';
//TODO: Adobe thinks its important to have the right size, no idea why this is the case var dummy = await sign_pki(signingCert, certificateChain, privateKey, stringToUint8Array('A'), date);
var crypto = new Array(round256(dummy.length * 2)).join( '0' ); //TODO: Adobe thinks its important to have the right size, no idea why this is the case
var middle = '>\n/Type/Sig/SubFilter/adbe.pkcs7.detached/Location()/M(D:'+now(date)+'\')\n/ByteRange '; var crypto = new Array(round256(dummy.length * 2)).join('0');
var byteRange = '[0000000000 0000000000 0000000000 0000000000]'; var middle = '>\n/Type/Sig/SubFilter/adbe.pkcs7.detached/Location()/M(D:' + now(date) + '\')\n/ByteRange ';
var end = '/Filter/Adobe.PPKLite/Reason()/ContactInfo()>>\nendobj\n\n'; var byteRange = '[0000000000 0000000000 0000000000 0000000000]';
//all together var end = '/Filter/Adobe.PPKLite/Reason()/ContactInfo()>>\nendobj\n\n';
var append2 = start+crypto+middle+byteRange+end; //all together
array = insertIntoArray(array, startSig, append2); var append2 = start + crypto + middle + byteRange + end;
array = insertIntoArray(array, startSig, append2);
var sha256Hex = await sha256(array);
var sha256Hex = await sha256(array);
var prev = pdf.xref.xrefBlocks[0];
var startxref = array.length; var prev = pdf.xref.xrefBlocks[0];
var xrefEntries = []; var startxref = array.length;
xrefEntries[0] = {offset:0, gen:65535, free:true}; var xrefEntries = [];
xrefEntries[pdf.xref.topDict.getRaw('Root').num] = {offset:startRoot, gen:0, free:false}; xrefEntries[0] = {offset: 0, gen: 65535, free: true};
xrefEntries[contentRef.num] = {offset:startContent, gen:0, free:false}; xrefEntries[pdf.xref.topDict.getRaw('Root').num] = {offset: startRoot, gen: 0, free: false};
xrefEntries[annotEntry] = {offset:startAnnot, gen:0, free:false}; xrefEntries[contentRef.num] = {offset: startContent, gen: 0, free: false};
xrefEntries[sigEntry] = {offset:startSig, gen:0, free:false}; xrefEntries[annotEntry] = {offset: startAnnot, gen: 0, free: false};
var xrefTable = createXrefTableAppend(xrefEntries); xrefEntries[sigEntry] = {offset: startSig, gen: 0, free: false};
xrefTable += createTrailer(pdf.xref.topDict, startxref, sha256Hex, xrefEntries.length, prev); var xrefTable = createXrefTableAppend(xrefEntries);
array = insertIntoArray(array, array.length, xrefTable); xrefTable += createTrailer(pdf.xref.topDict, startxref, sha256Hex, xrefEntries.length, prev);
array = insertIntoArray(array, array.length, xrefTable);
var from1 = 0;
var to1 = startSig + start.length; var from1 = 0;
var from2 = to1 + crypto.length; var to1 = startSig + start.length;
var to2 = (array.length - from2) - 1; var from2 = to1 + crypto.length;
var byteRange = '['+pad10(from1)+' '+pad10(to1 - 1) + ' ' +pad10(from2 + 1)+ ' ' + pad10(to2) + ']'; var to2 = (array.length - from2) - 1;
var byteRange = '[' + pad10(from1) + ' ' + pad10(to1 - 1) + ' ' + pad10(from2 + 1) + ' ' + pad10(to2) + ']';
array = updateArray(array, from2 + middle.length, byteRange);
//now sign from1-to1 / from2-to2 and update byterange array = updateArray(array, from2 + middle.length, byteRange);
//now sign from1-to1 / from2-to2 and update byterange
var data = removeFromArray(array, to1 - 1, from2 + 1);
var crypto2 = await sign_pki(signingCert, certificateChain, privateKey, data.buffer, date); var data = removeFromArray(array, to1 - 1, from2 + 1);
array = updateArray(array, to1, crypto2); var crypto2 = await sign_pki(signingCert, certificateChain, privateKey, data.buffer, date);
return array; array = updateArray(array, to1, crypto2);
return array;
} catch (err) {
throw new Error('Error appending signature in PDF: ' + err);
}
} }
function loadPdf(pdfArray) { function loadPdf(pdfArray) {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment