diff --git a/Source/DECCipherFormats.pas b/Source/DECCipherFormats.pas index b7fdfa87..e4799441 100644 --- a/Source/DECCipherFormats.pas +++ b/Source/DECCipherFormats.pas @@ -726,6 +726,7 @@ procedure TDECFormattedCipher.DoEncodeDecodeStream(const Source, Dest: TStream; const OnProgress: TDECProgressEvent); var Buffer: TBytes; + outBuffer: TBytes; BufferSize, Bytes: Integer; Max, StartPos, Pos: Int64; begin @@ -737,6 +738,7 @@ procedure TDECFormattedCipher.DoEncodeDecodeStream(const Source, Dest: TStream; StartPos := Pos; if DataSize > 0 then + begin try if Assigned(OnProgress) then OnProgress(Max, 0, Started); @@ -752,6 +754,11 @@ procedure TDECFormattedCipher.DoEncodeDecodeStream(const Source, Dest: TStream; SetLength(Buffer, BufferSize) else SetLength(Buffer, DataSize); + + outBuffer := Buffer; + if (FMode = cmGCM) then + SetLength(outBuffer, Length(Buffer)); + while DataSize > 0 do begin Bytes := BufferSize; @@ -760,8 +767,8 @@ procedure TDECFormattedCipher.DoEncodeDecodeStream(const Source, Dest: TStream; Source.ReadBuffer(Buffer[0], Bytes); // The real encryption or decryption routine - CipherProc(Buffer[0], Buffer[0], Bytes); - Dest.WriteBuffer(Buffer[0], Bytes); + CipherProc(Buffer[0], outBuffer[0], Bytes); + Dest.WriteBuffer(outBuffer[0], Bytes); Dec(DataSize, Bytes); Inc(Pos, Bytes); @@ -770,9 +777,18 @@ procedure TDECFormattedCipher.DoEncodeDecodeStream(const Source, Dest: TStream; end; finally ProtectBytes(Buffer); + if (FMode = cmGCM) then + ProtectBytes(outBuffer); if Assigned(OnProgress) then OnProgress(Max, Max, Finished); end; + end + else + if (FMode = cmGCM) then + begin + Buffer := nil; + CipherProc(Buffer, Buffer, 0); + end; end; procedure TDECFormattedCipher.EncodeStream(const Source, Dest: TStream; DataSize: Int64; diff --git a/Unit Tests/Tests/TestDECCipherModesGCM.pas b/Unit Tests/Tests/TestDECCipherModesGCM.pas index e9158fba..5a7ba9e0 100644 --- a/Unit Tests/Tests/TestDECCipherModesGCM.pas +++ b/Unit Tests/Tests/TestDECCipherModesGCM.pas @@ -223,7 +223,9 @@ TestTDECGCM = class(TTestCase) published procedure TestEncode; procedure TestDecode; + procedure TestDecodeStream; procedure TestDecodeAuthenticationFailure; + procedure TestEncodeStream; procedure TestSetGetDataToAuthenticate; procedure TestSetGetAuthenticationBitLength; procedure TestGetStandardAuthenticationTagBitLengths; @@ -472,7 +474,9 @@ procedure TestTDECGCM.TestDecode; BytesOf(TestDataSet.TestData[i].CT))); FCipherAES.Done; except - self.Status('CryptKey ' + string(TestDataSet.TestData[i].CryptKey)); + on E: Exception do + Status('CryptKey ' + string(TestDataSet.TestData[i].CryptKey) + + ' ' + E.ClassName + ': ' + E.Message); end; CheckEquals(string(TestDataSet.TestData[i].PT), @@ -631,6 +635,153 @@ function TestTDECGCM.IsEqual(const a, b : TBytes):Boolean; Result := true; end; +procedure TestTDECGCM.TestDecodeStream; +var + ctbStream: TBytesStream; + ctBytes: TBytes; + TestDataSet : TGCMTestSetEntry; + i : Integer; + DecryptData : TBytes; + ptbStream: TBytesStream; +begin + FTestDataLoader.LoadFile('..\..\Unit Tests\Data\gcmEncryptExtIV128.rsp', FTestDataList); + FTestDataLoader.LoadFile('..\..\Unit Tests\Data\gcmEncryptExtIV192.rsp', FTestDataList); + FTestDataLoader.LoadFile('..\..\Unit Tests\Data\gcmEncryptExtIV256.rsp', FTestDataList); + + for TestDataSet in FTestDataList do + begin + for i := Low(TestDataSet.TestData) to High(TestDataSet.TestData) do + begin + ctBytes := TFormat_HexL.Decode(BytesOf(TestDataSet.TestData[i].CT)); + + try + + + FCipherAES.Init(BytesOf(TFormat_HexL.Decode(TestDataSet.TestData[i].CryptKey)), + BytesOf(TFormat_HexL.Decode(TestDataSet.TestData[i].InitVector)), + $FF); + + FCipherAES.AuthenticationResultBitLength := TestDataSet.Taglen; + FCipherAES.DataToAuthenticate := TFormat_HexL.Decode( + BytesOf( + TestDataSet.TestData[i].AAD)); + + FCipherAES.ExpectedAuthenticationResult := + TFormat_HexL.Decode(BytesOf(TestDataSet.TestData[i].TagResult)); + + ctbStream := TBytesStream.Create(ctBytes); + ptbStream := TBytesStream.Create; + + FCipherAES.DecodeStream(ctbStream, ptbStream, ctbStream.Size); + + FCipherAES.Done; + + DecryptData := ptbStream.Bytes; + SetLength(DecryptData, ptbStream.Size); + + except + on E: Exception do + Status('CryptKey ' + string(TestDataSet.TestData[i].CryptKey) + + ' ' + E.ClassName + ': ' + E.Message); + end; + FreeAndNil(ptbStream); + FreeAndNil(ctbStream); + + CheckEquals(string(TestDataSet.TestData[i].PT), + StringOf(TFormat_HexL.Encode(DecryptData)), + 'Plaintext wrong for key ' + + string(TestDataSet.TestData[i].CryptKey) + ' IV ' + + string(TestDataSet.TestData[i].InitVector) + ' PT ' + + string(TestDataSet.TestData[i].PT) + ' AAD ' + + string(TestDataSet.TestData[i].AAD) + ' Exp.: ' + + string(TestDataSet.TestData[i].CT) + ' Act.: ' + + StringOf(TFormat_HexL.Encode(DecryptData))); + + // Additional Authentication Data prüfen + CheckEquals(string(TestDataSet.TestData[i].TagResult), + StringOf(TFormat_HexL.Encode(FCipherAES.CalculatedAuthenticationResult)), + 'Authentication tag wrong for key ' + + string(TestDataSet.TestData[i].CryptKey) + ' IV ' + + string(TestDataSet.TestData[i].InitVector) + ' PT ' + + string(TestDataSet.TestData[i].PT) + ' AAD ' + + string(TestDataSet.TestData[i].AAD) + ' Exp.: ' + + string(TestDataSet.TestData[i].TagResult) + ' Act.: ' + + StringOf(TFormat_HexL.Encode(FCipherAES.DataToAuthenticate))); + + end; + end; +end; + +procedure TestTDECGCM.TestEncodeStream; +var + ctbStream: TBytesStream; + ptBytes: TBytes; + TestDataSet : TGCMTestSetEntry; + i : Integer; + EncryptData : TBytes; + ptbStream: TBytesStream; +begin + FTestDataLoader.LoadFile('..\..\Unit Tests\Data\gcmEncryptExtIV128.rsp', FTestDataList); + FTestDataLoader.LoadFile('..\..\Unit Tests\Data\gcmEncryptExtIV192.rsp', FTestDataList); + FTestDataLoader.LoadFile('..\..\Unit Tests\Data\gcmEncryptExtIV256.rsp', FTestDataList); + + for TestDataSet in FTestDataList do + begin + for i := Low(TestDataSet.TestData) to High(TestDataSet.TestData) do + begin + ptBytes := TFormat_HexL.Decode(BytesOf(TestDataSet.TestData[i].PT)); + + FCipherAES.Init(BytesOf(TFormat_HexL.Decode(TestDataSet.TestData[i].CryptKey)), + BytesOf(TFormat_HexL.Decode(TestDataSet.TestData[i].InitVector)), + $FF); + + FCipherAES.AuthenticationResultBitLength := TestDataSet.Taglen; + FCipherAES.DataToAuthenticate := TFormat_HexL.Decode( + BytesOf( + TestDataSet.TestData[i].AAD)); + + ptbStream := TBytesStream.Create(ptBytes); + ctbStream := TBytesStream.Create; + try + FCipherAES.EncodeStream(ptbStream, ctbStream, ptbStream.Size); + + FCipherAES.Done; + + EncryptData := ctbStream.Bytes; + SetLength(EncryptData, ctbStream.Size); + except + on E: Exception do + Status('CryptKey ' + string(TestDataSet.TestData[i].CryptKey) + + ' ' + E.ClassName + ': ' + E.Message); + end; + + FreeAndNil(ptbStream); + FreeAndNil(ctbStream); + + CheckEquals(string(TestDataSet.TestData[i].CT), + StringOf(TFormat_HexL.Encode(EncryptData)), + 'Cipher text wrong for Key ' + + string(TestDataSet.TestData[i].CryptKey) + ' IV ' + + string(TestDataSet.TestData[i].InitVector) + ' PT ' + + string(TestDataSet.TestData[i].PT) + ' AAD ' + + string(TestDataSet.TestData[i].AAD) + ' Exp.: ' + + string(TestDataSet.TestData[i].CT) + ' Act.: ' + + StringOf(TFormat_HexL.Encode(EncryptData))); + + // Additional Authentication Data prüfen + CheckEquals(string(TestDataSet.TestData[i].TagResult), + StringOf(TFormat_HexL.Encode(FCipherAES.CalculatedAuthenticationResult)), + 'Authentication tag wrong for Key ' + + string(TestDataSet.TestData[i].CryptKey) + ' IV ' + + string(TestDataSet.TestData[i].InitVector) + ' PT ' + + string(TestDataSet.TestData[i].PT) + ' AAD ' + + string(TestDataSet.TestData[i].AAD) + ' Exp.: ' + + string(TestDataSet.TestData[i].TagResult) + ' Act.: ' + + StringOf(TFormat_HexL.Encode(FCipherAES.DataToAuthenticate))); + end; + end; +end; + procedure TestTDECGCM.TestGetStandardAuthenticationTagBitLengths; var BitLengths: TStandardBitLengths;