You've already forked lazarus-ccr
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@8913 8e941d3f-bd1b-0410-a28a-d453659cc2b4
192 lines
5.6 KiB
ObjectPascal
192 lines
5.6 KiB
ObjectPascal
unit fpsOpenDocument_Crypto;
|
|
|
|
{$MODE ObjFPC}{$H+}
|
|
{.$DEFINE UNZIP_ABBREVIA} // Remove this define when zipper is fixed.
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils,
|
|
{$IFDEF UNZIP_ABBREVIA}
|
|
ABUnzper,
|
|
{$ENDIF}
|
|
fpsTypes, fpsUtils, fpsOpenDocument;
|
|
|
|
type
|
|
TsSpreadOpenDocReaderCrypto = class(TsSpreadOpenDocReader)
|
|
private
|
|
function CalcPasswordHash(ADecryptionInfo: TsOpenDocManifestFileEntry;
|
|
APassword: String): TBytes;
|
|
protected
|
|
function Decrypt(AStream: TStream; ADecryptionInfo: TsOpenDocManifestFileEntry;
|
|
APassword: String; ADestStream: TStream; out AErrorMsg: String): Boolean; override;
|
|
function SupportsDecryption: Boolean; override;
|
|
{$IFDEF UNZIP_ABBREVIA}
|
|
function UnzipToStream(AStream: TStream; AZippedFile: String; ADestStream: TStream): Boolean; override;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
var
|
|
sfidOpenDocument_Crypto: TsSpreadFormatID;
|
|
|
|
implementation
|
|
|
|
uses
|
|
zStream,
|
|
fpsReaderWriter, fpsCryptoProc;
|
|
|
|
{ Decompresses the source stream and stored the output in the destination stream. }
|
|
procedure Decompress(ASrcStream, ADestStream: TStream);
|
|
var
|
|
decompressor: TDecompressionStream;
|
|
begin
|
|
decompressor := TDecompressionStream.Create(ASrcStream, true);
|
|
try
|
|
ADestStream.CopyFrom(decompressor, 0); // 0 --> entire src stream
|
|
finally
|
|
decompressor.Free;
|
|
end;
|
|
end;
|
|
|
|
{-------------------------------------------------------------------------------
|
|
TsSpreadOpenDocReaderCrypto
|
|
-------------------------------------------------------------------------------}
|
|
|
|
{ AStream contains one encrypted xml file of the ods file structure. The method
|
|
decrypts the stream based on the information provided in ADecryptionInfo and
|
|
using the given (unhashed) user password. The output is stored in the
|
|
destination stream. }
|
|
function TsSpreadOpenDocReaderCrypto.Decrypt(AStream: TStream;
|
|
ADecryptionInfo: TsOpenDocManifestFileEntry; APassword: String;
|
|
ADestStream: TStream; out AErrorMsg: String): Boolean;
|
|
var
|
|
pwdHash: TBytes;
|
|
iv: TBytes;
|
|
tmpStream: TStream;
|
|
algorithm: String;
|
|
begin
|
|
Result := false;
|
|
|
|
algorithm := LowerCase(ADecryptionInfo.AlgorithmName);
|
|
if (algorithm = 'aes128-cbc') or (algorithm='aes192-cbc') or (algorithm='aes256-cbc') then
|
|
algorithm := 'aes'
|
|
else
|
|
algorithm := '';
|
|
|
|
if algorithm = '' then
|
|
exit;
|
|
|
|
tmpStream := TMemoryStream.Create;
|
|
try
|
|
// Calculated password hash
|
|
pwdHash := CalcPasswordHash(ADecryptionInfo, APassword);
|
|
|
|
// Decrypt
|
|
iv := DecodeBase64(ADecryptionInfo.InitializationVector);
|
|
case algorithm of
|
|
'aes':
|
|
AErrorMsg := Decrypt_AES_CBC(pwdHash[0], Length(pwdHash)*8, @iv[0], AStream, tmpStream);
|
|
else
|
|
AErrorMsg := 'Encryption method not supported.';
|
|
end;
|
|
if (AErrorMsg <> '') then
|
|
exit;
|
|
|
|
// Verify decrypted (but still compressed) stream
|
|
// OpenDocument-v1.2-part3, section 3.8.3: "The digest is build from the compressed unencrypted file"
|
|
tmpStream.Position := 0;
|
|
if not VerifyDecrypt(tmpStream, ADecryptionInfo.EncryptionData_CheckSum, ADecryptionInfo.EncryptionData_ChecksumType) then
|
|
AErrorMsg := 'Checksum error';
|
|
if AErrorMsg <> '' then
|
|
exit;
|
|
|
|
// Decompress the decrypted stream
|
|
Decompress(tmpStream, ADestStream);
|
|
ADestStream.Position := 0;
|
|
|
|
// Success!
|
|
Result := true;
|
|
finally
|
|
tmpStream.Free;
|
|
end;
|
|
end;
|
|
|
|
{ Calculates the hash value of the user-provided passwort.
|
|
Hash creation is determined by information stored in ADecryptionInfo. }
|
|
function TsSpreadOpenDocReaderCrypto.CalcPasswordHash(
|
|
ADecryptionInfo: TsOpenDocManifestFileEntry;
|
|
APassword: String): TBytes;
|
|
var
|
|
pwdHash: TBytes;
|
|
salt: TBytes;
|
|
numIterations: Integer;
|
|
keySize: Integer;
|
|
begin
|
|
Result := nil;
|
|
|
|
// Generate start key
|
|
case LowerCase(ADecryptionInfo.StartKeyGenerationName) of
|
|
'sha256': pwdHash := Calc_SHA256(APassword[1], Length(APassword));
|
|
else
|
|
raise EFpSpreadsheetReader.Create('Unsupported start key generator ' + ADecryptionInfo.StartKeyGenerationName);
|
|
end;
|
|
|
|
// Generate derived key
|
|
numIterations := ADecryptionInfo.IterationCount;
|
|
keySize := ADecryptionInfo.KeySize;
|
|
salt := DecodeBase64(ADecryptionInfo.Salt);
|
|
if LowerCase(ADecryptionInfo.KeyDerivationName) = 'pbkdf2' then
|
|
Result := PBKDF2_HMAC_SHA1(pwdHash, salt, numIterations, keySize)
|
|
else
|
|
raise EFpSpreadsheetReader.Create('Unsupported key generation method ' + ADecryptionInfo.KeyDerivationName);
|
|
end;
|
|
|
|
{ Tells the calling routine that this reader is able to decrypt ods files. }
|
|
function TsSpreadOpenDocReaderCrypto.SupportsDecryption: Boolean;
|
|
begin
|
|
Result := true;
|
|
end;
|
|
|
|
|
|
{$IFDEF UNZIP_ABBREVIA}
|
|
{ Extracts the specified file from the compressed stream (AStream) to the
|
|
ADestStream.
|
|
Uses the ABBREVIA library for this purpose (because FCL Stripper fails to
|
|
extract the encrypted file). }
|
|
function TsSpreadOpenDocReaderCrypto.UnzipToStream(AStream: TStream;
|
|
AZippedFile: String; ADestStream: TStream): Boolean;
|
|
var
|
|
unzipper: TABUnzipper;
|
|
begin
|
|
Result := false;
|
|
unzipper := TABUnzipper.Create(nil);
|
|
try
|
|
unzipper.Stream := AStream;
|
|
try
|
|
unzipper.ExtractToStream(AZippedFile, ADestStream);
|
|
ADestStream.Position := 0;
|
|
Result := true;
|
|
except
|
|
raise;
|
|
end;
|
|
finally
|
|
unzipper.Free;
|
|
end;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
|
|
{==============================================================================}
|
|
initialization
|
|
{==============================================================================}
|
|
|
|
{ Registers this reader for fpSpreadsheet }
|
|
|
|
sfidOpenDocument_Crypto := RegisterSpreadFormat(sfUser,
|
|
TsSpreadOpenDocReaderCrypto, nil,
|
|
STR_FILEFORMAT_OPENDOCUMENT, 'ODS', [STR_OPENDOCUMENT_CALC_EXTENSION]
|
|
);
|
|
|
|
end.
|
|
|