xmlsecurity PDF verify: fix reading multiple subsections from an xref stream
This is especially needed, as we don't bother compressing updated objects into sections on signing, we simply use a separate section for each updated object. Work towards supporting xref streams and incremental updates at the same time. Change-Id: Ie9759edbba816991615fafc6602cdd440141b989
This commit is contained in:
@@ -1265,7 +1265,7 @@ bool PDFDocument::Read(SvStream& rStream)
|
|||||||
SAL_INFO("xmlsecurity.pdfio", "PDFDocument::Read: nStartXRef is " << nStartXRef);
|
SAL_INFO("xmlsecurity.pdfio", "PDFDocument::Read: nStartXRef is " << nStartXRef);
|
||||||
if (nStartXRef == 0)
|
if (nStartXRef == 0)
|
||||||
{
|
{
|
||||||
SAL_WARN("xmlsecurity.pdfio", "PDFDocument::Read: found no xref statrt offset");
|
SAL_WARN("xmlsecurity.pdfio", "PDFDocument::Read: found no xref start offset");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
while (true)
|
while (true)
|
||||||
@@ -1466,37 +1466,49 @@ void PDFDocument::ReadXRefStream(SvStream& rStream)
|
|||||||
|
|
||||||
// Look up the first and the last entry we need to read.
|
// Look up the first and the last entry we need to read.
|
||||||
auto pIndex = dynamic_cast<PDFArrayElement*>(pObject->Lookup("Index"));
|
auto pIndex = dynamic_cast<PDFArrayElement*>(pObject->Lookup("Index"));
|
||||||
size_t nFirstObject = 0;
|
std::vector<size_t> aFirstObjects;
|
||||||
size_t nNumberOfObjects = 0;
|
std::vector<size_t> aNumberOfObjects;
|
||||||
if (!pIndex || pIndex->GetElements().size() < 2)
|
if (!pIndex)
|
||||||
{
|
{
|
||||||
auto pSize = dynamic_cast<PDFNumberElement*>(pObject->Lookup("Size"));
|
auto pSize = dynamic_cast<PDFNumberElement*>(pObject->Lookup("Size"));
|
||||||
if (pSize)
|
if (pSize)
|
||||||
nNumberOfObjects = pSize->GetValue();
|
{
|
||||||
|
aFirstObjects.push_back(0);
|
||||||
|
aNumberOfObjects.push_back(pSize->GetValue());
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SAL_WARN("xmlsecurity.pdfio", "PDFDocument::ReadXRefStream: Index not found or has < 2 elements");
|
SAL_WARN("xmlsecurity.pdfio", "PDFDocument::ReadXRefStream: Index and Size not found");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const std::vector<PDFElement*>& rIndexElements = pIndex->GetElements();
|
const std::vector<PDFElement*>& rIndexElements = pIndex->GetElements();
|
||||||
auto pFirstObject = dynamic_cast<PDFNumberElement*>(rIndexElements[0]);
|
size_t nFirstObject = 0;
|
||||||
if (!pFirstObject)
|
for (size_t i = 0; i < rIndexElements.size(); ++i)
|
||||||
{
|
{
|
||||||
SAL_WARN("xmlsecurity.pdfio", "PDFDocument::ReadXRefStream: Index has no first object");
|
if (i % 2 == 0)
|
||||||
return;
|
{
|
||||||
}
|
auto pFirstObject = dynamic_cast<PDFNumberElement*>(rIndexElements[i]);
|
||||||
nFirstObject = pFirstObject->GetValue();
|
if (!pFirstObject)
|
||||||
|
{
|
||||||
|
SAL_WARN("xmlsecurity.pdfio", "PDFDocument::ReadXRefStream: Index has no first object");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
nFirstObject = pFirstObject->GetValue();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
auto pNumberOfObjects = dynamic_cast<PDFNumberElement*>(rIndexElements[1]);
|
auto pNumberOfObjects = dynamic_cast<PDFNumberElement*>(rIndexElements[i]);
|
||||||
if (!pNumberOfObjects)
|
if (!pNumberOfObjects)
|
||||||
{
|
{
|
||||||
SAL_WARN("xmlsecurity.pdfio", "PDFDocument::ReadXRefStream: Index has no number of objects");
|
SAL_WARN("xmlsecurity.pdfio", "PDFDocument::ReadXRefStream: Index has no number of objects");
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
aFirstObjects.push_back(nFirstObject);
|
||||||
|
aNumberOfObjects.push_back(pNumberOfObjects->GetValue());
|
||||||
}
|
}
|
||||||
nNumberOfObjects = pNumberOfObjects->GetValue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Look up the format of a single entry.
|
// Look up the format of a single entry.
|
||||||
@@ -1529,89 +1541,95 @@ void PDFDocument::ReadXRefStream(SvStream& rStream)
|
|||||||
}
|
}
|
||||||
|
|
||||||
aStream.Seek(0);
|
aStream.Seek(0);
|
||||||
// This is the line as read from the stream.
|
for (size_t nSubSection = 0; nSubSection < aFirstObjects.size(); ++nSubSection)
|
||||||
std::vector<unsigned char> aOrigLine(nLineLength);
|
|
||||||
// This is the line as it appears after tweaking according to nPredictor.
|
|
||||||
std::vector<unsigned char> aFilteredLine(nLineLength);
|
|
||||||
for (size_t nEntry = 0; nEntry < nNumberOfObjects; ++nEntry)
|
|
||||||
{
|
{
|
||||||
size_t nIndex = nFirstObject + nEntry;
|
size_t nFirstObject = aFirstObjects[nSubSection];
|
||||||
|
size_t nNumberOfObjects = aNumberOfObjects[nSubSection];
|
||||||
|
|
||||||
aStream.ReadBytes(aOrigLine.data(), aOrigLine.size());
|
// This is the line as read from the stream.
|
||||||
if (aOrigLine[0] + 10 != nPredictor)
|
std::vector<unsigned char> aOrigLine(nLineLength);
|
||||||
|
// This is the line as it appears after tweaking according to nPredictor.
|
||||||
|
std::vector<unsigned char> aFilteredLine(nLineLength);
|
||||||
|
for (size_t nEntry = 0; nEntry < nNumberOfObjects; ++nEntry)
|
||||||
{
|
{
|
||||||
SAL_WARN("xmlsecurity.pdfio", "PDFDocument::ReadXRefStream: in-stream predictor is inconsistent with /DecodeParms/Predictor for object #" << nIndex);
|
size_t nIndex = nFirstObject + nEntry;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < nLineLength; ++i)
|
aStream.ReadBytes(aOrigLine.data(), aOrigLine.size());
|
||||||
{
|
if (aOrigLine[0] + 10 != nPredictor)
|
||||||
switch (nPredictor)
|
|
||||||
{
|
{
|
||||||
case 1:
|
SAL_WARN("xmlsecurity.pdfio", "PDFDocument::ReadXRefStream: in-stream predictor is inconsistent with /DecodeParms/Predictor for object #" << nIndex);
|
||||||
// No prediction.
|
|
||||||
break;
|
|
||||||
case 12:
|
|
||||||
// PNG prediction: up (on all rows).
|
|
||||||
aFilteredLine[i] = aFilteredLine[i] + aOrigLine[i];
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
SAL_WARN("xmlsecurity.pdfio", "PDFDocument::ReadXRefStream: unexpected predictor: " << nPredictor);
|
|
||||||
return;
|
return;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// First character is already handled above.
|
for (int i = 0; i < nLineLength; ++i)
|
||||||
int nPos = 1;
|
|
||||||
size_t nType = 0;
|
|
||||||
// Start of the current field in the stream data.
|
|
||||||
int nOffset = nPos;
|
|
||||||
for (; nPos < nOffset + aW[0]; ++nPos)
|
|
||||||
{
|
|
||||||
unsigned char nCh = aFilteredLine[nPos];
|
|
||||||
nType = (nType << 8) + nCh;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start of the object in the file stream.
|
|
||||||
size_t nStreamOffset = 0;
|
|
||||||
nOffset = nPos;
|
|
||||||
for (; nPos < nOffset + aW[1]; ++nPos)
|
|
||||||
{
|
|
||||||
unsigned char nCh = aFilteredLine[nPos];
|
|
||||||
nStreamOffset = (nStreamOffset << 8) + nCh;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generation number of the object.
|
|
||||||
size_t nGenerationNumber = 0;
|
|
||||||
nOffset = nPos;
|
|
||||||
for (; nPos < nOffset + aW[2]; ++nPos)
|
|
||||||
{
|
|
||||||
unsigned char nCh = aFilteredLine[nPos];
|
|
||||||
nGenerationNumber = (nGenerationNumber << 8) + nCh;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ignore invalid nType.
|
|
||||||
if (nType <= 2)
|
|
||||||
{
|
|
||||||
if (m_aXRef.find(nIndex) == m_aXRef.end())
|
|
||||||
{
|
{
|
||||||
XRefEntry aEntry;
|
switch (nPredictor)
|
||||||
switch (nType)
|
|
||||||
{
|
{
|
||||||
case 0:
|
|
||||||
aEntry.m_eType = XRefEntryType::FREE;
|
|
||||||
break;
|
|
||||||
case 1:
|
case 1:
|
||||||
aEntry.m_eType = XRefEntryType::NOT_COMPRESSED;
|
// No prediction.
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 12:
|
||||||
aEntry.m_eType = XRefEntryType::COMPRESSED;
|
// PNG prediction: up (on all rows).
|
||||||
|
aFilteredLine[i] = aFilteredLine[i] + aOrigLine[i];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
SAL_WARN("xmlsecurity.pdfio", "PDFDocument::ReadXRefStream: unexpected predictor: " << nPredictor);
|
||||||
|
return;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
aEntry.m_nOffset = nStreamOffset;
|
}
|
||||||
aEntry.m_nGenerationNumber = nGenerationNumber;
|
|
||||||
m_aXRef[nIndex] = aEntry;
|
// First character is already handled above.
|
||||||
|
int nPos = 1;
|
||||||
|
size_t nType = 0;
|
||||||
|
// Start of the current field in the stream data.
|
||||||
|
int nOffset = nPos;
|
||||||
|
for (; nPos < nOffset + aW[0]; ++nPos)
|
||||||
|
{
|
||||||
|
unsigned char nCh = aFilteredLine[nPos];
|
||||||
|
nType = (nType << 8) + nCh;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start of the object in the file stream.
|
||||||
|
size_t nStreamOffset = 0;
|
||||||
|
nOffset = nPos;
|
||||||
|
for (; nPos < nOffset + aW[1]; ++nPos)
|
||||||
|
{
|
||||||
|
unsigned char nCh = aFilteredLine[nPos];
|
||||||
|
nStreamOffset = (nStreamOffset << 8) + nCh;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generation number of the object.
|
||||||
|
size_t nGenerationNumber = 0;
|
||||||
|
nOffset = nPos;
|
||||||
|
for (; nPos < nOffset + aW[2]; ++nPos)
|
||||||
|
{
|
||||||
|
unsigned char nCh = aFilteredLine[nPos];
|
||||||
|
nGenerationNumber = (nGenerationNumber << 8) + nCh;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore invalid nType.
|
||||||
|
if (nType <= 2)
|
||||||
|
{
|
||||||
|
if (m_aXRef.find(nIndex) == m_aXRef.end())
|
||||||
|
{
|
||||||
|
XRefEntry aEntry;
|
||||||
|
switch (nType)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
aEntry.m_eType = XRefEntryType::FREE;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
aEntry.m_eType = XRefEntryType::NOT_COMPRESSED;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
aEntry.m_eType = XRefEntryType::COMPRESSED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
aEntry.m_nOffset = nStreamOffset;
|
||||||
|
aEntry.m_nGenerationNumber = nGenerationNumber;
|
||||||
|
m_aXRef[nIndex] = aEntry;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user