fpspreadsheet: Detect whether ods files are encrypted.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@8898 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2023-07-24 22:45:34 +00:00
parent 96e0ace939
commit 20afe34c4b
2 changed files with 126 additions and 1 deletions

View File

@ -94,6 +94,7 @@ type
FRichTextFontList: TFPList; FRichTextFontList: TFPList;
FRepeatedCols: TsRowColRange; FRepeatedCols: TsRowColRange;
FRepeatedRows: TsRowColRange; FRepeatedRows: TsRowColRange;
FManifestFileEntries: TFPList;
procedure ApplyColData; procedure ApplyColData;
procedure ApplyStyleToCell(ACell: PCell; AStyleIndex: Integer); procedure ApplyStyleToCell(ACell: PCell; AStyleIndex: Integer);
function ApplyStyleToCell(ACell: PCell; AStyleName: String): Boolean; function ApplyStyleToCell(ACell: PCell; AStyleName: String): Boolean;
@ -133,6 +134,7 @@ type
var AFontColor: TsColor); var AFontColor: TsColor);
function ReadHeaderFooterText(ANode: TDOMNode): String; function ReadHeaderFooterText(ANode: TDOMNode): String;
procedure ReadMetaData(ANode: TDOMNode); procedure ReadMetaData(ANode: TDOMNode);
procedure ReadMetaInfManifest(ANode: TDOMNode; out IsEncrypted: Boolean);
procedure ReadPictures(AStream: TStream); procedure ReadPictures(AStream: TStream);
procedure ReadPrintRanges(ATableNode: TDOMNode; ASheet: TsBasicWorksheet); procedure ReadPrintRanges(ATableNode: TDOMNode; ASheet: TsBasicWorksheet);
procedure ReadRowsAndCells(ATableNode: TDOMNode); procedure ReadRowsAndCells(ATableNode: TDOMNode);
@ -573,6 +575,21 @@ type
end; end;
*) *)
type
TManifestFileEntry = class
FileName: String;
Encrypted: Boolean;
EncryptionData_Checksum: String;
EncryptionData_ChecksumType: String;
AlgorithmName: String;
InitializationVector: String;
StartKeyGenerationName: String;
StartKeySize: Integer;
IterationCount: Integer;
KeyDerivationName: String;
KeySize: Integer;
Salt: String;
end;
{******************************************************************************} {******************************************************************************}
{ Clipboard utility } { Clipboard utility }
@ -1170,6 +1187,7 @@ begin
FCellFormatList := TsCellFormatList.Create(true); FCellFormatList := TsCellFormatList.Create(true);
// true = allow duplicates because style names used in cell records will not be found any more. // true = allow duplicates because style names used in cell records will not be found any more.
FManifestFileEntries := TFPList.Create;
FTableStyleList := TFPList.Create; FTableStyleList := TFPList.Create;
FColumnStyleList := TFPList.Create; FColumnStyleList := TFPList.Create;
FColumnList := TFPList.Create; FColumnList := TFPList.Create;
@ -1220,6 +1238,9 @@ begin
for j := FMasterPageList.Count-1 downto 0 do TObject(FMasterPageList[j]).Free; for j := FMasterPageList.Count-1 downto 0 do TObject(FMasterPageList[j]).Free;
FMasterPageList.Free; FMasterPageList.Free;
for j := FManifestFileEntries.Count-1 downto 0 do TObject(FManifestFileEntries[j]).Free;
FManifestFileEntries.Free;
FHeaderFooterFontList.Free; FHeaderFooterFontList.Free;
inherited Destroy; inherited Destroy;
end; end;
@ -2026,6 +2047,91 @@ begin
end; end;
end; end;
// Reads the file META-INF/manifest.xml. It is never encrypted and contains
// decryption information.
procedure TsSpreadOpenDocReader.ReadMetaInfManifest(ANode: TDOMNode;
out IsEncrypted: Boolean);
function GetAlgorithmName(ASubNode: TDOMNode; AttrName: String): String;
var
s: String;
p: Integer;
begin
s := GetAttrValue(ASubNode, AttrName);
if s <> '' then
begin
p := pos('#', s);
if p <> 0 then
Result := Copy(s, p+1, MaxInt);
end else
Result := s;
end;
function GetIntValue(ASubNode: TDOMNode; AttrName: String): Integer;
var
s: String;
begin
s := GetAttrValue(ASubNode, AttrName);
Result := StrToIntDef(s, 0);
end;
var
encryptionDataNode, childNode: TDOMNode;
nodeName: String;
entry: TManifestFileEntry;
begin
IsEncrypted := false;
while ANode <> nil do
begin
nodeName := ANode.NodeName;
if nodeName = 'manifest:file-entry' then
begin
entry := TManifestFileEntry.Create;
entry.FileName := GetAttrValue(ANode, 'manifest:full-path');
encryptionDataNode := ANode.FirstChild;
while encryptionDataNode <> nil do
begin
nodeName := encryptionDataNode.NodeName;
if nodeName = 'manifest:encryption-data' then
begin
IsEncrypted := true;
entry.Encrypted := true;
entry.EncryptionData_ChecksumType := GetAlgorithmName(encryptionDataNode, 'manifest:checksum-type');
entry.EncryptionData_Checksum := GetAttrValue(encryptionDataNode, 'manifest:checksum');
childNode := encryptionDataNode.FirstChild;
while childNode <> nil do
begin
nodeName := childNode.NodeName;
case nodeName of
'manifest:algorithm':
begin
entry.AlgorithmName := GetAlgorithmName(childNode, 'manifest:algorithm-name');
entry.InitializationVector := GetAttrValue(childNode, 'manifest:initialisation-vector');
end;
'manifest:start-key-generation':
begin
entry.StartKeyGenerationName := GetAlgorithmName(childNode, 'manifest:start-key-generation-name');
entry.StartKeySize := GetIntValue(childNode, 'manifest:key-size');
end;
'manifest:key-derivation':
begin
entry.KeyDerivationName := GetAttrValue(childNode, 'manifest:key-derivation-name');
entry.KeySize := GetIntValue(childNode, 'manifest:key-size');
entry.IterationCount := GetIntValue(childNode, 'manifest:iteration-count');
entry.Salt := GetAttrValue(childNode, 'manifest:salt');
end;
end;
childNode := childNode.NextSibling;
end;
end;
encryptionDataNode := encryptionDataNode.NextSibling;
end;
FManifestFileEntries.Add(entry);
end;
ANode := ANode.NextSibling;
end;
end;
procedure TsSpreadOpenDocReader.ReadBlank(ARow, ACol: Cardinal; procedure TsSpreadOpenDocReader.ReadBlank(ARow, ACol: Cardinal;
AStyleIndex: Integer; ACellNode: TDOMNode); AStyleIndex: Integer; ACellNode: TDOMNode);
var var
@ -2778,6 +2884,7 @@ var
sheet: TsWorksheet; sheet: TsWorksheet;
sheetName: String; sheetName: String;
tablestyleName: String; tablestyleName: String;
isEncrypted: Boolean = false;
function CreateXMLStream: TStream; function CreateXMLStream: TStream;
begin begin
@ -2795,6 +2902,24 @@ begin
Doc := nil; Doc := nil;
try try
// Read the META-INF/manifest.xml file to learn about encryption
XMLStream := CreateXMLStream;
try
if UnzipToStream(AStream, 'META-INF/manifest.xml', XMLStream) then
begin
ReadXMLStream(Doc, XMLStream);
if Assigned(Doc) then
begin
ReadMetaInfManifest(Doc.DocumentElement.FindNode('manifest:file-entry'), isEncrypted);
if isEncrypted then
raise EFpSpreadsheetReader.Create('File is encrypted.');
end;
end;
finally
FreeAndNil(Doc);
XMLStream.Free;
end;
// Extract the embedded pictures // Extract the embedded pictures
ReadPictures(AStream); ReadPictures(AStream);

View File

@ -324,7 +324,7 @@ begin
LastHash := SHA1Buffer( ConcArr[0], Length(ConcArr) ); LastHash := SHA1Buffer( ConcArr[0], Length(ConcArr) );
end; end;
// 1.3.Claculate final hash, SHA1(final) = SHA1(H(n) + block) -- block = 0 (32bit) // 1.3.Calculate final hash, SHA1(final) = SHA1(H(n) + block) -- block = 0 (32bit)
ConcatTobyteArray(ConcArr, @LastHash[0], Length(LastHash), @Zero, Sizeof(Zero)); ConcatTobyteArray(ConcArr, @LastHash[0], Length(LastHash), @Zero, Sizeof(Zero));
// ConcatToByteArray(ConcArr, LastHash, 0); // ConcatToByteArray(ConcArr, LastHash, 0);
LastHash := SHA1Buffer( ConcArr[0], Length(ConcArr) ); LastHash := SHA1Buffer( ConcArr[0], Length(ConcArr) );