Skip to content

Commit 1b786eb

Browse files
authored
Merge pull request #48 from denovosoftware/Fix-GCM-Streams
Fix auth tag issues when using GCM cipher with steam-based methods
2 parents f2a7a2d + 35a5a5f commit 1b786eb

File tree

2 files changed

+170
-3
lines changed

2 files changed

+170
-3
lines changed

Source/DECCipherFormats.pas

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -726,6 +726,7 @@ procedure TDECFormattedCipher.DoEncodeDecodeStream(const Source, Dest: TStream;
726726
const OnProgress: TDECProgressEvent);
727727
var
728728
Buffer: TBytes;
729+
outBuffer: TBytes;
729730
BufferSize, Bytes: Integer;
730731
Max, StartPos, Pos: Int64;
731732
begin
@@ -737,6 +738,7 @@ procedure TDECFormattedCipher.DoEncodeDecodeStream(const Source, Dest: TStream;
737738
StartPos := Pos;
738739

739740
if DataSize > 0 then
741+
begin
740742
try
741743
if Assigned(OnProgress) then
742744
OnProgress(Max, 0, Started);
@@ -752,6 +754,11 @@ procedure TDECFormattedCipher.DoEncodeDecodeStream(const Source, Dest: TStream;
752754
SetLength(Buffer, BufferSize)
753755
else
754756
SetLength(Buffer, DataSize);
757+
758+
outBuffer := Buffer;
759+
if (FMode = cmGCM) then
760+
SetLength(outBuffer, Length(Buffer));
761+
755762
while DataSize > 0 do
756763
begin
757764
Bytes := BufferSize;
@@ -760,8 +767,8 @@ procedure TDECFormattedCipher.DoEncodeDecodeStream(const Source, Dest: TStream;
760767
Source.ReadBuffer(Buffer[0], Bytes);
761768

762769
// The real encryption or decryption routine
763-
CipherProc(Buffer[0], Buffer[0], Bytes);
764-
Dest.WriteBuffer(Buffer[0], Bytes);
770+
CipherProc(Buffer[0], outBuffer[0], Bytes);
771+
Dest.WriteBuffer(outBuffer[0], Bytes);
765772
Dec(DataSize, Bytes);
766773
Inc(Pos, Bytes);
767774

@@ -770,9 +777,18 @@ procedure TDECFormattedCipher.DoEncodeDecodeStream(const Source, Dest: TStream;
770777
end;
771778
finally
772779
ProtectBytes(Buffer);
780+
if (FMode = cmGCM) then
781+
ProtectBytes(outBuffer);
773782
if Assigned(OnProgress) then
774783
OnProgress(Max, Max, Finished);
775784
end;
785+
end
786+
else
787+
if (FMode = cmGCM) then
788+
begin
789+
Buffer := nil;
790+
CipherProc(Buffer, Buffer, 0);
791+
end;
776792
end;
777793

778794
procedure TDECFormattedCipher.EncodeStream(const Source, Dest: TStream; DataSize: Int64;

Unit Tests/Tests/TestDECCipherModesGCM.pas

Lines changed: 152 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,9 @@ TestTDECGCM = class(TTestCase)
223223
published
224224
procedure TestEncode;
225225
procedure TestDecode;
226+
procedure TestDecodeStream;
226227
procedure TestDecodeAuthenticationFailure;
228+
procedure TestEncodeStream;
227229
procedure TestSetGetDataToAuthenticate;
228230
procedure TestSetGetAuthenticationBitLength;
229231
procedure TestGetStandardAuthenticationTagBitLengths;
@@ -472,7 +474,9 @@ procedure TestTDECGCM.TestDecode;
472474
BytesOf(TestDataSet.TestData[i].CT)));
473475
FCipherAES.Done;
474476
except
475-
self.Status('CryptKey ' + string(TestDataSet.TestData[i].CryptKey));
477+
on E: Exception do
478+
Status('CryptKey ' + string(TestDataSet.TestData[i].CryptKey) +
479+
' ' + E.ClassName + ': ' + E.Message);
476480
end;
477481

478482
CheckEquals(string(TestDataSet.TestData[i].PT),
@@ -631,6 +635,153 @@ function TestTDECGCM.IsEqual(const a, b : TBytes):Boolean;
631635
Result := true;
632636
end;
633637

638+
procedure TestTDECGCM.TestDecodeStream;
639+
var
640+
ctbStream: TBytesStream;
641+
ctBytes: TBytes;
642+
TestDataSet : TGCMTestSetEntry;
643+
i : Integer;
644+
DecryptData : TBytes;
645+
ptbStream: TBytesStream;
646+
begin
647+
FTestDataLoader.LoadFile('..\..\Unit Tests\Data\gcmEncryptExtIV128.rsp', FTestDataList);
648+
FTestDataLoader.LoadFile('..\..\Unit Tests\Data\gcmEncryptExtIV192.rsp', FTestDataList);
649+
FTestDataLoader.LoadFile('..\..\Unit Tests\Data\gcmEncryptExtIV256.rsp', FTestDataList);
650+
651+
for TestDataSet in FTestDataList do
652+
begin
653+
for i := Low(TestDataSet.TestData) to High(TestDataSet.TestData) do
654+
begin
655+
ctBytes := TFormat_HexL.Decode(BytesOf(TestDataSet.TestData[i].CT));
656+
657+
try
658+
659+
660+
FCipherAES.Init(BytesOf(TFormat_HexL.Decode(TestDataSet.TestData[i].CryptKey)),
661+
BytesOf(TFormat_HexL.Decode(TestDataSet.TestData[i].InitVector)),
662+
$FF);
663+
664+
FCipherAES.AuthenticationResultBitLength := TestDataSet.Taglen;
665+
FCipherAES.DataToAuthenticate := TFormat_HexL.Decode(
666+
BytesOf(
667+
TestDataSet.TestData[i].AAD));
668+
669+
FCipherAES.ExpectedAuthenticationResult :=
670+
TFormat_HexL.Decode(BytesOf(TestDataSet.TestData[i].TagResult));
671+
672+
ctbStream := TBytesStream.Create(ctBytes);
673+
ptbStream := TBytesStream.Create;
674+
675+
FCipherAES.DecodeStream(ctbStream, ptbStream, ctbStream.Size);
676+
677+
FCipherAES.Done;
678+
679+
DecryptData := ptbStream.Bytes;
680+
SetLength(DecryptData, ptbStream.Size);
681+
682+
except
683+
on E: Exception do
684+
Status('CryptKey ' + string(TestDataSet.TestData[i].CryptKey) +
685+
' ' + E.ClassName + ': ' + E.Message);
686+
end;
687+
FreeAndNil(ptbStream);
688+
FreeAndNil(ctbStream);
689+
690+
CheckEquals(string(TestDataSet.TestData[i].PT),
691+
StringOf(TFormat_HexL.Encode(DecryptData)),
692+
'Plaintext wrong for key ' +
693+
string(TestDataSet.TestData[i].CryptKey) + ' IV ' +
694+
string(TestDataSet.TestData[i].InitVector) + ' PT ' +
695+
string(TestDataSet.TestData[i].PT) + ' AAD ' +
696+
string(TestDataSet.TestData[i].AAD) + ' Exp.: ' +
697+
string(TestDataSet.TestData[i].CT) + ' Act.: ' +
698+
StringOf(TFormat_HexL.Encode(DecryptData)));
699+
700+
// Additional Authentication Data prüfen
701+
CheckEquals(string(TestDataSet.TestData[i].TagResult),
702+
StringOf(TFormat_HexL.Encode(FCipherAES.CalculatedAuthenticationResult)),
703+
'Authentication tag wrong for key ' +
704+
string(TestDataSet.TestData[i].CryptKey) + ' IV ' +
705+
string(TestDataSet.TestData[i].InitVector) + ' PT ' +
706+
string(TestDataSet.TestData[i].PT) + ' AAD ' +
707+
string(TestDataSet.TestData[i].AAD) + ' Exp.: ' +
708+
string(TestDataSet.TestData[i].TagResult) + ' Act.: ' +
709+
StringOf(TFormat_HexL.Encode(FCipherAES.DataToAuthenticate)));
710+
711+
end;
712+
end;
713+
end;
714+
715+
procedure TestTDECGCM.TestEncodeStream;
716+
var
717+
ctbStream: TBytesStream;
718+
ptBytes: TBytes;
719+
TestDataSet : TGCMTestSetEntry;
720+
i : Integer;
721+
EncryptData : TBytes;
722+
ptbStream: TBytesStream;
723+
begin
724+
FTestDataLoader.LoadFile('..\..\Unit Tests\Data\gcmEncryptExtIV128.rsp', FTestDataList);
725+
FTestDataLoader.LoadFile('..\..\Unit Tests\Data\gcmEncryptExtIV192.rsp', FTestDataList);
726+
FTestDataLoader.LoadFile('..\..\Unit Tests\Data\gcmEncryptExtIV256.rsp', FTestDataList);
727+
728+
for TestDataSet in FTestDataList do
729+
begin
730+
for i := Low(TestDataSet.TestData) to High(TestDataSet.TestData) do
731+
begin
732+
ptBytes := TFormat_HexL.Decode(BytesOf(TestDataSet.TestData[i].PT));
733+
734+
FCipherAES.Init(BytesOf(TFormat_HexL.Decode(TestDataSet.TestData[i].CryptKey)),
735+
BytesOf(TFormat_HexL.Decode(TestDataSet.TestData[i].InitVector)),
736+
$FF);
737+
738+
FCipherAES.AuthenticationResultBitLength := TestDataSet.Taglen;
739+
FCipherAES.DataToAuthenticate := TFormat_HexL.Decode(
740+
BytesOf(
741+
TestDataSet.TestData[i].AAD));
742+
743+
ptbStream := TBytesStream.Create(ptBytes);
744+
ctbStream := TBytesStream.Create;
745+
try
746+
FCipherAES.EncodeStream(ptbStream, ctbStream, ptbStream.Size);
747+
748+
FCipherAES.Done;
749+
750+
EncryptData := ctbStream.Bytes;
751+
SetLength(EncryptData, ctbStream.Size);
752+
except
753+
on E: Exception do
754+
Status('CryptKey ' + string(TestDataSet.TestData[i].CryptKey) +
755+
' ' + E.ClassName + ': ' + E.Message);
756+
end;
757+
758+
FreeAndNil(ptbStream);
759+
FreeAndNil(ctbStream);
760+
761+
CheckEquals(string(TestDataSet.TestData[i].CT),
762+
StringOf(TFormat_HexL.Encode(EncryptData)),
763+
'Cipher text wrong for Key ' +
764+
string(TestDataSet.TestData[i].CryptKey) + ' IV ' +
765+
string(TestDataSet.TestData[i].InitVector) + ' PT ' +
766+
string(TestDataSet.TestData[i].PT) + ' AAD ' +
767+
string(TestDataSet.TestData[i].AAD) + ' Exp.: ' +
768+
string(TestDataSet.TestData[i].CT) + ' Act.: ' +
769+
StringOf(TFormat_HexL.Encode(EncryptData)));
770+
771+
// Additional Authentication Data prüfen
772+
CheckEquals(string(TestDataSet.TestData[i].TagResult),
773+
StringOf(TFormat_HexL.Encode(FCipherAES.CalculatedAuthenticationResult)),
774+
'Authentication tag wrong for Key ' +
775+
string(TestDataSet.TestData[i].CryptKey) + ' IV ' +
776+
string(TestDataSet.TestData[i].InitVector) + ' PT ' +
777+
string(TestDataSet.TestData[i].PT) + ' AAD ' +
778+
string(TestDataSet.TestData[i].AAD) + ' Exp.: ' +
779+
string(TestDataSet.TestData[i].TagResult) + ' Act.: ' +
780+
StringOf(TFormat_HexL.Encode(FCipherAES.DataToAuthenticate)));
781+
end;
782+
end;
783+
end;
784+
634785
procedure TestTDECGCM.TestGetStandardAuthenticationTagBitLengths;
635786
var
636787
BitLengths: TStandardBitLengths;

0 commit comments

Comments
 (0)